diff options
-rw-r--r-- | lib/spack/spack/cmd/env.py | 96 | ||||
-rw-r--r-- | lib/spack/spack/tengine.py | 1 | ||||
-rw-r--r-- | share/spack/templates/depfile/Makefile | 37 |
3 files changed, 69 insertions, 65 deletions
diff --git a/lib/spack/spack/cmd/env.py b/lib/spack/spack/cmd/env.py index 86953a7bcc..e421e300e1 100644 --- a/lib/spack/spack/cmd/env.py +++ b/lib/spack/spack/cmd/env.py @@ -24,6 +24,7 @@ import spack.config import spack.environment as ev import spack.environment.shell import spack.schema.env +import spack.tengine import spack.util.string as string from spack.util.environment import EnvironmentModifications @@ -641,6 +642,9 @@ def env_depfile(args): def get_install_target(name): return os.path.join(target_prefix, ".install", name) + def get_install_deps_target(name): + return os.path.join(target_prefix, ".install-deps", name) + for _, spec in env.concretized_specs(): for s in spec.traverse(root=True): hash_to_spec[s.dag_hash()] = s @@ -655,76 +659,38 @@ def env_depfile(args): # All package install targets, not just roots. all_install_targets = [get_install_target(h) for h in hash_to_spec.keys()] + all_install_deps_targets = [get_install_deps_target(h) for h, _ in hash_to_prereqs.items()] buf = six.StringIO() - buf.write( - """SPACK ?= spack - -.PHONY: {} {} - -{}: {} - -{}: {} -\t@touch $@ - -{}: -\t@mkdir -p {} - -{}: | {} -\t$(info Installing $(SPEC)) -\t{}$(SPACK) -e '{}' install $(SPACK_INSTALL_FLAGS) --only-concrete --only=package \ ---no-add /$(notdir $@) && touch $@ - -""".format( - get_target("all"), - get_target("clean"), - get_target("all"), - get_target("env"), - get_target("env"), - " ".join(root_install_targets), - get_target("dirs"), - get_target(".install"), - get_target(".install/%"), - get_target("dirs"), - "+" if args.jobserver else "", - env.path, - ) - ) + template = spack.tengine.make_environment().get_template(os.path.join("depfile", "Makefile")) - # Targets are of the form <prefix>/<name>: [<prefix>/<depname>]..., - # The prefix can be an empty string, in that case we don't add the `/`. - # The name is currently the dag hash of the spec. In principle it - # could be the package name in case of `concretization: together` so - # it can be more easily referred to, but for now we don't special case - # this. fmt = "{name}{@version}{%compiler}{variants}{arch=architecture}" - - # Set SPEC for each hash - buf.write("# Set the human-readable spec for each target\n") - for dag_hash in hash_to_prereqs.keys(): - formatted_spec = hash_to_spec[dag_hash].format(fmt) - buf.write("{}: SPEC = {}\n".format(get_target("%/" + dag_hash), formatted_spec)) - buf.write("\n") - - # Set install dependencies - buf.write("# Install dependencies\n") - for parent, children in hash_to_prereqs.items(): - if not children: - continue - buf.write("{}: {}\n".format(get_install_target(parent), " ".join(children))) - buf.write("\n") - - # Clean target: remove target files but not their folders, cause - # --make-target-prefix can be any existing directory we do not control, - # including empty string (which means deleting the containing folder - # would delete the folder with the Makefile) - buf.write( - "{}:\n\trm -f -- {} {}\n".format( - get_target("clean"), get_target("env"), " ".join(all_install_targets) - ) - ) - + hash_with_name = [(h, hash_to_spec[h].format(fmt)) for h in hash_to_prereqs.keys()] + targets_to_prereqs = [ + (get_install_deps_target(h), " ".join(prereqs)) for h, prereqs in hash_to_prereqs.items() + ] + + rendered = template.render( + { + "all_target": get_target("all"), + "env_target": get_target("env"), + "clean_target": get_target("clean"), + "all_install_targets": " ".join(all_install_targets), + "all_install_deps_targets": " ".join(all_install_deps_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"), + "any_hash_target": get_target("%"), + "hash_with_name": hash_with_name, + "jobserver_support": "+" if args.jobserver else "", + "targets_to_prereqs": targets_to_prereqs, + } + ) + + buf.write(rendered) makefile = buf.getvalue() # Finally write to stdout/file. diff --git a/lib/spack/spack/tengine.py b/lib/spack/spack/tengine.py index 34db15d832..75dae5c9c1 100644 --- a/lib/spack/spack/tengine.py +++ b/lib/spack/spack/tengine.py @@ -11,6 +11,7 @@ import six import llnl.util.lang import spack.config +import spack.extensions from spack.util.path import canonicalize_path diff --git a/share/spack/templates/depfile/Makefile b/share/spack/templates/depfile/Makefile new file mode 100644 index 0000000000..a149951d9f --- /dev/null +++ b/share/spack/templates/depfile/Makefile @@ -0,0 +1,37 @@ +SPACK ?= spack + +.PHONY: {{ all_target }} {{ clean_target }} + +{{ all_target }}: {{ env_target }} + +{{ env_target }}: {{ root_install_targets }} + @touch $@ + +{{ dirs_target }}: + @mkdir -p {{ install_target }} {{ install_deps_target }} + +# 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 }} + $(info Installing $(SPEC)) + {{ jobserver_support }}$(SPACK) -e '{{ environment }}' install $(SPACK_INSTALL_FLAGS) --only-concrete --only=package --no-add /$(notdir $@) + @touch $@ + +# Targets of the form {{ install_deps_target }}/<hash> install dependencies only +{{ install_deps_target }}/%: | {{ dirs_target }} + @touch $@ + +# Set a human-readable SPEC variable for each target that has a hash +{% for (hash, name) in hash_with_name -%} +{{ any_hash_target }}/{{ hash }}: SPEC = {{ name }} +{% endfor %} + +# The Spack DAG expressed in targets: +{% for (target, prereqs) in targets_to_prereqs -%} +{{ target }}: {{prereqs}} +{% endfor %} + +{{ clean_target }}: + rm -rf {{ env_target }} {{ all_install_targets }} {{ all_install_deps_targets }} |