diff --git a/docs/README.md b/docs/README.md index 3b70e85..a91cd2c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -43,7 +43,7 @@ main_app = link(files_in = main_o, files_out = "app") - The directory to search for input files - ```deps_dir``` (Default: ```{leaf_dir}```) - The directory to search for extra dependencies -- ```out_dir``` (Default: ```{start_dir / build_dir / build_tag / relpath(leaf_dir, start_dir)}```) +- ```out_dir``` (Default: ```{start_dir / build_dir / build_tag / rel_path(leaf_dir, start_dir)}```) - The full path to the build directory for a single task ### Building Hancho files in submodules diff --git a/hancho.py b/hancho.py index d388bc3..2d7024c 100755 --- a/hancho.py +++ b/hancho.py @@ -63,31 +63,31 @@ def log(message, *args, sameline=False, **kwargs): sys.stdout.flush() -def abspath(path): +def abs_path(path): """Pathlib's path.absolute() doesn't resolve "foo/../bar", so we use os.path.abspath.""" if isinstance(path, list): - return [abspath(p) for p in path] + return [abs_path(p) for p in path] return Path(os.path.abspath(path)) -def relpath(path1, path2): +def rel_path(path1, path2): """We don't want to generate paths with '..' in them, so we just try and remove the prefix. If we can't remove the prefix we'll still have an absolute path.""" if isinstance(path1, list): - return [relpath(p, path2) for p in path1] + return [rel_path(p, path2) for p in path1] if str(path1) == str(path2): return Path("") return Path(str(path1).removeprefix(str(path2) + "/")) -def joinpath(*args): +def join_path(*args): """Returns an array of all possible concatenated paths from the given paths (or arrays of paths).""" if len(args) > 2: - return joinpath(args[0], joinpath(*args[1:])) + return join_path(args[0], join_path(*args[1:])) if isinstance(args[0], list): - return [path for prefix in args[0] for path in joinpath(prefix, args[1])] + return [path for prefix in args[0] for path in join_path(prefix, args[1])] if isinstance(args[1], list): - return [path for suffix in args[1] for path in joinpath(args[0], suffix)] + return [path for suffix in args[1] for path in join_path(args[0], suffix)] return [Path(args[0]) / Path(args[1])] @@ -240,7 +240,7 @@ class Encoder(json.JSONEncoder): def default(self, o): if isinstance(o, Task): - return f"task {o.config.expanded['desc']}" + return f"task {o.config.expanded.desc}" return str(o) base = self.__dict__["_base"] @@ -309,6 +309,9 @@ def load(self, hancho_file, **kwargs): def include(self, hancho_file, **kwargs): return app.load_module(hancho_file, self, include=True, kwargs=kwargs) + def collapse(self): + return type(self)(**self.to_dict()) + class Rule(Config): @@ -600,7 +603,7 @@ async def run_commands(self): result = [] for exp_command in self.exp_command: if self.config.verbose or self.config.debug: - rel_command_path = relpath(self.exp_command_path, self.config.start_path) + rel_command_path = rel_path(self.exp_command_path, self.config.start_path) log(f"{color(128,128,255)}{rel_command_path}$ {color()}", end="") log("(DRY RUN) " if self.config.dry_run else "", end="") log(exp_command) @@ -727,9 +730,9 @@ def __init__(self): build_deps=[], # Helper functions - abspath=abspath, - relpath=relpath, - joinpath=joinpath, + abs_path=abs_path, + rel_path=rel_path, + join_path=join_path, color=color, glob=glob, len=len, @@ -738,19 +741,19 @@ def __init__(self): swap_ext=swap_ext, # Helper macros - rel_command_path = "{relpath(command_path, command_path)}", - rel_source_path = "{relpath(source_path, command_path)}", - rel_build_path = "{relpath(build_path, command_path)}", + rel_command_path = "{rel_path(command_path, command_path)}", + rel_source_path = "{rel_path(source_path, command_path)}", + rel_build_path = "{rel_path(build_path, command_path)}", - abs_command_files = "{joinpath(command_path, command_files)}", - abs_source_files = "{joinpath(source_path, source_files)}", - abs_build_files = "{joinpath(build_path, build_files)}", - abs_build_deps = "{joinpath(build_path, build_deps)}", + abs_command_files = "{join_path(command_path, command_files)}", + abs_source_files = "{join_path(source_path, source_files)}", + abs_build_files = "{join_path(build_path, build_files)}", + abs_build_deps = "{join_path(build_path, build_deps)}", - rel_command_files = "{relpath(abs_command_files, command_path)}", - rel_source_files = "{relpath(abs_source_files, command_path)}", - rel_build_files = "{relpath(abs_build_files, command_path)}", - rel_build_deps = "{relpath(abs_build_deps, command_path)}", + rel_command_files = "{rel_path(abs_command_files, command_path)}", + rel_source_files = "{rel_path(abs_source_files, command_path)}", + rel_build_files = "{rel_path(abs_build_files, command_path)}", + rel_build_deps = "{rel_path(abs_build_deps, command_path)}", # Global config has no base. base=None, @@ -853,34 +856,42 @@ async def async_run_tasks(self): def load_module(self, mod_filename, build_config=None, include=False, kwargs={}): """Loads a Hancho module ***while chdir'd into its directory***""" - # Create the module's initial build_config - new_build_config = build_config.clone() if build_config is not None else Config() - new_build_config.update(kwargs) + # Create the module's initial config object + new_initial_config = build_config.collapse() if build_config is not None else Config() + new_initial_config.update(kwargs) # Use the new config to expand the mod filename - mod_filename = new_build_config.expand(mod_filename) - mod_path = abspath(mod_filename) + mod_filename = new_initial_config.expand(mod_filename) + mod_path = abs_path(mod_filename) + phys_path = Path(mod_path).resolve() if not mod_path.exists(): raise FileNotFoundError(f"Could not load module {mod_path}") - new_build_config.phys_path = Path(mod_path).resolve() - new_build_config.this_path = mod_path.parent if not include: # If this module was loaded via load() and not include(), it gets its own source_path. - new_build_config.source_path = mod_path.parent + new_initial_config.source_path = mod_path.parent # Look through our loaded modules and see if there's already a compatible one loaded. - new_build_dict = new_build_config.to_dict() + new_initial_dict = new_initial_config.to_dict() reuse = None for mod in self.loaded_modules: - old_build_dict = mod.build_config.to_dict() - if old_build_dict | new_build_dict == old_build_dict: + if mod.phys_path != phys_path: + continue + + old_initial_dict = mod.initial_config.to_dict() + if old_initial_dict | new_initial_dict == old_initial_dict: if reuse is not None: raise RuntimeError(f"Module load for {mod_filename} is ambiguous") + print(f"old_build_dict {old_initial_dict}") + print(f"new_build_dict {new_initial_dict}") reuse = mod if reuse: + print(f"Reusing module {reuse.__file__}") return reuse + if (global_config.debug): + log(f"Loading module {mod_path} using config {new_initial_config}") + # There was no compatible module loaded, so make a new one. with open(mod_path, encoding="utf-8") as file: source = file.read() @@ -889,7 +900,11 @@ def load_module(self, mod_filename, build_config=None, include=False, kwargs={}) module = type(sys)(mod_path.stem) module.__file__ = mod_path module.__builtins__ = builtins - module.build_config = new_build_config + module.self = module + module.phys_path = phys_path + module.this_path = mod_path.parent + module.initial_config = new_initial_config + module.build_config = module.initial_config.extend() self.loaded_modules.append(module) # We must chdir()s into the .hancho file directory before running it so that diff --git a/tests/build_path_works.hancho b/tests/build_path_works.hancho index 525820a..2900ee0 100644 --- a/tests/build_path_works.hancho +++ b/tests/build_path_works.hancho @@ -1,4 +1,4 @@ -build_config.build_path = "{this_path/'build/build_path_works'}" +build_config.build_path = this_path / "build/build_path_works" rules = build_config.include("rules.hancho") rules.touch_outputs([], "result.txt") diff --git a/tutorial/tut12.hancho b/tutorial/tut12.hancho index 3b38bb6..d68979f 100644 --- a/tutorial/tut12.hancho +++ b/tutorial/tut12.hancho @@ -13,8 +13,8 @@ config = Config( compile = config.extend( desc = "Compile {source_files}", command = "g++ -MMD -c {source_files} -o {build_files}", - build_files = "{joinpath(build_dir, swap_ext(source_files, '.o'))}", - build_deps = "{joinpath(build_dir, swap_ext(source_files, '.d'))}", + build_files = "{join_path(build_dir, swap_ext(source_files, '.o'))}", + build_deps = "{join_path(build_dir, swap_ext(source_files, '.d'))}", ) main_o = compile.task( diff --git a/tutorial/tut13.hancho b/tutorial/tut13.hancho index 04cdf5f..8ded3cb 100644 --- a/tutorial/tut13.hancho +++ b/tutorial/tut13.hancho @@ -13,8 +13,8 @@ config = Config( compile = config.rule( desc = "Compile {source_files}", command = "g++ -MMD -c {source_files} -o {build_files}", - build_files = "{joinpath(build_dir, swap_ext(source_files, '.o'))}", - build_deps = "{joinpath(build_dir, swap_ext(source_files, '.d'))}", + build_files = "{join_path(build_dir, swap_ext(source_files, '.o'))}", + build_deps = "{join_path(build_dir, swap_ext(source_files, '.d'))}", ) link = config.rule( diff --git a/tutorial/tut14.hancho b/tutorial/tut14.hancho index 0edf25b..423ebcf 100644 --- a/tutorial/tut14.hancho +++ b/tutorial/tut14.hancho @@ -12,14 +12,14 @@ config = Config( compile = config.rule( desc = "Compile {rel_source_files}", - command = "g++ -MMD -c {rel_source_files} -o {build_files}", - build_files = "{joinpath(build_dir, swap_ext(rel_source_files, '.o'))}", - build_deps = "{joinpath(build_dir, swap_ext(rel_source_files, '.d'))}", + command = "g++ -MMD -c {rel_source_files} -o {rel_build_files}", + build_files = "{join_path(build_dir, swap_ext(rel_source_files, '.o'))}", + build_deps = "{join_path(build_dir, swap_ext(rel_source_files, '.d'))}", ) link = config.rule( - desc = "Link {rel_source_files} into {build_files}", - command = "g++ {rel_source_files} -o {build_files}", + desc = "Link {rel_source_files} into {rel_build_files}", + command = "g++ {rel_source_files} -o {rel_build_files}", ) main_o = compile("src/main.cpp") diff --git a/tutorial/tut40.hancho b/tutorial/tut40.hancho index ac93e5a..46196c2 100644 --- a/tutorial/tut40.hancho +++ b/tutorial/tut40.hancho @@ -1,6 +1,6 @@ # tutorial/tut40.hancho build_config.build_tag = "tut40" -build_config.rules_path = build_config.this_path +build_config.rules_path = this_path build_config.load("src/src.hancho")