blob: 9aa3aa5d9433bb20d97f15bd3caad31b65c41b41 [file] [log] [blame]
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright 2019 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Extract eclass variable names into Haskell list format."""
from __future__ import print_function
import datetime
import os
import re
import sys
import textwrap
# Matches a line that declares a variable in an eclass.
VAR_RE = re.compile(r'@(?:ECLASS-)?VARIABLE:\s*(\w+)$')
# Matches a line that declares inheritance.
INHERIT_RE = re.compile(r'^[^#]*\binherit((?:\s+[\w-]+)+)$')
VAR_FILE_HEADER = """module ShellCheck.PortageAutoInternalVariables (
portageAutoInternalVariables
) where
-- This file contains the variables generated by
-- third_party/chromiumos-overlay/dev-util/shellcheck/files/get_vars.py"""
PORTAGE_AUTO_VAR_NAME = 'portageAutoInternalVariables'
class Eclass:
"""Container for eclass information"""
def __init__(self, name, eclass_vars, inheritances):
self.name = name
self.vars = eclass_vars
self.inheritances = inheritances
def calculate_eclass_vars(self, eclasses):
while self.inheritances:
name = self.inheritances.pop()
try:
sub_eclass = eclasses[name]
new_vars = sub_eclass.calculate_eclass_vars(eclasses).vars
self.vars = self.vars.union(new_vars)
except Exception:
pass
return self
def print_var_list(eclass, eclass_vars):
var_list = ' '.join(['"%s",' % v for v in sorted(eclass_vars)])
print(' -- %s\n%s' %
(eclass,
textwrap.fill(
var_list, 80, initial_indent=' ', subsequent_indent=' ')))
def process_file(eclass_path):
eclass_name = os.path.splitext(os.path.basename(eclass_path))[0]
with open(eclass_path, 'r') as f:
eclass_vars = set()
eclass_inheritances = set()
for line in f:
line = line.strip()
if not line:
continue
while line[-1] == '\\':
line = line[:-1] + next(f).strip()
match = VAR_RE.search(line)
if match:
var_name = match.group(1)
eclass_vars.add(var_name.strip())
else:
match = INHERIT_RE.search(line)
if match:
for inheritance in re.split(r'\s+', match.group(1)):
if inheritance.strip():
eclass_inheritances.add(inheritance.strip())
return Eclass(eclass_name, eclass_vars, eclass_inheritances)
def format_eclasses_as_haskell_map(eclasses):
map_entries = []
join_string = '", "'
for value in sorted(eclasses, key=(lambda x: x.name)):
if value.vars:
var_list_string = f'"{join_string.join(sorted(list(value.vars)))}"'
map_entries.append(
textwrap.fill(
f'("{value.name}", [{var_list_string}])',
80,
initial_indent=' ',
subsequent_indent=' '))
return_string = ',\n\n'.join(map_entries)
return_string = f""" Data.Map.fromList
[
{return_string}
]"""
return f"""{VAR_FILE_HEADER}\n\n
-- Last Generated: {datetime.datetime.now().strftime("%x")}
import qualified Data.Map
{PORTAGE_AUTO_VAR_NAME} =
{return_string}"""
def main(argv):
eclasses = {}
for path in sorted(argv, key=os.path.basename):
if not path.endswith('.eclass'):
continue
new_eclass = process_file(path)
eclasses[new_eclass.name] = new_eclass
eclasses_list = [
value.calculate_eclass_vars(eclasses) for key, value in eclasses.items()
]
print(format_eclasses_as_haskell_map(eclasses_list))
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))