Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

self-hosted spack, or container spackOS #42082

Draft
wants to merge 54 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
fd6a43c
in-progress bootstrap setup
trws Dec 19, 2023
f6760d9
add libstdcxx
trws Dec 28, 2023
d2c4622
first successful gcc
trws Jan 3, 2024
0206d4d
allow setting a spackos through env for now
trws Jan 4, 2024
5f222cf
better gcc flag handling for the stage1 compiler
trws Jan 4, 2024
bacd8a0
cc: work around -v split between ld and ccld
trws Jan 10, 2024
11ee3b6
almost complete stage 3 gcc, should be fully independent now
trws Jan 10, 2024
defd831
avoid glibc cycle through gcc-runtime
trws Jan 16, 2024
1d9935a
gcc: inject -B into configure to build runtime libs
trws Jan 10, 2024
89ddde9
hide build deps from glibc, nasty but necessary
trws Jan 10, 2024
13c1f71
ensure stage1 does not try to build with threads
trws Jan 10, 2024
4f88d18
fix python library searching
trws Jan 10, 2024
fe582b5
tweak env, fix linux-pam build
trws Jan 11, 2024
3eff942
ensure headers are copied into place in glibc
trws Jan 12, 2024
f55d929
patch system paths out of gcc when built for spackos
trws Jan 12, 2024
ee01f52
first version of spack stage environment files
trws Jan 12, 2024
d55350f
switch sysroot variant to stage0 since we do not need the value
trws Jan 12, 2024
b4ca46a
stage0 refactor complete, successful build
trws Jan 13, 2024
21d4088
linux-headers needs to be NoDep
trws Jan 14, 2024
7e78e25
refactor up through stage2
trws Jan 14, 2024
37fa8f9
fix up glibc/libxcrypt include handling to use idirafter
trws Jan 14, 2024
e03009d
version 2 full bootstrap
trws Jan 15, 2024
0620bd9
fix broken stage4 path for docker, add dockerfile
trws Jan 15, 2024
515e2cd
mark more things as build-tools
trws Jan 15, 2024
fcfdbfd
tweak glibc deps
trws Jan 15, 2024
0650369
avoid python using system paths
trws Jan 17, 2024
4edd273
munge only installs a pkgconfig if /usr/lib/pkgconfig or similar exis…
trws Jan 17, 2024
594c5fc
add some bootstrap automation, use docker to make it reasonably repro…
trws Jan 17, 2024
73ca288
refactor bootstrap to work on multiple architectures
trws Jan 17, 2024
a20e337
add padding and fix ARCH replacement
trws Jan 17, 2024
a2be05a
make it easier to drop into the docker env
trws Jan 17, 2024
6466325
break cycle for gnuconfig dep
trws Jan 17, 2024
9eb2bb9
gcc needs crypt for libsanitizer
trws Jan 17, 2024
1b846b7
fix bootstrap
trws Jan 18, 2024
0494a52
do not use +bootstrap on gcc, too fragile
trws Jan 18, 2024
1bbf7d3
add iconv and remove vestigial versions list from glibc
trws Jan 22, 2024
2396517
krb5 needs libedit, only found during a highly parallel build of boot…
trws Jan 22, 2024
73d42e1
convert to a depfile/make based bootstrap
trws Jan 22, 2024
62f1bfa
move bootstraps to a more reasonable location
trws Jan 22, 2024
dccfd3b
add comment to glibc about linux-headers dep type
trws Jan 22, 2024
c0e64e7
update stage numbers, fix up makefile
trws Jan 23, 2024
d625d4a
perl, allow wrappers to work in perl packages built with spack perl
trws Jan 23, 2024
9dfe50e
first step to getting llvm building and working on spackos, builds a …
trws Jan 23, 2024
7035097
actually add top-level makefile, thanks @alalazo
trws Jan 24, 2024
9b755d9
Update share/spack/spackos-bootstrap/stage4/spack.in.yaml
trws Jan 25, 2024
4b52293
Update share/spack/spackos-bootstrap/stage4/spack.in.yaml
trws Jan 25, 2024
33f073f
fix PATH to point to stage1 view
trws Jan 25, 2024
ffbf3ad
fix write permission for owner on cpanm
trws Jan 25, 2024
40b36aa
update paths in dockerfile
trws Jan 26, 2024
1d048b3
add add nodep target for the container
trws Jan 26, 2024
95c8cf6
clean up stage packages
trws Jan 26, 2024
cf03ae1
remove gettext and texinfo from the bottom of the stack by default
trws Jan 29, 2024
03c4119
fix gcc-runtime behavior by version-naming each compiler
trws Jan 30, 2024
d853e0f
Merge remote-tracking branch 'upstream/develop' into bootstrap-test
trws May 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 15 additions & 14 deletions lib/spack/env/cc
Original file line number Diff line number Diff line change
Expand Up @@ -274,20 +274,6 @@ case "$command" in
;;
esac

# If any of the arguments below are present, then the mode is vcheck.
# In vcheck mode, nothing is added in terms of extra search paths or
# libraries.
if [ -z "$mode" ] || [ "$mode" = ld ]; then
for arg in "$@"; do
case $arg in
-v|-V|--version|-dumpversion)
mode=vcheck
break
;;
esac
done
fi

# Finish setting up the mode.
if [ -z "$mode" ]; then
mode=ccld
Expand All @@ -305,6 +291,21 @@ if [ -z "$mode" ]; then
done
fi

# If any of the arguments below are present, then the mode is vcheck.
# In vcheck mode, nothing is added in terms of extra search paths or
# libraries.
if [ -z "$mode" ] || [ "$mode" = ld ]; then
for arg in "$@"; do
case $arg in
-v|-V|--version|-dumpversion)
mode=vcheck
break
;;
esac
done
fi


# This is needed to ensure we set RPATH instead of RUNPATH
# (or the opposite, depending on the configuration in config.yaml)
#
Expand Down
18 changes: 18 additions & 0 deletions lib/spack/spack/build_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,21 @@ def set_compiler_environment_variables(pkg, env):
env_flags[flag] = envf or []
build_system_flags[flag] = bsf or []

if 'spack' in pkg.spec.os and '^glibc' in pkg.spec:
ldf = inject_flags.get('ldflags', [])
ldf.append(pkg.spec["glibc"].package.dynamic_linker_flag)
cxxf = inject_flags.get('cxxflags', [])
cppf = inject_flags.get('cppflags', [])
cppf.append("-B" + pkg.spec["glibc"].prefix.lib)
cppf.append("-idirafter")
cppf.append(pkg.spec["glibc"].prefix.include)
cppf.append("-idirafter")
cppf.append(pkg.spec["libxcrypt"].prefix.include)

inject_flags['ldflags'] = ldf
inject_flags['cppflags'] = cppf
inject_flags['cxxflags'] = cxxf

# Place compiler flags as specified by flag_handler
for flag in spack.spec.FlagMap.valid_compiler_flags():
# Concreteness guarantees key safety here
Expand Down Expand Up @@ -551,6 +566,9 @@ def update_compiler_args_for_dep(dep):
include_dirs = list(dedupe(filter_system_paths(include_dirs)))
rpath_dirs = list(dedupe(filter_system_paths(rpath_dirs)))

# NOTE: these are handled by the idirafter above
include_dirs = [p for p in include_dirs if 'glibc' not in p and 'libxcrypt' not in p]

# Spack managed directories include the stage, store and upstream stores. We extend this with
# their real paths to make it more robust (e.g. /tmp vs /private/tmp on macOS).
spack_managed_dirs: Set[str] = {
Expand Down
10 changes: 8 additions & 2 deletions lib/spack/spack/build_systems/autotools.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
)


class AutotoolsPackage(spack.package_base.PackageBase):
class AutotoolsPackageNoDep(spack.package_base.PackageBaseNoDep):
"""Specialized class for packages built using GNU Autotools."""

#: This attribute is used in UI queries that need to know the build
Expand All @@ -46,7 +46,6 @@ class AutotoolsPackage(spack.package_base.PackageBase):
depends_on("gnuconfig", type="build", when="target=ppc64le:")
depends_on("gnuconfig", type="build", when="target=aarch64:")
depends_on("gnuconfig", type="build", when="target=riscv64:")
depends_on("gmake", type="build")
conflicts("platform=windows")

def flags_to_build_system_args(self, flags):
Expand Down Expand Up @@ -854,3 +853,10 @@ def _autoreconf_search_path_args(spec):
flags = flags_external if dep.external else flags_spack
flags.extend(["-I", path])
return flags_spack + flags_external


class AutotoolsPackage(AutotoolsPackageNoDep):
spack.package_base.add_base_deps()
# NOTE: this is here to avoid glibc depending on gmake
with when("build_system=autotools"):
depends_on("gmake", type="build")
6 changes: 5 additions & 1 deletion lib/spack/spack/build_systems/bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import spack.package_base


class BundlePackage(spack.package_base.PackageBase):
class BundlePackageNoDep(spack.package_base.PackageBaseNoDep):
"""General purpose bundle, or no-code, package class."""

#: This attribute is used in UI queries that require to know which
Expand All @@ -23,6 +23,10 @@ class BundlePackage(spack.package_base.PackageBase):
spack.directives.build_system("bundle")


class BundlePackage(BundlePackageNoDep):
spack.package_base.add_base_deps()


@spack.builder.builder("bundle")
class BundleBuilder(spack.builder.Builder):
phases = ("install",)
Expand Down
6 changes: 5 additions & 1 deletion lib/spack/spack/build_systems/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from ._checks import BaseBuilder, apply_macos_rpath_fixups, execute_install_time_tests


class Package(spack.package_base.PackageBase):
class PackageNoDep(spack.package_base.PackageBaseNoDep):
"""General purpose class with a single ``install`` phase that needs to be
coded by packagers.
"""
Expand All @@ -25,6 +25,10 @@ class Package(spack.package_base.PackageBase):
spack.directives.build_system("generic")


class Package(PackageNoDep):
spack.package_base.add_base_deps()


@spack.builder.builder("generic")
class GenericBuilder(BaseBuilder):
"""A builder for a generic build system, that require packagers
Expand Down
6 changes: 5 additions & 1 deletion lib/spack/spack/build_systems/gnu.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import spack.util.url


class GNUMirrorPackage(spack.package_base.PackageBase):
class GNUMirrorPackageNoDep(spack.package_base.PackageBaseNoDep):
"""Mixin that takes care of setting url and mirrors for GNU packages."""

#: Path of the package in a GNU mirror
Expand Down Expand Up @@ -37,3 +37,7 @@ def _ensure_gnu_mirror_path_is_set_or_raise(self):
cls_name = type(self).__name__
msg = "{0} must define a `gnu_mirror_path` attribute" " [none defined]"
raise AttributeError(msg.format(cls_name))


class GNUMirrorPackage(GNUMirrorPackageNoDep):
spack.package_base.add_base_deps()
38 changes: 19 additions & 19 deletions lib/spack/spack/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,7 @@ def __init__(self, pkg: "spack.package_base.PackageBase", install_args: dict):
install_args: the install arguments associated with ``pkg``
"""
# Ensure dealing with a package that has a concrete spec
if not isinstance(pkg, spack.package_base.PackageBase):
if not isinstance(pkg, spack.package_base.PackageBaseNoDep):
raise ValueError(f"{str(pkg)} must be a package")

self.pkg = pkg
Expand Down Expand Up @@ -824,7 +824,7 @@ def _add_default_args(self) -> None:
]:
_ = self.install_args.setdefault(arg, default)

def get_depflags(self, pkg: "spack.package_base.PackageBase") -> int:
def get_depflags(self, pkg: "spack.package_base.PackageBaseNoDep") -> int:
"""Determine the required dependency types for the associated package.

Args:
Expand Down Expand Up @@ -856,7 +856,7 @@ def has_dependency(self, dep_id) -> bool:
of the requested package, ``False`` otherwise."""
return dep_id in self.dependencies

def run_tests(self, pkg: "spack.package_base.PackageBase") -> bool:
def run_tests(self, pkg: "spack.package_base.PackageBaseNoDep") -> bool:
"""Determine if the tests should be run for the provided packages

Args:
Expand Down Expand Up @@ -899,7 +899,7 @@ class BuildTask:

def __init__(
self,
pkg: "spack.package_base.PackageBase",
pkg: "spack.package_base.PackageBaseNoDep",
request: Optional[BuildRequest],
compiler: bool,
start: float,
Expand All @@ -923,7 +923,7 @@ def __init__(
"""

# Ensure dealing with a package that has a concrete spec
if not isinstance(pkg, spack.package_base.PackageBase):
if not isinstance(pkg, spack.package_base.PackageBaseNoDep):
raise ValueError(f"{str(pkg)} must be a package")

self.pkg = pkg
Expand Down Expand Up @@ -1130,7 +1130,7 @@ class PackageInstaller:
instance.
"""

def __init__(self, installs: List[Tuple["spack.package_base.PackageBase", dict]] = []) -> None:
def __init__(self, installs: List[Tuple["spack.package_base.PackageBaseNoDep", dict]] = []) -> None:
"""Initialize the installer.

Args:
Expand Down Expand Up @@ -1185,7 +1185,7 @@ def _add_bootstrap_compilers(
self,
compiler: "spack.spec.CompilerSpec",
architecture: "spack.spec.ArchSpec",
pkgs: List["spack.package_base.PackageBase"],
pkgs: List["spack.package_base.PackageBaseNoDep"],
request: BuildRequest,
all_deps,
) -> None:
Expand Down Expand Up @@ -1230,7 +1230,7 @@ def _modify_existing_task(self, pkgid: str, attr, value) -> None:

def _add_init_task(
self,
pkg: "spack.package_base.PackageBase",
pkg: "spack.package_base.PackageBaseNoDep",
request: Optional[BuildRequest],
is_compiler: bool,
all_deps: Dict[str, Set[str]],
Expand Down Expand Up @@ -1410,7 +1410,7 @@ def _cleanup_failed(self, pkg_id: str) -> None:
except Exception as exc:
tty.warn(err.format(exc.__class__.__name__, pkg_id, str(exc)))

def _cleanup_task(self, pkg: "spack.package_base.PackageBase") -> None:
def _cleanup_task(self, pkg: "spack.package_base.PackageBaseNoDep") -> None:
"""
Cleanup the build task for the spec

Expand All @@ -1423,7 +1423,7 @@ def _cleanup_task(self, pkg: "spack.package_base.PackageBase") -> None:
# spec during our installation.
self._ensure_locked("read", pkg)

def _ensure_install_ready(self, pkg: "spack.package_base.PackageBase") -> None:
def _ensure_install_ready(self, pkg: "spack.package_base.PackageBaseNoDep") -> None:
"""
Ensure the package is ready to install locally, which includes
already locked.
Expand All @@ -1447,7 +1447,7 @@ def _ensure_install_ready(self, pkg: "spack.package_base.PackageBase") -> None:
raise InstallLockError(f"{pre} not locked")

def _ensure_locked(
self, lock_type: str, pkg: "spack.package_base.PackageBase"
self, lock_type: str, pkg: "spack.package_base.PackageBaseNoDep"
) -> Tuple[str, Optional[lk.Lock]]:
"""
Add a prefix lock of the specified type for the package spec
Expand Down Expand Up @@ -1575,7 +1575,7 @@ def _add_tasks(self, request: BuildRequest, all_deps):
if install_deps and install_compilers:
packages_per_compiler: Dict[
"spack.spec.CompilerSpec",
Dict["spack.spec.ArchSpec", List["spack.package_base.PackageBase"]],
Dict["spack.spec.ArchSpec", List["spack.package_base.PackageBaseNoDep"]],
] = {}

for dep in request.traverse_dependencies():
Expand Down Expand Up @@ -1635,7 +1635,7 @@ def _add_tasks(self, request: BuildRequest, all_deps):
fail_fast = bool(request.install_args.get("fail_fast"))
self.fail_fast = self.fail_fast or fail_fast

def _add_compiler_package_to_config(self, pkg: "spack.package_base.PackageBase") -> None:
def _add_compiler_package_to_config(self, pkg: "spack.package_base.PackageBaseNoDep") -> None:
compiler_search_prefix = getattr(pkg, "compiler_search_prefix", pkg.spec.prefix)
spack.compilers.add_compilers_to_config(
spack.compilers.find_compilers([compiler_search_prefix])
Expand Down Expand Up @@ -1830,7 +1830,7 @@ def _requeue_task(self, task: BuildTask, install_status: InstallStatus) -> None:
new_task.status = STATUS_INSTALLING
self._push_task(new_task)

def _setup_install_dir(self, pkg: "spack.package_base.PackageBase") -> None:
def _setup_install_dir(self, pkg: "spack.package_base.PackageBaseNoDep") -> None:
"""
Create and ensure proper access controls for the install directory.
Write a small metadata file with the current spack environment.
Expand Down Expand Up @@ -1906,7 +1906,7 @@ def _update_installed(self, task: BuildTask) -> None:
self._flag_installed(task.pkg, task.dependents)

def _flag_installed(
self, pkg: "spack.package_base.PackageBase", dependent_ids: Optional[Set[str]] = None
self, pkg: "spack.package_base.PackageBaseNoDep", dependent_ids: Optional[Set[str]] = None
) -> None:
"""
Flag the package as installed and ensure known by all build tasks of
Expand Down Expand Up @@ -2259,7 +2259,7 @@ def install(self) -> None:
class BuildProcessInstaller:
"""This class implements the part installation that happens in the child process."""

def __init__(self, pkg: "spack.package_base.PackageBase", install_args: dict):
def __init__(self, pkg: "spack.package_base.PackageBaseNoDep", install_args: dict):
"""Create a new BuildProcessInstaller.

It is assumed that the lifecycle of this object is the same as the child
Expand Down Expand Up @@ -2341,8 +2341,8 @@ def run(self) -> bool:

# get verbosity from do_install() parameter or saved value
self.echo = self.verbose
if spack.package_base.PackageBase._verbose is not None:
self.echo = spack.package_base.PackageBase._verbose
if spack.package_base.PackageBaseNoDep._verbose is not None:
self.echo = spack.package_base.PackageBaseNoDep._verbose

# Run the pre-install hook in the child process after
# the directory is created.
Expand Down Expand Up @@ -2460,7 +2460,7 @@ def _real_install(self) -> None:
log(pkg)


def build_process(pkg: "spack.package_base.PackageBase", install_args: dict) -> bool:
def build_process(pkg: "spack.package_base.PackageBaseNoDep", install_args: dict) -> bool:
"""Perform the installation/build of the package.

This runs in a separate child process, and has its own process and
Expand Down
17 changes: 11 additions & 6 deletions lib/spack/spack/operating_systems/linux_distro.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import platform as py_platform
import re
from subprocess import check_output
Expand Down Expand Up @@ -37,13 +38,17 @@ class LinuxDistro(OperatingSystem):
"""

def __init__(self):
try:
# This will throw an error if imported on a non-Linux platform.
import distro
if os.environ.get("SPACKOS_VERSION"):
distname = "spack"
version = ""
else:
try:
# This will throw an error if imported on a non-Linux platform.
import distro

distname, version = distro.id(), distro.version()
except ImportError:
distname, version = "unknown", ""
distname, version = distro.id(), distro.version()
except ImportError:
distname, version = "unknown", ""

# Grabs major version from tuple on redhat; on other platforms
# grab the first legal identifier in the version field. On
Expand Down
10 changes: 9 additions & 1 deletion lib/spack/spack/package_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ def redistribute_binary(self):
return True


class PackageBase(WindowsRPath, PackageViewMixin, RedistributionMixin, metaclass=PackageMeta):
class PackageBaseNoDep(WindowsRPath, PackageViewMixin, RedistributionMixin, metaclass=PackageMeta):
"""This is the superclass for all spack packages.

***The Package class***
Expand Down Expand Up @@ -2480,6 +2480,14 @@ def builder(self):
return spack.builder.create(self)


def add_base_deps():
spack.directives.depends_on("spackos-base", when="os=spack")


class PackageBase(PackageBaseNoDep):
add_base_deps()


inject_flags = PackageBase.inject_flags
env_flags = PackageBase.env_flags
build_system_flags = PackageBase.build_system_flags
Expand Down
3 changes: 3 additions & 0 deletions lib/spack/spack/solver/asp.py
Original file line number Diff line number Diff line change
Expand Up @@ -2121,6 +2121,9 @@ def os_defaults(self, specs):
# make directives for buildable OS's
for build_os in sorted(buildable):
self.gen.fact(fn.buildable_os(build_os))
if os.environ.get("SPACKOS_BOOTSTRAP"):
self.gen.fact(fn.os_compatible("spack0", build_os))


def keyfun(os):
return (
Expand Down