Refactor modify_pc.py to be modulized 32/319932/1
authorSangYoun Kwak <sy.kwak@samsung.com>
Mon, 4 Nov 2024 10:44:09 +0000 (19:44 +0900)
committerSangYoun Kwak <sy.kwak@samsung.com>
Tue, 5 Nov 2024 08:46:58 +0000 (17:46 +0900)
A class PkgConfig that manages the contents of .pc file is used to
increase readability and maintainability.

Also, parsing part and modification parts are separated to make them
more maintainable. As parsing part is separated, some datas are moved to
the .pc.in files to make the module more data-independent.

Change-Id: I2855a862d8dfb7d4dc2af8af1d22addc02785f5c
Signed-off-by: SangYoun Kwak <sy.kwak@samsung.com>
modify_pc.py
packaging/hal-rootstrap-headed.pc.in
packaging/hal-rootstrap.pc.in

index b6594563378b67b20b26c594a273c202ad1e8a7c..c1ea980046d320fe706df4e6e8fffbd344ab4cac 100755 (executable)
 
 from sys import argv
 from os import listdir
-from re import compile as regex_compile
-
-include_dir_root_path = "hal_rootstrap_dir"
-
-regex_comment = regex_compile("([^#]*)(#.*)?")
-regex_assignment = regex_compile("\\s*([-_a-zA-Z0-9]+)\\s*=\\s*([^\\s]*)")
-regex_declaration = regex_compile("\\s*([-_a-zA-Z0-9]+)\\s*:\\s*(.+)\\s*")
-regex_substitution = regex_compile("\\${([-_a-zA-Z0-9]+)}")
-
-libs = { "-L${" + include_dir_root_path + "}" }
-cflags = { "-I${" + include_dir_root_path + "}" }
-cxxflags = { "-I${" + include_dir_root_path + "}" }
-
-def remove_comment(line):
-       matches = regex_comment.findall(line)
-       line, _ = matches[0][0], matches[0][1]
-       return line
-
-def parse_pc(fname):
-       variables = dict()
-
-       f = open(fname, "r")
-       for line in f:
-               line = remove_comment(line)
-               if matches := regex_assignment.findall(line):
-                       symbol, value = matches[0][0], matches[0][1]
-                       matched_symbols = regex_substitution.findall(value)
-                       for matched_symbol in matched_symbols:
-                               value = value.replace("${{{}}}".format(matched_symbol), "{}".format(variables[matched_symbol]))
-                       variables[symbol] = value
-                       continue
-               if matches := regex_declaration.findall(line):
-                       symbol, value = matches[0][0], matches[0][1]
-                       matched_symbols = regex_substitution.findall(value)
-                       for matched_symbol in matched_symbols:
-                               value = value.replace("${{{}}}".format(matched_symbol), "{}".format(variables[matched_symbol]))
-                       if symbol == "Libs":
-                               libs.update(set(map(lambda value: value[:2] + "${" + include_dir_root_path + "}" + value[2:] if value[2] == '/' else value, value.split())))
-                               continue
-                       if symbol == "Cflags":
-                               cflags.update((map(lambda value: value[:2] + "${" + include_dir_root_path + "}" + value[2:] if value[2] == '/' else value, value.split())))
-                               continue
-                       if symbol == "CXXflags":
-                               cxxflags.update(list(map(lambda value: value[:2] + "${" + include_dir_root_path + "}" + value[2:] if value[2] == '/' else value, value.split())))
-                               continue
-
-       f.close()
-
-def write_to_pc(fname):
-       f = open(fname, "a")
-       f.write("\n")
-       f.write("Libs: {}\n".format(' '.join(libs)))
-       f.write("Cflags: {}\n".format(' '.join(cflags)))
-       f.write("CXXflags: {}\n".format(' '.join(cxxflags)))
-       f.close()
-
-rootstrap_pc_path = argv[1]
-pc_dir = argv[2]
-pc_files = listdir(pc_dir)
-
-for pc_file in pc_files:
-       parse_pc("{}/{}".format(pc_dir, pc_file))
-
-write_to_pc(rootstrap_pc_path)
+from re import compile as compile_regex
+
+class FileLineReadGenerator:
+    def __init__(self, fname):
+        self.fname = fname
+    def readline(self):
+        with open(self.fname, "r") as f:
+            for line in f:
+                yield line
+        return None
+
+class RecursiveReferenceException(Exception):
+    pass
+
+class UnknownVariableException(Exception):
+    pass
+
+class PkgConfig:
+    regex_comment = compile_regex("([^#]*)(#.*)?")
+    regex_assignment = compile_regex("\\s*([-_a-zA-Z0-9]+)\\s*=\\s*([^\\s]*)")
+    regex_declaration = compile_regex("\\s*([-_a-zA-Z0-9]+)\\s*:\\s*(.*)\\s*")
+    regex_substitution = compile_regex("\\${([-_a-zA-Z0-9]+)}")
+    regex_root_substitution = compile_regex("^-([a-zA-Z])/(.*)$")
+
+    def __init__(self):
+        self.variables = dict()
+        self.declarations = dict()
+
+    def remove_comment(self, line):
+        matches = self.regex_comment.findall(line)
+        line, _ = matches[0][0], matches[0][1]
+        return line
+
+    def substitute_value(self, sub_value, symbol, value):
+        return sub_value.replace(f"${{{symbol}}}", str(value))
+
+    def substitute_symbols(self, target_dict):
+        for symbol, value in self.variables.items():
+            for sub_symbol, sub_value in target_dict.items():
+                target_dict[sub_symbol] = self.substitute_value(sub_value, symbol, value)
+
+    def substitute_variables(self):
+        self.substitute_symbols(self.variables)
+
+        for symbol, value in self.variables.items():
+            if matched := self.regex_substitution.findall(value):
+                if any(map(lambda symbol: symbol in self.variables, matched)):
+                    raise RecursiveReferenceException(f"Variable is referenced recursively: {symbol}")
+                raise UnknownVariableException(f"Unknown variable(s): {matched}")
+
+    def substitute_declarations(self):
+        self.substitute_symbols(self.declarations)
+
+        for symbol, value in self.declarations.items():
+            if matched := self.regex_substitution.findall(value):
+                raise UnknownVariableException(f"Unknown variable(s): {matched}")
+
+    def substitute_declaration_root(self, value, root):
+        substituted_values = list()
+        value_entities = value.split()
+
+        for value_entity in value_entities:
+            if matched := self.regex_root_substitution.findall(value_entity):
+                substituted_values.append(f"-{matched[0][0]}${{{root}}}/{matched[0][1]}")
+                continue
+            substituted_values.append(value_entity)
+
+        return ' '.join(substituted_values)
+
+    def substitute_declarations_root(self, root):
+        target_declaration_symbols = ( "Libs", "Cflags", "CXXflags" )
+        for target_declaration_symbol in target_declaration_symbols:
+            if target_declaration_symbol not in self.declarations:
+                continue
+            self.declarations[target_declaration_symbol] = self.substitute_declaration_root(self.declarations[target_declaration_symbol], root)
+
+    def parse(self, fname):
+        linegen = FileLineReadGenerator(fname)
+        for line in linegen.readline():
+            line = self.remove_comment(line.strip())
+            if not line:
+                continue
+            if matches := self.regex_assignment.findall(line):
+                symbol, value = matches[0][0], matches[0][1]
+                self.variables[symbol] = value
+                continue
+            if matches := self.regex_declaration.findall(line):
+                symbol, value = matches[0][0], matches[0][1]
+                self.declarations[symbol] = value
+                continue
+
+    def merge_declarations(self, other_pkgconfig):
+        symbols_to_merge = ( "Libs", "Cflags", "CXXflags" )
+        for symbol in symbols_to_merge:
+            if symbol not in other_pkgconfig.declarations:
+                continue
+            value = other_pkgconfig.declarations[symbol]
+            if symbol not in self.declarations:
+                self.declarations[symbol] = value
+                continue
+            new_values = set(self.declarations[symbol].split())
+            new_values.update(value.split())
+            self.declarations[symbol] = ' '.join(new_values)
+
+    def export_file(self, fname):
+        with open(fname, "w") as f:
+            for symbol, value in self.variables.items():
+                f.write("{}={}\n".format(symbol, value))
+
+            f.write("\n")
+
+            for symbol, value in self.declarations.items():
+                f.write("{}: {}\n".format(symbol, value))
+
+def main():
+    rootstrap_pc_path = argv[1]
+    pc_dir = argv[2]
+    pc_files = listdir(pc_dir)
+
+    hal_pkgconfig = PkgConfig()
+    hal_pkgconfig.parse(rootstrap_pc_path)
+
+    include_dir_root_path = hal_pkgconfig.variables["hal_rootstrap_dir_symbol"]
+
+    for pc_file in pc_files:
+        pc_file_path = "{}/{}".format(pc_dir, pc_file)
+        pkgconfig = PkgConfig()
+        pkgconfig.parse(pc_file_path)
+        pkgconfig.substitute_variables()
+        pkgconfig.substitute_declarations()
+        hal_pkgconfig.merge_declarations(pkgconfig)
+
+    hal_pkgconfig.substitute_declarations_root(include_dir_root_path)
+
+    hal_pkgconfig.export_file(rootstrap_pc_path)
+
+if __name__ == "__main__":
+    main()
index eb84c3c767d6d32f88949f0c3419b67ad4a9f702..6dfcc0607b053665e69decc3957469cd6953531f 100644 (file)
@@ -3,6 +3,7 @@
 package_name=hal-rootstrap
 prefix=/opt/data/hal-rootstrap@PREFIX@
 exec_prefix=/opt/data/hal-rootstrap@EXEC_PREFIX@
+hal_rootstrap_dir_symbol=hal_rootstrap_dir
 hal_rootstrap_dir=/opt/data/hal-rootstrap/headed
 hal_rootstrap_libdir=${hal_rootstrap_dir}@LIBDIR@
 hal_rootstrap_include_dir=${hal_rootstrap_dir}@INCLUDEDIR@
@@ -12,3 +13,7 @@ Description: ${package_name} interface
 Version: @VERSION@
 
 Requires:
+
+Libs: -L${hal_rootstrap_dir}
+Cflags: -I${hal_rootstrap_dir}
+CXXflags: -I${hal_rootstrap_dir}
index 5ce9317cea9794f590cebd86ffccf1c960b2f878..b6f06a18d56e36868175afb35321792b38217d2a 100644 (file)
@@ -3,6 +3,7 @@
 package_name=hal-rootstrap
 prefix=/opt/data/hal-rootstrap@PREFIX@
 exec_prefix=/opt/data/hal-rootstrap@EXEC_PREFIX@
+hal_rootstrap_dir_symbol=hal_rootstrap_dir
 hal_rootstrap_dir=/opt/data/hal-rootstrap/common
 hal_rootstrap_libdir=${hal_rootstrap_dir}@LIBDIR@
 hal_rootstrap_include_dir=${hal_rootstrap_dir}@INCLUDEDIR@
@@ -12,3 +13,7 @@ Description: ${package_name} interface
 Version: @VERSION@
 
 Requires:
+
+Libs: -L${hal_rootstrap_dir}
+Cflags: -I${hal_rootstrap_dir}
+CXXflags: -I${hal_rootstrap_dir}