summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarmen Stoppels <harmenstoppels@gmail.com>2022-10-12 23:30:00 +0200
committerGitHub <noreply@github.com>2022-10-12 14:30:00 -0700
commit5009e3d94a2182c976c4f240caeacf21978300ed (patch)
treece6f306613a36a80d55e9e9bfeef3fde1c995ed0
parent8dbdfbd1eb326c7eb14e0c0b6ca7280642a8e74a (diff)
downloadspack-5009e3d94a2182c976c4f240caeacf21978300ed.tar.gz
spack-5009e3d94a2182c976c4f240caeacf21978300ed.tar.bz2
spack-5009e3d94a2182c976c4f240caeacf21978300ed.tar.xz
spack-5009e3d94a2182c976c4f240caeacf21978300ed.zip
env depfile: allow deps only install (#33245)
* env depfile: allow deps only install - Refactor `spack env depfile` to use a Jinja template, making it a bit easier to follow as a human being. - Add a layer of indirection in the generated Makefile through an `<prefix>/.install-deps/<hash>` target, which allows one to specify different options when installing dependencies. For example, only verbose/debug mode on when installing some particular spec: ``` $ spack -e my_env env depfile -o Makefile --make-target-prefix example $ make example/.install-deps/<hash> -j16 $ make example/.install/<hash> SPACK="spack -d" SPACK_INSTALL_FLAGS=--verbose -j16 ``` This could be used to speed up `spack ci rebuild`: - Parallel install of dependencies from buildcache - Better readability of logs, e.g. reducing verbosity when installing dependencies, and splitting logs into deps.log and current_spec.log * Silence please!
-rw-r--r--lib/spack/spack/cmd/env.py96
-rw-r--r--lib/spack/spack/tengine.py1
-rw-r--r--share/spack/templates/depfile/Makefile37
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 }}