diff --git a/.github/workflows/unit-tests.yaml b/.github/workflows/unit-tests.yaml index 922ec92d0..a847da518 100644 --- a/.github/workflows/unit-tests.yaml +++ b/.github/workflows/unit-tests.yaml @@ -6,11 +6,11 @@ on: jobs: test: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - name: checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: submodules: true diff --git a/.gitmodules b/.gitmodules index fe8a7fe7e..160ccb9d4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,7 @@ [submodule "spack"] path = spack url = https://github.com/jcsda/spack - branch = spack-stack-1.9.1 + branch = spack-stack-1.9.2rc1 [submodule "doc/CMakeModules"] path = doc/CMakeModules url = https://github.com/noaa-emc/cmakemodules diff --git a/configs/sites/tier1/wcoss2/compilers.yaml b/configs/sites/tier1/wcoss2/compilers.yaml index 8721b0e9d..772000f92 100644 --- a/configs/sites/tier1/wcoss2/compilers.yaml +++ b/configs/sites/tier1/wcoss2/compilers.yaml @@ -43,3 +43,49 @@ compilers: CONFIG_SITE: '' unset: [PYTHONPATH] extra_rpaths: [] +- compiler: + spec: oneapi@2024.2.1 + paths: + cc: cc + cxx: CC + f77: ftn + fc: ftn + flags: + cflags: '--gcc-toolchain=/opt/cray/pe/gcc/12.1.0/snos/' + fflags: '-gcc-name=/opt/cray/pe/gcc/12.1.0/snos/bin/gcc -gxx-name=/opt/cray/pe/gcc/12.1.0/snos/bin/g++' + cxxflags: '--gcc-toolchain=/opt/cray/pe/gcc/12.1.0/snos/' + operating_system: sles15 + modules: + - PrgEnv-intel/8.3.3 + - craype/2.7.17 + - intel-oneapi/2024.2 + - libfabric + environment: + prepend_path: + PATH: /opt/cray/pe/gcc/12.1.0/snos/bin + LD_LIBRARY_PATH: /opt/cray/pe/gcc/12.1.0/snos/lib64 + LIBRARY_PATH: /opt/cray/pe/gcc/12.1.0/snos/lib64 + CPATH: /opt/cray/pe/gcc/12.1.0/snos/include + set: + # OpenSUSE on WCOSS2 machines sets CONFIG_SITE so + # Automake-based builds are installed in lib64 + # which confuses some packages. + CONFIG_SITE: '' + extra_rpaths: [] +- compiler: + spec: gcc@12.1.0 + paths: + cc: /opt/cray/pe/gcc/12.1.0/snos/bin/gcc + cxx: /opt/cray/pe/gcc/12.1.0/snos/bin/g++ + f77: /opt/cray/pe/gcc/12.1.0/snos/bin/gfortran + fc: /opt/cray/pe/gcc/12.1.0/snos/bin/gfortran + flags: {} + operating_system: sles15 + modules: [] + environment: + set: + # OpenSUSE on WCOSS2 machines sets CONFIG_SITE so + # Automake-based builds are installed in lib64 + # which confuses some packages. + CONFIG_SITE: '' + extra_rpaths: [] diff --git a/configs/sites/tier1/wcoss2/packages.yaml b/configs/sites/tier1/wcoss2/packages.yaml index 8cedff6af..8d5ae1a7c 100644 --- a/configs/sites/tier1/wcoss2/packages.yaml +++ b/configs/sites/tier1/wcoss2/packages.yaml @@ -1,14 +1,6 @@ packages: # - ### Set compiler. - # - all: - prefer: ['%intel@19.1.3.304'] - compiler:: [intel@19.1.3.304] - providers: - mpi: [cray-mpich] - # - ### Strictly set virtual package providers. + ### Strictly set virtual package providers (+disable mkl). # mpi: buildable: False @@ -21,16 +13,8 @@ require: openblas jpeg: require: libjpeg-turbo - # - ### External packages. - # - cray-mpich: - externals: - - spec: cray-mpich@8.1.9~wrappers - modules: - - libfabric - - craype-network-ofi - - cray-mpich/8.1.9 + intel-oneapi-mkl: + buildable: false # ### Individual package settings. # Use `require::` to override require's from {common,site}/packages.yaml, @@ -44,7 +28,6 @@ cdo: require: - 'grib2=none' # avoids eccodes/grib-api dependency - - '%intel' - any_of: ["@2.2.2"] when: "%intel@2022.0.2.262" message: "2.3.0 is the last version to use C++17" @@ -72,13 +55,13 @@ - snapshot=none # make sure spack-stack doesn't accidentally give us a beta snapshot - '~python' flex: - require: '@2.6.4' - gcc-runtime: - require: '%gcc' + require: ['@2.6.4'] gdal: variants: ~curl gettext: - version: ['0.19.7'] + require: + - any_of: ['@0.19.7'] + when: '%intel@19.1.3.304' git-lfs: require: '%gcc' glib: @@ -88,27 +71,31 @@ gsibec: require:: - '@1.2.1 ~mkl' - harfbuzz: - require: '%gcc' libffi: - require: '@3.3' + require: + - any_of: ['@3.3'] + when: '%intel' libunistring: - require: '@:1.0' + require: + - any_of: ['@:1.0'] + when: '%intel' mapl: require: - ~pflogger ~fargparse ~extdata2g # minimize dependencies netcdf-c: 'require:': +mpi ~parallel-netcdf ~dap ~blosc ~szip build_system=autotools # disabling dap prevents network access; disabling blosc and szip reduces dependencies patchelf: - version: ['0.13.1'] + require: + - any_of: ['@0.13.1'] + when: '%intel' py-cython:: - require: '@0.29.36' + require: + - any_of: ['@0.29.36'] + when: '%intel' py-numpy: require:: - '^[virtuals=lapack,blas] openblas' - - '@:1.25' - - any_of: ['@:1.24'] - when: '%intel@:19' + - '@1.26' py-pandas: 'require:': ~excel # minimize dependencies py-scipy: @@ -116,9 +103,17 @@ - any_of: ["@1.10.1"] when: '%intel@:19' py-setuptools: - require: '@63.4.3' # avoid duplicate build deps + require: ['@63.4.3'] # avoid duplicate build deps rhash: - version: ['1.3.5'] + require: + - any_of: ['@1.3.5'] + when: '%intel' subversion: require: - ~serf +pic # avoid serf dependency + zlib-ng: + require: ['+compat ~new_strategies'] + crtm: + require: ['@2.4.0.1 +fix'] + scotch: + require: ['@7.0.4 +mpi+metis~shared~threads~mpi_thread+noarch+esmumps build_system=cmake'] diff --git a/configs/sites/tier1/wcoss2/packages_intel.yaml b/configs/sites/tier1/wcoss2/packages_intel.yaml new file mode 100644 index 000000000..e2393324c --- /dev/null +++ b/configs/sites/tier1/wcoss2/packages_intel.yaml @@ -0,0 +1,30 @@ +packages: + # + ### Set compiler. + # + all: + compiler:: [intel@19.1.3.304] + # + ### External packages. + # + cray-mpich: + externals: + - spec: cray-mpich@8.1.9~wrappers + modules: + - libfabric + - craype-network-ofi + - cray-mpich/8.1.9 + # + ### Set GCC version. + # + gcc-runtime: + require: ['%gcc@10.2.0'] + # + ### Package settings. + # + harfbuzz: + require: ['%gcc'] + ecflow: + require:: ['@5.11.4', '+ui', '^boost %gcc', '%intel'] + py-numpy: + require: ['%gcc'] diff --git a/configs/sites/tier1/wcoss2/packages_oneapi.yaml b/configs/sites/tier1/wcoss2/packages_oneapi.yaml new file mode 100644 index 000000000..a9bb4b49f --- /dev/null +++ b/configs/sites/tier1/wcoss2/packages_oneapi.yaml @@ -0,0 +1,45 @@ +packages: + all: + compiler:: [oneapi@2024.2.1] + gcc-runtime: + require: '%gcc@12.1.0' + cray-mpich: + externals: + - spec: cray-mpich@8.1.29~wrappers + modules: + - libfabric + - craype-network-ofi + - cray-mpich/8.1.29 + - cray-pals + bison: + require: ['%gcc'] + cairo: + require: ['+fc'] + ectrans: + require:: + - '@1.2.0 ~mkl +fftw' + elfutils: + require: ['@0.191'] + gsibec: + require:: + - '@1.2.1 ~mkl' + subversion: + require: ['%gcc'] + cmake: + require: ['%gcc'] + proj: + require: ['@9.4.1'] + libtiff: + require: ['build_system=cmake'] + icu4c: + require: ['%gcc'] + ninja: + require: ['~re2c %gcc'] + pixman: + require: ['build_system=meson'] + apr: + require: ['@:1.6', '%gcc'] + glib: + require: ['%gcc'] + zlib-ng: + require: ['cflags="-static-intel"', 'cxxflags="-static-intel"'] diff --git a/configs/templates/nco/spack.yaml b/configs/templates/nco/spack.yaml index e14f7c125..daa20ab72 100644 --- a/configs/templates/nco/spack.yaml +++ b/configs/templates/nco/spack.yaml @@ -14,14 +14,14 @@ spack: specs: - awscli - bacio - - boost %intel + - boost - bufr - cdo - cfitsio - cmake - - crtm + - crtm@=2.4.0.1 - curl - - ecflow ^boost%gcc + - ecflow - eckit - ecmwf-atlas - eigen diff --git a/spack b/spack index ab2da2468..1ef7450f5 160000 --- a/spack +++ b/spack @@ -1 +1 @@ -Subproject commit ab2da2468530a580a8100faedf79f66f2cf178fd +Subproject commit 1ef7450f5e0f8401cb0ec8beadd273581d0a40fd diff --git a/util/fetch_go_deps.py b/util/fetch_go_deps.py new file mode 100755 index 000000000..53246c433 --- /dev/null +++ b/util/fetch_go_deps.py @@ -0,0 +1,116 @@ +#!/usr/bin/env spack-python +# +# Run this script in an active, concretized Spack environment to fetch Go +# dependencies and store them in $GOMODCACHE. You must either run it with +# 'spack-python' or have 'spack-python' in your $PATH. Ensure $GOMODCACHE has +# the same value when 'spack install' is run. +# +# For each spec that depends on 'go' and/or is specified by the user, it will +# attempt to use that spec's 'go' dependency to execute 'go mod download', but +# will fall back to searching for 'go' in $PATH if that dependency has not +# already been installed. +# +# Alex Richert, Apr 2025 +# + +import os +import argparse + +from spack.environment import active_environment +from spack.error import SpackError +from spack.installer import PackageInstaller +from spack.spec import Spec +from spack.util.executable import Executable, which +from llnl.util.filesystem import working_dir + +parser = argparse.ArgumentParser( + description="Fetch Go dependencies for packages in a Spack environment" +) +parser.add_argument( + "--spec", "-s", + nargs="+", + default=[], + help="Additional specs for which to fetch go dependencies", +) +parser.add_argument( + "--install-go", + action="store_true", + help="Install go dependency if not already installed", +) +parser.add_argument( + "--only-listed", "-o", + action="store_true", + help="Only fetch user-provided specs (no auto-detection of go dependents)", +) + +args = parser.parse_args() + +# Load the current environment +env = active_environment() +if not env: + raise SpackError("No active Spack environment") + +gomodcache = os.getenv("GOMODCACHE") +if not gomodcache: + raise SpackError("GOMODCACHE must be set") + +user_specs = [] +for spec_str in args.spec: + try: + user_specs.append(Spec(spec_str)) + except Exception as e: + print(f"Warning: Invalid spec '{spec_str}': {e}") + +# Find each spec that depends on 'go' or is in the user-specified package list +for spec in env.all_specs(): + if not spec.concrete: + continue + + # Check if the package depends on go or is user specified + is_user_specified = False + for user_spec in user_specs: + if spec.satisfies(user_spec): + is_user_specified = True + break + if is_user_specified: + fetch_it = True + elif not args.only_listed: + fetch_it = any(dep.name == 'go' for dep in spec.dependencies()) + else: + fetch_it = False + + if fetch_it: + print(f"Processing: {spec.name}@{spec.version}/{spec.dag_hash()}") + + # Check if package actually has a go dependency + if 'go' not in spec: + print(f" Warning: {spec.name} does not have a 'go' dependency, skipping") + continue + + pkg = spec.package + pkg.do_stage() + + # Install go dependency if requested and not already installed + go_dep = spec["go"] + dep_go_path = os.path.join(go_dep.prefix.bin, "go") + + if not which(dep_go_path) and args.install_go: + print(f" Installing go dependency: {go_dep}") + installer = PackageInstaller([go_dep.package]) + installer.install() + + # Now try to use the go dependency's go executable + if which(dep_go_path): + go_exe = Executable(dep_go_path) + elif which("go"): + go_exe = Executable("go") + else: + raise SpackError("Could not find 'go' executable") + + # Execute go mod download + with working_dir(pkg.stage.source_path): + if os.path.isfile("go.mod"): + go_exe("mod", "download") + print(f" Successfully fetched dependencies for {spec.name}") + else: + print(f" No go.mod for {spec.name}") diff --git a/util/filter_spack_compilers.py b/util/filter_spack_compilers.py new file mode 100755 index 000000000..94db94ef3 --- /dev/null +++ b/util/filter_spack_compilers.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +# +# Run "./filter_spack_compilers.py --help" for documentation. NOTE: This script +# will be of no use after +# https://github.com/spack/spack/commit/5b3942a48935ee4aeef724b4a28c9666a75821c4 +# and the deprecation of compilers.yaml. Use with +# https://github.com/spack/spack/commit/a9c879d53e758f67cdbf4cec919425cb2a3a1082 +# or prior commits. +# + +import argparse +import os +import re +import sys + + +def parse_arguments(): + """Parse and return command line arguments.""" + parser = argparse.ArgumentParser( + prog='filter_spack_compilers.py', + description='Inclusively or exclusively delete compilers from compilers.yaml or spack.yaml.', + epilog='Written by Alex Richert, March 2025', + ) + parser.add_argument('yamlfile', help='Input YAML file (compilers.yaml or spack.yaml)') + parser.add_argument('compilerspecs', nargs='+', help='Compiler specs to keep/remove') + + inc_or_exc_group = parser.add_mutually_exclusive_group(required=True) + inc_or_exc_group.add_argument('--remove', action='store_true', + help='Remove specified compiler specs') + inc_or_exc_group.add_argument('--keep-only', action='store_true', + help='Remove all compiler specs except those specified') + inc_or_exc_group.add_argument('--keep-only-this-version', action='store_true', + help='Remove all compiler specs of the same name(s) specified (i.e., have only one version of intel)') + + parser.add_argument('--use-ruamel', action='store_true', + help='Use ruamel.yaml (preserves comments) instead of standard yaml module') + + return parser.parse_args() + + +def load_yaml_file(filename, use_ruamel=False): + """Load and return YAML file contents using either standard yaml or ruamel.yaml.""" + if use_ruamel: + try: + from ruamel.yaml import YAML + yaml_parser = YAML(typ='rt') + yaml_parser.default_flow_style = False + + with open(filename, 'r') as file: + raw_yaml_data = yaml_parser.load(file) + except ImportError: + print("Error: ruamel.yaml module not available. Install with,, e.g., 'pip install ruamel.yaml'") + sys.exit(1) + else: + # Use standard yaml module + import yaml + + with open(filename, 'r') as file: + raw_yaml_data = yaml.safe_load(file) + yaml_parser = yaml # For consistency in return values + + # Handle both spack.yaml and compilers.yaml formats + if 'spack' in raw_yaml_data.keys(): + yaml_data = raw_yaml_data['spack'] + is_spack_yaml = True + else: + yaml_data = raw_yaml_data + is_spack_yaml = False + + return yaml_parser, raw_yaml_data, yaml_data, is_spack_yaml + + +def filter_compilers(yaml_data, args): + """Filter compilers based on command line arguments.""" + + # In the future, `spack.spec.Spec("foo@1.2.3").satisfies("foo@1")` may provide + # a more robust way to match specs. + + # Change "@=" to "@" to make sure we don't miss anything. For matching purposes + # we'll treat the two as equivalent. + args.compilerspecs = [x.replace("@=", "@") for x in args.compilerspecs] + + n_compilers = len(yaml_data['compilers']) + + for i in range(n_compilers-1, -1, -1): + compiler_spec = yaml_data['compilers'][i]['compiler']['spec'].replace("@=", "@") + + if args.keep_only_this_version: + names = [re.sub('@.*', '', x) for x in args.compilerspecs] + compiler_name = re.sub('@.*', '', compiler_spec) + if (compiler_name in names) and (compiler_spec not in args.compilerspecs): + del yaml_data['compilers'][i] + continue + + if (compiler_spec in args.compilerspecs) and args.remove: + del yaml_data['compilers'][i] + continue + + if (compiler_spec not in args.compilerspecs) and args.keep_only: + del yaml_data['compilers'][i] + + return yaml_data + + +def save_yaml_file(yaml_parser, yaml_data, raw_yaml_data, yamlfile, is_spack_yaml, use_ruamel=False): + """Save modified YAML data back to file with backup of original.""" + if is_spack_yaml: + raw_yaml_data['spack'] = yaml_data + output_data = raw_yaml_data + else: + output_data = yaml_data + + # Create backup of original file + bkp_path = yamlfile + '.bkp' + os.rename(yamlfile, bkp_path) + + # Write updated file + with open(yamlfile, 'w') as outputfile: + if use_ruamel: + # ruamel.yaml has its own dump method + yaml_parser.dump(output_data, outputfile) + else: + # Standard yaml module is already imported in yaml_parser + yaml_parser.dump(output_data, outputfile, sort_keys=False, default_flow_style=False) + + +def main(): + """Main function to run the script.""" + args = parse_arguments() + yaml_parser, raw_yaml_data, yaml_data, is_spack_yaml = load_yaml_file(args.yamlfile, args.use_ruamel) + yaml_data = filter_compilers(yaml_data, args) + save_yaml_file(yaml_parser, yaml_data, raw_yaml_data, args.yamlfile, is_spack_yaml, args.use_ruamel) + + +if __name__ == "__main__": + main() diff --git a/util/parallel_install.sh b/util/parallel_install.sh index 0156bd664..06e5504c4 100755 --- a/util/parallel_install.sh +++ b/util/parallel_install.sh @@ -42,7 +42,7 @@ echo "Installing with $n_instances instances and $n_threads threads in environme for i in $(seq $n_instances); do cmd="spack install -j $n_threads $*" - echo $cmd | tee ${SPACK_ENV}/log.install.proc${i} + echo $cmd | tee -a ${SPACK_ENV}/log.install.proc${i} $cmd &>> ${SPACK_ENV}/log.install.proc${i} & pids+=($!) done