From af74680405c931dab16c6674f9b97a32bf3f1122 Mon Sep 17 00:00:00 2001 From: Harmen Stoppels Date: Tue, 15 Nov 2022 18:03:17 +0100 Subject: depfile: improve tab completion (#33773) This PR allows you to do: ``` spack env create -d . spack -e . add python spack -e . concretize spack -e . env depfile -o Makefile make in # -> install make install- # -> install-deps/ make install-deps/py # -> install-deps/python-x.y.z-hash make install/zl # -> install/zlib-x.y.z-hash make SP # -> make SPACK make SPACK_ # -> make SPACK_INSTALL_FLAGS= ``` --- lib/spack/spack/cmd/env.py | 44 +++++++++++++++++++++++----------- share/spack/templates/depfile/Makefile | 23 +++++++++++++----- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/lib/spack/spack/cmd/env.py b/lib/spack/spack/cmd/env.py index 5510c7443a..e4c62758c4 100644 --- a/lib/spack/spack/cmd/env.py +++ b/lib/spack/spack/cmd/env.py @@ -673,13 +673,16 @@ class MakeTargetVisitor(object): return "" def accept(self, node): - dag_hash = node.edge.spec.dag_hash() + fmt = "{name}-{version}-{hash}" + tgt = node.edge.spec.format(fmt) spec_str = node.edge.spec.format( "{name}{@version}{%compiler}{variants}{arch=architecture}" ) buildcache_flag = self.build_cache_flag(node.depth) - prereqs = " ".join([self.target(dep.spec.dag_hash()) for dep in self.neighbors(node)]) - self.adjacency_list.append((dag_hash, spec_str, buildcache_flag, prereqs)) + prereqs = " ".join([self.target(dep.spec.format(fmt)) for dep in self.neighbors(node)]) + self.adjacency_list.append( + (tgt, prereqs, node.edge.spec.dag_hash(), spec_str, buildcache_flag) + ) # We already accepted this return True @@ -690,6 +693,8 @@ def env_depfile(args): spack.cmd.require_active_env(cmd_name="env depfile") env = ev.active_environment() + # Special make targets are useful when including a makefile in another, and you + # need to "namespace" the targets to avoid conflicts. if args.make_target_prefix is None: target_prefix = os.path.join(env.env_subdir_path, "makedeps") else: @@ -706,10 +711,10 @@ def env_depfile(args): return os.path.join(target_prefix, name) def get_install_target(name): - return os.path.join(target_prefix, ".install", name) + return os.path.join(target_prefix, "install", name) def get_install_deps_target(name): - return os.path.join(target_prefix, ".install-deps", name) + return os.path.join(target_prefix, "install-deps", name) # What things do we build when running make? By default, we build the # root specs. If specific specs are provided as input, we build those. @@ -728,13 +733,22 @@ def env_depfile(args): ) # Root specs without deps are the prereqs for the environment target - root_install_targets = [get_install_target(h.dag_hash()) for h in roots] + root_install_targets = [get_install_target(h.format("{name}-{version}-{hash}")) for h in roots] - # Cleanable targets... - cleanable_targets = [get_install_target(h) for h, _, _, _ in make_targets.adjacency_list] - cleanable_targets.extend( - [get_install_deps_target(h) for h, _, _, _ in make_targets.adjacency_list] - ) + # All install and install-deps targets + all_install_related_targets = [] + + # Convenience shortcuts: ensure that `make install/pkg-version-hash` triggers + # /.spack-env/makedeps/install/pkg-version-hash in case + # we don't have a custom make target prefix. + phony_convenience_targets = [] + + for tgt, _, _, _, _ in make_targets.adjacency_list: + all_install_related_targets.append(get_install_target(tgt)) + all_install_related_targets.append(get_install_deps_target(tgt)) + if args.make_target_prefix is None: + phony_convenience_targets.append(os.path.join("install", tgt)) + phony_convenience_targets.append(os.path.join("install-deps", tgt)) buf = io.StringIO() @@ -745,15 +759,17 @@ def env_depfile(args): "all_target": get_target("all"), "env_target": get_target("env"), "clean_target": get_target("clean"), - "cleanable_targets": " ".join(cleanable_targets), + "all_install_related_targets": " ".join(all_install_related_targets), "root_install_targets": " ".join(root_install_targets), "dirs_target": get_target("dirs"), "environment": env.path, - "install_target": get_target(".install"), - "install_deps_target": get_target(".install-deps"), + "install_target": get_target("install"), + "install_deps_target": get_target("install-deps"), "any_hash_target": get_target("%"), "jobserver_support": "+" if args.jobserver else "", "adjacency_list": make_targets.adjacency_list, + "phony_convenience_targets": " ".join(phony_convenience_targets), + "target_prefix": target_prefix, } ) diff --git a/share/spack/templates/depfile/Makefile b/share/spack/templates/depfile/Makefile index dc0aeb79ea..4ff7fff0a3 100644 --- a/share/spack/templates/depfile/Makefile +++ b/share/spack/templates/depfile/Makefile @@ -1,4 +1,5 @@ SPACK ?= spack +SPACK_INSTALL_FLAGS ?= .PHONY: {{ all_target }} {{ clean_target }} @@ -10,27 +11,37 @@ SPACK ?= spack {{ dirs_target }}: @mkdir -p {{ install_target }} {{ install_deps_target }} +{% if phony_convenience_targets %} +.PHONY: {{ phony_convenience_targets }} +{% endif %} + # The spack install commands are of the form: # spack -e my_env --no-add --only=package --only=concrete /hash # This is an involved way of expressing that Spack should only install # an individual concrete spec from the environment without deps. -{{ install_target }}/%: {{ install_deps_target }}/% | {{ dirs_target }} - {{ jobserver_support }}$(SPACK) -e '{{ environment }}' install $(SPACK_BUILDCACHE_FLAG) $(SPACK_INSTALL_FLAGS) --only-concrete --only=package --no-add /$(notdir $@) # $(SPEC) +{{ install_target }}/%: | {{ dirs_target }} + {{ jobserver_support }}$(SPACK) -e '{{ environment }}' install $(SPACK_BUILDCACHE_FLAG) $(SPACK_INSTALL_FLAGS) --only-concrete --only=package --no-add /$(HASH) # $(SPEC) @touch $@ {{ install_deps_target }}/%: | {{ dirs_target }} @touch $@ # Set a human-readable SPEC variable for each target that has a hash -{% for (parent, name, build_cache, _) in adjacency_list -%} +{% for (parent, _, hash, name, build_cache) in adjacency_list -%} +{{ any_hash_target }}/{{ parent }}: HASH = {{ hash }} {{ any_hash_target }}/{{ parent }}: SPEC = {{ name }} {{ any_hash_target }}/{{ parent }}: SPACK_BUILDCACHE_FLAG = {{ build_cache }} {% endfor %} # The Spack DAG expressed in targets: -{% for (parent, _, _, prereqs) in adjacency_list -%} -{{ install_deps_target }}/{{ parent }}: {{prereqs}} +{% for (parent, prereqs, _, _, _) in adjacency_list -%} +{{ install_target }}/{{ parent }}: {{ install_deps_target }}/{{ parent }} +{{ install_deps_target }}/{{ parent }}: {{ prereqs }} +{% if phony_convenience_targets %} +install/{{ parent }}: {{ install_target }}/{{ parent }} +install-deps/{{ parent }}: {{ install_deps_target }}/{{ parent }} +{% endif %} {% endfor %} {{ clean_target }}: - rm -rf {{ env_target }} {{ cleanable_targets }} + rm -rf {{ env_target }} {{ all_install_related_targets }} -- cgit v1.2.3-70-g09d2