diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /python/mozbuild/mozbuild/makeutil.py | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'python/mozbuild/mozbuild/makeutil.py')
-rw-r--r-- | python/mozbuild/mozbuild/makeutil.py | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/python/mozbuild/mozbuild/makeutil.py b/python/mozbuild/mozbuild/makeutil.py new file mode 100644 index 0000000000..76691c5fa1 --- /dev/null +++ b/python/mozbuild/mozbuild/makeutil.py @@ -0,0 +1,209 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import os +import re +from collections.abc import Iterable + +import six + + +class Makefile(object): + """Provides an interface for writing simple makefiles + + Instances of this class are created, populated with rules, then + written. + """ + + def __init__(self): + self._statements = [] + + def create_rule(self, targets=()): + """ + Create a new rule in the makefile for the given targets. + Returns the corresponding Rule instance. + """ + targets = list(targets) + for target in targets: + assert isinstance(target, six.text_type) + rule = Rule(targets) + self._statements.append(rule) + return rule + + def add_statement(self, statement): + """ + Add a raw statement in the makefile. Meant to be used for + simple variable assignments. + """ + assert isinstance(statement, six.text_type) + self._statements.append(statement) + + def dump(self, fh, removal_guard=True): + """ + Dump all the rules to the given file handle. Optionally (and by + default), add guard rules for file removals (empty rules for other + rules' dependencies) + """ + all_deps = set() + all_targets = set() + for statement in self._statements: + if isinstance(statement, Rule): + statement.dump(fh) + all_deps.update(statement.dependencies()) + all_targets.update(statement.targets()) + else: + fh.write("%s\n" % statement) + if removal_guard: + guard = Rule(sorted(all_deps - all_targets)) + guard.dump(fh) + + +class _SimpleOrderedSet(object): + """ + Simple ordered set, specialized for used in Rule below only. + It doesn't expose a complete API, and normalizes path separators + at insertion. + """ + + def __init__(self): + self._list = [] + self._set = set() + + def __nonzero__(self): + return bool(self._set) + + def __bool__(self): + return bool(self._set) + + def __iter__(self): + return iter(self._list) + + def __contains__(self, key): + return key in self._set + + def update(self, iterable): + def _add(iterable): + emitted = set() + for i in iterable: + i = i.replace(os.sep, "/") + if i not in self._set and i not in emitted: + yield i + emitted.add(i) + + added = list(_add(iterable)) + self._set.update(added) + self._list.extend(added) + + +class Rule(object): + """Class handling simple rules in the form: + target1 target2 ... : dep1 dep2 ... + command1 command2 ... + """ + + def __init__(self, targets=()): + self._targets = _SimpleOrderedSet() + self._dependencies = _SimpleOrderedSet() + self._commands = [] + self.add_targets(targets) + + def add_targets(self, targets): + """Add additional targets to the rule.""" + assert isinstance(targets, Iterable) and not isinstance( + targets, six.string_types + ) + targets = list(targets) + for target in targets: + assert isinstance(target, six.text_type) + self._targets.update(targets) + return self + + def add_dependencies(self, deps): + """Add dependencies to the rule.""" + assert isinstance(deps, Iterable) and not isinstance(deps, six.string_types) + deps = list(deps) + for dep in deps: + assert isinstance(dep, six.text_type) + self._dependencies.update(deps) + return self + + def add_commands(self, commands): + """Add commands to the rule.""" + assert isinstance(commands, Iterable) and not isinstance( + commands, six.string_types + ) + commands = list(commands) + for command in commands: + assert isinstance(command, six.text_type) + self._commands.extend(commands) + return self + + def targets(self): + """Return an iterator on the rule targets.""" + # Ensure the returned iterator is actually just that, an iterator. + # Avoids caller fiddling with the set itself. + return iter(self._targets) + + def dependencies(self): + """Return an iterator on the rule dependencies.""" + return iter(d for d in self._dependencies if d not in self._targets) + + def commands(self): + """Return an iterator on the rule commands.""" + return iter(self._commands) + + def dump(self, fh): + """ + Dump the rule to the given file handle. + """ + if not self._targets: + return + fh.write("%s:" % " ".join(self._targets)) + if self._dependencies: + fh.write(" %s" % " ".join(self.dependencies())) + fh.write("\n") + for cmd in self._commands: + fh.write("\t%s\n" % cmd) + + +# colon followed by anything except a slash (Windows path detection) +_depfilesplitter = re.compile(r":(?![\\/])") + + +def read_dep_makefile(fh): + """ + Read the file handler containing a dep makefile (simple makefile only + containing dependencies) and returns an iterator of the corresponding Rules + it contains. Ignores removal guard rules. + """ + + rule = "" + for line in fh.readlines(): + line = six.ensure_text(line) + assert not line.startswith("\t") + line = line.strip() + if line.endswith("\\"): + rule += line[:-1] + else: + rule += line + split_rule = _depfilesplitter.split(rule, 1) + if len(split_rule) > 1 and split_rule[1].strip(): + yield Rule(split_rule[0].strip().split()).add_dependencies( + split_rule[1].strip().split() + ) + rule = "" + + if rule: + raise Exception("Makefile finishes with a backslash. Expected more input.") + + +def write_dep_makefile(fh, target, deps): + """ + Write a Makefile containing only target's dependencies to the file handle + specified. + """ + mk = Makefile() + rule = mk.create_rule(targets=[target]) + rule.add_dependencies(deps) + mk.dump(fh, removal_guard=True) |