summaryrefslogtreecommitdiffstats
path: root/mesonbuild/depfile.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--mesonbuild/depfile.py91
1 files changed, 91 insertions, 0 deletions
diff --git a/mesonbuild/depfile.py b/mesonbuild/depfile.py
new file mode 100644
index 0000000..d346136
--- /dev/null
+++ b/mesonbuild/depfile.py
@@ -0,0 +1,91 @@
+# Copyright 2019 Red Hat, Inc.
+# 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.
+from __future__ import annotations
+
+import typing as T
+
+
+def parse(lines: T.Iterable[str]) -> T.List[T.Tuple[T.List[str], T.List[str]]]:
+ rules: T.List[T.Tuple[T.List[str], T.List[str]]] = []
+ targets: T.List[str] = []
+ deps: T.List[str] = []
+ in_deps = False
+ out = ''
+ for line in lines:
+ if not line.endswith('\n'):
+ line += '\n'
+ escape = None
+ for c in line:
+ if escape:
+ if escape == '$' and c != '$':
+ out += '$'
+ if escape == '\\' and c == '\n':
+ continue
+ out += c
+ escape = None
+ continue
+ if c in {'\\', '$'}:
+ escape = c
+ continue
+ elif c in {' ', '\n'}:
+ if out != '':
+ if in_deps:
+ deps.append(out)
+ else:
+ targets.append(out)
+ out = ''
+ if c == '\n':
+ rules.append((targets, deps))
+ targets = []
+ deps = []
+ in_deps = False
+ continue
+ elif c == ':':
+ targets.append(out)
+ out = ''
+ in_deps = True
+ continue
+ out += c
+ return rules
+
+class Target(T.NamedTuple):
+
+ deps: T.Set[str]
+
+
+class DepFile:
+ def __init__(self, lines: T.Iterable[str]):
+ rules = parse(lines)
+ depfile: T.Dict[str, Target] = {}
+ for (targets, deps) in rules:
+ for target in targets:
+ t = depfile.setdefault(target, Target(deps=set()))
+ for dep in deps:
+ t.deps.add(dep)
+ self.depfile = depfile
+
+ def get_all_dependencies(self, name: str, visited: T.Optional[T.Set[str]] = None) -> T.List[str]:
+ deps: T.Set[str] = set()
+ if not visited:
+ visited = set()
+ if name in visited:
+ return []
+ visited.add(name)
+
+ target = self.depfile.get(name)
+ if not target:
+ return []
+ deps.update(target.deps)
+ for dep in target.deps:
+ deps.update(self.get_all_dependencies(dep, visited))
+ return sorted(deps)