aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/scripts/depscan.py
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild/scripts/depscan.py')
-rw-r--r--mesonbuild/scripts/depscan.py87
1 files changed, 87 insertions, 0 deletions
diff --git a/mesonbuild/scripts/depscan.py b/mesonbuild/scripts/depscan.py
new file mode 100644
index 0000000..161faf8
--- /dev/null
+++ b/mesonbuild/scripts/depscan.py
@@ -0,0 +1,87 @@
+# Copyright 2020 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pathlib
+import pickle
+import re
+import typing as T
+
+import_re = re.compile('\w*import ([a-zA-Z0-9]+);')
+export_re = re.compile('\w*export module ([a-zA-Z0-9]+);')
+
+class DependencyScanner:
+ def __init__(self, pickle_file, outfile, sources):
+ with open(pickle_file, 'rb') as pf:
+ self.target_data = pickle.load(pf)
+ self.outfile = outfile
+ self.sources = sources
+ self.provided_by = {}
+ self.exports = {}
+ self.needs = {}
+ self.sources_with_exports = []
+
+ def scan_file(self, fname):
+ for line in pathlib.Path(fname).read_text().split('\n'):
+ import_match = import_re.match(line)
+ export_match = export_re.match(line)
+ if import_match:
+ needed = import_match.group(1)
+ if fname in self.needs:
+ self.needs[fname].append(needed)
+ else:
+ self.needs[fname] = [needed]
+ if export_match:
+ exported_module = export_match.group(1)
+ if exported_module in self.provided_by:
+ raise RuntimeError('Multiple files provide module {}.'.format(exported_module))
+ self.sources_with_exports.append(fname)
+ self.provided_by[exported_module] = fname
+ self.exports[fname] = exported_module
+
+ def objname_for(self, src):
+ return self.target_data.source2object[src]
+
+ def ifcname_for(self, src):
+ return '{}.ifc'.format(self.exports[src])
+
+ def scan(self):
+ for s in self.sources:
+ self.scan_file(s)
+ with open(self.outfile, 'w') as ofile:
+ ofile.write('ninja_dyndep_version = 1\n')
+ for src in self.sources:
+ objfilename = self.objname_for(src)
+ if src in self.sources_with_exports:
+ ifc_entry = '| ' + self.ifcname_for(src)
+ else:
+ ifc_entry = ''
+ if src in self.needs:
+ # FIXME, handle all sources, not just the first one
+ modname = self.needs[src][0]
+ provider_src = self.provided_by[modname]
+ provider_ifc = self.ifcname_for(provider_src)
+ mod_dep = '| ' + provider_ifc
+ else:
+ mod_dep = ''
+ ofile.write('build {} {}: dyndep {}\n'.format(objfilename,
+ ifc_entry,
+ mod_dep))
+ return 0
+
+def run(args: T.List[str]) -> int:
+ pickle_file = args[0]
+ outfile = args[1]
+ sources = args[2:]
+ scanner = DependencyScanner(pickle_file, outfile, sources)
+ return scanner.scan()