ci: remove compiler bootstrapping code (#38543)
This commit is contained in:
parent
41582f76bd
commit
b6b33cfe7a
@ -28,7 +28,6 @@
|
|||||||
|
|
||||||
import spack
|
import spack
|
||||||
import spack.binary_distribution as bindist
|
import spack.binary_distribution as bindist
|
||||||
import spack.compilers as compilers
|
|
||||||
import spack.config as cfg
|
import spack.config as cfg
|
||||||
import spack.environment as ev
|
import spack.environment as ev
|
||||||
import spack.main
|
import spack.main
|
||||||
@ -70,17 +69,10 @@ def __exit__(self, exc_type, exc_value, exc_traceback):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def _is_main_phase(phase_name):
|
def get_job_name(spec, osarch, build_group):
|
||||||
return True if phase_name == "specs" else False
|
|
||||||
|
|
||||||
|
|
||||||
def get_job_name(phase, strip_compiler, spec, osarch, build_group):
|
|
||||||
"""Given the necessary parts, format the gitlab job name
|
"""Given the necessary parts, format the gitlab job name
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
phase (str): Either 'specs' for the main phase, or the name of a
|
|
||||||
bootstrapping phase
|
|
||||||
strip_compiler (bool): Should compiler be stripped from job name
|
|
||||||
spec (spack.spec.Spec): Spec job will build
|
spec (spack.spec.Spec): Spec job will build
|
||||||
osarch: Architecture TODO: (this is a spack.spec.ArchSpec,
|
osarch: Architecture TODO: (this is a spack.spec.ArchSpec,
|
||||||
but sphinx doesn't recognize the type and fails).
|
but sphinx doesn't recognize the type and fails).
|
||||||
@ -93,12 +85,7 @@ def get_job_name(phase, strip_compiler, spec, osarch, build_group):
|
|||||||
format_str = ""
|
format_str = ""
|
||||||
format_args = []
|
format_args = []
|
||||||
|
|
||||||
if phase:
|
format_str += "{{{0}}}".format(item_idx)
|
||||||
format_str += "({{{0}}})".format(item_idx)
|
|
||||||
format_args.append(phase)
|
|
||||||
item_idx += 1
|
|
||||||
|
|
||||||
format_str += " {{{0}}}".format(item_idx)
|
|
||||||
format_args.append(spec.name)
|
format_args.append(spec.name)
|
||||||
item_idx += 1
|
item_idx += 1
|
||||||
|
|
||||||
@ -110,10 +97,9 @@ def get_job_name(phase, strip_compiler, spec, osarch, build_group):
|
|||||||
format_args.append(spec.version)
|
format_args.append(spec.version)
|
||||||
item_idx += 1
|
item_idx += 1
|
||||||
|
|
||||||
if _is_main_phase(phase) is True or strip_compiler is False:
|
format_str += " {{{0}}}".format(item_idx)
|
||||||
format_str += " {{{0}}}".format(item_idx)
|
format_args.append(spec.compiler)
|
||||||
format_args.append(spec.compiler)
|
item_idx += 1
|
||||||
item_idx += 1
|
|
||||||
|
|
||||||
format_str += " {{{0}}}".format(item_idx)
|
format_str += " {{{0}}}".format(item_idx)
|
||||||
format_args.append(osarch)
|
format_args.append(osarch)
|
||||||
@ -371,14 +357,7 @@ def _spec_matches(spec, match_string):
|
|||||||
|
|
||||||
|
|
||||||
def _format_job_needs(
|
def _format_job_needs(
|
||||||
phase_name,
|
dep_jobs, osname, build_group, prune_dag, stage_spec_dict, enable_artifacts_buildcache
|
||||||
strip_compilers,
|
|
||||||
dep_jobs,
|
|
||||||
osname,
|
|
||||||
build_group,
|
|
||||||
prune_dag,
|
|
||||||
stage_spec_dict,
|
|
||||||
enable_artifacts_buildcache,
|
|
||||||
):
|
):
|
||||||
needs_list = []
|
needs_list = []
|
||||||
for dep_job in dep_jobs:
|
for dep_job in dep_jobs:
|
||||||
@ -388,9 +367,7 @@ def _format_job_needs(
|
|||||||
if not prune_dag or dep_spec_info["needs_rebuild"]:
|
if not prune_dag or dep_spec_info["needs_rebuild"]:
|
||||||
needs_list.append(
|
needs_list.append(
|
||||||
{
|
{
|
||||||
"job": get_job_name(
|
"job": get_job_name(dep_job, dep_job.architecture, build_group),
|
||||||
phase_name, strip_compilers, dep_job, dep_job.architecture, build_group
|
|
||||||
),
|
|
||||||
"artifacts": enable_artifacts_buildcache,
|
"artifacts": enable_artifacts_buildcache,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -490,17 +467,13 @@ def get_spec_filter_list(env, affected_pkgs, dependent_traverse_depth=None):
|
|||||||
return affected_specs
|
return affected_specs
|
||||||
|
|
||||||
|
|
||||||
def _build_jobs(phases, staged_phases):
|
def _build_jobs(spec_labels, stages):
|
||||||
for phase in phases:
|
for stage_jobs in stages:
|
||||||
phase_name = phase["name"]
|
for spec_label in stage_jobs:
|
||||||
spec_labels, dependencies, stages = staged_phases[phase_name]
|
spec_record = spec_labels[spec_label]
|
||||||
|
release_spec = spec_record["spec"]
|
||||||
for stage_jobs in stages:
|
release_spec_dag_hash = release_spec.dag_hash()
|
||||||
for spec_label in stage_jobs:
|
yield release_spec, release_spec_dag_hash
|
||||||
spec_record = spec_labels[spec_label]
|
|
||||||
release_spec = spec_record["spec"]
|
|
||||||
release_spec_dag_hash = release_spec.dag_hash()
|
|
||||||
yield release_spec, release_spec_dag_hash
|
|
||||||
|
|
||||||
|
|
||||||
def _noop(x):
|
def _noop(x):
|
||||||
@ -524,9 +497,9 @@ class SpackCI:
|
|||||||
used by the CI generator(s).
|
used by the CI generator(s).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, ci_config, phases, staged_phases):
|
def __init__(self, ci_config, spec_labels, stages):
|
||||||
"""Given the information from the ci section of the config
|
"""Given the information from the ci section of the config
|
||||||
and the job phases setup meta data needed for generating Spack
|
and the staged jobs, set up meta data needed for generating Spack
|
||||||
CI IR.
|
CI IR.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -541,9 +514,6 @@ def __init__(self, ci_config, phases, staged_phases):
|
|||||||
"enable-artifacts-buildcache": self.ci_config.get(
|
"enable-artifacts-buildcache": self.ci_config.get(
|
||||||
"enable-artifacts-buildcache", False
|
"enable-artifacts-buildcache", False
|
||||||
),
|
),
|
||||||
"bootstrap": self.ci_config.get(
|
|
||||||
"bootstrap", []
|
|
||||||
), # This is deprecated and should be removed
|
|
||||||
"rebuild-index": self.ci_config.get("rebuild-index", True),
|
"rebuild-index": self.ci_config.get("rebuild-index", True),
|
||||||
"broken-specs-url": self.ci_config.get("broken-specs-url", None),
|
"broken-specs-url": self.ci_config.get("broken-specs-url", None),
|
||||||
"broken-tests-packages": self.ci_config.get("broken-tests-packages", []),
|
"broken-tests-packages": self.ci_config.get("broken-tests-packages", []),
|
||||||
@ -551,7 +521,7 @@ def __init__(self, ci_config, phases, staged_phases):
|
|||||||
}
|
}
|
||||||
jobs = self.ir["jobs"]
|
jobs = self.ir["jobs"]
|
||||||
|
|
||||||
for spec, dag_hash in _build_jobs(phases, staged_phases):
|
for spec, dag_hash in _build_jobs(spec_labels, stages):
|
||||||
jobs[dag_hash] = self.__init_job(spec)
|
jobs[dag_hash] = self.__init_job(spec)
|
||||||
|
|
||||||
for name in self.named_jobs:
|
for name in self.named_jobs:
|
||||||
@ -873,25 +843,6 @@ def generate_gitlab_ci_yaml(
|
|||||||
if "temporary-storage-url-prefix" in ci_config:
|
if "temporary-storage-url-prefix" in ci_config:
|
||||||
temp_storage_url_prefix = ci_config["temporary-storage-url-prefix"]
|
temp_storage_url_prefix = ci_config["temporary-storage-url-prefix"]
|
||||||
|
|
||||||
bootstrap_specs = []
|
|
||||||
phases = []
|
|
||||||
if "bootstrap" in ci_config:
|
|
||||||
for phase in ci_config["bootstrap"]:
|
|
||||||
try:
|
|
||||||
phase_name = phase.get("name")
|
|
||||||
strip_compilers = phase.get("compiler-agnostic")
|
|
||||||
except AttributeError:
|
|
||||||
phase_name = phase
|
|
||||||
strip_compilers = False
|
|
||||||
phases.append({"name": phase_name, "strip-compilers": strip_compilers})
|
|
||||||
|
|
||||||
for bs in env.spec_lists[phase_name]:
|
|
||||||
bootstrap_specs.append(
|
|
||||||
{"spec": bs, "phase-name": phase_name, "strip-compilers": strip_compilers}
|
|
||||||
)
|
|
||||||
|
|
||||||
phases.append({"name": "specs", "strip-compilers": False})
|
|
||||||
|
|
||||||
# If a remote mirror override (alternate buildcache destination) was
|
# If a remote mirror override (alternate buildcache destination) was
|
||||||
# specified, add it here in case it has already built hashes we might
|
# specified, add it here in case it has already built hashes we might
|
||||||
# generate.
|
# generate.
|
||||||
@ -993,33 +944,17 @@ def generate_gitlab_ci_yaml(
|
|||||||
except bindist.FetchCacheError as e:
|
except bindist.FetchCacheError as e:
|
||||||
tty.warn(e)
|
tty.warn(e)
|
||||||
|
|
||||||
staged_phases = {}
|
|
||||||
try:
|
try:
|
||||||
for phase in phases:
|
concrete_env_specs = [
|
||||||
phase_name = phase["name"]
|
concrete
|
||||||
if phase_name == "specs":
|
for abstract, concrete in env.concretized_specs()
|
||||||
# Anything in the "specs" of the environment are already
|
if abstract in env.spec_lists["specs"]
|
||||||
# concretized by the block at the top of this method, so we
|
]
|
||||||
# only need to find the concrete versions, and then avoid
|
spec_labels, dependencies, stages = stage_spec_jobs(
|
||||||
# re-concretizing them needlessly later on.
|
concrete_env_specs,
|
||||||
concrete_phase_specs = [
|
check_index_only=check_index_only,
|
||||||
concrete
|
mirrors_to_check=mirrors_to_check,
|
||||||
for abstract, concrete in env.concretized_specs()
|
)
|
||||||
if abstract in env.spec_lists[phase_name]
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
# Any specs lists in other definitions (but not in the
|
|
||||||
# "specs") of the environment are not yet concretized so we
|
|
||||||
# have to concretize them explicitly here.
|
|
||||||
concrete_phase_specs = env.spec_lists[phase_name]
|
|
||||||
with spack.concretize.disable_compiler_existence_check():
|
|
||||||
for phase_spec in concrete_phase_specs:
|
|
||||||
phase_spec.concretize()
|
|
||||||
staged_phases[phase_name] = stage_spec_jobs(
|
|
||||||
concrete_phase_specs,
|
|
||||||
check_index_only=check_index_only,
|
|
||||||
mirrors_to_check=mirrors_to_check,
|
|
||||||
)
|
|
||||||
finally:
|
finally:
|
||||||
# Clean up remote mirror override if enabled
|
# Clean up remote mirror override if enabled
|
||||||
if remote_mirror_override:
|
if remote_mirror_override:
|
||||||
@ -1048,276 +983,189 @@ def generate_gitlab_ci_yaml(
|
|||||||
else:
|
else:
|
||||||
broken_spec_urls = web_util.list_url(broken_specs_url)
|
broken_spec_urls = web_util.list_url(broken_specs_url)
|
||||||
|
|
||||||
spack_ci = SpackCI(ci_config, phases, staged_phases)
|
spack_ci = SpackCI(ci_config, spec_labels, stages)
|
||||||
spack_ci_ir = spack_ci.generate_ir()
|
spack_ci_ir = spack_ci.generate_ir()
|
||||||
|
|
||||||
for phase in phases:
|
for stage_jobs in stages:
|
||||||
phase_name = phase["name"]
|
stage_name = "stage-{0}".format(stage_id)
|
||||||
strip_compilers = phase["strip-compilers"]
|
stage_names.append(stage_name)
|
||||||
|
stage_id += 1
|
||||||
|
|
||||||
spec_labels, dependencies, stages = staged_phases[phase_name]
|
for spec_label in stage_jobs:
|
||||||
|
spec_record = spec_labels[spec_label]
|
||||||
|
release_spec = spec_record["spec"]
|
||||||
|
release_spec_dag_hash = release_spec.dag_hash()
|
||||||
|
|
||||||
for stage_jobs in stages:
|
if prune_untouched_packages:
|
||||||
stage_name = "stage-{0}".format(stage_id)
|
if release_spec not in affected_specs:
|
||||||
stage_names.append(stage_name)
|
|
||||||
stage_id += 1
|
|
||||||
|
|
||||||
for spec_label in stage_jobs:
|
|
||||||
spec_record = spec_labels[spec_label]
|
|
||||||
release_spec = spec_record["spec"]
|
|
||||||
release_spec_dag_hash = release_spec.dag_hash()
|
|
||||||
|
|
||||||
if prune_untouched_packages:
|
|
||||||
if release_spec not in affected_specs:
|
|
||||||
tty.debug(
|
|
||||||
"Pruning {0}/{1}, untouched by change.".format(
|
|
||||||
release_spec.name, release_spec.dag_hash()[:7]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
spec_record["needs_rebuild"] = False
|
|
||||||
continue
|
|
||||||
|
|
||||||
job_object = spack_ci_ir["jobs"][release_spec_dag_hash]["attributes"]
|
|
||||||
|
|
||||||
if not job_object:
|
|
||||||
tty.warn("No match found for {0}, skipping it".format(release_spec))
|
|
||||||
continue
|
|
||||||
|
|
||||||
if spack_pipeline_type is not None:
|
|
||||||
# For spack pipelines "public" and "protected" are reserved tags
|
|
||||||
job_object["tags"] = _remove_reserved_tags(job_object.get("tags", []))
|
|
||||||
if spack_pipeline_type == "spack_protected_branch":
|
|
||||||
job_object["tags"].extend(["protected"])
|
|
||||||
elif spack_pipeline_type == "spack_pull_request":
|
|
||||||
job_object["tags"].extend(["public"])
|
|
||||||
|
|
||||||
if "script" not in job_object:
|
|
||||||
raise AttributeError
|
|
||||||
|
|
||||||
def main_script_replacements(cmd):
|
|
||||||
return cmd.replace("{env_dir}", rel_concrete_env_dir)
|
|
||||||
|
|
||||||
job_object["script"] = _unpack_script(
|
|
||||||
job_object["script"], op=main_script_replacements
|
|
||||||
)
|
|
||||||
|
|
||||||
if "before_script" in job_object:
|
|
||||||
job_object["before_script"] = _unpack_script(job_object["before_script"])
|
|
||||||
|
|
||||||
if "after_script" in job_object:
|
|
||||||
job_object["after_script"] = _unpack_script(job_object["after_script"])
|
|
||||||
|
|
||||||
osname = str(release_spec.architecture)
|
|
||||||
job_name = get_job_name(
|
|
||||||
phase_name, strip_compilers, release_spec, osname, build_group
|
|
||||||
)
|
|
||||||
|
|
||||||
compiler_action = "NONE"
|
|
||||||
if len(phases) > 1:
|
|
||||||
compiler_action = "FIND_ANY"
|
|
||||||
if _is_main_phase(phase_name):
|
|
||||||
compiler_action = "INSTALL_MISSING"
|
|
||||||
|
|
||||||
job_vars = job_object.setdefault("variables", {})
|
|
||||||
job_vars["SPACK_JOB_SPEC_DAG_HASH"] = release_spec_dag_hash
|
|
||||||
job_vars["SPACK_JOB_SPEC_PKG_NAME"] = release_spec.name
|
|
||||||
job_vars["SPACK_COMPILER_ACTION"] = compiler_action
|
|
||||||
|
|
||||||
job_object["needs"] = []
|
|
||||||
if spec_label in dependencies:
|
|
||||||
if enable_artifacts_buildcache:
|
|
||||||
# Get dependencies transitively, so they're all
|
|
||||||
# available in the artifacts buildcache.
|
|
||||||
dep_jobs = [d for d in release_spec.traverse(deptype=all, root=False)]
|
|
||||||
else:
|
|
||||||
# In this case, "needs" is only used for scheduling
|
|
||||||
# purposes, so we only get the direct dependencies.
|
|
||||||
dep_jobs = []
|
|
||||||
for dep_label in dependencies[spec_label]:
|
|
||||||
dep_jobs.append(spec_labels[dep_label]["spec"])
|
|
||||||
|
|
||||||
job_object["needs"].extend(
|
|
||||||
_format_job_needs(
|
|
||||||
phase_name,
|
|
||||||
strip_compilers,
|
|
||||||
dep_jobs,
|
|
||||||
osname,
|
|
||||||
build_group,
|
|
||||||
prune_dag,
|
|
||||||
spec_labels,
|
|
||||||
enable_artifacts_buildcache,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
rebuild_spec = spec_record["needs_rebuild"]
|
|
||||||
|
|
||||||
# This next section helps gitlab make sure the right
|
|
||||||
# bootstrapped compiler exists in the artifacts buildcache by
|
|
||||||
# creating an artificial dependency between this spec and its
|
|
||||||
# compiler. So, if we are in the main phase, and if the
|
|
||||||
# compiler we are supposed to use is listed in any of the
|
|
||||||
# bootstrap spec lists, then we will add more dependencies to
|
|
||||||
# the job (that compiler and maybe it's dependencies as well).
|
|
||||||
if _is_main_phase(phase_name):
|
|
||||||
spec_arch_family = release_spec.architecture.target.microarchitecture.family
|
|
||||||
compiler_pkg_spec = compilers.pkg_spec_for_compiler(release_spec.compiler)
|
|
||||||
for bs in bootstrap_specs:
|
|
||||||
c_spec = bs["spec"]
|
|
||||||
bs_arch = c_spec.architecture
|
|
||||||
bs_arch_family = bs_arch.target.microarchitecture.family
|
|
||||||
if (
|
|
||||||
c_spec.intersects(compiler_pkg_spec)
|
|
||||||
and bs_arch_family == spec_arch_family
|
|
||||||
):
|
|
||||||
# We found the bootstrap compiler this release spec
|
|
||||||
# should be built with, so for DAG scheduling
|
|
||||||
# purposes, we will at least add the compiler spec
|
|
||||||
# to the jobs "needs". But if artifact buildcache
|
|
||||||
# is enabled, we'll have to add all transtive deps
|
|
||||||
# of the compiler as well.
|
|
||||||
|
|
||||||
# Here we check whether the bootstrapped compiler
|
|
||||||
# needs to be rebuilt. Until compilers are proper
|
|
||||||
# dependencies, we artificially force the spec to
|
|
||||||
# be rebuilt if the compiler targeted to build it
|
|
||||||
# needs to be rebuilt.
|
|
||||||
bs_specs, _, _ = staged_phases[bs["phase-name"]]
|
|
||||||
c_spec_key = _spec_deps_key(c_spec)
|
|
||||||
rbld_comp = bs_specs[c_spec_key]["needs_rebuild"]
|
|
||||||
rebuild_spec = rebuild_spec or rbld_comp
|
|
||||||
# Also update record so dependents do not fail to
|
|
||||||
# add this spec to their "needs"
|
|
||||||
spec_record["needs_rebuild"] = rebuild_spec
|
|
||||||
|
|
||||||
dep_jobs = [c_spec]
|
|
||||||
if enable_artifacts_buildcache:
|
|
||||||
dep_jobs = [d for d in c_spec.traverse(deptype=all)]
|
|
||||||
|
|
||||||
job_object["needs"].extend(
|
|
||||||
_format_job_needs(
|
|
||||||
bs["phase-name"],
|
|
||||||
bs["strip-compilers"],
|
|
||||||
dep_jobs,
|
|
||||||
str(bs_arch),
|
|
||||||
build_group,
|
|
||||||
prune_dag,
|
|
||||||
bs_specs,
|
|
||||||
enable_artifacts_buildcache,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
debug_msg = "".join(
|
|
||||||
[
|
|
||||||
"Considered compiler {0} for spec ",
|
|
||||||
"{1}, but rejected it either because it was ",
|
|
||||||
"not the compiler required by the spec, or ",
|
|
||||||
"because the target arch families of the ",
|
|
||||||
"spec and the compiler did not match",
|
|
||||||
]
|
|
||||||
).format(c_spec, release_spec)
|
|
||||||
tty.debug(debug_msg)
|
|
||||||
|
|
||||||
if prune_dag and not rebuild_spec and not copy_only_pipeline:
|
|
||||||
tty.debug(
|
tty.debug(
|
||||||
"Pruning {0}/{1}, does not need rebuild.".format(
|
"Pruning {0}/{1}, untouched by change.".format(
|
||||||
release_spec.name, release_spec.dag_hash()
|
release_spec.name, release_spec.dag_hash()[:7]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
spec_record["needs_rebuild"] = False
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if broken_spec_urls is not None and release_spec_dag_hash in broken_spec_urls:
|
job_object = spack_ci_ir["jobs"][release_spec_dag_hash]["attributes"]
|
||||||
known_broken_specs_encountered.append(release_spec_dag_hash)
|
|
||||||
|
|
||||||
# Only keep track of these if we are copying rebuilt cache entries
|
if not job_object:
|
||||||
if spack_buildcache_copy:
|
tty.warn("No match found for {0}, skipping it".format(release_spec))
|
||||||
# TODO: This assumes signed version of the spec
|
continue
|
||||||
buildcache_copies[release_spec_dag_hash] = [
|
|
||||||
{
|
|
||||||
"src": url_util.join(
|
|
||||||
buildcache_copy_src_prefix,
|
|
||||||
bindist.build_cache_relative_path(),
|
|
||||||
bindist.tarball_name(release_spec, ".spec.json.sig"),
|
|
||||||
),
|
|
||||||
"dest": url_util.join(
|
|
||||||
buildcache_copy_dest_prefix,
|
|
||||||
bindist.build_cache_relative_path(),
|
|
||||||
bindist.tarball_name(release_spec, ".spec.json.sig"),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": url_util.join(
|
|
||||||
buildcache_copy_src_prefix,
|
|
||||||
bindist.build_cache_relative_path(),
|
|
||||||
bindist.tarball_path_name(release_spec, ".spack"),
|
|
||||||
),
|
|
||||||
"dest": url_util.join(
|
|
||||||
buildcache_copy_dest_prefix,
|
|
||||||
bindist.build_cache_relative_path(),
|
|
||||||
bindist.tarball_path_name(release_spec, ".spack"),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
if artifacts_root:
|
if spack_pipeline_type is not None:
|
||||||
job_object["needs"].append(
|
# For spack pipelines "public" and "protected" are reserved tags
|
||||||
{"job": generate_job_name, "pipeline": "{0}".format(parent_pipeline_id)}
|
job_object["tags"] = _remove_reserved_tags(job_object.get("tags", []))
|
||||||
|
if spack_pipeline_type == "spack_protected_branch":
|
||||||
|
job_object["tags"].extend(["protected"])
|
||||||
|
elif spack_pipeline_type == "spack_pull_request":
|
||||||
|
job_object["tags"].extend(["public"])
|
||||||
|
|
||||||
|
if "script" not in job_object:
|
||||||
|
raise AttributeError
|
||||||
|
|
||||||
|
def main_script_replacements(cmd):
|
||||||
|
return cmd.replace("{env_dir}", rel_concrete_env_dir)
|
||||||
|
|
||||||
|
job_object["script"] = _unpack_script(
|
||||||
|
job_object["script"], op=main_script_replacements
|
||||||
|
)
|
||||||
|
|
||||||
|
if "before_script" in job_object:
|
||||||
|
job_object["before_script"] = _unpack_script(job_object["before_script"])
|
||||||
|
|
||||||
|
if "after_script" in job_object:
|
||||||
|
job_object["after_script"] = _unpack_script(job_object["after_script"])
|
||||||
|
|
||||||
|
osname = str(release_spec.architecture)
|
||||||
|
job_name = get_job_name(release_spec, osname, build_group)
|
||||||
|
|
||||||
|
job_vars = job_object.setdefault("variables", {})
|
||||||
|
job_vars["SPACK_JOB_SPEC_DAG_HASH"] = release_spec_dag_hash
|
||||||
|
job_vars["SPACK_JOB_SPEC_PKG_NAME"] = release_spec.name
|
||||||
|
|
||||||
|
job_object["needs"] = []
|
||||||
|
if spec_label in dependencies:
|
||||||
|
if enable_artifacts_buildcache:
|
||||||
|
# Get dependencies transitively, so they're all
|
||||||
|
# available in the artifacts buildcache.
|
||||||
|
dep_jobs = [d for d in release_spec.traverse(deptype=all, root=False)]
|
||||||
|
else:
|
||||||
|
# In this case, "needs" is only used for scheduling
|
||||||
|
# purposes, so we only get the direct dependencies.
|
||||||
|
dep_jobs = []
|
||||||
|
for dep_label in dependencies[spec_label]:
|
||||||
|
dep_jobs.append(spec_labels[dep_label]["spec"])
|
||||||
|
|
||||||
|
job_object["needs"].extend(
|
||||||
|
_format_job_needs(
|
||||||
|
dep_jobs,
|
||||||
|
osname,
|
||||||
|
build_group,
|
||||||
|
prune_dag,
|
||||||
|
spec_labels,
|
||||||
|
enable_artifacts_buildcache,
|
||||||
)
|
)
|
||||||
|
|
||||||
job_vars["SPACK_SPEC_NEEDS_REBUILD"] = str(rebuild_spec)
|
|
||||||
|
|
||||||
if cdash_handler:
|
|
||||||
cdash_handler.current_spec = release_spec
|
|
||||||
build_name = cdash_handler.build_name
|
|
||||||
all_job_names.append(build_name)
|
|
||||||
job_vars["SPACK_CDASH_BUILD_NAME"] = build_name
|
|
||||||
|
|
||||||
build_stamp = cdash_handler.build_stamp
|
|
||||||
job_vars["SPACK_CDASH_BUILD_STAMP"] = build_stamp
|
|
||||||
|
|
||||||
job_object["artifacts"] = spack.config.merge_yaml(
|
|
||||||
job_object.get("artifacts", {}),
|
|
||||||
{
|
|
||||||
"when": "always",
|
|
||||||
"paths": [
|
|
||||||
rel_job_log_dir,
|
|
||||||
rel_job_repro_dir,
|
|
||||||
rel_job_test_dir,
|
|
||||||
rel_user_artifacts_dir,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if enable_artifacts_buildcache:
|
rebuild_spec = spec_record["needs_rebuild"]
|
||||||
bc_root = os.path.join(local_mirror_dir, "build_cache")
|
|
||||||
job_object["artifacts"]["paths"].extend(
|
if prune_dag and not rebuild_spec and not copy_only_pipeline:
|
||||||
[
|
tty.debug(
|
||||||
os.path.join(bc_root, p)
|
"Pruning {0}/{1}, does not need rebuild.".format(
|
||||||
for p in [
|
release_spec.name, release_spec.dag_hash()
|
||||||
bindist.tarball_name(release_spec, ".spec.json"),
|
|
||||||
bindist.tarball_directory_name(release_spec),
|
|
||||||
]
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
job_object["stage"] = stage_name
|
if broken_spec_urls is not None and release_spec_dag_hash in broken_spec_urls:
|
||||||
job_object["retry"] = {"max": 2, "when": JOB_RETRY_CONDITIONS}
|
known_broken_specs_encountered.append(release_spec_dag_hash)
|
||||||
job_object["interruptible"] = True
|
|
||||||
|
|
||||||
length_needs = len(job_object["needs"])
|
# Only keep track of these if we are copying rebuilt cache entries
|
||||||
if length_needs > max_length_needs:
|
if spack_buildcache_copy:
|
||||||
max_length_needs = length_needs
|
# TODO: This assumes signed version of the spec
|
||||||
max_needs_job = job_name
|
buildcache_copies[release_spec_dag_hash] = [
|
||||||
|
{
|
||||||
|
"src": url_util.join(
|
||||||
|
buildcache_copy_src_prefix,
|
||||||
|
bindist.build_cache_relative_path(),
|
||||||
|
bindist.tarball_name(release_spec, ".spec.json.sig"),
|
||||||
|
),
|
||||||
|
"dest": url_util.join(
|
||||||
|
buildcache_copy_dest_prefix,
|
||||||
|
bindist.build_cache_relative_path(),
|
||||||
|
bindist.tarball_name(release_spec, ".spec.json.sig"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": url_util.join(
|
||||||
|
buildcache_copy_src_prefix,
|
||||||
|
bindist.build_cache_relative_path(),
|
||||||
|
bindist.tarball_path_name(release_spec, ".spack"),
|
||||||
|
),
|
||||||
|
"dest": url_util.join(
|
||||||
|
buildcache_copy_dest_prefix,
|
||||||
|
bindist.build_cache_relative_path(),
|
||||||
|
bindist.tarball_path_name(release_spec, ".spack"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
if not copy_only_pipeline:
|
if artifacts_root:
|
||||||
output_object[job_name] = job_object
|
job_object["needs"].append(
|
||||||
job_id += 1
|
{"job": generate_job_name, "pipeline": "{0}".format(parent_pipeline_id)}
|
||||||
|
)
|
||||||
|
|
||||||
|
job_vars["SPACK_SPEC_NEEDS_REBUILD"] = str(rebuild_spec)
|
||||||
|
|
||||||
|
if cdash_handler:
|
||||||
|
cdash_handler.current_spec = release_spec
|
||||||
|
build_name = cdash_handler.build_name
|
||||||
|
all_job_names.append(build_name)
|
||||||
|
job_vars["SPACK_CDASH_BUILD_NAME"] = build_name
|
||||||
|
|
||||||
|
build_stamp = cdash_handler.build_stamp
|
||||||
|
job_vars["SPACK_CDASH_BUILD_STAMP"] = build_stamp
|
||||||
|
|
||||||
|
job_object["artifacts"] = spack.config.merge_yaml(
|
||||||
|
job_object.get("artifacts", {}),
|
||||||
|
{
|
||||||
|
"when": "always",
|
||||||
|
"paths": [
|
||||||
|
rel_job_log_dir,
|
||||||
|
rel_job_repro_dir,
|
||||||
|
rel_job_test_dir,
|
||||||
|
rel_user_artifacts_dir,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if enable_artifacts_buildcache:
|
||||||
|
bc_root = os.path.join(local_mirror_dir, "build_cache")
|
||||||
|
job_object["artifacts"]["paths"].extend(
|
||||||
|
[
|
||||||
|
os.path.join(bc_root, p)
|
||||||
|
for p in [
|
||||||
|
bindist.tarball_name(release_spec, ".spec.json"),
|
||||||
|
bindist.tarball_directory_name(release_spec),
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
job_object["stage"] = stage_name
|
||||||
|
job_object["retry"] = {"max": 2, "when": JOB_RETRY_CONDITIONS}
|
||||||
|
job_object["interruptible"] = True
|
||||||
|
|
||||||
|
length_needs = len(job_object["needs"])
|
||||||
|
if length_needs > max_length_needs:
|
||||||
|
max_length_needs = length_needs
|
||||||
|
max_needs_job = job_name
|
||||||
|
|
||||||
|
if not copy_only_pipeline:
|
||||||
|
output_object[job_name] = job_object
|
||||||
|
job_id += 1
|
||||||
|
|
||||||
if print_summary:
|
if print_summary:
|
||||||
for phase in phases:
|
_print_staging_summary(spec_labels, dependencies, stages)
|
||||||
phase_name = phase["name"]
|
|
||||||
tty.msg('Stages for phase "{0}"'.format(phase_name))
|
|
||||||
phase_stages = staged_phases[phase_name]
|
|
||||||
_print_staging_summary(*phase_stages)
|
|
||||||
|
|
||||||
tty.debug("{0} build jobs generated in {1} stages".format(job_id, stage_id))
|
tty.debug("{0} build jobs generated in {1} stages".format(job_id, stage_id))
|
||||||
|
|
||||||
@ -1576,44 +1424,6 @@ def can_verify_binaries():
|
|||||||
return len(gpg_util.public_keys()) >= 1
|
return len(gpg_util.public_keys()) >= 1
|
||||||
|
|
||||||
|
|
||||||
def configure_compilers(compiler_action, scope=None):
|
|
||||||
"""Depending on the compiler_action parameter, either turn on the
|
|
||||||
install_missing_compilers config option, or find spack compilers,
|
|
||||||
or do nothing. This is used from rebuild jobs in bootstrapping
|
|
||||||
pipelines, where in the bootsrapping phase we would pass
|
|
||||||
FIND_ANY in case of compiler-agnostic bootstrapping, while in the
|
|
||||||
spec building phase we would pass INSTALL_MISSING in order to get
|
|
||||||
spack to use the compiler which was built in the previous phase and
|
|
||||||
is now sitting in the binary mirror.
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
compiler_action (str): 'FIND_ANY', 'INSTALL_MISSING' have meanings
|
|
||||||
described above. Any other value essentially results in a no-op.
|
|
||||||
scope (spack.config.ConfigScope): Optional. The scope in which to look for
|
|
||||||
compilers, in case 'FIND_ANY' was provided.
|
|
||||||
"""
|
|
||||||
if compiler_action == "INSTALL_MISSING":
|
|
||||||
tty.debug("Make sure bootstrapped compiler will be installed")
|
|
||||||
config = cfg.get("config")
|
|
||||||
config["install_missing_compilers"] = True
|
|
||||||
cfg.set("config", config)
|
|
||||||
elif compiler_action == "FIND_ANY":
|
|
||||||
tty.debug("Just find any available compiler")
|
|
||||||
find_args = ["find"]
|
|
||||||
if scope:
|
|
||||||
find_args.extend(["--scope", scope])
|
|
||||||
output = spack_compiler(*find_args)
|
|
||||||
tty.debug("spack compiler find")
|
|
||||||
tty.debug(output)
|
|
||||||
output = spack_compiler("list")
|
|
||||||
tty.debug("spack compiler list")
|
|
||||||
tty.debug(output)
|
|
||||||
else:
|
|
||||||
tty.debug("No compiler action to be taken")
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def _push_mirror_contents(input_spec, sign_binaries, mirror_url):
|
def _push_mirror_contents(input_spec, sign_binaries, mirror_url):
|
||||||
"""Unchecked version of the public API, for easier mocking"""
|
"""Unchecked version of the public API, for easier mocking"""
|
||||||
unsigned = not sign_binaries
|
unsigned = not sign_binaries
|
||||||
|
@ -274,7 +274,6 @@ def ci_rebuild(args):
|
|||||||
signing_key = os.environ.get("SPACK_SIGNING_KEY")
|
signing_key = os.environ.get("SPACK_SIGNING_KEY")
|
||||||
job_spec_pkg_name = os.environ.get("SPACK_JOB_SPEC_PKG_NAME")
|
job_spec_pkg_name = os.environ.get("SPACK_JOB_SPEC_PKG_NAME")
|
||||||
job_spec_dag_hash = os.environ.get("SPACK_JOB_SPEC_DAG_HASH")
|
job_spec_dag_hash = os.environ.get("SPACK_JOB_SPEC_DAG_HASH")
|
||||||
compiler_action = os.environ.get("SPACK_COMPILER_ACTION")
|
|
||||||
spack_pipeline_type = os.environ.get("SPACK_PIPELINE_TYPE")
|
spack_pipeline_type = os.environ.get("SPACK_PIPELINE_TYPE")
|
||||||
remote_mirror_override = os.environ.get("SPACK_REMOTE_MIRROR_OVERRIDE")
|
remote_mirror_override = os.environ.get("SPACK_REMOTE_MIRROR_OVERRIDE")
|
||||||
remote_mirror_url = os.environ.get("SPACK_REMOTE_MIRROR_URL")
|
remote_mirror_url = os.environ.get("SPACK_REMOTE_MIRROR_URL")
|
||||||
@ -295,7 +294,6 @@ def ci_rebuild(args):
|
|||||||
tty.debug("pipeline_artifacts_dir = {0}".format(pipeline_artifacts_dir))
|
tty.debug("pipeline_artifacts_dir = {0}".format(pipeline_artifacts_dir))
|
||||||
tty.debug("remote_mirror_url = {0}".format(remote_mirror_url))
|
tty.debug("remote_mirror_url = {0}".format(remote_mirror_url))
|
||||||
tty.debug("job_spec_pkg_name = {0}".format(job_spec_pkg_name))
|
tty.debug("job_spec_pkg_name = {0}".format(job_spec_pkg_name))
|
||||||
tty.debug("compiler_action = {0}".format(compiler_action))
|
|
||||||
|
|
||||||
# Query the environment manifest to find out whether we're reporting to a
|
# Query the environment manifest to find out whether we're reporting to a
|
||||||
# CDash instance, and if so, gather some information from the manifest to
|
# CDash instance, and if so, gather some information from the manifest to
|
||||||
@ -411,14 +409,6 @@ def ci_rebuild(args):
|
|||||||
if signing_key:
|
if signing_key:
|
||||||
spack_ci.import_signing_key(signing_key)
|
spack_ci.import_signing_key(signing_key)
|
||||||
|
|
||||||
# Depending on the specifics of this job, we might need to turn on the
|
|
||||||
# "config:install_missing compilers" option (to build this job spec
|
|
||||||
# with a bootstrapped compiler), or possibly run "spack compiler find"
|
|
||||||
# (to build a bootstrap compiler or one of its deps in a
|
|
||||||
# compiler-agnostic way), or maybe do nothing at all (to build a spec
|
|
||||||
# using a compiler already installed on the target system).
|
|
||||||
spack_ci.configure_compilers(compiler_action)
|
|
||||||
|
|
||||||
# Write this job's spec json into the reproduction directory, and it will
|
# Write this job's spec json into the reproduction directory, and it will
|
||||||
# also be used in the generated "spack install" command to install the spec
|
# also be used in the generated "spack install" command to install the spec
|
||||||
tty.debug("job concrete spec path: {0}".format(job_spec_json_path))
|
tty.debug("job concrete spec path: {0}".format(job_spec_json_path))
|
||||||
|
@ -134,23 +134,6 @@
|
|||||||
core_shared_properties = union_dicts(
|
core_shared_properties = union_dicts(
|
||||||
{
|
{
|
||||||
"pipeline-gen": pipeline_gen_schema,
|
"pipeline-gen": pipeline_gen_schema,
|
||||||
"bootstrap": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"anyOf": [
|
|
||||||
{"type": "string"},
|
|
||||||
{
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": False,
|
|
||||||
"required": ["name"],
|
|
||||||
"properties": {
|
|
||||||
"name": {"type": "string"},
|
|
||||||
"compiler-agnostic": {"type": "boolean", "default": False},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"rebuild-index": {"type": "boolean"},
|
"rebuild-index": {"type": "boolean"},
|
||||||
"broken-specs-url": {"type": "string"},
|
"broken-specs-url": {"type": "string"},
|
||||||
"broken-tests-packages": {"type": "array", "items": {"type": "string"}},
|
"broken-tests-packages": {"type": "array", "items": {"type": "string"}},
|
||||||
|
@ -46,31 +46,6 @@ def test_import_signing_key(mock_gnupghome):
|
|||||||
ci.import_signing_key(signing_key)
|
ci.import_signing_key(signing_key)
|
||||||
|
|
||||||
|
|
||||||
def test_configure_compilers(mutable_config):
|
|
||||||
def assert_missing(config):
|
|
||||||
assert (
|
|
||||||
"install_missing_compilers" not in config
|
|
||||||
or config["install_missing_compilers"] is False
|
|
||||||
)
|
|
||||||
|
|
||||||
def assert_present(config):
|
|
||||||
assert (
|
|
||||||
"install_missing_compilers" in config and config["install_missing_compilers"] is True
|
|
||||||
)
|
|
||||||
|
|
||||||
original_config = spack.config.get("config")
|
|
||||||
assert_missing(original_config)
|
|
||||||
|
|
||||||
ci.configure_compilers("FIND_ANY", scope="site")
|
|
||||||
|
|
||||||
second_config = spack.config.get("config")
|
|
||||||
assert_missing(second_config)
|
|
||||||
|
|
||||||
ci.configure_compilers("INSTALL_MISSING")
|
|
||||||
last_config = spack.config.get("config")
|
|
||||||
assert_present(last_config)
|
|
||||||
|
|
||||||
|
|
||||||
class FakeWebResponder(object):
|
class FakeWebResponder(object):
|
||||||
def __init__(self, response_code=200, content_to_read=[]):
|
def __init__(self, response_code=200, content_to_read=[]):
|
||||||
self._resp_code = response_code
|
self._resp_code = response_code
|
||||||
@ -248,7 +223,7 @@ def test_ci_workarounds():
|
|||||||
fake_root_spec = "x" * 544
|
fake_root_spec = "x" * 544
|
||||||
fake_spack_ref = "x" * 40
|
fake_spack_ref = "x" * 40
|
||||||
|
|
||||||
common_variables = {"SPACK_COMPILER_ACTION": "NONE", "SPACK_IS_PR_PIPELINE": "False"}
|
common_variables = {"SPACK_IS_PR_PIPELINE": "False"}
|
||||||
|
|
||||||
common_before_script = [
|
common_before_script = [
|
||||||
'git clone "https://github.com/spack/spack"',
|
'git clone "https://github.com/spack/spack"',
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
import spack
|
import spack
|
||||||
import spack.binary_distribution
|
import spack.binary_distribution
|
||||||
import spack.ci as ci
|
import spack.ci as ci
|
||||||
import spack.compilers as compilers
|
|
||||||
import spack.config
|
import spack.config
|
||||||
import spack.environment as ev
|
import spack.environment as ev
|
||||||
import spack.hash_types as ht
|
import spack.hash_types as ht
|
||||||
@ -30,7 +29,7 @@
|
|||||||
from spack.schema.buildcache_spec import schema as specfile_schema
|
from spack.schema.buildcache_spec import schema as specfile_schema
|
||||||
from spack.schema.ci import schema as ci_schema
|
from spack.schema.ci import schema as ci_schema
|
||||||
from spack.schema.database_index import schema as db_idx_schema
|
from spack.schema.database_index import schema as db_idx_schema
|
||||||
from spack.spec import CompilerSpec, Spec
|
from spack.spec import Spec
|
||||||
from spack.util.pattern import Bunch
|
from spack.util.pattern import Bunch
|
||||||
|
|
||||||
config_cmd = spack.main.SpackCommand("config")
|
config_cmd = spack.main.SpackCommand("config")
|
||||||
@ -163,8 +162,6 @@ def test_ci_generate_with_env(
|
|||||||
"""\
|
"""\
|
||||||
spack:
|
spack:
|
||||||
definitions:
|
definitions:
|
||||||
- bootstrap:
|
|
||||||
- cmake@3.4.3
|
|
||||||
- old-gcc-pkgs:
|
- old-gcc-pkgs:
|
||||||
- archive-files
|
- archive-files
|
||||||
- callpath
|
- callpath
|
||||||
@ -179,9 +176,6 @@ def test_ci_generate_with_env(
|
|||||||
mirrors:
|
mirrors:
|
||||||
some-mirror: {0}
|
some-mirror: {0}
|
||||||
ci:
|
ci:
|
||||||
bootstrap:
|
|
||||||
- name: bootstrap
|
|
||||||
compiler-agnostic: true
|
|
||||||
pipeline-gen:
|
pipeline-gen:
|
||||||
- submapping:
|
- submapping:
|
||||||
- match:
|
- match:
|
||||||
@ -221,16 +215,10 @@ def test_ci_generate_with_env(
|
|||||||
with open(outputfile) as f:
|
with open(outputfile) as f:
|
||||||
contents = f.read()
|
contents = f.read()
|
||||||
yaml_contents = syaml.load(contents)
|
yaml_contents = syaml.load(contents)
|
||||||
found_spec = False
|
|
||||||
for ci_key in yaml_contents.keys():
|
|
||||||
if "(bootstrap)" in ci_key:
|
|
||||||
found_spec = True
|
|
||||||
assert "cmake" in ci_key
|
|
||||||
assert found_spec
|
|
||||||
assert "stages" in yaml_contents
|
assert "stages" in yaml_contents
|
||||||
assert len(yaml_contents["stages"]) == 6
|
assert len(yaml_contents["stages"]) == 5
|
||||||
assert yaml_contents["stages"][0] == "stage-0"
|
assert yaml_contents["stages"][0] == "stage-0"
|
||||||
assert yaml_contents["stages"][5] == "stage-rebuild-index"
|
assert yaml_contents["stages"][4] == "stage-rebuild-index"
|
||||||
|
|
||||||
assert "rebuild-index" in yaml_contents
|
assert "rebuild-index" in yaml_contents
|
||||||
rebuild_job = yaml_contents["rebuild-index"]
|
rebuild_job = yaml_contents["rebuild-index"]
|
||||||
@ -244,155 +232,6 @@ def test_ci_generate_with_env(
|
|||||||
assert artifacts_root == "jobs_scratch_dir"
|
assert artifacts_root == "jobs_scratch_dir"
|
||||||
|
|
||||||
|
|
||||||
def _validate_needs_graph(yaml_contents, needs_graph, artifacts):
|
|
||||||
"""Validate the needs graph in the generate CI"""
|
|
||||||
|
|
||||||
# TODO: Fix the logic to catch errors where expected packages/needs are not
|
|
||||||
# found.
|
|
||||||
for job_name, job_def in yaml_contents.items():
|
|
||||||
for needs_def_name, needs_list in needs_graph.items():
|
|
||||||
if job_name.startswith(needs_def_name):
|
|
||||||
# check job needs against the expected needs definition
|
|
||||||
j_needs = job_def["needs"]
|
|
||||||
assert all(
|
|
||||||
[
|
|
||||||
job_needs["job"][: job_needs["job"].index("/")] in needs_list
|
|
||||||
for job_needs in j_needs
|
|
||||||
]
|
|
||||||
)
|
|
||||||
assert all(
|
|
||||||
[nl in [n["job"][: n["job"].index("/")] for n in j_needs] for nl in needs_list]
|
|
||||||
)
|
|
||||||
assert all([job_needs["artifacts"] == artifacts for job_needs in j_needs])
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
def test_ci_generate_bootstrap_gcc(
|
|
||||||
tmpdir, working_env, mutable_mock_env_path, install_mockery, mock_packages, ci_base_environment
|
|
||||||
):
|
|
||||||
"""Test that we can bootstrap a compiler and use it as the
|
|
||||||
compiler for a spec in the environment"""
|
|
||||||
filename = str(tmpdir.join("spack.yaml"))
|
|
||||||
with open(filename, "w") as f:
|
|
||||||
f.write(
|
|
||||||
"""\
|
|
||||||
spack:
|
|
||||||
definitions:
|
|
||||||
- bootstrap:
|
|
||||||
- gcc@3.0
|
|
||||||
specs:
|
|
||||||
- dyninst%gcc@=3.0
|
|
||||||
mirrors:
|
|
||||||
some-mirror: https://my.fake.mirror
|
|
||||||
ci:
|
|
||||||
bootstrap:
|
|
||||||
- name: bootstrap
|
|
||||||
compiler-agnostic: true
|
|
||||||
pipeline-gen:
|
|
||||||
- submapping:
|
|
||||||
- match:
|
|
||||||
- arch=test-debian6-x86_64
|
|
||||||
build-job:
|
|
||||||
tags:
|
|
||||||
- donotcare
|
|
||||||
- match:
|
|
||||||
- arch=test-debian6-aarch64
|
|
||||||
build-job:
|
|
||||||
tags:
|
|
||||||
- donotcare
|
|
||||||
- any-job:
|
|
||||||
tags:
|
|
||||||
- donotcare
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
needs_graph = {
|
|
||||||
"(bootstrap) conflict": [],
|
|
||||||
"(bootstrap) gcc": ["(bootstrap) conflict"],
|
|
||||||
"(specs) libelf": ["(bootstrap) gcc"],
|
|
||||||
"(specs) libdwarf": ["(bootstrap) gcc", "(specs) libelf"],
|
|
||||||
"(specs) dyninst": ["(bootstrap) gcc", "(specs) libelf", "(specs) libdwarf"],
|
|
||||||
}
|
|
||||||
|
|
||||||
with tmpdir.as_cwd():
|
|
||||||
env_cmd("create", "test", "./spack.yaml")
|
|
||||||
outputfile = str(tmpdir.join(".gitlab-ci.yml"))
|
|
||||||
|
|
||||||
with ev.read("test"):
|
|
||||||
ci_cmd("generate", "--output-file", outputfile)
|
|
||||||
|
|
||||||
with open(outputfile) as f:
|
|
||||||
contents = f.read()
|
|
||||||
yaml_contents = syaml.load(contents)
|
|
||||||
_validate_needs_graph(yaml_contents, needs_graph, False)
|
|
||||||
|
|
||||||
|
|
||||||
def test_ci_generate_bootstrap_artifacts_buildcache(
|
|
||||||
tmpdir, working_env, mutable_mock_env_path, install_mockery, mock_packages, ci_base_environment
|
|
||||||
):
|
|
||||||
"""Test that we can bootstrap a compiler when artifacts buildcache
|
|
||||||
is turned on"""
|
|
||||||
filename = str(tmpdir.join("spack.yaml"))
|
|
||||||
with open(filename, "w") as f:
|
|
||||||
f.write(
|
|
||||||
"""\
|
|
||||||
spack:
|
|
||||||
definitions:
|
|
||||||
- bootstrap:
|
|
||||||
- gcc@3.0
|
|
||||||
specs:
|
|
||||||
- dyninst%gcc@=3.0
|
|
||||||
mirrors:
|
|
||||||
some-mirror: https://my.fake.mirror
|
|
||||||
ci:
|
|
||||||
bootstrap:
|
|
||||||
- name: bootstrap
|
|
||||||
compiler-agnostic: true
|
|
||||||
pipeline-gen:
|
|
||||||
- submapping:
|
|
||||||
- match:
|
|
||||||
- arch=test-debian6-x86_64
|
|
||||||
build-job:
|
|
||||||
tags:
|
|
||||||
- donotcare
|
|
||||||
- match:
|
|
||||||
- arch=test-debian6-aarch64
|
|
||||||
build-job:
|
|
||||||
tags:
|
|
||||||
- donotcare
|
|
||||||
- any-job:
|
|
||||||
tags:
|
|
||||||
- donotcare
|
|
||||||
enable-artifacts-buildcache: True
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
needs_graph = {
|
|
||||||
"(bootstrap) conflict": [],
|
|
||||||
"(bootstrap) gcc": ["(bootstrap) conflict"],
|
|
||||||
"(specs) libelf": ["(bootstrap) gcc", "(bootstrap) conflict"],
|
|
||||||
"(specs) libdwarf": ["(bootstrap) gcc", "(bootstrap) conflict", "(specs) libelf"],
|
|
||||||
"(specs) dyninst": [
|
|
||||||
"(bootstrap) gcc",
|
|
||||||
"(bootstrap) conflict",
|
|
||||||
"(specs) libelf",
|
|
||||||
"(specs) libdwarf",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
with tmpdir.as_cwd():
|
|
||||||
env_cmd("create", "test", "./spack.yaml")
|
|
||||||
outputfile = str(tmpdir.join(".gitlab-ci.yml"))
|
|
||||||
|
|
||||||
with ev.read("test"):
|
|
||||||
ci_cmd("generate", "--output-file", outputfile)
|
|
||||||
|
|
||||||
with open(outputfile) as f:
|
|
||||||
contents = f.read()
|
|
||||||
yaml_contents = syaml.load(contents)
|
|
||||||
_validate_needs_graph(yaml_contents, needs_graph, True)
|
|
||||||
|
|
||||||
|
|
||||||
def test_ci_generate_with_env_missing_section(
|
def test_ci_generate_with_env_missing_section(
|
||||||
tmpdir,
|
tmpdir,
|
||||||
working_env,
|
working_env,
|
||||||
@ -889,7 +728,7 @@ def activate_rebuild_env(tmpdir, pkg_name, rebuild_env):
|
|||||||
"SPACK_JOB_SPEC_DAG_HASH": rebuild_env.root_spec_dag_hash,
|
"SPACK_JOB_SPEC_DAG_HASH": rebuild_env.root_spec_dag_hash,
|
||||||
"SPACK_JOB_SPEC_PKG_NAME": pkg_name,
|
"SPACK_JOB_SPEC_PKG_NAME": pkg_name,
|
||||||
"SPACK_COMPILER_ACTION": "NONE",
|
"SPACK_COMPILER_ACTION": "NONE",
|
||||||
"SPACK_CDASH_BUILD_NAME": "(specs) {0}".format(pkg_name),
|
"SPACK_CDASH_BUILD_NAME": pkg_name,
|
||||||
"SPACK_REMOTE_MIRROR_URL": rebuild_env.mirror_url,
|
"SPACK_REMOTE_MIRROR_URL": rebuild_env.mirror_url,
|
||||||
"SPACK_PIPELINE_TYPE": "spack_protected_branch",
|
"SPACK_PIPELINE_TYPE": "spack_protected_branch",
|
||||||
"CI_JOB_URL": rebuild_env.ci_job_url,
|
"CI_JOB_URL": rebuild_env.ci_job_url,
|
||||||
@ -1283,7 +1122,7 @@ def test_push_mirror_contents(
|
|||||||
found_spec_job = False
|
found_spec_job = False
|
||||||
|
|
||||||
for ci_key in yaml_contents.keys():
|
for ci_key in yaml_contents.keys():
|
||||||
if "(specs) patchelf" in ci_key:
|
if "patchelf" in ci_key:
|
||||||
the_elt = yaml_contents[ci_key]
|
the_elt = yaml_contents[ci_key]
|
||||||
assert "variables" in the_elt
|
assert "variables" in the_elt
|
||||||
job_vars = the_elt["variables"]
|
job_vars = the_elt["variables"]
|
||||||
@ -1457,7 +1296,7 @@ def test_ci_generate_override_runner_attrs(
|
|||||||
assert global_vars["SPACK_CHECKOUT_VERSION"] == "12ad69eb1"
|
assert global_vars["SPACK_CHECKOUT_VERSION"] == "12ad69eb1"
|
||||||
|
|
||||||
for ci_key in yaml_contents.keys():
|
for ci_key in yaml_contents.keys():
|
||||||
if "(specs) a" in ci_key:
|
if ci_key.startswith("a"):
|
||||||
# Make sure a's attributes override variables, and all the
|
# Make sure a's attributes override variables, and all the
|
||||||
# scripts. Also, make sure the 'toplevel' tag doesn't
|
# scripts. Also, make sure the 'toplevel' tag doesn't
|
||||||
# appear twice, but that a's specific extra tag does appear
|
# appear twice, but that a's specific extra tag does appear
|
||||||
@ -1477,7 +1316,7 @@ def test_ci_generate_override_runner_attrs(
|
|||||||
assert the_elt["script"][0] == "custom main step"
|
assert the_elt["script"][0] == "custom main step"
|
||||||
assert len(the_elt["after_script"]) == 1
|
assert len(the_elt["after_script"]) == 1
|
||||||
assert the_elt["after_script"][0] == "custom post step one"
|
assert the_elt["after_script"][0] == "custom post step one"
|
||||||
if "(specs) dependency-install" in ci_key:
|
if "dependency-install" in ci_key:
|
||||||
# Since the dependency-install match omits any
|
# Since the dependency-install match omits any
|
||||||
# runner-attributes, make sure it inherited all the
|
# runner-attributes, make sure it inherited all the
|
||||||
# top-level attributes.
|
# top-level attributes.
|
||||||
@ -1495,7 +1334,7 @@ def test_ci_generate_override_runner_attrs(
|
|||||||
assert the_elt["script"][0] == "main step"
|
assert the_elt["script"][0] == "main step"
|
||||||
assert len(the_elt["after_script"]) == 1
|
assert len(the_elt["after_script"]) == 1
|
||||||
assert the_elt["after_script"][0] == "post step one"
|
assert the_elt["after_script"][0] == "post step one"
|
||||||
if "(specs) flatten-deps" in ci_key:
|
if "flatten-deps" in ci_key:
|
||||||
# The flatten-deps match specifies that we keep the two
|
# The flatten-deps match specifies that we keep the two
|
||||||
# top level variables, but add a third specifc one. It
|
# top level variables, but add a third specifc one. It
|
||||||
# also adds a custom tag which should be combined with
|
# also adds a custom tag which should be combined with
|
||||||
@ -1554,9 +1393,10 @@ def test_ci_generate_with_workarounds(
|
|||||||
yaml_contents = syaml.load(contents)
|
yaml_contents = syaml.load(contents)
|
||||||
|
|
||||||
found_one = False
|
found_one = False
|
||||||
|
non_rebuild_keys = ["workflow", "stages", "variables", "rebuild-index"]
|
||||||
|
|
||||||
for ci_key in yaml_contents.keys():
|
for ci_key in yaml_contents.keys():
|
||||||
if ci_key.startswith("(specs) "):
|
if ci_key not in non_rebuild_keys:
|
||||||
found_one = True
|
found_one = True
|
||||||
job_obj = yaml_contents[ci_key]
|
job_obj = yaml_contents[ci_key]
|
||||||
assert "needs" not in job_obj
|
assert "needs" not in job_obj
|
||||||
@ -1623,140 +1463,6 @@ def test_ci_rebuild_index(
|
|||||||
jsonschema.validate(index_object, db_idx_schema)
|
jsonschema.validate(index_object, db_idx_schema)
|
||||||
|
|
||||||
|
|
||||||
def test_ci_generate_bootstrap_prune_dag(
|
|
||||||
install_mockery_mutable_config,
|
|
||||||
mock_packages,
|
|
||||||
mock_fetch,
|
|
||||||
mock_archive,
|
|
||||||
mutable_config,
|
|
||||||
monkeypatch,
|
|
||||||
tmpdir,
|
|
||||||
mutable_mock_env_path,
|
|
||||||
ci_base_environment,
|
|
||||||
):
|
|
||||||
"""Test compiler bootstrapping with DAG pruning. Specifically, make
|
|
||||||
sure that if we detect the bootstrapped compiler needs to be rebuilt,
|
|
||||||
we ensure the spec we want to build with that compiler is scheduled
|
|
||||||
for rebuild as well."""
|
|
||||||
|
|
||||||
# Create a temp mirror directory for buildcache usage
|
|
||||||
mirror_dir = tmpdir.join("mirror_dir")
|
|
||||||
mirror_url = "file://{0}".format(mirror_dir.strpath)
|
|
||||||
|
|
||||||
# Install a compiler, because we want to put it in a buildcache
|
|
||||||
install_cmd("gcc@=12.2.0%gcc@10.2.1")
|
|
||||||
|
|
||||||
# Put installed compiler in the buildcache
|
|
||||||
buildcache_cmd("push", "-u", "-a", "-f", mirror_dir.strpath, "gcc@12.2.0%gcc@10.2.1")
|
|
||||||
|
|
||||||
# Now uninstall the compiler
|
|
||||||
uninstall_cmd("-y", "gcc@12.2.0%gcc@10.2.1")
|
|
||||||
|
|
||||||
monkeypatch.setattr(spack.concretize.Concretizer, "check_for_compiler_existence", False)
|
|
||||||
spack.config.set("config:install_missing_compilers", True)
|
|
||||||
assert CompilerSpec("gcc@=12.2.0") not in compilers.all_compiler_specs()
|
|
||||||
|
|
||||||
# Configure the mirror where we put that buildcache w/ the compiler
|
|
||||||
mirror_cmd("add", "test-mirror", mirror_url)
|
|
||||||
|
|
||||||
install_cmd("--no-check-signature", "b%gcc@=12.2.0")
|
|
||||||
|
|
||||||
# Put spec built with installed compiler in the buildcache
|
|
||||||
buildcache_cmd("push", "-u", "-a", "-f", mirror_dir.strpath, "b%gcc@12.2.0")
|
|
||||||
|
|
||||||
# Now uninstall the spec
|
|
||||||
uninstall_cmd("-y", "b%gcc@12.2.0")
|
|
||||||
|
|
||||||
filename = str(tmpdir.join("spack.yaml"))
|
|
||||||
with open(filename, "w") as f:
|
|
||||||
f.write(
|
|
||||||
"""\
|
|
||||||
spack:
|
|
||||||
definitions:
|
|
||||||
- bootstrap:
|
|
||||||
- gcc@=12.2.0%gcc@10.2.1
|
|
||||||
specs:
|
|
||||||
- b%gcc@12.2.0
|
|
||||||
mirrors:
|
|
||||||
atestm: {0}
|
|
||||||
ci:
|
|
||||||
bootstrap:
|
|
||||||
- name: bootstrap
|
|
||||||
compiler-agnostic: true
|
|
||||||
pipeline-gen:
|
|
||||||
- submapping:
|
|
||||||
- match:
|
|
||||||
- arch=test-debian6-x86_64
|
|
||||||
build-job:
|
|
||||||
tags:
|
|
||||||
- donotcare
|
|
||||||
- match:
|
|
||||||
- arch=test-debian6-core2
|
|
||||||
build-job:
|
|
||||||
tags:
|
|
||||||
- meh
|
|
||||||
- match:
|
|
||||||
- arch=test-debian6-aarch64
|
|
||||||
build-job:
|
|
||||||
tags:
|
|
||||||
- donotcare
|
|
||||||
- match:
|
|
||||||
- arch=test-debian6-m1
|
|
||||||
build-job:
|
|
||||||
tags:
|
|
||||||
- meh
|
|
||||||
""".format(
|
|
||||||
mirror_url
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Without this monkeypatch, pipeline generation process would think that
|
|
||||||
# nothing in the environment needs rebuilding. With the monkeypatch, the
|
|
||||||
# process sees the compiler as needing a rebuild, which should then result
|
|
||||||
# in the specs built with that compiler needing a rebuild too.
|
|
||||||
def fake_get_mirrors_for_spec(spec=None, mirrors_to_check=None, index_only=False):
|
|
||||||
if spec.name == "gcc":
|
|
||||||
return []
|
|
||||||
else:
|
|
||||||
return [{"spec": spec, "mirror_url": mirror_url}]
|
|
||||||
|
|
||||||
with tmpdir.as_cwd():
|
|
||||||
env_cmd("create", "test", "./spack.yaml")
|
|
||||||
outputfile = str(tmpdir.join(".gitlab-ci.yml"))
|
|
||||||
|
|
||||||
with ev.read("test"):
|
|
||||||
ci_cmd("generate", "--output-file", outputfile)
|
|
||||||
|
|
||||||
with open(outputfile) as of:
|
|
||||||
yaml_contents = of.read()
|
|
||||||
original_yaml_contents = syaml.load(yaml_contents)
|
|
||||||
|
|
||||||
# without the monkeypatch, everything appears up to date and no
|
|
||||||
# rebuild jobs are generated.
|
|
||||||
assert original_yaml_contents
|
|
||||||
assert "no-specs-to-rebuild" in original_yaml_contents
|
|
||||||
|
|
||||||
monkeypatch.setattr(
|
|
||||||
spack.binary_distribution, "get_mirrors_for_spec", fake_get_mirrors_for_spec
|
|
||||||
)
|
|
||||||
|
|
||||||
ci_cmd("generate", "--output-file", outputfile)
|
|
||||||
|
|
||||||
with open(outputfile) as of:
|
|
||||||
yaml_contents = of.read()
|
|
||||||
new_yaml_contents = syaml.load(yaml_contents)
|
|
||||||
|
|
||||||
assert new_yaml_contents
|
|
||||||
|
|
||||||
# This 'needs' graph reflects that even though specs 'a' and 'b' do
|
|
||||||
# not otherwise need to be rebuilt (thanks to DAG pruning), they
|
|
||||||
# both end up in the generated pipeline because the compiler they
|
|
||||||
# depend on is bootstrapped, and *does* need to be rebuilt.
|
|
||||||
needs_graph = {"(bootstrap) gcc": [], "(specs) b": ["(bootstrap) gcc"]}
|
|
||||||
|
|
||||||
_validate_needs_graph(new_yaml_contents, needs_graph, False)
|
|
||||||
|
|
||||||
|
|
||||||
def test_ci_get_stack_changed(mock_git_repo, monkeypatch):
|
def test_ci_get_stack_changed(mock_git_repo, monkeypatch):
|
||||||
"""Test that we can detect the change to .gitlab-ci.yml in a
|
"""Test that we can detect the change to .gitlab-ci.yml in a
|
||||||
mock spack git repo."""
|
mock spack git repo."""
|
||||||
@ -1828,7 +1534,7 @@ def fake_stack_changed(env_path, rev1="HEAD^", rev2="HEAD"):
|
|||||||
generated_hashes = []
|
generated_hashes = []
|
||||||
|
|
||||||
for ci_key in yaml_contents.keys():
|
for ci_key in yaml_contents.keys():
|
||||||
if ci_key.startswith("(specs)"):
|
if "variables" in yaml_contents[ci_key]:
|
||||||
generated_hashes.append(
|
generated_hashes.append(
|
||||||
yaml_contents[ci_key]["variables"]["SPACK_JOB_SPEC_DAG_HASH"]
|
yaml_contents[ci_key]["variables"]["SPACK_JOB_SPEC_DAG_HASH"]
|
||||||
)
|
)
|
||||||
@ -2240,9 +1946,7 @@ def test_ci_reproduce(
|
|||||||
ci_cmd("generate", "--output-file", pipeline_path, "--artifacts-root", artifacts_root)
|
ci_cmd("generate", "--output-file", pipeline_path, "--artifacts-root", artifacts_root)
|
||||||
|
|
||||||
target_name = spack.platforms.test.Test.default
|
target_name = spack.platforms.test.Test.default
|
||||||
job_name = ci.get_job_name(
|
job_name = ci.get_job_name(job_spec, "test-debian6-%s" % target_name, None)
|
||||||
"specs", False, job_spec, "test-debian6-%s" % target_name, None
|
|
||||||
)
|
|
||||||
|
|
||||||
repro_file = os.path.join(working_dir.strpath, "repro.json")
|
repro_file = os.path.join(working_dir.strpath, "repro.json")
|
||||||
repro_details = {
|
repro_details = {
|
||||||
@ -2309,8 +2013,6 @@ def test_cmd_first_line():
|
|||||||
legacy_spack_yaml_contents = """
|
legacy_spack_yaml_contents = """
|
||||||
spack:
|
spack:
|
||||||
definitions:
|
definitions:
|
||||||
- bootstrap:
|
|
||||||
- cmake@3.4.3
|
|
||||||
- old-gcc-pkgs:
|
- old-gcc-pkgs:
|
||||||
- archive-files
|
- archive-files
|
||||||
- callpath
|
- callpath
|
||||||
@ -2325,9 +2027,6 @@ def test_cmd_first_line():
|
|||||||
mirrors:
|
mirrors:
|
||||||
test-mirror: file:///some/fake/mirror
|
test-mirror: file:///some/fake/mirror
|
||||||
{0}:
|
{0}:
|
||||||
bootstrap:
|
|
||||||
- name: bootstrap
|
|
||||||
compiler-agnostic: true
|
|
||||||
match_behavior: first
|
match_behavior: first
|
||||||
mappings:
|
mappings:
|
||||||
- match:
|
- match:
|
||||||
@ -2379,16 +2078,10 @@ def test_gitlab_ci_deprecated(
|
|||||||
contents = f.read()
|
contents = f.read()
|
||||||
yaml_contents = syaml.load(contents)
|
yaml_contents = syaml.load(contents)
|
||||||
|
|
||||||
found_spec = False
|
|
||||||
for ci_key in yaml_contents.keys():
|
|
||||||
if "(bootstrap)" in ci_key:
|
|
||||||
found_spec = True
|
|
||||||
assert "cmake" in ci_key
|
|
||||||
assert found_spec
|
|
||||||
assert "stages" in yaml_contents
|
assert "stages" in yaml_contents
|
||||||
assert len(yaml_contents["stages"]) == 6
|
assert len(yaml_contents["stages"]) == 5
|
||||||
assert yaml_contents["stages"][0] == "stage-0"
|
assert yaml_contents["stages"][0] == "stage-0"
|
||||||
assert yaml_contents["stages"][5] == "stage-rebuild-index"
|
assert yaml_contents["stages"][4] == "stage-rebuild-index"
|
||||||
|
|
||||||
assert "rebuild-index" in yaml_contents
|
assert "rebuild-index" in yaml_contents
|
||||||
rebuild_job = yaml_contents["rebuild-index"]
|
rebuild_job = yaml_contents["rebuild-index"]
|
||||||
|
Loading…
Reference in New Issue
Block a user