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!
This commit is contained in:
parent
8dbdfbd1eb
commit
5009e3d94a
@ -24,6 +24,7 @@
|
||||
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 get_target(name):
|
||||
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 get_install_target(name):
|
||||
|
||||
# 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
|
||||
template = spack.tengine.make_environment().get_template(os.path.join("depfile", "Makefile"))
|
||||
|
||||
.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,
|
||||
)
|
||||
)
|
||||
|
||||
# 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}"
|
||||
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()
|
||||
]
|
||||
|
||||
# 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)
|
||||
)
|
||||
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.
|
||||
|
@ -11,6 +11,7 @@
|
||||
import llnl.util.lang
|
||||
|
||||
import spack.config
|
||||
import spack.extensions
|
||||
from spack.util.path import canonicalize_path
|
||||
|
||||
|
||||
|
37
share/spack/templates/depfile/Makefile
Normal file
37
share/spack/templates/depfile/Makefile
Normal file
@ -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 }}
|
Loading…
Reference in New Issue
Block a user