diff --git a/build/ab.py b/build/ab.py index 75f84c98d..81e3c9fff 100644 --- a/build/ab.py +++ b/build/ab.py @@ -388,6 +388,10 @@ def _filetarget(value, cwd): return t +def getcwd(): + return cwdStack[-1] + + def targetof(value, cwd=None): if not cwd: cwd = cwdStack[-1] @@ -544,12 +548,14 @@ def shell(*args): def add_commanddb_entry(commands, file): global commandsDb - commandsDb += [{ - "directory": os.getcwd(), - "command": (" && ".join(commands)), - "file": file - } - ] + commandsDb += [ + { + "directory": os.getcwd(), + "command": (" && ".join(commands)), + "file": file, + } + ] + def emit_rule(self, ins, outs, cmds=[], label=None): name = self.name diff --git a/build/utils.py b/build/utils.py index e0af59110..7c786a066 100644 --- a/build/utils.py +++ b/build/utils.py @@ -4,16 +4,19 @@ Targets, filenameof, filenamesof, - cwdStack, + getcwd, error, simplerule, - G + G, ) -from os.path import relpath, splitext, join, basename, isfile +from os.path import relpath, splitext, join, basename, isfile, normpath +from os import walk from glob import iglob import fnmatch import subprocess import shutil +import re +import functools def filenamesmatchingof(xs, pattern): @@ -35,9 +38,64 @@ def collectattrs(*, targets, name, initial=[]): return sorted(s) +@functools.cache +def _glob_to_re(glob_str): + opts = re.compile("([.]|[*][*]/|[*]|[?])|(.)") + out = "" + for pattern_match, literal_text in opts.findall(glob_str): + if pattern_match == ".": + out += "[.]" + elif pattern_match == "**/": + out += "(?:.*/)?" + elif pattern_match == "*": + out += "[^/]*" + elif pattern_match == "?": + out += "." + elif literal_text: + out += literal_text + return re.compile(out) + + +def _glob_filter(paths, pattern): + r = _glob_to_re(pattern) + for f in paths: + if r.match(f): + yield f + + +def _glob_matches(path, pattern): + r = _glob_to_re(pattern) + return r.match(path) + + +def glob(include=["*"], exclude=[], dir=None, relative_to="."): + if not dir: + dir = getcwd() + if dir.startswith("./"): + dir = normpath(join(getcwd(), dir)) + if relative_to.startswith("./"): + relative_to = normpath(join(getcwd(), relative_to)) + + def iterate(): + for dirpath, dirnames, filenames in walk( + dir, topdown=True, followlinks=True + ): + dirpath = relpath(dirpath, dir) + filenames = [normpath(join(dirpath, f)) for f in filenames] + matching = set() + for p in include: + matching.update(_glob_filter(filenames, p)) + for p in exclude: + matching = [n for n in matching if not _glob_matches(n, p)] + for f in matching: + yield normpath(relpath(join(dir, f), relative_to)) + + return list(iterate()) + + def itemsof(pattern, root=None, cwd=None): if not cwd: - cwd = cwdStack[-1] + cwd = getcwd() if not root: root = "."