Building Compatible Updated and New Packages

Often we need to update versions of packages in our environment, like data management tools, etc. that are relased on a different schedule than the main software releases; maintainers may wish to distribute a new version of a package compatible with the existing distribution. Or we may need to build an altogether new package which we would like to be compatible with an existing package suite. Then users can spack load the new package version without changing other packages out from underneath their current environment.

Setting up

Make a build spack instance if you don’t have it, and put your spack signing keys in it. If you don’t have a spack signing key, see these instructions

. /cvmfs/larsoft.opensciencegrid.org/spack-fnal-v1.1.0/spack_env/setup-env.sh
spack subspack --with-padding $PWD/build_instance
spack gpg trust $HOME/.gnupg/spack/pubring.gpg
spack gpg trust $HOME/.gnupg/spack/secring.gpg

make and configure a build environment

. $PWD/build_instance/setup-env.sh
spack env create my_package_1_2_3
spack env activate my_package_1_2_3
spack cd --env
vi spack.yaml

(interestingly, it is the “spack env activate” step here that is slowest!)

where the spack.yaml ends up looking like

spack:

  # things to build:
  specs:
  - my-package@1.2.3

  # have concretizer reuse from the particular upstream environment
  concretizer:
    unify: true
    reuse:
      from:
      - type: environment
        path: /cvmfs/larsoft.opensciencegrid.org/spack-fnal-v1.1.0/spack_env/var/spack/environments/larsoft-v10_11_01-unified-cuda-python-3_9-trimmed

  # set preferred compilers, target, etc.
  packages:
    all:
      providers:
        libc:
        - glibc
        'zlib-api:':
        - zlib
        c:
        - gcc@12.5.0
        cxx:
        - gcc@12.5.0
        fortran:
        - gcc@12.5.0
      variants:
      - generator=ninja
      - build_type=Release
      - cxxstd=17
      target:
        - x86_64_v2

And now when you concretize your package, it should be basically the only package rebuilt… but sometimes it isn’t.

Reading the spack concretize output

When you concretize, spack should give you output that looks like:

==> Concretized 1 spec:
 -   wzwrx6v  metacat@4.1.4+client_only build_system=python_pip platform=linux os=almalinux9 target=x86_64_v3
 -   ahw4ylu      ^py-lark@1.1.2 build_system=python_pip platform=linux os=almalinux9 target=x86_64_v3
 -   eyadajq      ^py-pip@25.1.1 build_system=generic platform=linux os=almalinux9 target=x86_64_v3
[^]  l7q24lu      ^py-pyjwt@2.4.0~crypto build_system=python_pip platform=linux os=almalinux9 target=x86_64_v2
[^]  yezicjn          ^py-pip@25.1.1 build_system=generic platform=linux os=almalinux9 target=x86_64_v2
[^]  rjohrzr          ^py-setuptools@80.9.0 build_system=generic platform=linux os=almalinux9 target=x86_64_v2
[^]  6qyc252          ^py-wheel@0.45.1 build_system=generic platform=linux os=almalinux9 target=x86_64_v2
[^]  rbt2w2m      ^py-pythreader@2.15.0 build_system=python_pip platform=linux os=almalinux9 target=x86_64_v
...

where each dependent package with all of its version, variant, etc flags are listed. The lines all start with a status, one of:

  •   -   a dash in the first 3 columns is a package spack thinks it should rebuild

  • [^] bracketed carat indicates a package reused from the upstream spack instance

  • [+] bracketed plus indicates a package installed in the current spack instance

  • [e] brackeded e indicates an external (system) package

Basically, our goal is for the output of our concretize to only show a dash-ed entry for our new version of our package, and possibly one or two supporting packages not used by anything else in our software environments. You might also find spack wanting to rebuild/reinstall some build dependencies that didn’t get put into the upstream spack instance – packages like py-setuptools and py-wheel – this is okay.

Getting more reuse

If you’re finding the spack concretize step is wanting to rebuild a lot of packages that already exist in the upstream environment, you can update the spec in the spack.yaml file to be more specific about reuse. Basically you want to look in the reuse environment for the exact hash of the package it didn’t reuse, and add that as an explicit dependency. This looks like:

$ spack -e larsoft-v10_11_01-unified-cuda-python-3_9-trimmed find --long py-webpie python
==> In environment larsoft-v10_11_01-unified-cuda-python-3_9-trimmed
==> 61 root specs
...
-- linux-almalinux9-x86_64_v2 / %c,cxx=gcc@12.5.0 ---------------
ppik5zk python@3.10.16

-- linux-almalinux9-x86_64_v2 / no compilers --------------------
lojz5wk py-webpie@5.16.3
==> 2 installed packages

and then update the spack.yaml file with dependencies on the specific hashed versions:

spack:
  specs:
  - my-package@1.2.3 ^python/ppik5zk ^py-webpie/lojz5wk
...

And do a spack concretize -f to reconcretize it. With just a few iterations like this, you should be able to get a minimal rebuild.

A short note on strategy here; it is usually fastest to specify the dependencies that are least indented in the “spack concretize” output. It can also be very helpful to do

spack concretize -f | tee concretize.out

when concretizing to save a copy of the concretizer output to review what is being rebuilt and what isn’t. Then you can do things like

grep '[a-z0-9]      ^' concretize.out

(that is with 6 blanks) to see all the first-level dependencies to see which of them might need specifying.

Distributing the package

Now that you have the concretization issues settled, you should be able to build the package, make a buildcache image, and put it onto scisoft.fnal.gov or spack-cache-1. This looks like:

spack cd --env
spack install
spack localbuildcache --local --key mykey
scp -r bc/* scisoftbuild02.fnal.gov:/SciSoft/spack-mirror/spack-binary-cache-v3
ssh scisoftbuild02.fnal.gov <<EOF
  source /cvmfs/larsoft.opensciencegrid.org/spack-fnal-v1.1.0/spack_env/setup-env.sh
  spack buildcache update-index /SciSoft/spack-mirror/spack-binary-cache-v3
EOF

Now you or the experiment package maintainers can install the package into cvmfs following these instructions

Replacing package dependencies

Sometimes there is a package that needs to be updated in existing environments, either for security reasons, or because it reflects a configuration change that represents real world changes, where you need to flat out replace a package with a new version. A common example of this is the data management package ifdhc_config, which knows thinks like service servernames, etc. and needs to be updated when those servers change. Having built a compatible version of the package, as above, one can make a change like this using the spack deprecate command, specifying that the old version of the package be replaced by the new one. In a CVMFS instance this would be done right after installing the new version, but before you publish the CVMFS transaction; and looks like

$ spack find --long ifdhc_config
-- linux-almalinux9-x86_64_v2 / no compilers --------------------
vtkszyk ifdhc-config@2.6.20  zgxkyy3 ifdhc-config@2.7.2
kebqcx2 ifdhc-config@2.6.20  gnj5lfy ifdhc-config@2.7.2
qxgzxem ifdhc-config@2.6.20  t7gveqv ifdhc-config@2.7.3
...
$ spack deprecate -D ifdhc-config/zgxkyy3 ifdhc-config/gnj5lfy ifdhc-config/t7gveqv

where the two 2.7.2 versions of ifdhc-config would be pointed at ifdhc-config@2.7.3. It is recommended to do these explicitly by hash values like this so you don’t inadvertently modify the wrong instances. The -D flag says to not similarly deprecate the dependencies of those packages. Again, this is to not modify more things than you meant to.

You can read more about spack deprecate in the Spack documentation