Compare commits

..

2 Commits

Author SHA1 Message Date
Gregory Becker
1c6bb8cfc3 add regression number 2023-06-21 11:35:57 -07:00
Gregory Becker
9244ecacf0 bugfix: environments with unify:false can concretize abstract hash without name 2023-06-21 11:34:16 -07:00
987 changed files with 12125 additions and 19443 deletions

View File

@@ -17,13 +17,10 @@ concurrency:
jobs:
# Run audits on all the packages in the built-in repository
package-audits:
runs-on: ${{ matrix.operating_system }}
strategy:
matrix:
operating_system: ["ubuntu-latest", "macos-latest"]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # @v2
- uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # @v2
- uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0 # @v2
with:
python-version: ${{inputs.python_version}}
- name: Install Python packages
@@ -44,4 +41,4 @@ jobs:
- uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # @v2.1.0
if: ${{ inputs.with_coverage == 'true' }}
with:
flags: unittests,audits
flags: unittests,linux,audits

View File

@@ -95,7 +95,7 @@ jobs:
uses: docker/setup-qemu-action@2b82ce82d56a2a04d2637cd93a637ae1b359c0a7 # @v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4c0219f9ac95b02789c1075625400b2acbff50b1 # @v1
uses: docker/setup-buildx-action@ecf95283f03858871ff00b787d79c419715afc34 # @v1
- name: Log in to GitHub Container Registry
uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc # @v1

View File

@@ -17,7 +17,7 @@ jobs:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
with:
fetch-depth: 0
- uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1
- uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0
with:
python-version: 3.9
- name: Install Python packages

View File

@@ -50,7 +50,7 @@ jobs:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # @v2
with:
fetch-depth: 0
- uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # @v2
- uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0 # @v2
with:
python-version: ${{ matrix.python-version }}
- name: Install System packages
@@ -97,7 +97,7 @@ jobs:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # @v2
with:
fetch-depth: 0
- uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # @v2
- uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0 # @v2
with:
python-version: '3.11'
- name: Install System packages
@@ -155,7 +155,7 @@ jobs:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # @v2
with:
fetch-depth: 0
- uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # @v2
- uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0 # @v2
with:
python-version: '3.11'
- name: Install System packages
@@ -189,7 +189,7 @@ jobs:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # @v2
with:
fetch-depth: 0
- uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # @v2
- uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0 # @v2
with:
python-version: ${{ matrix.python-version }}
- name: Install Python packages

View File

@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # @v2
- uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # @v2
- uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0 # @v2
with:
python-version: '3.11'
cache: 'pip'
@@ -38,7 +38,7 @@ jobs:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # @v2
with:
fetch-depth: 0
- uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # @v2
- uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0 # @v2
with:
python-version: '3.11'
cache: 'pip'

View File

@@ -18,7 +18,7 @@ jobs:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
with:
fetch-depth: 0
- uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1
- uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0
with:
python-version: 3.9
- name: Install Python packages
@@ -42,7 +42,7 @@ jobs:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
with:
fetch-depth: 0
- uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1
- uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0
with:
python-version: 3.9
- name: Install Python packages
@@ -66,7 +66,7 @@ jobs:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
with:
fetch-depth: 0
- uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1
- uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0
with:
python-version: 3.9
- name: Install Python packages

View File

@@ -1,21 +1,3 @@
# v0.20.1 (2023-07-10)
## Spack Bugfixes
- Spec removed from an environment where not actually removed if `--force` was not given (#37877)
- Speed-up module file generation (#37739)
- Hotfix for a few recipes that treat CMake as a link dependency (#35816)
- Fix re-running stand-alone test a second time, which was getting a trailing spurious failure (#37840)
- Fixed reading JSON manifest on Cray, reporting non-concrete specs (#37909)
- Fixed a few bugs when generating Dockerfiles from Spack (#37766,#37769)
- Fixed a few long-standing bugs when generating module files (#36678,#38347,#38465,#38455)
- Fixed issues with building Python extensions using an external Python (#38186)
- Fixed compiler removal from command line (#38057)
- Show external status as [e] (#33792)
- Backported `archspec` fixes (#37793)
- Improved a few error messages (#37791)
# v0.20.0 (2023-05-21)
`v0.20.0` is a major feature release.

View File

@@ -25,6 +25,8 @@ exit 1
# Line above is a shell no-op, and ends a python multi-line comment.
# The code above runs this file with our preferred python interpreter.
from __future__ import print_function
import os
import os.path
import sys

View File

@@ -214,7 +214,7 @@ goto :end_switch
if defined _sp_args (
if NOT "%_sp_args%"=="%_sp_args:--help=%" (
goto :default_case
) else if NOT "%_sp_args%"=="%_sp_args:-h=%" (
) else if NOT "%_sp_args%"=="%_sp_args: -h=%" (
goto :default_case
) else if NOT "%_sp_args%"=="%_sp_args:--bat=%" (
goto :default_case

View File

@@ -1,132 +0,0 @@
# Copyright 2013-2023 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
# #######################################################################
function Compare-CommonArgs {
$CMDArgs = $args[0]
# These aruments take precedence and call for no futher parsing of arguments
# invoke actual Spack entrypoint with that context and exit after
"--help", "-h", "--version", "-V" | ForEach-Object {
$arg_opt = $_
if(($CMDArgs) -and ([bool]($CMDArgs.Where({$_ -eq $arg_opt})))) {
return $true
}
}
return $false
}
function Read-SpackArgs {
$SpackCMD_params = @()
$SpackSubCommand = $NULL
$SpackSubCommandArgs = @()
$args_ = $args[0]
$args_ | ForEach-Object {
if (!$SpackSubCommand) {
if($_.SubString(0,1) -eq "-")
{
$SpackCMD_params += $_
}
else{
$SpackSubCommand = $_
}
}
else{
$SpackSubCommandArgs += $_
}
}
return $SpackCMD_params, $SpackSubCommand, $SpackSubCommandArgs
}
function Invoke-SpackCD {
if (Compare-CommonArgs $SpackSubCommandArgs) {
python $Env:SPACK_ROOT/bin/spack cd -h
}
else {
$LOC = $(python $Env:SPACK_ROOT/bin/spack location $SpackSubCommandArgs)
if (($NULL -ne $LOC)){
if ( Test-Path -Path $LOC){
Set-Location $LOC
}
else{
exit 1
}
}
else {
exit 1
}
}
}
function Invoke-SpackEnv {
if (Compare-CommonArgs $SpackSubCommandArgs[0]) {
python $Env:SPACK_ROOT/bin/spack env -h
}
else {
$SubCommandSubCommand = $SpackSubCommandArgs[0]
$SubCommandSubCommandArgs = $SpackSubCommandArgs[1..$SpackSubCommandArgs.Count]
switch ($SubCommandSubCommand) {
"activate" {
if (Compare-CommonArgs $SubCommandSubCommandArgs) {
python $Env:SPACK_ROOT/bin/spack env activate $SubCommandSubCommandArgs
}
elseif ([bool]($SubCommandSubCommandArgs.Where({$_ -eq "--pwsh"}))) {
python $Env:SPACK_ROOT/bin/spack env activate $SubCommandSubCommandArgs
}
elseif (!$SubCommandSubCommandArgs) {
python $Env:SPACK_ROOT/bin/spack env activate $SubCommandSubCommandArgs
}
else {
$SpackEnv = $(python $Env:SPACK_ROOT/bin/spack $SpackCMD_params env activate "--pwsh" $SubCommandSubCommandArgs)
$ExecutionContext.InvokeCommand($SpackEnv)
}
}
"deactivate" {
if ([bool]($SubCommandSubCommandArgs.Where({$_ -eq "--pwsh"}))) {
python $Env:SPACK_ROOT/bin/spack env deactivate $SubCommandSubCommandArgs
}
elseif($SubCommandSubCommandArgs) {
python $Env:SPACK_ROOT/bin/spack env deactivate -h
}
else {
$SpackEnv = $(python $Env:SPACK_ROOT/bin/spack $SpackCMD_params env deactivate --pwsh)
$ExecutionContext.InvokeCommand($SpackEnv)
}
}
default {python $Env:SPACK_ROOT/bin/spack $SpackCMD_params $SpackSubCommand $SpackSubCommandArgs}
}
}
}
function Invoke-SpackLoad {
if (Compare-CommonArgs $SpackSubCommandArgs) {
python $Env:SPACK_ROOT/bin/spack $SpackCMD_params $SpackSubCommand $SpackSubCommandArgs
}
elseif ([bool]($SpackSubCommandArgs.Where({($_ -eq "--pwsh") -or ($_ -eq "--list")}))) {
python $Env:SPACK_ROOT/bin/spack $SpackCMD_params $SpackSubCommand $SpackSubCommandArgs
}
else {
$SpackEnv = $(python $Env:SPACK_ROOT/bin/spack $SpackCMD_params $SpackSubCommand "--pwsh" $SpackSubCommandArgs)
$ExecutionContext.InvokeCommand($SpackEnv)
}
}
$SpackCMD_params, $SpackSubCommand, $SpackSubCommandArgs = Read-SpackArgs $args
if (Compare-CommonArgs $SpackCMD_params) {
python $Env:SPACK_ROOT/bin/spack $SpackCMD_params $SpackSubCommand $SpackSubCommandArgs
exit $LASTEXITCODE
}
# Process Spack commands with special conditions
# all other commands are piped directly to Spack
switch($SpackSubCommand)
{
"cd" {Invoke-SpackCD}
"env" {Invoke-SpackEnv}
"load" {Invoke-SpackLoad}
"unload" {Invoke-SpackLoad}
default {python $Env:SPACK_ROOT/bin/spack $SpackCMD_params $SpackSubCommand $SpackSubCommandArgs}
}

View File

@@ -216,11 +216,10 @@ config:
# manipulation by unprivileged user (e.g. AFS)
allow_sgid: true
# Whether to show status information during building and installing packages.
# This gives information about Spack's current progress as well as the current
# and total number of packages. Information is shown both in the terminal
# title and inline.
install_status: true
# Whether to set the terminal title to display status information during
# building and installing packages. This gives information about Spack's
# current progress as well as the current and total number of packages.
terminal_title: false
# Number of seconds a buildcache's index.json is cached locally before probing
# for updates, within a single Spack invocation. Defaults to 10 minutes.

View File

@@ -1,4 +1,2 @@
mirrors:
spack-public:
binary: false
url: https://mirror.spack.io
spack-public: https://mirror.spack.io

View File

@@ -48,10 +48,14 @@ Here is an example where a build cache is created in a local directory named
.. code-block:: console
$ spack buildcache push ./spack-cache ninja
$ spack buildcache push --allow-root ./spack-cache ninja
==> Pushing binary packages to file:///home/spackuser/spack/spack-cache/build_cache
Note that ``ninja`` must be installed locally for this to work.
Not that ``ninja`` must be installed locally for this to work.
We're using the ``--allow-root`` flag to tell Spack that is OK when any of
the binaries we're pushing contain references to the local Spack install
directory.
Once you have a build cache, you can add it as a mirror, discussed next.
@@ -143,7 +147,7 @@ and then install from it exclusively, you would do:
$ spack mirror add E4S https://cache.e4s.io
$ spack buildcache keys --install --trust
$ spack install --use-buildcache only <package>
$ spack install --use-buildache only <package>
We use ``--install`` and ``--trust`` to say that we are installing keys to our
keyring, and trusting all downloaded keys.

View File

@@ -76,53 +76,6 @@ To build with with ``icx``, do ::
spack install patchelf%oneapi
Using oneAPI Spack environment
-------------------------------
In this example, we build lammps with ``icx`` using Spack environment for oneAPI packages created by Intel. The
compilers are installed with Spack like in example above.
Install the oneAPI compilers::
spack install intel-oneapi-compilers
Add the compilers to your ``compilers.yaml`` so Spack can use them::
spack compiler add `spack location -i intel-oneapi-compilers`/compiler/latest/linux/bin/intel64
spack compiler add `spack location -i intel-oneapi-compilers`/compiler/latest/linux/bin
Verify that the compilers are available::
spack compiler list
Clone `spack-configs <https://github.com/spack/spack-configs>`_ repo and activate Intel oneAPI CPU environment::
git clone https://github.com/spack/spack-configs
spack env activate spack-configs/INTEL/CPU
spack concretize -f
`Intel oneAPI CPU environment <https://github.com/spack/spack-configs/blob/main/INTEL/CPU/spack.yaml>`_ contains applications tested and validated by Intel, this list is constantly extended. And currently it supports:
- `GROMACS <https://www.gromacs.org/>`_
- `HPCG <https://www.hpcg-benchmark.org/>`_
- `HPL <https://netlib.org/benchmark/hpl/>`_
- `LAMMPS <https://www.lammps.org/#gsc.tab=0>`_
- `OpenFOAM <https://www.openfoam.com/>`_
- `STREAM <https://www.cs.virginia.edu/stream/>`_
- `WRF <https://github.com/wrf-model/WRF>`_
To build lammps with oneAPI compiler from this environment just run::
spack install lammps
Compiled binaries can be find using::
spack cd -i lammps
You can do the same for all other applications from this environment.
Using oneAPI MPI to Satisfy a Virtual Dependence
------------------------------------------------------

View File

@@ -72,7 +72,7 @@ arguments to the configure phase, you can use:
.. code-block:: python
def configure_args(self):
def configure_args(self, spec, prefix):
return ['--no-python-dbus']

View File

@@ -97,7 +97,9 @@ class PatchedPythonDomain(PythonDomain):
def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode):
if "refspecific" in node:
del node["refspecific"]
return super().resolve_xref(env, fromdocname, builder, typ, target, node, contnode)
return super(PatchedPythonDomain, self).resolve_xref(
env, fromdocname, builder, typ, target, node, contnode
)
#
@@ -214,7 +216,6 @@ def setup(sphinx):
# Spack classes that intersphinx is unable to resolve
("py:class", "spack.version.StandardVersion"),
("py:class", "spack.spec.DependencySpec"),
("py:class", "spack.spec.SpecfileReaderBase"),
("py:class", "spack.install_test.Pb"),
]

View File

@@ -292,13 +292,12 @@ It is also worth noting that:
non_bindable_shared_objects = ["libinterface.so"]
----------------------
``install_status``
``terminal_title``
----------------------
When set to ``true``, Spack will show information about its current progress
as well as the current and total package numbers. Progress is shown both
in the terminal title and inline. Setting it to ``false`` will not show any
progress information.
By setting this option to ``true``, Spack will update the terminal's title to
provide information about its current progress as well as the current and
total package numbers.
To work properly, this requires your terminal to reset its title after
Spack has finished its work, otherwise Spack's status information will

View File

@@ -916,9 +916,9 @@ function, as shown in the example below:
.. code-block:: yaml
projections:
zlib: "{name}-{version}"
^mpi: "{name}-{version}/{^mpi.name}-{^mpi.version}-{compiler.name}-{compiler.version}"
all: "{name}-{version}/{compiler.name}-{compiler.version}"
zlib: {name}-{version}
^mpi: {name}-{version}/{^mpi.name}-{^mpi.version}-{compiler.name}-{compiler.version}
all: {name}-{version}/{compiler.name}-{compiler.version}
The entries in the projections configuration file must all be either
specs or the keyword ``all``. For each spec, the projection used will

View File

@@ -275,12 +275,10 @@ of the installed software. For instance, in the snippet below:
set:
BAR: 'bar'
# This anonymous spec selects any package that
# depends on mpi. The double colon at the
# depends on openmpi. The double colon at the
# end clears the set of rules that matched so far.
^mpi::
^openmpi::
environment:
prepend_path:
PATH: '{^mpi.prefix}/bin'
set:
BAR: 'baz'
# Selects any zlib package
@@ -295,9 +293,7 @@ of the installed software. For instance, in the snippet below:
- FOOBAR
you are instructing Spack to set the environment variable ``BAR=bar`` for every module,
unless the associated spec satisfies the abstract dependency ``^mpi`` in which case
``BAR=baz``, and the directory containing the respective MPI executables is prepended
to the ``PATH`` variable.
unless the associated spec satisfies ``^openmpi`` in which case ``BAR=baz``.
In addition in any spec that satisfies ``zlib`` the value ``foo`` will be
prepended to ``LD_LIBRARY_PATH`` and in any spec that satisfies ``zlib%gcc@4.8``
the variable ``FOOBAR`` will be unset.
@@ -400,30 +396,28 @@ that are already in the Lmod hierarchy.
.. note::
Tcl and Lua modules also allow for explicit conflicts between modulefiles.
Tcl modules
Tcl modules also allow for explicit conflicts between modulefiles.
.. code-block:: yaml
.. code-block:: yaml
modules:
default:
enable:
- tcl
tcl:
projections:
all: '{name}/{version}-{compiler.name}-{compiler.version}'
all:
conflict:
- '{name}'
- 'intel/14.0.1'
modules:
default:
enable:
- tcl
tcl:
projections:
all: '{name}/{version}-{compiler.name}-{compiler.version}'
all:
conflict:
- '{name}'
- 'intel/14.0.1'
will create module files that will conflict with ``intel/14.0.1`` and with the
base directory of the same module, effectively preventing the possibility to
load two or more versions of the same software at the same time. The tokens
that are available for use in this directive are the same understood by the
:meth:`~spack.spec.Spec.format` method.
For Lmod and Environment Modules versions prior 4.2, it is important to
express the conflict on both modulefiles conflicting with each other.
will create module files that will conflict with ``intel/14.0.1`` and with the
base directory of the same module, effectively preventing the possibility to
load two or more versions of the same software at the same time. The tokens
that are available for use in this directive are the same understood by
the :meth:`~spack.spec.Spec.format` method.
.. note::

View File

@@ -121,7 +121,7 @@ Since v0.19, Spack supports two ways of writing a package recipe. The most comm
def url_for_version(self, version):
if version >= Version("2.1.1"):
return super().url_for_version(version)
return super(Openjpeg, self).url_for_version(version)
url_fmt = "https://github.com/uclouvain/openjpeg/archive/version.{0}.tar.gz"
return url_fmt.format(version)
@@ -155,7 +155,7 @@ builder class explicitly. Using the same example as above, this reads:
def url_for_version(self, version):
if version >= Version("2.1.1"):
return super().url_for_version(version)
return super(Openjpeg, self).url_for_version(version)
url_fmt = "https://github.com/uclouvain/openjpeg/archive/version.{0}.tar.gz"
return url_fmt.format(version)

View File

@@ -6,8 +6,3 @@ python-levenshtein==0.21.1
docutils==0.18.1
pygments==2.15.1
urllib3==2.0.3
pytest==7.4.0
isort==5.12.0
black==23.1.0
flake8==6.0.0
mypy==1.4.1

428
lib/spack/env/cc vendored
View File

@@ -416,14 +416,30 @@ input_command="$*"
# The lists are all bell-separated to be as flexible as possible, as their
# contents may come from the command line, from ' '-separated lists,
# ':'-separated lists, etc.
include_dirs_list=""
lib_dirs_list=""
rpath_dirs_list=""
system_include_dirs_list=""
system_lib_dirs_list=""
system_rpath_dirs_list=""
isystem_system_include_dirs_list=""
isystem_include_dirs_list=""
libs_list=""
other_args_list=""
# Global state for keeping track of -Wl,-rpath -Wl,/path
wl_expect_rpath=no
# Same, but for -Xlinker -rpath -Xlinker /path
xlinker_expect_rpath=no
parse_Wl() {
while [ $# -ne 0 ]; do
if [ "$wl_expect_rpath" = yes ]; then
if system_dir "$1"; then
append return_system_rpath_dirs_list "$1"
append system_rpath_dirs_list "$1"
else
append return_rpath_dirs_list "$1"
append rpath_dirs_list "$1"
fi
wl_expect_rpath=no
else
@@ -433,9 +449,9 @@ parse_Wl() {
if [ -z "$arg" ]; then
shift; continue
elif system_dir "$arg"; then
append return_system_rpath_dirs_list "$arg"
append system_rpath_dirs_list "$arg"
else
append return_rpath_dirs_list "$arg"
append rpath_dirs_list "$arg"
fi
;;
--rpath=*)
@@ -443,9 +459,9 @@ parse_Wl() {
if [ -z "$arg" ]; then
shift; continue
elif system_dir "$arg"; then
append return_system_rpath_dirs_list "$arg"
append system_rpath_dirs_list "$arg"
else
append return_rpath_dirs_list "$arg"
append rpath_dirs_list "$arg"
fi
;;
-rpath|--rpath)
@@ -459,7 +475,7 @@ parse_Wl() {
return 1
;;
*)
append return_other_args_list "-Wl,$1"
append other_args_list "-Wl,$1"
;;
esac
fi
@@ -467,210 +483,177 @@ parse_Wl() {
done
}
categorize_arguments() {
unset IFS
while [ $# -ne 0 ]; do
return_other_args_list=""
return_isystem_was_used=""
return_isystem_system_include_dirs_list=""
return_isystem_include_dirs_list=""
return_system_include_dirs_list=""
return_include_dirs_list=""
return_system_lib_dirs_list=""
return_lib_dirs_list=""
return_system_rpath_dirs_list=""
return_rpath_dirs_list=""
# an RPATH to be added after the case statement.
rp=""
# Global state for keeping track of -Wl,-rpath -Wl,/path
wl_expect_rpath=no
# Multiple consecutive spaces in the command line can
# result in blank arguments
if [ -z "$1" ]; then
shift
continue
fi
# Same, but for -Xlinker -rpath -Xlinker /path
xlinker_expect_rpath=no
while [ $# -ne 0 ]; do
# an RPATH to be added after the case statement.
rp=""
# Multiple consecutive spaces in the command line can
# result in blank arguments
if [ -z "$1" ]; then
shift
continue
fi
if [ -n "${SPACK_COMPILER_FLAGS_KEEP}" ] ; then
# NOTE: the eval is required to allow `|` alternatives inside the variable
eval "\
case \"\$1\" in
$SPACK_COMPILER_FLAGS_KEEP)
append return_other_args_list \"\$1\"
shift
continue
;;
esac
"
fi
# the replace list is a space-separated list of pipe-separated pairs,
# the first in each pair is the original prefix to be matched, the
# second is the replacement prefix
if [ -n "${SPACK_COMPILER_FLAGS_REPLACE}" ] ; then
for rep in ${SPACK_COMPILER_FLAGS_REPLACE} ; do
before=${rep%|*}
after=${rep#*|}
eval "\
stripped=\"\${1##$before}\"
"
if [ "$stripped" = "$1" ] ; then
continue
fi
replaced="$after$stripped"
# it matched, remove it
if [ -n "${SPACK_COMPILER_FLAGS_KEEP}" ] ; then
# NOTE: the eval is required to allow `|` alternatives inside the variable
eval "\
case \"\$1\" in
$SPACK_COMPILER_FLAGS_KEEP)
append other_args_list \"\$1\"
shift
if [ -z "$replaced" ] ; then
# completely removed, continue OUTER loop
continue 2
fi
# re-build argument list with replacement
set -- "$replaced" "$@"
done
fi
case "$1" in
-isystem*)
arg="${1#-isystem}"
return_isystem_was_used=true
if [ -z "$arg" ]; then shift; arg="$1"; fi
if system_dir "$arg"; then
append return_isystem_system_include_dirs_list "$arg"
else
append return_isystem_include_dirs_list "$arg"
fi
;;
-I*)
arg="${1#-I}"
if [ -z "$arg" ]; then shift; arg="$1"; fi
if system_dir "$arg"; then
append return_system_include_dirs_list "$arg"
else
append return_include_dirs_list "$arg"
fi
;;
-L*)
arg="${1#-L}"
if [ -z "$arg" ]; then shift; arg="$1"; fi
if system_dir "$arg"; then
append return_system_lib_dirs_list "$arg"
else
append return_lib_dirs_list "$arg"
fi
;;
-l*)
# -loopopt=0 is generated erroneously in autoconf <= 2.69,
# and passed by ifx to the linker, which confuses it with a
# library. Filter it out.
# TODO: generalize filtering of args with an env var, so that
# TODO: we do not have to special case this here.
if { [ "$mode" = "ccld" ] || [ $mode = "ld" ]; } \
&& [ "$1" != "${1#-loopopt}" ]; then
shift
continue
fi
arg="${1#-l}"
if [ -z "$arg" ]; then shift; arg="$1"; fi
append return_other_args_list "-l$arg"
;;
-Wl,*)
IFS=,
if ! parse_Wl ${1#-Wl,}; then
append return_other_args_list "$1"
fi
unset IFS
;;
-Xlinker)
shift
if [ $# -eq 0 ]; then
# -Xlinker without value: let the compiler error about it.
append return_other_args_list -Xlinker
xlinker_expect_rpath=no
break
elif [ "$xlinker_expect_rpath" = yes ]; then
# Register the path of -Xlinker -rpath <other args> -Xlinker <path>
if system_dir "$1"; then
append return_system_rpath_dirs_list "$1"
else
append return_rpath_dirs_list "$1"
fi
xlinker_expect_rpath=no
else
case "$1" in
-rpath=*)
arg="${1#-rpath=}"
if system_dir "$arg"; then
append return_system_rpath_dirs_list "$arg"
else
append return_rpath_dirs_list "$arg"
fi
;;
--rpath=*)
arg="${1#--rpath=}"
if system_dir "$arg"; then
append return_system_rpath_dirs_list "$arg"
else
append return_rpath_dirs_list "$arg"
fi
;;
-rpath|--rpath)
xlinker_expect_rpath=yes
;;
"$dtags_to_strip")
;;
*)
append return_other_args_list -Xlinker
append return_other_args_list "$1"
;;
esac
fi
;;
"$dtags_to_strip")
;;
*)
append return_other_args_list "$1"
continue
;;
esac
shift
done
"
fi
# the replace list is a space-separated list of pipe-separated pairs,
# the first in each pair is the original prefix to be matched, the
# second is the replacement prefix
if [ -n "${SPACK_COMPILER_FLAGS_REPLACE}" ] ; then
for rep in ${SPACK_COMPILER_FLAGS_REPLACE} ; do
before=${rep%|*}
after=${rep#*|}
eval "\
stripped=\"\${1##$before}\"
"
if [ "$stripped" = "$1" ] ; then
continue
fi
# We found `-Xlinker -rpath` but no matching value `-Xlinker /path`. Just append
# `-Xlinker -rpath` again and let the compiler or linker handle the error during arg
# parsing.
if [ "$xlinker_expect_rpath" = yes ]; then
append return_other_args_list -Xlinker
append return_other_args_list -rpath
replaced="$after$stripped"
# it matched, remove it
shift
if [ -z "$replaced" ] ; then
# completely removed, continue OUTER loop
continue 2
fi
# re-build argument list with replacement
set -- "$replaced" "$@"
done
fi
# Same, but for -Wl flags.
if [ "$wl_expect_rpath" = yes ]; then
append return_other_args_list -Wl,-rpath
fi
}
case "$1" in
-isystem*)
arg="${1#-isystem}"
isystem_was_used=true
if [ -z "$arg" ]; then shift; arg="$1"; fi
if system_dir "$arg"; then
append isystem_system_include_dirs_list "$arg"
else
append isystem_include_dirs_list "$arg"
fi
;;
-I*)
arg="${1#-I}"
if [ -z "$arg" ]; then shift; arg="$1"; fi
if system_dir "$arg"; then
append system_include_dirs_list "$arg"
else
append include_dirs_list "$arg"
fi
;;
-L*)
arg="${1#-L}"
if [ -z "$arg" ]; then shift; arg="$1"; fi
if system_dir "$arg"; then
append system_lib_dirs_list "$arg"
else
append lib_dirs_list "$arg"
fi
;;
-l*)
# -loopopt=0 is generated erroneously in autoconf <= 2.69,
# and passed by ifx to the linker, which confuses it with a
# library. Filter it out.
# TODO: generalize filtering of args with an env var, so that
# TODO: we do not have to special case this here.
if { [ "$mode" = "ccld" ] || [ $mode = "ld" ]; } \
&& [ "$1" != "${1#-loopopt}" ]; then
shift
continue
fi
arg="${1#-l}"
if [ -z "$arg" ]; then shift; arg="$1"; fi
append other_args_list "-l$arg"
;;
-Wl,*)
IFS=,
if ! parse_Wl ${1#-Wl,}; then
append other_args_list "$1"
fi
unset IFS
;;
-Xlinker)
shift
if [ $# -eq 0 ]; then
# -Xlinker without value: let the compiler error about it.
append other_args_list -Xlinker
xlinker_expect_rpath=no
break
elif [ "$xlinker_expect_rpath" = yes ]; then
# Register the path of -Xlinker -rpath <other args> -Xlinker <path>
if system_dir "$1"; then
append system_rpath_dirs_list "$1"
else
append rpath_dirs_list "$1"
fi
xlinker_expect_rpath=no
else
case "$1" in
-rpath=*)
arg="${1#-rpath=}"
if system_dir "$arg"; then
append system_rpath_dirs_list "$arg"
else
append rpath_dirs_list "$arg"
fi
;;
--rpath=*)
arg="${1#--rpath=}"
if system_dir "$arg"; then
append system_rpath_dirs_list "$arg"
else
append rpath_dirs_list "$arg"
fi
;;
-rpath|--rpath)
xlinker_expect_rpath=yes
;;
"$dtags_to_strip")
;;
*)
append other_args_list -Xlinker
append other_args_list "$1"
;;
esac
fi
;;
"$dtags_to_strip")
;;
*)
append other_args_list "$1"
;;
esac
shift
done
categorize_arguments "$@"
include_dirs_list="$return_include_dirs_list"
lib_dirs_list="$return_lib_dirs_list"
rpath_dirs_list="$return_rpath_dirs_list"
system_include_dirs_list="$return_system_include_dirs_list"
system_lib_dirs_list="$return_system_lib_dirs_list"
system_rpath_dirs_list="$return_system_rpath_dirs_list"
isystem_was_used="$return_isystem_was_used"
isystem_system_include_dirs_list="$return_isystem_system_include_dirs_list"
isystem_include_dirs_list="$return_isystem_include_dirs_list"
other_args_list="$return_other_args_list"
# We found `-Xlinker -rpath` but no matching value `-Xlinker /path`. Just append
# `-Xlinker -rpath` again and let the compiler or linker handle the error during arg
# parsing.
if [ "$xlinker_expect_rpath" = yes ]; then
append other_args_list -Xlinker
append other_args_list -rpath
fi
# Same, but for -Wl flags.
if [ "$wl_expect_rpath" = yes ]; then
append other_args_list -Wl,-rpath
fi
#
# Add flags from Spack's cppflags, cflags, cxxflags, fcflags, fflags, and
@@ -690,14 +673,12 @@ elif [ "$SPACK_ADD_DEBUG_FLAGS" = "custom" ]; then
extend flags_list SPACK_DEBUG_FLAGS
fi
spack_flags_list=""
# Fortran flags come before CPPFLAGS
case "$mode" in
cc|ccld)
case $lang_flags in
F)
extend spack_flags_list SPACK_FFLAGS
extend flags_list SPACK_FFLAGS
;;
esac
;;
@@ -706,7 +687,7 @@ esac
# C preprocessor flags come before any C/CXX flags
case "$mode" in
cpp|as|cc|ccld)
extend spack_flags_list SPACK_CPPFLAGS
extend flags_list SPACK_CPPFLAGS
;;
esac
@@ -716,10 +697,10 @@ case "$mode" in
cc|ccld)
case $lang_flags in
C)
extend spack_flags_list SPACK_CFLAGS
extend flags_list SPACK_CFLAGS
;;
CXX)
extend spack_flags_list SPACK_CXXFLAGS
extend flags_list SPACK_CXXFLAGS
;;
esac
@@ -731,25 +712,10 @@ esac
# Linker flags
case "$mode" in
ld|ccld)
extend spack_flags_list SPACK_LDFLAGS
extend flags_list SPACK_LDFLAGS
;;
esac
IFS="$lsep"
categorize_arguments $spack_flags_list
unset IFS
spack_flags_include_dirs_list="$return_include_dirs_list"
spack_flags_lib_dirs_list="$return_lib_dirs_list"
spack_flags_rpath_dirs_list="$return_rpath_dirs_list"
spack_flags_system_include_dirs_list="$return_system_include_dirs_list"
spack_flags_system_lib_dirs_list="$return_system_lib_dirs_list"
spack_flags_system_rpath_dirs_list="$return_system_rpath_dirs_list"
spack_flags_isystem_was_used="$return_isystem_was_used"
spack_flags_isystem_system_include_dirs_list="$return_isystem_system_include_dirs_list"
spack_flags_isystem_include_dirs_list="$return_isystem_include_dirs_list"
spack_flags_other_args_list="$return_other_args_list"
# On macOS insert headerpad_max_install_names linker flag
if [ "$mode" = ld ] || [ "$mode" = ccld ]; then
if [ "${SPACK_SHORT_SPEC#*darwin}" != "${SPACK_SHORT_SPEC}" ]; then
@@ -775,8 +741,6 @@ if [ "$mode" = ccld ] || [ "$mode" = ld ]; then
extend lib_dirs_list SPACK_LINK_DIRS
fi
libs_list=""
# add RPATHs if we're in in any linking mode
case "$mode" in
ld|ccld)
@@ -805,16 +769,12 @@ args_list="$flags_list"
# Insert include directories just prior to any system include directories
# NOTE: adding ${lsep} to the prefix here turns every added element into two
extend args_list spack_flags_include_dirs_list "-I"
extend args_list include_dirs_list "-I"
extend args_list spack_flags_isystem_include_dirs_list "-isystem${lsep}"
extend args_list isystem_include_dirs_list "-isystem${lsep}"
case "$mode" in
cpp|cc|as|ccld)
if [ "$spack_flags_isystem_was_used" = "true" ]; then
extend args_list SPACK_INCLUDE_DIRS "-isystem${lsep}"
elif [ "$isystem_was_used" = "true" ]; then
if [ "$isystem_was_used" = "true" ]; then
extend args_list SPACK_INCLUDE_DIRS "-isystem${lsep}"
else
extend args_list SPACK_INCLUDE_DIRS "-I"
@@ -822,15 +782,11 @@ case "$mode" in
;;
esac
extend args_list spack_flags_system_include_dirs_list -I
extend args_list system_include_dirs_list -I
extend args_list spack_flags_isystem_system_include_dirs_list "-isystem${lsep}"
extend args_list isystem_system_include_dirs_list "-isystem${lsep}"
# Library search paths
extend args_list spack_flags_lib_dirs_list "-L"
extend args_list lib_dirs_list "-L"
extend args_list spack_flags_system_lib_dirs_list "-L"
extend args_list system_lib_dirs_list "-L"
# RPATHs arguments
@@ -839,25 +795,20 @@ case "$mode" in
if [ -n "$dtags_to_add" ] ; then
append args_list "$linker_arg$dtags_to_add"
fi
extend args_list spack_flags_rpath_dirs_list "$rpath"
extend args_list rpath_dirs_list "$rpath"
extend args_list spack_flags_system_rpath_dirs_list "$rpath"
extend args_list system_rpath_dirs_list "$rpath"
;;
ld)
if [ -n "$dtags_to_add" ] ; then
append args_list "$dtags_to_add"
fi
extend args_list spack_flags_rpath_dirs_list "-rpath${lsep}"
extend args_list rpath_dirs_list "-rpath${lsep}"
extend args_list spack_flags_system_rpath_dirs_list "-rpath${lsep}"
extend args_list system_rpath_dirs_list "-rpath${lsep}"
;;
esac
# Other arguments from the input command
extend args_list other_args_list
extend args_list spack_flags_other_args_list
# Inject SPACK_LDLIBS, if supplied
extend args_list libs_list "-l"
@@ -913,4 +864,3 @@ fi
# Execute the full command, preserving spaces with IFS set
# to the alarm bell separator.
IFS="$lsep"; exec $full_command_list

View File

@@ -65,6 +65,9 @@
up to date with CTest, just make sure the ``*_matches`` and
``*_exceptions`` lists are kept up to date with CTest's build handler.
"""
from __future__ import print_function
from __future__ import division
import re
import math
import multiprocessing
@@ -208,7 +211,7 @@
]
class LogEvent:
class LogEvent(object):
"""Class representing interesting events (e.g., errors) in a build log."""
def __init__(self, text, line_no,
source_file=None, source_line_no=None,
@@ -345,7 +348,7 @@ def _parse_unpack(args):
return _parse(*args)
class CTestLogParser:
class CTestLogParser(object):
"""Log file parser that extracts errors and warnings."""
def __init__(self, profile=False):
# whether to record timing information

View File

@@ -3,42 +3,33 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import abc
from __future__ import print_function
import argparse
import errno
import io
import re
import sys
from argparse import ArgumentParser
from typing import IO, Any, Iterable, List, Optional, Sequence, Tuple, Union
class Command:
class Command(object):
"""Parsed representation of a command from argparse.
This is a single command from an argparse parser. ``ArgparseWriter`` creates these and returns
them from ``parse()``, and it passes one of these to each call to ``format()`` so that we can
take an action for a single command.
This is a single command from an argparse parser. ``ArgparseWriter``
creates these and returns them from ``parse()``, and it passes one of
these to each call to ``format()`` so that we can take an action for
a single command.
Parts of a Command:
- prog: command name (str)
- description: command description (str)
- usage: command usage (str)
- positionals: list of positional arguments (list)
- optionals: list of optional arguments (list)
- subcommands: list of subcommand parsers (list)
"""
def __init__(
self,
prog: str,
description: Optional[str],
usage: str,
positionals: List[Tuple[str, Optional[Iterable[Any]], Union[int, str, None], str]],
optionals: List[Tuple[Sequence[str], List[str], str, Union[int, str, None], str]],
subcommands: List[Tuple[ArgumentParser, str, str]],
) -> None:
"""Initialize a new Command instance.
Args:
prog: Program name.
description: Command description.
usage: Command usage.
positionals: List of positional arguments.
optionals: List of optional arguments.
subcommands: List of subcommand parsers.
"""
def __init__(self, prog, description, usage, positionals, optionals, subcommands):
self.prog = prog
self.description = description
self.usage = usage
@@ -47,34 +38,35 @@ def __init__(
self.subcommands = subcommands
# NOTE: The only reason we subclass argparse.HelpFormatter is to get access to self._expand_help(),
# ArgparseWriter is not intended to be used as a formatter_class.
class ArgparseWriter(argparse.HelpFormatter, abc.ABC):
"""Analyze an argparse ArgumentParser for easy generation of help."""
# NOTE: The only reason we subclass argparse.HelpFormatter is to get access
# to self._expand_help(), ArgparseWriter is not intended to be used as a
# formatter_class.
class ArgparseWriter(argparse.HelpFormatter):
"""Analyzes an argparse ArgumentParser for easy generation of help."""
def __init__(self, prog: str, out: IO = sys.stdout, aliases: bool = False) -> None:
"""Initialize a new ArgparseWriter instance.
def __init__(self, prog, out=None, aliases=False):
"""Initializes a new ArgparseWriter instance.
Args:
prog: Program name.
out: File object to write to.
aliases: Whether or not to include subparsers for aliases.
Parameters:
prog (str): the program name
out (file object): the file to write to (default sys.stdout)
aliases (bool): whether or not to include subparsers for aliases
"""
super().__init__(prog)
super(ArgparseWriter, self).__init__(prog)
self.level = 0
self.prog = prog
self.out = out
self.out = sys.stdout if out is None else out
self.aliases = aliases
def parse(self, parser: ArgumentParser, prog: str) -> Command:
"""Parse the parser object and return the relavent components.
def parse(self, parser, prog):
"""Parses the parser object and returns the relavent components.
Args:
parser: Command parser.
prog: Program name.
Parameters:
parser (argparse.ArgumentParser): the parser
prog (str): the command name
Returns:
Information about the command from the parser.
(Command) information about the command from the parser
"""
self.parser = parser
@@ -88,7 +80,8 @@ def parse(self, parser: ArgumentParser, prog: str) -> Command:
groups = parser._mutually_exclusive_groups
usage = fmt._format_usage(None, actions, groups, "").strip()
# Go through actions and split them into optionals, positionals, and subcommands
# Go through actions and split them into optionals, positionals,
# and subcommands
optionals = []
positionals = []
subcommands = []
@@ -96,97 +89,74 @@ def parse(self, parser: ArgumentParser, prog: str) -> Command:
if action.option_strings:
flags = action.option_strings
dest_flags = fmt._format_action_invocation(action)
nargs = action.nargs
help = (
self._expand_help(action)
if action.help and action.help != argparse.SUPPRESS
else ""
)
help = help.split("\n")[0]
if action.choices is not None:
dest = [str(choice) for choice in action.choices]
else:
dest = [action.dest]
optionals.append((flags, dest, dest_flags, nargs, help))
help = self._expand_help(action) if action.help else ""
help = help.replace("\n", " ")
optionals.append((flags, dest_flags, help))
elif isinstance(action, argparse._SubParsersAction):
for subaction in action._choices_actions:
subparser = action._name_parser_map[subaction.dest]
help = (
self._expand_help(subaction)
if subaction.help and action.help != argparse.SUPPRESS
else ""
)
help = help.split("\n")[0]
subcommands.append((subparser, subaction.dest, help))
subcommands.append((subparser, subaction.dest))
# Look for aliases of the form 'name (alias, ...)'
if self.aliases and isinstance(subaction.metavar, str):
if self.aliases:
match = re.match(r"(.*) \((.*)\)", subaction.metavar)
if match:
aliases = match.group(2).split(", ")
for alias in aliases:
subparser = action._name_parser_map[alias]
help = (
self._expand_help(subaction)
if subaction.help and action.help != argparse.SUPPRESS
else ""
)
help = help.split("\n")[0]
subcommands.append((subparser, alias, help))
subcommands.append((subparser, alias))
else:
args = fmt._format_action_invocation(action)
help = (
self._expand_help(action)
if action.help and action.help != argparse.SUPPRESS
else ""
)
help = help.split("\n")[0]
positionals.append((args, action.choices, action.nargs, help))
help = self._expand_help(action) if action.help else ""
help = help.replace("\n", " ")
positionals.append((args, help))
return Command(prog, description, usage, positionals, optionals, subcommands)
@abc.abstractmethod
def format(self, cmd: Command) -> str:
"""Return the string representation of a single node in the parser tree.
def format(self, cmd):
"""Returns the string representation of a single node in the
parser tree.
Override this in subclasses to define how each subcommand should be displayed.
Override this in subclasses to define how each subcommand
should be displayed.
Args:
cmd: Parsed information about a command or subcommand.
Parameters:
(Command): parsed information about a command or subcommand
Returns:
String representation of this subcommand.
str: the string representation of this subcommand
"""
raise NotImplementedError
def _write(self, parser: ArgumentParser, prog: str, level: int = 0) -> None:
"""Recursively write a parser.
def _write(self, parser, prog, level=0):
"""Recursively writes a parser.
Args:
parser: Command parser.
prog: Program name.
level: Current level.
Parameters:
parser (argparse.ArgumentParser): the parser
prog (str): the command name
level (int): the current level
"""
self.level = level
cmd = self.parse(parser, prog)
self.out.write(self.format(cmd))
for subparser, prog, help in cmd.subcommands:
for subparser, prog in cmd.subcommands:
self._write(subparser, prog, level=level + 1)
def write(self, parser: ArgumentParser) -> None:
def write(self, parser):
"""Write out details about an ArgumentParser.
Args:
parser: Command parser.
parser (argparse.ArgumentParser): the parser
"""
try:
self._write(parser, self.prog)
except BrokenPipeError:
except IOError as e:
# Swallow pipe errors
pass
# Raises IOError in Python 2 and BrokenPipeError in Python 3
if e.errno != errno.EPIPE:
raise
_rst_levels = ["=", "-", "^", "~", ":", "`"]
@@ -195,33 +165,21 @@ def write(self, parser: ArgumentParser) -> None:
class ArgparseRstWriter(ArgparseWriter):
"""Write argparse output as rst sections."""
def __init__(
self,
prog: str,
out: IO = sys.stdout,
aliases: bool = False,
rst_levels: Sequence[str] = _rst_levels,
) -> None:
"""Initialize a new ArgparseRstWriter instance.
def __init__(self, prog, out=None, aliases=False, rst_levels=_rst_levels):
"""Create a new ArgparseRstWriter.
Args:
prog: Program name.
out: File object to write to.
aliases: Whether or not to include subparsers for aliases.
rst_levels: List of characters for rst section headings.
Parameters:
prog (str): program name
out (file object): file to write to
aliases (bool): whether or not to include subparsers for aliases
rst_levels (list of str): list of characters
for rst section headings
"""
super().__init__(prog, out, aliases)
out = sys.stdout if out is None else out
super(ArgparseRstWriter, self).__init__(prog, out, aliases)
self.rst_levels = rst_levels
def format(self, cmd: Command) -> str:
"""Return the string representation of a single node in the parser tree.
Args:
cmd: Parsed information about a command or subcommand.
Returns:
String representation of a node.
"""
def format(self, cmd):
string = io.StringIO()
string.write(self.begin_command(cmd.prog))
@@ -232,13 +190,13 @@ def format(self, cmd: Command) -> str:
if cmd.positionals:
string.write(self.begin_positionals())
for args, choices, nargs, help in cmd.positionals:
for args, help in cmd.positionals:
string.write(self.positional(args, help))
string.write(self.end_positionals())
if cmd.optionals:
string.write(self.begin_optionals())
for flags, dest, dest_flags, nargs, help in cmd.optionals:
for flags, dest_flags, help in cmd.optionals:
string.write(self.optional(dest_flags, help))
string.write(self.end_optionals())
@@ -247,15 +205,7 @@ def format(self, cmd: Command) -> str:
return string.getvalue()
def begin_command(self, prog: str) -> str:
"""Text to print before a command.
Args:
prog: Program name.
Returns:
Text before a command.
"""
def begin_command(self, prog):
return """
----
@@ -268,26 +218,10 @@ def begin_command(self, prog: str) -> str:
prog.replace(" ", "-"), prog, self.rst_levels[self.level] * len(prog)
)
def description(self, description: str) -> str:
"""Description of a command.
Args:
description: Command description.
Returns:
Description of a command.
"""
def description(self, description):
return description + "\n\n"
def usage(self, usage: str) -> str:
"""Example usage of a command.
Args:
usage: Command usage.
Returns:
Usage of a command.
"""
def usage(self, usage):
return """\
.. code-block:: console
@@ -297,24 +231,10 @@ def usage(self, usage: str) -> str:
usage
)
def begin_positionals(self) -> str:
"""Text to print before positional arguments.
Returns:
Positional arguments header.
"""
def begin_positionals(self):
return "\n**Positional arguments**\n\n"
def positional(self, name: str, help: str) -> str:
"""Description of a positional argument.
Args:
name: Argument name.
help: Help text.
Returns:
Positional argument description.
"""
def positional(self, name, help):
return """\
{0}
{1}
@@ -323,32 +243,13 @@ def positional(self, name: str, help: str) -> str:
name, help
)
def end_positionals(self) -> str:
"""Text to print after positional arguments.
Returns:
Positional arguments footer.
"""
def end_positionals(self):
return ""
def begin_optionals(self) -> str:
"""Text to print before optional arguments.
Returns:
Optional arguments header.
"""
def begin_optionals(self):
return "\n**Optional arguments**\n\n"
def optional(self, opts: str, help: str) -> str:
"""Description of an optional argument.
Args:
opts: Optional argument.
help: Help text.
Returns:
Optional argument description.
"""
def optional(self, opts, help):
return """\
``{0}``
{1}
@@ -357,23 +258,10 @@ def optional(self, opts: str, help: str) -> str:
opts, help
)
def end_optionals(self) -> str:
"""Text to print after optional arguments.
Returns:
Optional arguments footer.
"""
def end_optionals(self):
return ""
def begin_subcommands(self, subcommands: List[Tuple[ArgumentParser, str, str]]) -> str:
"""Table with links to other subcommands.
Arguments:
subcommands: List of subcommands.
Returns:
Subcommand linking text.
"""
def begin_subcommands(self, subcommands):
string = """
**Subcommands**
@@ -382,8 +270,116 @@ def begin_subcommands(self, subcommands: List[Tuple[ArgumentParser, str, str]])
"""
for cmd, _, _ in subcommands:
for cmd, _ in subcommands:
prog = re.sub(r"^[^ ]* ", "", cmd.prog)
string += " * :ref:`{0} <{1}>`\n".format(prog, cmd.prog.replace(" ", "-"))
return string + "\n"
class ArgparseCompletionWriter(ArgparseWriter):
"""Write argparse output as shell programmable tab completion functions."""
def format(self, cmd):
"""Returns the string representation of a single node in the
parser tree.
Override this in subclasses to define how each subcommand
should be displayed.
Parameters:
(Command): parsed information about a command or subcommand
Returns:
str: the string representation of this subcommand
"""
assert cmd.optionals # we should always at least have -h, --help
assert not (cmd.positionals and cmd.subcommands) # one or the other
# We only care about the arguments/flags, not the help messages
positionals = []
if cmd.positionals:
positionals, _ = zip(*cmd.positionals)
optionals, _, _ = zip(*cmd.optionals)
subcommands = []
if cmd.subcommands:
_, subcommands = zip(*cmd.subcommands)
# Flatten lists of lists
optionals = [x for xx in optionals for x in xx]
return (
self.start_function(cmd.prog)
+ self.body(positionals, optionals, subcommands)
+ self.end_function(cmd.prog)
)
def start_function(self, prog):
"""Returns the syntax needed to begin a function definition.
Parameters:
prog (str): the command name
Returns:
str: the function definition beginning
"""
name = prog.replace("-", "_").replace(" ", "_")
return "\n_{0}() {{".format(name)
def end_function(self, prog=None):
"""Returns the syntax needed to end a function definition.
Parameters:
prog (str or None): the command name
Returns:
str: the function definition ending
"""
return "}\n"
def body(self, positionals, optionals, subcommands):
"""Returns the body of the function.
Parameters:
positionals (list): list of positional arguments
optionals (list): list of optional arguments
subcommands (list): list of subcommand parsers
Returns:
str: the function body
"""
return ""
def positionals(self, positionals):
"""Returns the syntax for reporting positional arguments.
Parameters:
positionals (list): list of positional arguments
Returns:
str: the syntax for positional arguments
"""
return ""
def optionals(self, optionals):
"""Returns the syntax for reporting optional flags.
Parameters:
optionals (list): list of optional arguments
Returns:
str: the syntax for optional flags
"""
return ""
def subcommands(self, subcommands):
"""Returns the syntax for reporting subcommands.
Parameters:
subcommands (list): list of subcommand parsers
Returns:
str: the syntax for subcommand parsers
"""
return ""

View File

@@ -402,7 +402,7 @@ def groupid_to_group(x):
os.remove(backup_filename)
class FileFilter:
class FileFilter(object):
"""Convenience class for calling ``filter_file`` a lot."""
def __init__(self, *filenames):
@@ -610,8 +610,6 @@ def chgrp(path, group, follow_symlinks=True):
gid = grp.getgrnam(group).gr_gid
else:
gid = group
if os.stat(path).st_gid == gid:
return
if follow_symlinks:
os.chown(path, -1, gid)
else:
@@ -1338,7 +1336,7 @@ def lexists_islink_isdir(path):
return True, is_link, is_dir
class BaseDirectoryVisitor:
class BaseDirectoryVisitor(object):
"""Base class and interface for :py:func:`visit_directory_tree`."""
def visit_file(self, root, rel_path, depth):
@@ -1892,7 +1890,7 @@ class HeaderList(FileList):
include_regex = re.compile(r"(.*?)(\binclude\b)(.*)")
def __init__(self, files):
super().__init__(files)
super(HeaderList, self).__init__(files)
self._macro_definitions = []
self._directories = None
@@ -1918,7 +1916,7 @@ def _default_directories(self):
"""Default computation of directories based on the list of
header files.
"""
dir_list = super().directories
dir_list = super(HeaderList, self).directories
values = []
for d in dir_list:
# If the path contains a subdirectory named 'include' then stop
@@ -2354,7 +2352,7 @@ def find_all_libraries(root, recursive=False):
)
class WindowsSimulatedRPath:
class WindowsSimulatedRPath(object):
"""Class representing Windows filesystem rpath analog
One instance of this class is associated with a package (only on Windows)

View File

@@ -3,6 +3,8 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import division
import collections.abc
import contextlib
import functools
@@ -766,10 +768,10 @@ def pretty_seconds(seconds):
class RequiredAttributeError(ValueError):
def __init__(self, message):
super().__init__(message)
super(RequiredAttributeError, self).__init__(message)
class ObjectWrapper:
class ObjectWrapper(object):
"""Base class that wraps an object. Derived classes can add new behavior
while staying undercover.
@@ -796,7 +798,7 @@ def __init__(self, wrapped_object):
self.__dict__ = wrapped_object.__dict__
class Singleton:
class Singleton(object):
"""Simple wrapper for lazily initialized singleton objects."""
def __init__(self, factory):
@@ -821,7 +823,7 @@ def __getattr__(self, name):
# 'instance'/'_instance' to be defined or it will enter an infinite
# loop, so protect against that here.
if name in ["_instance", "instance"]:
raise AttributeError(f"cannot create {name}")
raise AttributeError()
return getattr(self.instance, name)
def __getitem__(self, name):
@@ -843,6 +845,27 @@ def __repr__(self):
return repr(self.instance)
class LazyReference(object):
"""Lazily evaluated reference to part of a singleton."""
def __init__(self, ref_function):
self.ref_function = ref_function
def __getattr__(self, name):
if name == "ref_function":
raise AttributeError()
return getattr(self.ref_function(), name)
def __getitem__(self, name):
return self.ref_function()[name]
def __str__(self):
return str(self.ref_function())
def __repr__(self):
return repr(self.ref_function())
def load_module_from_file(module_name, module_path):
"""Loads a python module from the path of the corresponding file.
@@ -920,7 +943,7 @@ def _wrapper(args):
return _wrapper
class Devnull:
class Devnull(object):
"""Null stream with less overhead than ``os.devnull``.
See https://stackoverflow.com/a/2929954.
@@ -1037,7 +1060,7 @@ def __str__(self):
return str(self.data)
class GroupedExceptionHandler:
class GroupedExceptionHandler(object):
"""A generic mechanism to coalesce multiple exceptions and preserve tracebacks."""
def __init__(self):
@@ -1068,7 +1091,7 @@ def grouped_message(self, with_tracebacks: bool = True) -> str:
return "due to the following failures:\n{0}".format("\n".join(each_exception_message))
class GroupedExceptionForwarder:
class GroupedExceptionForwarder(object):
"""A contextmanager to capture exceptions and forward them to a
GroupedExceptionHandler."""
@@ -1088,7 +1111,7 @@ def __exit__(self, exc_type, exc_value, tb):
return True
class classproperty:
class classproperty(object):
"""Non-data descriptor to evaluate a class-level property. The function that performs
the evaluation is injected at creation time and take an instance (could be None) and
an owner (i.e. the class that originated the instance)

View File

@@ -5,6 +5,8 @@
"""LinkTree class for setting up trees of symbolic links."""
from __future__ import print_function
import filecmp
import os
import shutil
@@ -285,7 +287,7 @@ def visit_symlinked_file(self, root, rel_path, depth):
self.visit_file(root, rel_path, depth)
class LinkTree:
class LinkTree(object):
"""Class to create trees of symbolic links from a source directory.
LinkTree objects are constructed with a source root. Their
@@ -430,12 +432,12 @@ class MergeConflictError(Exception):
class ConflictingSpecsError(MergeConflictError):
def __init__(self, spec_1, spec_2):
super().__init__(spec_1, spec_2)
super(MergeConflictError, self).__init__(spec_1, spec_2)
class SingleMergeConflictError(MergeConflictError):
def __init__(self, path):
super().__init__("Package merge blocked by file: %s" % path)
super(MergeConflictError, self).__init__("Package merge blocked by file: %s" % path)
class MergeConflictSummary(MergeConflictError):
@@ -450,4 +452,4 @@ def __init__(self, conflicts):
msg += "\n `{0}` and `{1}` both project to `{2}`".format(
conflict.src_a, conflict.src_b, conflict.dst
)
super().__init__(msg)
super(MergeConflictSummary, self).__init__(msg)

View File

@@ -9,10 +9,9 @@
import sys
import time
from datetime import datetime
from types import TracebackType
from typing import IO, Any, Callable, ContextManager, Dict, Generator, Optional, Tuple, Type, Union
from llnl.util import lang, tty
import llnl.util.tty as tty
from llnl.util.lang import pretty_seconds
import spack.util.string
@@ -35,15 +34,12 @@
]
ReleaseFnType = Optional[Callable[[], bool]]
#: A useful replacement for functions that should return True when not provided
#: for example.
true_fn = lambda: True
def true_fn() -> bool:
"""A function that always returns True."""
return True
class OpenFile:
class OpenFile(object):
"""Record for keeping track of open lockfiles (with reference counting).
There's really only one ``OpenFile`` per inode, per process, but we record the
@@ -52,12 +48,12 @@ class OpenFile:
file descriptors as well in the future.
"""
def __init__(self, fh: IO) -> None:
def __init__(self, fh):
self.fh = fh
self.refs = 0
class OpenFileTracker:
class OpenFileTracker(object):
"""Track open lockfiles, to minimize number of open file descriptors.
The ``fcntl`` locks that Spack uses are associated with an inode and a process.
@@ -82,11 +78,11 @@ class OpenFileTracker:
work in Python and assume the GIL.
"""
def __init__(self) -> None:
def __init__(self):
"""Create a new ``OpenFileTracker``."""
self._descriptors: Dict[Any, OpenFile] = {}
self._descriptors = {}
def get_fh(self, path: str) -> IO:
def get_fh(self, path):
"""Get a filehandle for a lockfile.
This routine will open writable files for read/write even if you're asking
@@ -94,7 +90,7 @@ def get_fh(self, path: str) -> IO:
(write) lock later if requested.
Arguments:
path: path to lock file we want a filehandle for
path (str): path to lock file we want a filehandle for
"""
# Open writable files as 'r+' so we can upgrade to write later
os_mode, fh_mode = (os.O_RDWR | os.O_CREAT), "r+"
@@ -161,7 +157,7 @@ def purge(self):
#: Open file descriptors for locks in this process. Used to prevent one process
#: from opening the sam file many times for different byte range locks
FILE_TRACKER = OpenFileTracker()
file_tracker = OpenFileTracker()
def _attempts_str(wait_time, nattempts):
@@ -170,10 +166,10 @@ def _attempts_str(wait_time, nattempts):
return ""
attempts = spack.util.string.plural(nattempts, "attempt")
return " after {} and {}".format(lang.pretty_seconds(wait_time), attempts)
return " after {} and {}".format(pretty_seconds(wait_time), attempts)
class LockType:
class LockType(object):
READ = 0
WRITE = 1
@@ -192,11 +188,11 @@ def to_module(tid):
return lock
@staticmethod
def is_valid(op: int) -> bool:
def is_valid(op):
return op == LockType.READ or op == LockType.WRITE
class Lock:
class Lock(object):
"""This is an implementation of a filesystem lock using Python's lockf.
In Python, ``lockf`` actually calls ``fcntl``, so this should work with
@@ -211,16 +207,7 @@ class Lock:
overlapping byte ranges in the same file).
"""
def __init__(
self,
path: str,
*,
start: int = 0,
length: int = 0,
default_timeout: Optional[float] = None,
debug: bool = False,
desc: str = "",
) -> None:
def __init__(self, path, start=0, length=0, default_timeout=None, debug=False, desc=""):
"""Construct a new lock on the file at ``path``.
By default, the lock applies to the whole file. Optionally,
@@ -233,17 +220,17 @@ def __init__(
beginning of the file.
Args:
path: path to the lock
start: optional byte offset at which the lock starts
length: optional number of bytes to lock
default_timeout: seconds to wait for lock attempts,
path (str): path to the lock
start (int): optional byte offset at which the lock starts
length (int): optional number of bytes to lock
default_timeout (int): number of seconds to wait for lock attempts,
where None means to wait indefinitely
debug: debug mode specific to locking
desc: optional debug message lock description, which is
debug (bool): debug mode specific to locking
desc (str): optional debug message lock description, which is
helpful for distinguishing between different Spack locks.
"""
self.path = path
self._file: Optional[IO] = None
self._file = None
self._reads = 0
self._writes = 0
@@ -255,7 +242,7 @@ def __init__(
self.debug = debug
# optional debug description
self.desc = f" ({desc})" if desc else ""
self.desc = " ({0})".format(desc) if desc else ""
# If the user doesn't set a default timeout, or if they choose
# None, 0, etc. then lock attempts will not time out (unless the
@@ -263,15 +250,11 @@ def __init__(
self.default_timeout = default_timeout or None
# PID and host of lock holder (only used in debug mode)
self.pid: Optional[int] = None
self.old_pid: Optional[int] = None
self.host: Optional[str] = None
self.old_host: Optional[str] = None
self.pid = self.old_pid = None
self.host = self.old_host = None
@staticmethod
def _poll_interval_generator(
_wait_times: Optional[Tuple[float, float, float]] = None
) -> Generator[float, None, None]:
def _poll_interval_generator(_wait_times=None):
"""This implements a backoff scheme for polling a contended resource
by suggesting a succession of wait times between polls.
@@ -294,21 +277,21 @@ def _poll_interval_generator(
num_requests += 1
yield wait_time
def __repr__(self) -> str:
def __repr__(self):
"""Formal representation of the lock."""
rep = "{0}(".format(self.__class__.__name__)
for attr, value in self.__dict__.items():
rep += "{0}={1}, ".format(attr, value.__repr__())
return "{0})".format(rep.strip(", "))
def __str__(self) -> str:
def __str__(self):
"""Readable string (with key fields) of the lock."""
location = "{0}[{1}:{2}]".format(self.path, self._start, self._length)
timeout = "timeout={0}".format(self.default_timeout)
activity = "#reads={0}, #writes={1}".format(self._reads, self._writes)
return "({0}, {1}, {2})".format(location, timeout, activity)
def _lock(self, op: int, timeout: Optional[float] = None) -> Tuple[float, int]:
def _lock(self, op, timeout=None):
"""This takes a lock using POSIX locks (``fcntl.lockf``).
The lock is implemented as a spin lock using a nonblocking call
@@ -327,7 +310,7 @@ def _lock(self, op: int, timeout: Optional[float] = None) -> Tuple[float, int]:
# Create file and parent directories if they don't exist.
if self._file is None:
self._ensure_parent_directory()
self._file = FILE_TRACKER.get_fh(self.path)
self._file = file_tracker.get_fh(self.path)
if LockType.to_module(op) == fcntl.LOCK_EX and self._file.mode == "r":
# Attempt to upgrade to write lock w/a read-only file.
@@ -336,7 +319,7 @@ def _lock(self, op: int, timeout: Optional[float] = None) -> Tuple[float, int]:
self._log_debug(
"{} locking [{}:{}]: timeout {}".format(
op_str.lower(), self._start, self._length, lang.pretty_seconds(timeout or 0)
op_str.lower(), self._start, self._length, pretty_seconds(timeout or 0)
)
)
@@ -360,20 +343,15 @@ def _lock(self, op: int, timeout: Optional[float] = None) -> Tuple[float, int]:
total_wait_time = time.time() - start_time
raise LockTimeoutError(op_str.lower(), self.path, total_wait_time, num_attempts)
def _poll_lock(self, op: int) -> bool:
def _poll_lock(self, op):
"""Attempt to acquire the lock in a non-blocking manner. Return whether
the locking attempt succeeds
"""
assert self._file is not None, "cannot poll a lock without the file being set"
module_op = LockType.to_module(op)
try:
# Try to get the lock (will raise if not available.)
fcntl.lockf(
self._file.fileno(),
module_op | fcntl.LOCK_NB,
self._length,
self._start,
os.SEEK_SET,
self._file, module_op | fcntl.LOCK_NB, self._length, self._start, os.SEEK_SET
)
# help for debugging distributed locking
@@ -399,7 +377,7 @@ def _poll_lock(self, op: int) -> bool:
return False
def _ensure_parent_directory(self) -> str:
def _ensure_parent_directory(self):
parent = os.path.dirname(self.path)
# relative paths to lockfiles in the current directory have no parent
@@ -418,22 +396,20 @@ def _ensure_parent_directory(self) -> str:
raise
return parent
def _read_log_debug_data(self) -> None:
def _read_log_debug_data(self):
"""Read PID and host data out of the file if it is there."""
assert self._file is not None, "cannot read debug log without the file being set"
self.old_pid = self.pid
self.old_host = self.host
line = self._file.read()
if line:
pid, host = line.strip().split(",")
_, _, pid = pid.rpartition("=")
_, _, self.pid = pid.rpartition("=")
_, _, self.host = host.rpartition("=")
self.pid = int(pid)
self.pid = int(self.pid)
def _write_log_debug_data(self) -> None:
def _write_log_debug_data(self):
"""Write PID and host data to the file, recording old values."""
assert self._file is not None, "cannot write debug log without the file being set"
self.old_pid = self.pid
self.old_host = self.host
@@ -447,21 +423,20 @@ def _write_log_debug_data(self) -> None:
self._file.flush()
os.fsync(self._file.fileno())
def _unlock(self) -> None:
def _unlock(self):
"""Releases a lock using POSIX locks (``fcntl.lockf``)
Releases the lock regardless of mode. Note that read locks may
be masquerading as write locks, but this removes either.
"""
assert self._file is not None, "cannot unlock without the file being set"
fcntl.lockf(self._file.fileno(), fcntl.LOCK_UN, self._length, self._start, os.SEEK_SET)
FILE_TRACKER.release_by_fh(self._file)
fcntl.lockf(self._file, fcntl.LOCK_UN, self._length, self._start, os.SEEK_SET)
file_tracker.release_by_fh(self._file)
self._file = None
self._reads = 0
self._writes = 0
def acquire_read(self, timeout: Optional[float] = None) -> bool:
def acquire_read(self, timeout=None):
"""Acquires a recursive, shared lock for reading.
Read and write locks can be acquired and released in arbitrary
@@ -486,7 +461,7 @@ def acquire_read(self, timeout: Optional[float] = None) -> bool:
self._reads += 1
return False
def acquire_write(self, timeout: Optional[float] = None) -> bool:
def acquire_write(self, timeout=None):
"""Acquires a recursive, exclusive lock for writing.
Read and write locks can be acquired and released in arbitrary
@@ -516,7 +491,7 @@ def acquire_write(self, timeout: Optional[float] = None) -> bool:
self._writes += 1
return False
def is_write_locked(self) -> bool:
def is_write_locked(self):
"""Check if the file is write locked
Return:
@@ -533,7 +508,7 @@ def is_write_locked(self) -> bool:
return False
def downgrade_write_to_read(self, timeout: Optional[float] = None) -> None:
def downgrade_write_to_read(self, timeout=None):
"""
Downgrade from an exclusive write lock to a shared read.
@@ -552,7 +527,7 @@ def downgrade_write_to_read(self, timeout: Optional[float] = None) -> None:
else:
raise LockDowngradeError(self.path)
def upgrade_read_to_write(self, timeout: Optional[float] = None) -> None:
def upgrade_read_to_write(self, timeout=None):
"""
Attempts to upgrade from a shared read lock to an exclusive write.
@@ -571,7 +546,7 @@ def upgrade_read_to_write(self, timeout: Optional[float] = None) -> None:
else:
raise LockUpgradeError(self.path)
def release_read(self, release_fn: ReleaseFnType = None) -> bool:
def release_read(self, release_fn=None):
"""Releases a read lock.
Arguments:
@@ -607,7 +582,7 @@ def release_read(self, release_fn: ReleaseFnType = None) -> bool:
self._reads -= 1
return False
def release_write(self, release_fn: ReleaseFnType = None) -> bool:
def release_write(self, release_fn=None):
"""Releases a write lock.
Arguments:
@@ -648,65 +623,65 @@ def release_write(self, release_fn: ReleaseFnType = None) -> bool:
else:
return False
def cleanup(self) -> None:
def cleanup(self):
if self._reads == 0 and self._writes == 0:
os.unlink(self.path)
else:
raise LockError("Attempting to cleanup active lock.")
def _get_counts_desc(self) -> str:
def _get_counts_desc(self):
return (
"(reads {0}, writes {1})".format(self._reads, self._writes) if tty.is_verbose() else ""
)
def _log_acquired(self, locktype, wait_time, nattempts) -> None:
def _log_acquired(self, locktype, wait_time, nattempts):
attempts_part = _attempts_str(wait_time, nattempts)
now = datetime.now()
desc = "Acquired at %s" % now.strftime("%H:%M:%S.%f")
self._log_debug(self._status_msg(locktype, "{0}{1}".format(desc, attempts_part)))
def _log_acquiring(self, locktype) -> None:
def _log_acquiring(self, locktype):
self._log_debug(self._status_msg(locktype, "Acquiring"), level=3)
def _log_debug(self, *args, **kwargs) -> None:
def _log_debug(self, *args, **kwargs):
"""Output lock debug messages."""
kwargs["level"] = kwargs.get("level", 2)
tty.debug(*args, **kwargs)
def _log_downgraded(self, wait_time, nattempts) -> None:
def _log_downgraded(self, wait_time, nattempts):
attempts_part = _attempts_str(wait_time, nattempts)
now = datetime.now()
desc = "Downgraded at %s" % now.strftime("%H:%M:%S.%f")
self._log_debug(self._status_msg("READ LOCK", "{0}{1}".format(desc, attempts_part)))
def _log_downgrading(self) -> None:
def _log_downgrading(self):
self._log_debug(self._status_msg("WRITE LOCK", "Downgrading"), level=3)
def _log_released(self, locktype) -> None:
def _log_released(self, locktype):
now = datetime.now()
desc = "Released at %s" % now.strftime("%H:%M:%S.%f")
self._log_debug(self._status_msg(locktype, desc))
def _log_releasing(self, locktype) -> None:
def _log_releasing(self, locktype):
self._log_debug(self._status_msg(locktype, "Releasing"), level=3)
def _log_upgraded(self, wait_time, nattempts) -> None:
def _log_upgraded(self, wait_time, nattempts):
attempts_part = _attempts_str(wait_time, nattempts)
now = datetime.now()
desc = "Upgraded at %s" % now.strftime("%H:%M:%S.%f")
self._log_debug(self._status_msg("WRITE LOCK", "{0}{1}".format(desc, attempts_part)))
def _log_upgrading(self) -> None:
def _log_upgrading(self):
self._log_debug(self._status_msg("READ LOCK", "Upgrading"), level=3)
def _status_msg(self, locktype: str, status: str) -> str:
def _status_msg(self, locktype, status):
status_desc = "[{0}] {1}".format(status, self._get_counts_desc())
return "{0}{1.desc}: {1.path}[{1._start}:{1._length}] {2}".format(
locktype, self, status_desc
)
class LockTransaction:
class LockTransaction(object):
"""Simple nested transaction context manager that uses a file lock.
Arguments:
@@ -734,13 +709,7 @@ class LockTransaction:
"""
def __init__(
self,
lock: Lock,
acquire: Union[ReleaseFnType, ContextManager] = None,
release: Union[ReleaseFnType, ContextManager] = None,
timeout: Optional[float] = None,
) -> None:
def __init__(self, lock, acquire=None, release=None, timeout=None):
self._lock = lock
self._timeout = timeout
self._acquire_fn = acquire
@@ -755,20 +724,15 @@ def __enter__(self):
else:
return self._as
def __exit__(
self,
exc_type: Optional[Type[BaseException]],
exc_value: Optional[BaseException],
traceback: Optional[TracebackType],
) -> bool:
def __exit__(self, type, value, traceback):
suppress = False
def release_fn():
if self._release_fn is not None:
return self._release_fn(exc_type, exc_value, traceback)
return self._release_fn(type, value, traceback)
if self._as and hasattr(self._as, "__exit__"):
if self._as.__exit__(exc_type, exc_value, traceback):
if self._as.__exit__(type, value, traceback):
suppress = True
if self._exit(release_fn):
@@ -776,12 +740,6 @@ def release_fn():
return suppress
def _enter(self) -> bool:
return NotImplemented
def _exit(self, release_fn: ReleaseFnType) -> bool:
return NotImplemented
class ReadTransaction(LockTransaction):
"""LockTransaction context manager that does a read and releases it."""
@@ -812,7 +770,7 @@ class LockDowngradeError(LockError):
def __init__(self, path):
msg = "Cannot downgrade lock from write to read on file: %s" % path
super().__init__(msg)
super(LockDowngradeError, self).__init__(msg)
class LockLimitError(LockError):
@@ -824,10 +782,10 @@ class LockTimeoutError(LockError):
def __init__(self, lock_type, path, time, attempts):
fmt = "Timed out waiting for a {} lock after {}.\n Made {} {} on file: {}"
super().__init__(
super(LockTimeoutError, self).__init__(
fmt.format(
lock_type,
lang.pretty_seconds(time),
pretty_seconds(time),
attempts,
"attempt" if attempts == 1 else "attempts",
path,
@@ -840,7 +798,7 @@ class LockUpgradeError(LockError):
def __init__(self, path):
msg = "Cannot upgrade lock from read to write on file: %s" % path
super().__init__(msg)
super(LockUpgradeError, self).__init__(msg)
class LockPermissionError(LockError):
@@ -852,7 +810,7 @@ class LockROFileError(LockPermissionError):
def __init__(self, path):
msg = "Can't take write lock on read-only file: %s" % path
super().__init__(msg)
super(LockROFileError, self).__init__(msg)
class CantCreateLockError(LockPermissionError):
@@ -861,4 +819,4 @@ class CantCreateLockError(LockPermissionError):
def __init__(self, path):
msg = "cannot create lock '%s': " % path
msg += "file does not exist and location is not writable"
super().__init__(msg)
super(LockError, self).__init__(msg)

View File

@@ -3,6 +3,8 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import unicode_literals
import contextlib
import io
import os

View File

@@ -6,6 +6,8 @@
"""
Routines for printing columnar output. See ``colify()`` for more information.
"""
from __future__ import division, unicode_literals
import io
import os
import sys

View File

@@ -59,6 +59,8 @@
To output an @, use '@@'. To output a } inside braces, use '}}'.
"""
from __future__ import unicode_literals
import re
import sys
from contextlib import contextmanager
@@ -68,7 +70,7 @@ class ColorParseError(Exception):
"""Raised when a color format fails to parse."""
def __init__(self, message):
super().__init__(message)
super(ColorParseError, self).__init__(message)
# Text styles for ansi codes
@@ -203,7 +205,7 @@ def color_when(value):
set_color_when(old_value)
class match_to_ansi:
class match_to_ansi(object):
def __init__(self, color=True, enclose=False):
self.color = _color_when_value(color)
self.enclose = enclose
@@ -319,7 +321,7 @@ def cescape(string):
return string
class ColorStream:
class ColorStream(object):
def __init__(self, stream, color=None):
self._stream = stream
self._color = color

View File

@@ -5,6 +5,8 @@
"""Utility classes for logging the output of blocks of code.
"""
from __future__ import unicode_literals
import atexit
import ctypes
import errno
@@ -65,7 +67,7 @@ def _strip(line):
return _escape.sub("", line)
class keyboard_input:
class keyboard_input(object):
"""Context manager to disable line editing and echoing.
Use this with ``sys.stdin`` for keyboard input, e.g.::
@@ -242,7 +244,7 @@ def __exit__(self, exc_type, exception, traceback):
signal.signal(signum, old_handler)
class Unbuffered:
class Unbuffered(object):
"""Wrapper for Python streams that forces them to be unbuffered.
This is implemented by forcing a flush after each write.
@@ -287,7 +289,7 @@ def _file_descriptors_work(*streams):
return False
class FileWrapper:
class FileWrapper(object):
"""Represents a file. Can be an open stream, a path to a file (not opened
yet), or neither. When unwrapped, it returns an open file (or file-like)
object.
@@ -329,7 +331,7 @@ def close(self):
self.file.close()
class MultiProcessFd:
class MultiProcessFd(object):
"""Return an object which stores a file descriptor and can be passed as an
argument to a function run with ``multiprocessing.Process``, such that
the file descriptor is available in the subprocess."""
@@ -429,7 +431,7 @@ def log_output(*args, **kwargs):
return nixlog(*args, **kwargs)
class nixlog:
class nixlog(object):
"""
Under the hood, we spawn a daemon and set up a pipe between this
process and the daemon. The daemon writes our output to both the
@@ -750,7 +752,7 @@ def close(self):
os.close(self.saved_stream)
class winlog:
class winlog(object):
"""
Similar to nixlog, with underlying
functionality ported to support Windows.

View File

@@ -13,6 +13,8 @@
Note: The functionality in this module is unsupported on Windows
"""
from __future__ import print_function
import multiprocessing
import os
import re
@@ -34,7 +36,7 @@
pass
class ProcessController:
class ProcessController(object):
"""Wrapper around some fundamental process control operations.
This allows one process (the controller) to drive another (the
@@ -155,7 +157,7 @@ def wait_running(self):
self.wait(lambda: "T" not in self.proc_status())
class PseudoShell:
class PseudoShell(object):
"""Sets up controller and minion processes with a PTY.
You can create a ``PseudoShell`` if you want to test how some

View File

@@ -13,7 +13,7 @@
from spack.util.executable import Executable, ProcessError
class ABI:
class ABI(object):
"""This class provides methods to test ABI compatibility between specs.
The current implementation is rather rough and could be improved."""

View File

@@ -60,7 +60,7 @@ def _search_duplicate_compilers(error_cls):
GROUPS = collections.defaultdict(list)
class Error:
class Error(object):
"""Information on an error reported in a test."""
def __init__(self, summary, details):
@@ -725,22 +725,11 @@ def _version_constraints_are_satisfiable_by_some_version_in_repo(pkgs, error_cls
dependencies_to_check.extend([edge.spec for edge in dependency_data.values()])
host_architecture = spack.spec.ArchSpec.default_arch()
for s in dependencies_to_check:
dependency_pkg_cls = None
try:
dependency_pkg_cls = spack.repo.path.get_pkg_class(s.name)
# Some packages have hacks that might cause failures on some platform
# Allow to explicitly set conditions to skip version checks in that case
skip_conditions = getattr(dependency_pkg_cls, "skip_version_audit", [])
skip_version_check = False
for condition in skip_conditions:
if host_architecture.satisfies(spack.spec.Spec(condition).architecture):
skip_version_check = True
break
assert skip_version_check or any(
v.intersects(s.versions) for v in list(dependency_pkg_cls.versions)
)
assert any(v.intersects(s.versions) for v in list(dependency_pkg_cls.versions))
except Exception:
summary = (
"{0}: dependency on {1} cannot be satisfied " "by known versions of {1.name}"

View File

@@ -61,22 +61,6 @@
_build_cache_keys_relative_path = "_pgp"
class BuildCacheDatabase(spack_db.Database):
"""A database for binary buildcaches.
A database supports writing buildcache index files, in which case certain fields are not
needed in each install record, and no locking is required. To use this feature, it provides
``lock_cfg=NO_LOCK``, and override the list of ``record_fields``.
"""
record_fields = ("spec", "ref_count", "in_buildcache")
def __init__(self, root):
super().__init__(root, lock_cfg=spack_db.NO_LOCK)
self._write_transaction_impl = llnl.util.lang.nullcontext
self._read_transaction_impl = llnl.util.lang.nullcontext
class FetchCacheError(Exception):
"""Error thrown when fetching the cache failed, usually a composite error list."""
@@ -96,14 +80,14 @@ def __init__(self, errors):
else:
err = errors[0]
self.message = "{0}: {1}".format(err.__class__.__name__, str(err))
super().__init__(self.message)
super(FetchCacheError, self).__init__(self.message)
class ListMirrorSpecsError(spack.error.SpackError):
"""Raised when unable to retrieve list of specs from the mirror"""
class BinaryCacheIndex:
class BinaryCacheIndex(object):
"""
The BinaryCacheIndex tracks what specs are available on (usually remote)
binary caches.
@@ -206,7 +190,8 @@ def _associate_built_specs_with_mirror(self, cache_key, mirror_url):
tmpdir = tempfile.mkdtemp()
try:
db = BuildCacheDatabase(tmpdir)
db_root_dir = os.path.join(tmpdir, "db_root")
db = spack_db.Database(None, db_dir=db_root_dir, enable_transaction_locking=False)
try:
self._index_file_cache.init_entry(cache_key)
@@ -332,9 +317,9 @@ def update(self, with_cooldown=False):
from each configured mirror and stored locally (both in memory and
on disk under ``_index_cache_root``)."""
self._init_local_index_cache()
configured_mirror_urls = [
m.fetch_url for m in spack.mirror.MirrorCollection(binary=True).values()
]
mirrors = spack.mirror.MirrorCollection()
configured_mirror_urls = [m.fetch_url for m in mirrors.values()]
items_to_remove = []
spec_cache_clear_needed = False
spec_cache_regenerate_needed = not self._mirrors_for_spec
@@ -532,7 +517,9 @@ class NoOverwriteException(spack.error.SpackError):
"""Raised when a file would be overwritten"""
def __init__(self, file_path):
super().__init__(f"Refusing to overwrite the following file: {file_path}")
super(NoOverwriteException, self).__init__(
f"Refusing to overwrite the following file: {file_path}"
)
class NoGpgException(spack.error.SpackError):
@@ -541,7 +528,7 @@ class NoGpgException(spack.error.SpackError):
"""
def __init__(self, msg):
super().__init__(msg)
super(NoGpgException, self).__init__(msg)
class NoKeyException(spack.error.SpackError):
@@ -550,7 +537,7 @@ class NoKeyException(spack.error.SpackError):
"""
def __init__(self, msg):
super().__init__(msg)
super(NoKeyException, self).__init__(msg)
class PickKeyException(spack.error.SpackError):
@@ -561,7 +548,7 @@ class PickKeyException(spack.error.SpackError):
def __init__(self, keys):
err_msg = "Multiple keys available for signing\n%s\n" % keys
err_msg += "Use spack buildcache create -k <key hash> to pick a key."
super().__init__(err_msg)
super(PickKeyException, self).__init__(err_msg)
class NoVerifyException(spack.error.SpackError):
@@ -578,7 +565,7 @@ class NoChecksumException(spack.error.SpackError):
"""
def __init__(self, path, size, contents, algorithm, expected, computed):
super().__init__(
super(NoChecksumException, self).__init__(
f"{algorithm} checksum failed for {path}",
f"Expected {expected} but got {computed}. "
f"File size = {size} bytes. Contents = {contents!r}",
@@ -591,7 +578,7 @@ class NewLayoutException(spack.error.SpackError):
"""
def __init__(self, msg):
super().__init__(msg)
super(NewLayoutException, self).__init__(msg)
class UnsignedPackageException(spack.error.SpackError):
@@ -718,7 +705,7 @@ def get_buildfile_manifest(spec):
# look for them to decide if text file needs to be relocated or not
prefixes = [d.prefix for d in spec.traverse(root=True, deptype="all") if not d.external]
prefixes.append(spack.hooks.sbang.sbang_install_path())
prefixes.append(str(spack.store.STORE.layout.root))
prefixes.append(str(spack.store.layout.root))
# Create a giant regex that matches all prefixes
regex = utf8_paths_to_single_binary_regex(prefixes)
@@ -731,7 +718,7 @@ def get_buildfile_manifest(spec):
for rel_path in visitor.symlinks:
abs_path = os.path.join(root, rel_path)
link = os.readlink(abs_path)
if os.path.isabs(link) and link.startswith(spack.store.STORE.layout.root):
if os.path.isabs(link) and link.startswith(spack.store.layout.root):
data["link_to_relocate"].append(rel_path)
# Non-symlinks.
@@ -779,9 +766,9 @@ def get_buildinfo_dict(spec):
return {
"sbang_install_path": spack.hooks.sbang.sbang_install_path(),
"buildpath": spack.store.STORE.layout.root,
"buildpath": spack.store.layout.root,
"spackprefix": spack.paths.prefix,
"relative_prefix": os.path.relpath(spec.prefix, spack.store.STORE.layout.root),
"relative_prefix": os.path.relpath(spec.prefix, spack.store.layout.root),
"relocate_textfiles": manifest["text_to_relocate"],
"relocate_binaries": manifest["binary_to_relocate"],
"relocate_links": manifest["link_to_relocate"],
@@ -1074,10 +1061,13 @@ def generate_package_index(cache_prefix, concurrency=32):
tty.debug("Retrieving spec descriptor files from {0} to build index".format(cache_prefix))
tmpdir = tempfile.mkdtemp()
db = BuildCacheDatabase(tmpdir)
db.root = None
db_root_dir = db.database_directory
db_root_dir = os.path.join(tmpdir, "db_root")
db = spack_db.Database(
None,
db_dir=db_root_dir,
enable_transaction_locking=False,
record_fields=["spec", "ref_count", "in_buildcache"],
)
try:
_read_specs_and_push_index(file_list, read_fn, cache_prefix, db, db_root_dir, concurrency)
@@ -1208,17 +1198,9 @@ def tar_add_metadata(tar: tarfile.TarFile, path: str, data: dict):
tar.addfile(deterministic_tarinfo(tarinfo), io.BytesIO(bstring))
def deterministic_tarinfo_without_buildinfo(tarinfo: tarfile.TarInfo):
"""Skip buildinfo file when creating a tarball, and normalize other tarinfo fields."""
if tarinfo.name.endswith("/.spack/binary_distribution"):
return None
return deterministic_tarinfo(tarinfo)
def _do_create_tarball(tarfile_path: str, binaries_dir: str, pkg_dir: str, buildinfo: dict):
def _do_create_tarball(tarfile_path, binaries_dir, pkg_dir, buildinfo):
with gzip_compressed_tarfile(tarfile_path) as tar:
tar.add(name=binaries_dir, arcname=pkg_dir, filter=deterministic_tarinfo_without_buildinfo)
tar.add(name=binaries_dir, arcname=pkg_dir, filter=deterministic_tarinfo)
tar_add_metadata(tar, buildinfo_file_name(pkg_dir), buildinfo)
@@ -1226,6 +1208,9 @@ class PushOptions(NamedTuple):
#: Overwrite existing tarball/metadata files in buildcache
force: bool = False
#: Allow absolute paths to package prefixes when creating a tarball
allow_root: bool = False
#: Regenerated indices after pushing
regenerate_index: bool = False
@@ -1270,7 +1255,7 @@ def _build_tarball_in_stage_dir(spec: Spec, out_url: str, stage_dir: str, option
# without concretizing with the current spack packages
# and preferences
spec_file = spack.store.STORE.layout.spec_file_path(spec)
spec_file = spack.store.layout.spec_file_path(spec)
specfile_name = tarball_name(spec, ".spec.json")
specfile_path = os.path.realpath(os.path.join(cache_prefix, specfile_name))
signed_specfile_path = "{0}.sig".format(specfile_path)
@@ -1298,6 +1283,9 @@ def _build_tarball_in_stage_dir(spec: Spec, out_url: str, stage_dir: str, option
# create info for later relocation and create tar
buildinfo = get_buildinfo_dict(spec)
if not options.allow_root:
ensure_package_relocatable(buildinfo, binaries_dir)
_do_create_tarball(tarfile_path, binaries_dir, pkg_dir, buildinfo)
# get the sha256 checksum of the tarball
@@ -1319,7 +1307,7 @@ def _build_tarball_in_stage_dir(spec: Spec, out_url: str, stage_dir: str, option
# Add original install prefix relative to layout root to spec.json.
# This will be used to determine is the directory layout has changed.
buildinfo = {}
buildinfo["relative_prefix"] = os.path.relpath(spec.prefix, spack.store.STORE.layout.root)
buildinfo["relative_prefix"] = os.path.relpath(spec.prefix, spack.store.layout.root)
spec_dict["buildinfo"] = buildinfo
with open(specfile_path, "w") as outfile:
@@ -1377,7 +1365,7 @@ def specs_to_be_packaged(
packageable = lambda n: not n.external and n.installed
# Mass install check
with spack.store.STORE.db.read_transaction():
with spack.store.db.read_transaction():
return list(filter(packageable, nodes))
@@ -1479,9 +1467,8 @@ def download_tarball(spec, unsigned=False, mirrors_for_spec=None):
"signature_verified": "true-if-binary-pkg-was-already-verified"
}
"""
configured_mirrors = spack.mirror.MirrorCollection(binary=True).values()
if not configured_mirrors:
tty.die("Please add a spack mirror to allow download of pre-compiled packages.")
if not spack.mirror.MirrorCollection():
tty.die("Please add a spack mirror to allow " + "download of pre-compiled packages.")
tarball = tarball_path_name(spec, ".spack")
specfile_prefix = tarball_name(spec, ".spec")
@@ -1498,7 +1485,11 @@ def download_tarball(spec, unsigned=False, mirrors_for_spec=None):
# we need was in an un-indexed mirror. No need to check any
# mirror for the spec twice though.
try_first = [i["mirror_url"] for i in mirrors_for_spec] if mirrors_for_spec else []
try_next = [i.fetch_url for i in configured_mirrors if i.fetch_url not in try_first]
try_next = [
i.fetch_url
for i in spack.mirror.MirrorCollection().values()
if i.fetch_url not in try_first
]
for url in try_first + try_next:
mirrors_to_try.append(
@@ -1576,6 +1567,12 @@ def download_tarball(spec, unsigned=False, mirrors_for_spec=None):
return None
def ensure_package_relocatable(buildinfo, binaries_dir):
"""Check if package binaries are relocatable."""
binaries = [os.path.join(binaries_dir, f) for f in buildinfo["relocate_binaries"]]
relocate.ensure_binaries_are_relocatable(binaries)
def dedupe_hardlinks_if_necessary(root, buildinfo):
"""Updates a buildinfo dict for old archives that did
not dedupe hardlinks. De-duping hardlinks is necessary
@@ -1614,7 +1611,7 @@ def relocate_package(spec):
"""
workdir = str(spec.prefix)
buildinfo = read_buildinfo_file(workdir)
new_layout_root = str(spack.store.STORE.layout.root)
new_layout_root = str(spack.store.layout.root)
new_prefix = str(spec.prefix)
new_rel_prefix = str(os.path.relpath(new_prefix, new_layout_root))
new_spack_prefix = str(spack.paths.prefix)
@@ -1862,7 +1859,7 @@ def extract_tarball(spec, download_result, unsigned=False, force=False):
tarfile_path, size, contents, "sha256", expected, local_checksum
)
new_relative_prefix = str(os.path.relpath(spec.prefix, spack.store.STORE.layout.root))
new_relative_prefix = str(os.path.relpath(spec.prefix, spack.store.layout.root))
# if the original relative prefix is in the spec file use it
buildinfo = spec_dict.get("buildinfo", {})
old_relative_prefix = buildinfo.get("relative_prefix", new_relative_prefix)
@@ -1874,7 +1871,7 @@ def extract_tarball(spec, download_result, unsigned=False, force=False):
# The directory created is the base directory name of the old prefix.
# Moving the old prefix name to the new prefix location should preserve
# hard links and symbolic links.
extract_tmp = os.path.join(spack.store.STORE.layout.root, ".tmp")
extract_tmp = os.path.join(spack.store.layout.root, ".tmp")
mkdirp(extract_tmp)
extracted_dir = os.path.join(extract_tmp, old_relative_prefix.split(os.path.sep)[-1])
@@ -1901,9 +1898,7 @@ def extract_tarball(spec, download_result, unsigned=False, force=False):
raise e
else:
manifest_file = os.path.join(
spec.prefix,
spack.store.STORE.layout.metadata_dir,
spack.store.STORE.layout.manifest_file_name,
spec.prefix, spack.store.layout.metadata_dir, spack.store.layout.manifest_file_name
)
if not os.path.exists(manifest_file):
spec_id = spec.format("{name}/{hash:7}")
@@ -1962,7 +1957,7 @@ def install_root_node(spec, unsigned=False, force=False, sha256=None):
tty.msg('Installing "{0}" from a buildcache'.format(spec.format()))
extract_tarball(spec, download_result, unsigned, force)
spack.hooks.post_install(spec, False)
spack.store.STORE.db.add(spec, spack.store.STORE.layout)
spack.store.db.add(spec, spack.store.layout)
def install_single_spec(spec, unsigned=False, force=False):
@@ -1987,9 +1982,7 @@ def try_direct_fetch(spec, mirrors=None):
specfile_is_signed = False
found_specs = []
binary_mirrors = spack.mirror.MirrorCollection(mirrors=mirrors, binary=True).values()
for mirror in binary_mirrors:
for mirror in spack.mirror.MirrorCollection(mirrors=mirrors).values():
buildcache_fetch_url_json = url_util.join(
mirror.fetch_url, _build_cache_relative_path, specfile_name
)
@@ -2052,7 +2045,7 @@ def get_mirrors_for_spec(spec=None, mirrors_to_check=None, index_only=False):
if spec is None:
return []
if not spack.mirror.MirrorCollection(mirrors=mirrors_to_check, binary=True):
if not spack.mirror.MirrorCollection(mirrors=mirrors_to_check):
tty.debug("No Spack mirrors are currently configured")
return {}
@@ -2091,7 +2084,7 @@ def clear_spec_cache():
def get_keys(install=False, trust=False, force=False, mirrors=None):
"""Get pgp public keys available on mirror with suffix .pub"""
mirror_collection = mirrors or spack.mirror.MirrorCollection(binary=True)
mirror_collection = mirrors or spack.mirror.MirrorCollection()
if not mirror_collection:
tty.die("Please add a spack mirror to allow " + "download of build caches.")
@@ -2252,7 +2245,7 @@ def check_specs_against_mirrors(mirrors, specs, output_file=None):
"""
rebuilds = {}
for mirror in spack.mirror.MirrorCollection(mirrors, binary=True).values():
for mirror in spack.mirror.MirrorCollection(mirrors).values():
tty.debug("Checking for built specs at {0}".format(mirror.fetch_url))
rebuild_list = []
@@ -2296,7 +2289,7 @@ def _download_buildcache_entry(mirror_root, descriptions):
def download_buildcache_entry(file_descriptions, mirror_url=None):
if not mirror_url and not spack.mirror.MirrorCollection(binary=True):
if not mirror_url and not spack.mirror.MirrorCollection():
tty.die(
"Please provide or add a spack mirror to allow " + "download of buildcache entries."
)
@@ -2305,7 +2298,7 @@ def download_buildcache_entry(file_descriptions, mirror_url=None):
mirror_root = os.path.join(mirror_url, _build_cache_relative_path)
return _download_buildcache_entry(mirror_root, file_descriptions)
for mirror in spack.mirror.MirrorCollection(binary=True).values():
for mirror in spack.mirror.MirrorCollection().values():
mirror_root = os.path.join(mirror.fetch_url, _build_cache_relative_path)
if _download_buildcache_entry(mirror_root, file_descriptions):
@@ -2344,7 +2337,7 @@ def download_single_spec(concrete_spec, destination, mirror_url=None):
return download_buildcache_entry(files_to_fetch, mirror_url)
class BinaryCacheQuery:
class BinaryCacheQuery(object):
"""Callable object to query if a spec is in a binary cache"""
def __init__(self, all_architectures):

View File

@@ -4,7 +4,7 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
"""Function and classes needed to bootstrap Spack itself."""
from .config import ensure_bootstrap_configuration, is_bootstrapping, store_path
from .config import ensure_bootstrap_configuration, is_bootstrapping
from .core import all_core_root_specs, ensure_core_dependencies, ensure_patchelf_in_path_or_raise
from .environment import BootstrapEnvironment, ensure_environment_dependencies
from .status import status_message
@@ -18,5 +18,4 @@
"ensure_environment_dependencies",
"BootstrapEnvironment",
"status_message",
"store_path",
]

View File

@@ -50,7 +50,7 @@ def _try_import_from_store(
# We have to run as part of this python interpreter
query_spec += " ^" + spec_for_current_python()
installed_specs = spack.store.STORE.db.query(query_spec, installed=True)
installed_specs = spack.store.db.query(query_spec, installed=True)
for candidate_spec in installed_specs:
pkg = candidate_spec["python"].package
@@ -183,7 +183,7 @@ def _executables_in_store(
executables_str = ", ".join(executables)
msg = "[BOOTSTRAP EXECUTABLES {0}] Try installed specs with query '{1}'"
tty.debug(msg.format(executables_str, query_spec))
installed_specs = spack.store.STORE.db.query(query_spec, installed=True)
installed_specs = spack.store.db.query(query_spec, installed=True)
if installed_specs:
for concrete_spec in installed_specs:
bin_dir = concrete_spec.prefix.bin

View File

@@ -150,19 +150,18 @@ def _add_compilers_if_missing() -> None:
@contextlib.contextmanager
def _ensure_bootstrap_configuration() -> Generator:
spack.store.ensure_singleton_created()
bootstrap_store_path = store_path()
user_configuration = _read_and_sanitize_configuration()
with spack.environment.no_active_environment():
with spack.platforms.prevent_cray_detection(), spack.platforms.use_platform(
spack.platforms.real_host()
), spack.repo.use_repositories(spack.paths.packages_path):
), spack.repo.use_repositories(spack.paths.packages_path), spack.store.use_store(
bootstrap_store_path
):
# Default configuration scopes excluding command line
# and builtin but accounting for platform specific scopes
config_scopes = _bootstrap_config_scopes()
with spack.config.use_configuration(*config_scopes), spack.store.use_store(
bootstrap_store_path, extra_data={"padded_length": 0}
):
with spack.config.use_configuration(*config_scopes):
# We may need to compile code from sources, so ensure we
# have compilers for the current platform
_add_compilers_if_missing()

View File

@@ -148,7 +148,7 @@ class MakeExecutable(Executable):
def __init__(self, name, jobs, **kwargs):
supports_jobserver = kwargs.pop("supports_jobserver", True)
super().__init__(name, **kwargs)
super(MakeExecutable, self).__init__(name, **kwargs)
self.supports_jobserver = supports_jobserver
self.jobs = jobs
@@ -175,7 +175,7 @@ def __call__(self, *args, **kwargs):
if jobs_env_jobs is not None:
kwargs["extra_env"] = {jobs_env: str(jobs_env_jobs)}
return super().__call__(*args, **kwargs)
return super(MakeExecutable, self).__call__(*args, **kwargs)
def _on_cray():
@@ -1332,7 +1332,7 @@ class ChildError(InstallError):
build_errors = [("spack.util.executable", "ProcessError")]
def __init__(self, msg, module, classname, traceback_string, log_name, log_type, context):
super().__init__(msg)
super(ChildError, self).__init__(msg)
self.module = module
self.name = classname
self.traceback = traceback_string

View File

@@ -39,7 +39,7 @@ def check_paths(path_list, filetype, predicate):
check_paths(pkg.sanity_check_is_file, "file", os.path.isfile)
check_paths(pkg.sanity_check_is_dir, "directory", os.path.isdir)
ignore_file = llnl.util.lang.match_predicate(spack.store.STORE.layout.hidden_file_regexes)
ignore_file = llnl.util.lang.match_predicate(spack.store.layout.hidden_file_regexes)
if all(map(ignore_file, os.listdir(pkg.prefix))):
msg = "Install failed for {0}. Nothing was installed!"
raise spack.installer.InstallError(msg.format(pkg.name))

View File

@@ -289,7 +289,6 @@ def std_initconfig_entries(self):
"# CMake executable path: {0}".format(self.pkg.spec["cmake"].command.path),
"#------------------{0}\n".format("-" * 60),
cmake_cache_path("CMAKE_PREFIX_PATH", cmake_prefix_path),
self.define_cmake_cache_from_variant("CMAKE_BUILD_TYPE", "build_type"),
]
def initconfig_package_entries(self):
@@ -312,7 +311,7 @@ def initconfig(self, pkg, spec, prefix):
@property
def std_cmake_args(self):
args = super().std_cmake_args
args = super(CachedCMakeBuilder, self).std_cmake_args
args.extend(["-C", self.cache_path])
return args

View File

@@ -296,46 +296,8 @@ def std_args(pkg, generator=None):
define("CMAKE_PREFIX_PATH", spack.build_environment.get_cmake_prefix_path(pkg)),
]
)
return args
@staticmethod
def define_cuda_architectures(pkg):
"""Returns the str ``-DCMAKE_CUDA_ARCHITECTURES:STRING=(expanded cuda_arch)``.
``cuda_arch`` is variant composed of a list of target CUDA architectures and
it is declared in the cuda package.
This method is no-op for cmake<3.18 and when ``cuda_arch`` variant is not set.
"""
cmake_flag = str()
if "cuda_arch" in pkg.spec.variants and pkg.spec.satisfies("^cmake@3.18:"):
cmake_flag = CMakeBuilder.define(
"CMAKE_CUDA_ARCHITECTURES", pkg.spec.variants["cuda_arch"].value
)
return cmake_flag
@staticmethod
def define_hip_architectures(pkg):
"""Returns the str ``-DCMAKE_HIP_ARCHITECTURES:STRING=(expanded amdgpu_target)``.
``amdgpu_target`` is variant composed of a list of the target HIP
architectures and it is declared in the rocm package.
This method is no-op for cmake<3.18 and when ``amdgpu_target`` variant is
not set.
"""
cmake_flag = str()
if "amdgpu_target" in pkg.spec.variants and pkg.spec.satisfies("^cmake@3.21:"):
cmake_flag = CMakeBuilder.define(
"CMAKE_HIP_ARCHITECTURES", pkg.spec.variants["amdgpu_target"].value
)
return cmake_flag
@staticmethod
def define(cmake_var, value):
"""Return a CMake command line argument that defines a variable.

View File

@@ -102,10 +102,11 @@ def cuda_flags(arch_list):
depends_on("cuda@11.0:", when="cuda_arch=80")
depends_on("cuda@11.1:", when="cuda_arch=86")
depends_on("cuda@11.4:", when="cuda_arch=87")
depends_on("cuda@11.8:", when="cuda_arch=89")
depends_on("cuda@12.0:", when="cuda_arch=90")
depends_on("cuda@11.4:", when="cuda_arch=87")
depends_on("cuda@11.8:", when="cuda_arch=89")
depends_on("cuda@11.8:", when="cuda_arch=90")
# From the NVIDIA install guide we know of conflicts for particular
# platforms (linux, darwin), architectures (x86, powerpc) and compilers

View File

@@ -121,7 +121,7 @@ def setup_run_environment(self, env):
$ source {prefix}/{component}/{version}/env/vars.sh
"""
# Only if environment modifications are desired (default is +envmods)
if "~envmods" not in self.spec:
if "+envmods" in self.spec:
env.extend(
EnvironmentModifications.from_sourcing_file(
join_path(self.component_prefix, "env", "vars.sh")
@@ -175,7 +175,7 @@ def libs(self):
return find_libraries("*", root=lib_path, shared=True, recursive=True)
class IntelOneApiStaticLibraryList:
class IntelOneApiStaticLibraryList(object):
"""Provides ld_flags when static linking is needed
Oneapi puts static and dynamic libraries in the same directory, so

View File

@@ -23,7 +23,6 @@
import spack.store
from spack.directives import build_system, depends_on, extends, maintainers
from spack.error import NoHeadersError, NoLibrariesError, SpecError
from spack.install_test import test_part
from spack.version import Version
from ._checks import BaseBuilder, execute_install_time_tests
@@ -168,20 +167,18 @@ def remove_files_from_view(self, view, merge_map):
view.remove_files(to_remove)
def test_imports(self):
def test(self):
"""Attempts to import modules of the installed package."""
# Make sure we are importing the installed modules,
# not the ones in the source directory
python = inspect.getmodule(self).python
for module in self.import_modules:
with test_part(
self,
f"test_imports_{module}",
purpose=f"checking import of {module}",
self.run_test(
inspect.getmodule(self).python.path,
["-c", "import {0}".format(module)],
purpose="checking import of {0}".format(module),
work_dir="spack-test",
):
python("-c", f"import {module}")
)
def update_external_dependencies(self, extendee_spec=None):
"""
@@ -286,7 +283,7 @@ def get_external_python_for_prefix(self):
spack.spec.Spec: The external Spec for python most likely to be compatible with self.spec
"""
python_externals_installed = [
s for s in spack.store.STORE.db.query("python") if s.prefix == self.spec.external_path
s for s in spack.store.db.query("python") if s.prefix == self.spec.external_path
]
if python_externals_installed:
return python_externals_installed[0]
@@ -401,8 +398,7 @@ def build_directory(self):
def config_settings(self, spec, prefix):
"""Configuration settings to be passed to the PEP 517 build backend.
Requires pip 22.1 or newer.
Requires pip 22.1+, which requires Python 3.7+.
Args:
spec (spack.spec.Spec): build spec
@@ -416,8 +412,6 @@ def config_settings(self, spec, prefix):
def install_options(self, spec, prefix):
"""Extra arguments to be supplied to the setup.py install command.
Requires pip 23.0 or older.
Args:
spec (spack.spec.Spec): build spec
prefix (spack.util.prefix.Prefix): installation prefix
@@ -431,8 +425,6 @@ def global_options(self, spec, prefix):
"""Extra global options to be supplied to the setup.py call before the install
or bdist_wheel command.
Deprecated in pip 23.1.
Args:
spec (spack.spec.Spec): build spec
prefix (spack.util.prefix.Prefix): installation prefix

View File

@@ -63,7 +63,7 @@ def create(pkg):
return _BUILDERS[id(pkg)]
class _PhaseAdapter:
class _PhaseAdapter(object):
def __init__(self, builder, phase_fn):
self.builder = builder
self.phase_fn = phase_fn
@@ -115,7 +115,7 @@ class hierarchy (look at AspellDictPackage for an example of that)
# package. The semantic should be the same as the method in the base builder were still
# present in the base class of the package.
class _ForwardToBaseBuilder:
class _ForwardToBaseBuilder(object):
def __init__(self, wrapped_pkg_object, root_builder):
self.wrapped_package_object = wrapped_pkg_object
self.root_builder = root_builder
@@ -188,7 +188,7 @@ def __init__(self, pkg):
# Attribute containing the package wrapped in dispatcher with a `__getattr__`
# method that will forward certain calls to the default builder.
self.pkg_with_dispatcher = _ForwardToBaseBuilder(pkg, root_builder=self)
super().__init__(pkg)
super(Adapter, self).__init__(pkg)
# These two methods don't follow the (self, spec, prefix) signature of phases nor
# the (self) signature of methods, so they are added explicitly to avoid using a
@@ -388,7 +388,7 @@ def __new__(mcs, name, bases, attr_dict):
return super(_PackageAdapterMeta, mcs).__new__(mcs, name, bases, attr_dict)
class InstallationPhase:
class InstallationPhase(object):
"""Manages a single phase of the installation.
This descriptor stores at creation time the name of the method it should
@@ -530,9 +530,9 @@ def setup_build_environment(self, env):
modifications to be applied when the package is built. Package authors
can call methods on it to alter the build environment.
"""
if not hasattr(super(), "setup_build_environment"):
if not hasattr(super(Builder, self), "setup_build_environment"):
return
super().setup_build_environment(env)
super(Builder, self).setup_build_environment(env)
def setup_dependent_build_environment(self, env, dependent_spec):
"""Sets up the build environment of packages that depend on this one.
@@ -563,9 +563,9 @@ def setup_dependent_build_environment(self, env, dependent_spec):
the dependent's state. Note that *this* package's spec is
available as ``self.spec``
"""
if not hasattr(super(), "setup_dependent_build_environment"):
if not hasattr(super(Builder, self), "setup_dependent_build_environment"):
return
super().setup_dependent_build_environment(env, dependent_spec)
super(Builder, self).setup_dependent_build_environment(env, dependent_spec)
def __getitem__(self, idx):
key = self.phases[idx]

View File

@@ -58,7 +58,7 @@ def _fetch_cache():
return spack.fetch_strategy.FsCache(path)
class MirrorCache:
class MirrorCache(object):
def __init__(self, root, skip_unstable_versions):
self.root = os.path.abspath(root)
self.skip_unstable_versions = skip_unstable_versions

View File

@@ -28,6 +28,7 @@
import spack
import spack.binary_distribution as bindist
import spack.compilers as compilers
import spack.config as cfg
import spack.environment as ev
import spack.main
@@ -57,7 +58,7 @@
PushResult = namedtuple("PushResult", "success url")
class TemporaryDirectory:
class TemporaryDirectory(object):
def __init__(self):
self.temporary_directory = tempfile.mkdtemp()
@@ -69,10 +70,17 @@ def __exit__(self, exc_type, exc_value, exc_traceback):
return False
def get_job_name(spec, osarch, build_group):
def _is_main_phase(phase_name):
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
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
osarch: Architecture TODO: (this is a spack.spec.ArchSpec,
but sphinx doesn't recognize the type and fails).
@@ -85,7 +93,12 @@ def get_job_name(spec, osarch, build_group):
format_str = ""
format_args = []
format_str += "{{{0}}}".format(item_idx)
if phase:
format_str += "({{{0}}})".format(item_idx)
format_args.append(phase)
item_idx += 1
format_str += " {{{0}}}".format(item_idx)
format_args.append(spec.name)
item_idx += 1
@@ -97,9 +110,10 @@ def get_job_name(spec, osarch, build_group):
format_args.append(spec.version)
item_idx += 1
format_str += " {{{0}}}".format(item_idx)
format_args.append(spec.compiler)
item_idx += 1
if _is_main_phase(phase) is True or strip_compiler is False:
format_str += " {{{0}}}".format(item_idx)
format_args.append(spec.compiler)
item_idx += 1
format_str += " {{{0}}}".format(item_idx)
format_args.append(osarch)
@@ -139,33 +153,49 @@ def _add_dependency(spec_label, dep_label, deps):
deps[spec_label].add(dep_label)
def _get_spec_dependencies(specs, deps, spec_labels):
spec_deps_obj = _compute_spec_deps(specs)
def _get_spec_dependencies(
specs, deps, spec_labels, check_index_only=False, mirrors_to_check=None
):
spec_deps_obj = _compute_spec_deps(
specs, check_index_only=check_index_only, mirrors_to_check=mirrors_to_check
)
if spec_deps_obj:
dependencies = spec_deps_obj["dependencies"]
specs = spec_deps_obj["specs"]
for entry in specs:
spec_labels[entry["label"]] = entry["spec"]
spec_labels[entry["label"]] = {
"spec": entry["spec"],
"needs_rebuild": entry["needs_rebuild"],
}
for entry in dependencies:
_add_dependency(entry["spec"], entry["depends"], deps)
def stage_spec_jobs(specs):
def stage_spec_jobs(specs, check_index_only=False, mirrors_to_check=None):
"""Take a set of release specs and generate a list of "stages", where the
jobs in any stage are dependent only on jobs in previous stages. This
allows us to maximize build parallelism within the gitlab-ci framework.
Arguments:
specs (Iterable): Specs to build
check_index_only (bool): Regardless of whether DAG pruning is enabled,
all configured mirrors are searched to see if binaries for specs
are up to date on those mirrors. This flag limits that search to
the binary cache indices on those mirrors to speed the process up,
even though there is no garantee the index is up to date.
mirrors_to_checK: Optional mapping giving mirrors to check instead of
any configured mirrors.
Returns: A tuple of information objects describing the specs, dependencies
and stages:
spec_labels: A dictionary mapping the spec labels (which are formatted
as pkg-name/hash-prefix) to concrete specs.
spec_labels: A dictionary mapping the spec labels which are made of
(pkg-name/hash-prefix), to objects containing "spec" and "needs_rebuild"
keys. The root spec is the spec of which this spec is a dependency
and the spec is the formatted spec string for this spec.
deps: A dictionary where the keys should also have appeared as keys in
the spec_labels dictionary, and the values are the set of
@@ -194,7 +224,13 @@ def _remove_satisfied_deps(deps, satisfied_list):
deps = {}
spec_labels = {}
_get_spec_dependencies(specs, deps, spec_labels)
_get_spec_dependencies(
specs,
deps,
spec_labels,
check_index_only=check_index_only,
mirrors_to_check=mirrors_to_check,
)
# Save the original deps, as we need to return them at the end of the
# function. In the while loop below, the "dependencies" variable is
@@ -220,36 +256,24 @@ def _remove_satisfied_deps(deps, satisfied_list):
return spec_labels, deps, stages
def _print_staging_summary(spec_labels, stages, mirrors_to_check, rebuild_decisions):
def _print_staging_summary(spec_labels, dependencies, stages):
if not stages:
return
mirrors = spack.mirror.MirrorCollection(mirrors=mirrors_to_check, binary=True)
tty.msg("Checked the following mirrors for binaries:")
for m in mirrors.values():
tty.msg(" {0}".format(m.fetch_url))
tty.msg("Staging summary ([x] means a job needs rebuilding):")
tty.msg(" Staging summary ([x] means a job needs rebuilding):")
for stage_index, stage in enumerate(stages):
tty.msg(" stage {0} ({1} jobs):".format(stage_index, len(stage)))
tty.msg(" stage {0} ({1} jobs):".format(stage_index, len(stage)))
for job in sorted(stage):
s = spec_labels[job]
rebuild = rebuild_decisions[job].rebuild
reason = rebuild_decisions[job].reason
reason_msg = " ({0})".format(reason) if reason else ""
s = spec_labels[job]["spec"]
tty.msg(
" [{1}] {0} -> {2}{3}".format(
job, "x" if rebuild else " ", _get_spec_string(s), reason_msg
" [{1}] {0} -> {2}".format(
job, "x" if spec_labels[job]["needs_rebuild"] else " ", _get_spec_string(s)
)
)
if rebuild_decisions[job].mirrors:
tty.msg(" found on the following mirrors:")
for murl in rebuild_decisions[job].mirrors:
tty.msg(" {0}".format(murl))
def _compute_spec_deps(spec_list):
def _compute_spec_deps(spec_list, check_index_only=False, mirrors_to_check=None):
"""
Computes all the dependencies for the spec(s) and generates a JSON
object which provides both a list of unique spec names as well as a
@@ -313,8 +337,12 @@ def append_dep(s, d):
tty.msg("Will not stage external pkg: {0}".format(s))
continue
up_to_date_mirrors = bindist.get_mirrors_for_spec(
spec=s, mirrors_to_check=mirrors_to_check, index_only=check_index_only
)
skey = _spec_deps_key(s)
spec_labels[skey] = s
spec_labels[skey] = {"spec": s, "needs_rebuild": not up_to_date_mirrors}
for d in s.dependencies(deptype=all):
dkey = _spec_deps_key(d)
@@ -324,8 +352,14 @@ def append_dep(s, d):
append_dep(skey, dkey)
for spec_label, concrete_spec in spec_labels.items():
specs.append({"label": spec_label, "spec": concrete_spec})
for spec_label, spec_holder in spec_labels.items():
specs.append(
{
"label": spec_label,
"spec": spec_holder["spec"],
"needs_rebuild": spec_holder["needs_rebuild"],
}
)
deps_json_obj = {"specs": specs, "dependencies": dependencies}
@@ -337,17 +371,26 @@ def _spec_matches(spec, match_string):
def _format_job_needs(
dep_jobs, osname, build_group, prune_dag, rebuild_decisions, enable_artifacts_buildcache
phase_name,
strip_compilers,
dep_jobs,
osname,
build_group,
prune_dag,
stage_spec_dict,
enable_artifacts_buildcache,
):
needs_list = []
for dep_job in dep_jobs:
dep_spec_key = _spec_deps_key(dep_job)
rebuild = rebuild_decisions[dep_spec_key].rebuild
dep_spec_info = stage_spec_dict[dep_spec_key]
if not prune_dag or rebuild:
if not prune_dag or dep_spec_info["needs_rebuild"]:
needs_list.append(
{
"job": get_job_name(dep_job, dep_job.architecture, build_group),
"job": get_job_name(
phase_name, strip_compilers, dep_job, dep_job.architecture, build_group
),
"artifacts": enable_artifacts_buildcache,
}
)
@@ -447,12 +490,17 @@ def get_spec_filter_list(env, affected_pkgs, dependent_traverse_depth=None):
return affected_specs
def _build_jobs(spec_labels, stages):
for stage_jobs in stages:
for spec_label in stage_jobs:
release_spec = spec_labels[spec_label]
release_spec_dag_hash = release_spec.dag_hash()
yield release_spec, release_spec_dag_hash
def _build_jobs(phases, staged_phases):
for phase in phases:
phase_name = phase["name"]
spec_labels, dependencies, stages = staged_phases[phase_name]
for stage_jobs in stages:
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()
yield release_spec, release_spec_dag_hash
def _noop(x):
@@ -471,21 +519,14 @@ def _unpack_script(script_section, op=_noop):
return script
class RebuildDecision:
def __init__(self):
self.rebuild = True
self.mirrors = []
self.reason = ""
class SpackCI:
"""Spack CI object used to generate intermediate representation
used by the CI generator(s).
"""
def __init__(self, ci_config, spec_labels, stages):
def __init__(self, ci_config, phases, staged_phases):
"""Given the information from the ci section of the config
and the staged jobs, set up meta data needed for generating Spack
and the job phases setup meta data needed for generating Spack
CI IR.
"""
@@ -500,6 +541,9 @@ def __init__(self, ci_config, spec_labels, stages):
"enable-artifacts-buildcache": self.ci_config.get(
"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),
"broken-specs-url": self.ci_config.get("broken-specs-url", None),
"broken-tests-packages": self.ci_config.get("broken-tests-packages", []),
@@ -507,7 +551,7 @@ def __init__(self, ci_config, spec_labels, stages):
}
jobs = self.ir["jobs"]
for spec, dag_hash in _build_jobs(spec_labels, stages):
for spec, dag_hash in _build_jobs(phases, staged_phases):
jobs[dag_hash] = self.__init_job(spec)
for name in self.named_jobs:
@@ -829,6 +873,25 @@ def generate_gitlab_ci_yaml(
if "temporary-storage-url-prefix" in ci_config:
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
# specified, add it here in case it has already built hashes we might
# generate.
@@ -930,13 +993,39 @@ def generate_gitlab_ci_yaml(
except bindist.FetchCacheError as e:
tty.warn(e)
spec_labels, dependencies, stages = stage_spec_jobs(
[
concrete
for abstract, concrete in env.concretized_specs()
if abstract in env.spec_lists["specs"]
]
)
staged_phases = {}
try:
for phase in phases:
phase_name = phase["name"]
if phase_name == "specs":
# Anything in the "specs" of the environment are already
# concretized by the block at the top of this method, so we
# only need to find the concrete versions, and then avoid
# re-concretizing them needlessly later on.
concrete_phase_specs = [
concrete
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:
# Clean up remote mirror override if enabled
if remote_mirror_override:
spack.mirror.remove("ci_pr_mirror", cfg.default_modify_scope())
if spack_pipeline_type == "spack_pull_request":
spack.mirror.remove("ci_shared_pr_mirror", cfg.default_modify_scope())
all_job_names = []
output_object = {}
@@ -959,212 +1048,276 @@ def generate_gitlab_ci_yaml(
else:
broken_spec_urls = web_util.list_url(broken_specs_url)
spack_ci = SpackCI(ci_config, spec_labels, stages)
spack_ci = SpackCI(ci_config, phases, staged_phases)
spack_ci_ir = spack_ci.generate_ir()
rebuild_decisions = {}
for phase in phases:
phase_name = phase["name"]
strip_compilers = phase["strip-compilers"]
for stage_jobs in stages:
stage_name = "stage-{0}".format(stage_id)
stage_names.append(stage_name)
stage_id += 1
spec_labels, dependencies, stages = staged_phases[phase_name]
for spec_label in stage_jobs:
release_spec = spec_labels[spec_label]
release_spec_dag_hash = release_spec.dag_hash()
for stage_jobs in stages:
stage_name = "stage-{0}".format(stage_id)
stage_names.append(stage_name)
stage_id += 1
spec_record = RebuildDecision()
rebuild_decisions[spec_label] = spec_record
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:
spec_record.rebuild = False
spec_record.reason = "Pruned, untouched by change."
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
up_to_date_mirrors = bindist.get_mirrors_for_spec(
spec=release_spec, mirrors_to_check=mirrors_to_check, index_only=check_index_only
)
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"])
spec_record.rebuild = not up_to_date_mirrors
if up_to_date_mirrors:
spec_record.reason = "Pruned, found in mirrors"
spec_record.mirrors = [m["mirror_url"] for m in up_to_date_mirrors]
else:
spec_record.reason = "Scheduled, not found anywhere"
if "script" not in job_object:
raise AttributeError
job_object = spack_ci_ir["jobs"][release_spec_dag_hash]["attributes"]
def main_script_replacements(cmd):
return cmd.replace("{env_dir}", rel_concrete_env_dir)
if not job_object:
tty.warn("No match found for {0}, skipping it".format(release_spec))
continue
job_object["script"] = _unpack_script(
job_object["script"], op=main_script_replacements
)
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 "before_script" in job_object:
job_object["before_script"] = _unpack_script(job_object["before_script"])
if "script" not in job_object:
raise AttributeError
if "after_script" in job_object:
job_object["after_script"] = _unpack_script(job_object["after_script"])
def main_script_replacements(cmd):
return cmd.replace("{env_dir}", rel_concrete_env_dir)
osname = str(release_spec.architecture)
job_name = get_job_name(
phase_name, strip_compilers, release_spec, osname, build_group
)
job_object["script"] = _unpack_script(
job_object["script"], op=main_script_replacements
)
compiler_action = "NONE"
if len(phases) > 1:
compiler_action = "FIND_ANY"
if _is_main_phase(phase_name):
compiler_action = "INSTALL_MISSING"
if "before_script" in job_object:
job_object["before_script"] = _unpack_script(job_object["before_script"])
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
if "after_script" in job_object:
job_object["after_script"] = _unpack_script(job_object["after_script"])
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"])
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])
job_object["needs"].extend(
_format_job_needs(
dep_jobs,
osname,
build_group,
prune_dag,
rebuild_decisions,
enable_artifacts_buildcache,
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.rebuild
rebuild_spec = spec_record["needs_rebuild"]
if not rebuild_spec and not copy_only_pipeline:
if prune_dag:
spec_record.reason = "Pruned, up-to-date"
# 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(
"Pruning {0}/{1}, does not need rebuild.".format(
release_spec.name, release_spec.dag_hash()
)
)
continue
else:
# DAG pruning is disabled, force the spec to rebuild. The
# record still contains any mirrors on which the spec
# may have been found, so we can print them in the staging
# summary.
spec_record.rebuild = True
spec_record.reason = "Scheduled, DAG pruning disabled"
if broken_spec_urls is not None and release_spec_dag_hash in broken_spec_urls:
known_broken_specs_encountered.append(release_spec_dag_hash)
if broken_spec_urls is not None and release_spec_dag_hash in broken_spec_urls:
known_broken_specs_encountered.append(release_spec_dag_hash)
# Only keep track of these if we are copying rebuilt cache entries
if spack_buildcache_copy:
# TODO: This assumes signed version of the spec
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:
job_object["needs"].append(
{"job": generate_job_name, "pipeline": "{0}".format(parent_pipeline_id)}
)
# Let downstream jobs know whether the spec needed rebuilding, regardless
# whether DAG pruning was enabled or not.
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),
]
# Only keep track of these if we are copying rebuilt cache entries
if spack_buildcache_copy:
# TODO: This assumes signed version of the spec
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:
job_object["needs"].append(
{"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,
],
},
)
job_object["stage"] = stage_name
job_object["retry"] = {"max": 2, "when": JOB_RETRY_CONDITIONS}
job_object["interruptible"] = True
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),
]
]
)
length_needs = len(job_object["needs"])
if length_needs > max_length_needs:
max_length_needs = length_needs
max_needs_job = job_name
job_object["stage"] = stage_name
job_object["retry"] = {"max": 2, "when": JOB_RETRY_CONDITIONS}
job_object["interruptible"] = True
if not copy_only_pipeline:
output_object[job_name] = job_object
job_id += 1
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:
_print_staging_summary(spec_labels, stages, mirrors_to_check, rebuild_decisions)
# Clean up remote mirror override if enabled
if remote_mirror_override:
spack.mirror.remove("ci_pr_mirror", cfg.default_modify_scope())
if spack_pipeline_type == "spack_pull_request":
spack.mirror.remove("ci_shared_pr_mirror", cfg.default_modify_scope())
for phase in phases:
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))
@@ -1257,11 +1410,20 @@ def main_script_replacements(cmd):
output_object["stages"] = stage_names
# Capture the version of Spack used to generate the pipeline, that can be
# passed to `git checkout` for version consistency. If we aren't in a Git
# repository, presume we are a Spack release and use the Git tag instead.
# Capture the version of spack used to generate the pipeline, transform it
# into a value that can be passed to "git checkout", and save it in a
# global yaml variable
spack_version = spack.main.get_version()
version_to_clone = spack.main.get_spack_commit() or f"v{spack.spack_version}"
version_to_clone = None
v_match = re.match(r"^\d+\.\d+\.\d+$", spack_version)
if v_match:
version_to_clone = "v{0}".format(v_match.group(0))
else:
v_match = re.match(r"^[^-]+-[^-]+-([a-f\d]+)$", spack_version)
if v_match:
version_to_clone = v_match.group(1)
else:
version_to_clone = spack_version
output_object["variables"] = {
"SPACK_ARTIFACTS_ROOT": rel_artifacts_root,
@@ -1278,7 +1440,6 @@ def main_script_replacements(cmd):
"SPACK_CI_SHARED_PR_MIRROR_URL": shared_pr_mirror or "None",
"SPACK_REBUILD_CHECK_UP_TO_DATE": str(prune_dag),
"SPACK_REBUILD_EVERYTHING": str(rebuild_everything),
"SPACK_REQUIRE_SIGNING": os.environ.get("SPACK_REQUIRE_SIGNING", "False"),
}
if remote_mirror_override:
@@ -1288,6 +1449,9 @@ def main_script_replacements(cmd):
if spack_stack_name:
output_object["variables"]["SPACK_CI_STACK_NAME"] = spack_stack_name
# Ensure the child pipeline always runs
output_object["workflow"] = {"rules": [{"when": "always"}]}
if spack_buildcache_copy:
# Write out the file describing specs that should be copied
copy_specs_dir = os.path.join(pipeline_artifacts_dir, "specs_to_copy")
@@ -1303,17 +1467,21 @@ def main_script_replacements(cmd):
with open(copy_specs_file, "w") as fd:
fd.write(json.dumps(buildcache_copies))
sorted_output = {}
for output_key, output_value in sorted(output_object.items()):
sorted_output[output_key] = output_value
# TODO(opadron): remove this or refactor
if run_optimizer:
import spack.ci_optimization as ci_opt
output_object = ci_opt.optimizer(output_object)
sorted_output = ci_opt.optimizer(sorted_output)
# TODO(opadron): remove this or refactor
if use_dependencies:
import spack.ci_needs_workaround as cinw
output_object = cinw.needs_to_dependencies(output_object)
sorted_output = cinw.needs_to_dependencies(sorted_output)
else:
# No jobs were generated
noop_job = spack_ci_ir["jobs"]["noop"]["attributes"]
@@ -1324,17 +1492,10 @@ def main_script_replacements(cmd):
noop_job["script"] = [
'echo "copy-only pipelines are not supported with deprecated ci configs"'
]
output_object = {"unsupported-copy": noop_job}
sorted_output = {"unsupported-copy": noop_job}
else:
tty.debug("No specs to rebuild, generating no-op job")
output_object = {"no-specs-to-rebuild": noop_job}
# Ensure the child pipeline always runs
output_object["workflow"] = {"rules": [{"when": "always"}]}
sorted_output = {}
for output_key, output_value in sorted(output_object.items()):
sorted_output[output_key] = output_value
sorted_output = {"no-specs-to-rebuild": noop_job}
if known_broken_specs_encountered:
tty.error("This pipeline generated hashes known to be broken on develop:")
@@ -1415,12 +1576,52 @@ def can_verify_binaries():
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):
"""Unchecked version of the public API, for easier mocking"""
unsigned = not sign_binaries
tty.debug("Creating buildcache ({0})".format("unsigned" if unsigned else "signed"))
push_url = spack.mirror.Mirror.from_url(mirror_url).push_url
return bindist.push(input_spec, push_url, bindist.PushOptions(force=True, unsigned=unsigned))
return bindist.push(
input_spec, push_url, bindist.PushOptions(force=True, allow_root=True, unsigned=unsigned)
)
def push_mirror_contents(input_spec: spack.spec.Spec, mirror_url, sign_binaries):
@@ -1958,9 +2159,9 @@ def process_command(name, commands, repro_dir):
def create_buildcache(
input_spec: spack.spec.Spec,
*,
pr_pipeline: bool,
pipeline_mirror_url: Optional[str] = None,
buildcache_mirror_url: Optional[str] = None,
sign_binaries: bool = False,
) -> List[PushResult]:
"""Create the buildcache at the provided mirror(s).
@@ -1968,10 +2169,12 @@ def create_buildcache(
input_spec: Installed spec to package and push
buildcache_mirror_url: URL for the buildcache mirror
pipeline_mirror_url: URL for the pipeline mirror
sign_binaries: Whether or not to sign buildcache entry
pr_pipeline: True if the CI job is for a PR
Returns: A list of PushResults, indicating success or failure.
"""
sign_binaries = pr_pipeline is False and can_sign_binaries()
results = []
# Create buildcache in either the main remote mirror, or in the
@@ -2116,7 +2319,7 @@ def run_standalone_tests(**kwargs):
tty.debug("spack test exited {0}".format(exit_code))
class CDashHandler:
class CDashHandler(object):
"""
Class for managing CDash data and processing.
"""

View File

@@ -3,6 +3,8 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import print_function
import argparse
import os
import re
@@ -147,7 +149,7 @@ def get_command(cmd_name):
return getattr(get_module(cmd_name), pname)
class _UnquotedFlags:
class _UnquotedFlags(object):
"""Use a heuristic in `.extract()` to detect whether the user is trying to set
multiple flags like the docker ENV attribute allows (e.g. 'cflags=-Os -pipe').
@@ -273,9 +275,9 @@ def disambiguate_spec_from_hashes(spec, hashes, local=False, installed=True, fir
See ``spack.database.Database._query`` for details.
"""
if local:
matching_specs = spack.store.STORE.db.query_local(spec, hashes=hashes, installed=installed)
matching_specs = spack.store.db.query_local(spec, hashes=hashes, installed=installed)
else:
matching_specs = spack.store.STORE.db.query(spec, hashes=hashes, installed=installed)
matching_specs = spack.store.db.query(spec, hashes=hashes, installed=installed)
if not matching_specs:
tty.die("Spec '%s' matches no installed packages." % spec)
@@ -473,7 +475,7 @@ def format_list(specs):
out = ""
# getting lots of prefixes requires DB lookups. Ensure
# all spec.prefix calls are in one transaction.
with spack.store.STORE.db.read_transaction():
with spack.store.db.read_transaction():
for string, spec in formatted:
if not string:
# print newline from above
@@ -545,7 +547,7 @@ class PythonNameError(spack.error.SpackError):
def __init__(self, name):
self.name = name
super().__init__("{0} is not a permissible Python name.".format(name))
super(PythonNameError, self).__init__("{0} is not a permissible Python name.".format(name))
class CommandNameError(spack.error.SpackError):
@@ -553,7 +555,9 @@ class CommandNameError(spack.error.SpackError):
def __init__(self, name):
self.name = name
super().__init__("{0} is not a permissible Spack command name.".format(name))
super(CommandNameError, self).__init__(
"{0} is not a permissible Spack command name.".format(name)
)
########################################

View File

@@ -3,6 +3,8 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import print_function
import collections
import archspec.cpu

View File

@@ -59,7 +59,7 @@ def setup_parser(subparser):
subparser.add_argument(
"package_or_file",
help="name of package to show contributions for, or path to a file in the spack repo",
help="name of package to show contributions for, " "or path to a file in the spack repo",
)

View File

@@ -2,6 +2,8 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import print_function
import os.path
import shutil
import tempfile

View File

@@ -43,50 +43,52 @@ def setup_parser(subparser):
subparsers = subparser.add_subparsers(help="buildcache sub-commands")
push = subparsers.add_parser("push", aliases=["create"], help=push_fn.__doc__)
push.add_argument("-f", "--force", action="store_true", help="overwrite tarball if it exists")
push.add_argument("-f", "--force", action="store_true", help="overwrite tarball if it exists.")
push.add_argument(
"-u", "--unsigned", action="store_true", help="push unsigned buildcache tarballs"
)
push.add_argument(
"--allow-root",
"-a",
"--allow-root",
action="store_true",
help="allow install root string in binary files after RPATH substitution",
)
push_sign = push.add_mutually_exclusive_group(required=False)
push_sign.add_argument(
"--unsigned", "-u", action="store_true", help="push unsigned buildcache tarballs"
push.add_argument(
"-k", "--key", metavar="key", type=str, default=None, help="Key for signing."
)
push_sign.add_argument(
"--key", "-k", metavar="key", type=str, default=None, help="key for signing"
)
push.add_argument("mirror", type=str, help="mirror name, path, or URL")
push.add_argument("mirror", type=str, help="Mirror name, path, or URL.")
push.add_argument(
"--update-index",
"--rebuild-index",
action="store_true",
default=False,
help="regenerate buildcache index after building package(s)",
help="Regenerate buildcache index after building package(s)",
)
push.add_argument(
"--spec-file", default=None, help="create buildcache entry for spec from json or yaml file"
"--spec-file", default=None, help="Create buildcache entry for spec from json or yaml file"
)
push.add_argument(
"--only",
default="package,dependencies",
dest="things_to_install",
choices=["package", "dependencies"],
help="select the buildcache mode. "
"The default is to build a cache for the package along with all its dependencies. "
"Alternatively, one can decide to build a cache for only the package or only the "
"dependencies",
help=(
"Select the buildcache mode. the default is to"
" build a cache for the package along with all"
" its dependencies. Alternatively, one can"
" decide to build a cache for only the package"
" or only the dependencies"
),
)
arguments.add_common_arguments(push, ["specs"])
push.set_defaults(func=push_fn)
install = subparsers.add_parser("install", help=install_fn.__doc__)
install.add_argument(
"-f", "--force", action="store_true", help="overwrite install directory if it exists"
"-f", "--force", action="store_true", help="overwrite install directory if it exists."
)
install.add_argument(
"-m", "--multiple", action="store_true", help="allow all matching packages"
"-m", "--multiple", action="store_true", help="allow all matching packages "
)
install.add_argument(
"-u",
@@ -140,11 +142,11 @@ def setup_parser(subparser):
"-m",
"--mirror-url",
default=None,
help="override any configured mirrors with this mirror URL",
help="Override any configured mirrors with this mirror URL",
)
check.add_argument(
"-o", "--output-file", default=None, help="file where rebuild info should be written"
"-o", "--output-file", default=None, help="File where rebuild info should be written"
)
# used to construct scope arguments below
@@ -160,13 +162,13 @@ def setup_parser(subparser):
)
check.add_argument(
"-s", "--spec", default=None, help="check single spec instead of release specs file"
"-s", "--spec", default=None, help="Check single spec instead of release specs file"
)
check.add_argument(
"--spec-file",
default=None,
help="check single spec from json or yaml file instead of release specs file",
help=("Check single spec from json or yaml file instead of release specs file"),
)
check.set_defaults(func=check_fn)
@@ -174,15 +176,15 @@ def setup_parser(subparser):
# Download tarball and specfile
download = subparsers.add_parser("download", help=download_fn.__doc__)
download.add_argument(
"-s", "--spec", default=None, help="download built tarball for spec from mirror"
"-s", "--spec", default=None, help="Download built tarball for spec from mirror"
)
download.add_argument(
"--spec-file",
default=None,
help="download built tarball for spec (from json or yaml file) from mirror",
help=("Download built tarball for spec (from json or yaml file) from mirror"),
)
download.add_argument(
"-p", "--path", default=None, help="path to directory where tarball should be downloaded"
"-p", "--path", default=None, help="Path to directory where tarball should be downloaded"
)
download.set_defaults(func=download_fn)
@@ -191,52 +193,52 @@ def setup_parser(subparser):
"get-buildcache-name", help=get_buildcache_name_fn.__doc__
)
getbuildcachename.add_argument(
"-s", "--spec", default=None, help="spec string for which buildcache name is desired"
"-s", "--spec", default=None, help="Spec string for which buildcache name is desired"
)
getbuildcachename.add_argument(
"--spec-file",
default=None,
help="path to spec json or yaml file for which buildcache name is desired",
help=("Path to spec json or yaml file for which buildcache name is desired"),
)
getbuildcachename.set_defaults(func=get_buildcache_name_fn)
# Given the root spec, save the yaml of the dependent spec to a file
savespecfile = subparsers.add_parser("save-specfile", help=save_specfile_fn.__doc__)
savespecfile.add_argument("--root-spec", default=None, help="root spec of dependent spec")
savespecfile.add_argument("--root-spec", default=None, help="Root spec of dependent spec")
savespecfile.add_argument(
"--root-specfile",
default=None,
help="path to json or yaml file containing root spec of dependent spec",
help="Path to json or yaml file containing root spec of dependent spec",
)
savespecfile.add_argument(
"-s",
"--specs",
default=None,
help="list of dependent specs for which saved yaml is desired",
help="List of dependent specs for which saved yaml is desired",
)
savespecfile.add_argument(
"--specfile-dir", default=None, help="path to directory where spec yamls should be saved"
"--specfile-dir", default=None, help="Path to directory where spec yamls should be saved"
)
savespecfile.set_defaults(func=save_specfile_fn)
# Sync buildcache entries from one mirror to another
sync = subparsers.add_parser("sync", help=sync_fn.__doc__)
sync.add_argument(
"--manifest-glob", help="a quoted glob pattern identifying copy manifest files"
"--manifest-glob",
default=None,
help="A quoted glob pattern identifying copy manifest files",
)
sync.add_argument(
"src_mirror",
metavar="source mirror",
type=arguments.mirror_name_or_url,
nargs="?",
help="source mirror name, path, or URL",
help="Source mirror name, path, or URL",
)
sync.add_argument(
"dest_mirror",
metavar="destination mirror",
type=arguments.mirror_name_or_url,
nargs="?",
help="destination mirror name, path, or URL",
help="Destination mirror name, path, or URL",
)
sync.set_defaults(func=sync_fn)
@@ -245,14 +247,14 @@ def setup_parser(subparser):
"update-index", aliases=["rebuild-index"], help=update_index_fn.__doc__
)
update_index.add_argument(
"mirror", type=arguments.mirror_name_or_url, help="destination mirror name, path, or URL"
"mirror", type=arguments.mirror_name_or_url, help="Destination mirror name, path, or URL"
)
update_index.add_argument(
"-k",
"--keys",
default=False,
action="store_true",
help="if provided, key index will be updated as well as package index",
help="If provided, key index will be updated as well as package index",
)
update_index.set_defaults(func=update_index_fn)
@@ -307,11 +309,6 @@ def push_fn(args):
"""create a binary package and push it to a mirror"""
mirror = arguments.mirror_name_or_url(args.mirror)
if args.allow_root:
tty.warn(
"The flag `--allow-root` is the default in Spack 0.21, will be removed in Spack 0.22"
)
url = mirror.push_url
specs = bindist.specs_to_be_packaged(
@@ -341,6 +338,7 @@ def push_fn(args):
bindist.PushOptions(
force=args.force,
unsigned=args.unsigned,
allow_root=args.allow_root,
key=args.key,
regenerate_index=args.update_index,
),
@@ -413,19 +411,25 @@ def keys_fn(args):
def preview_fn(args):
"""analyze an installed spec and reports whether executables and libraries are relocatable"""
tty.warn(
"`spack buildcache preview` is deprecated since `spack buildcache push --allow-root` is "
"now the default. This command will be removed in Spack 0.22"
)
"""analyze an installed spec and reports whether executables
and libraries are relocatable
"""
constraints = spack.cmd.parse_specs(args.specs)
specs = spack.store.find(constraints, multiple=True)
# Cycle over the specs that match
for spec in specs:
print("Relocatable nodes")
print("--------------------------------")
print(spec.tree(status_fn=spack.relocate.is_relocatable))
def check_fn(args):
"""check specs against remote binary mirror(s) to see if any need to be rebuilt
either a single spec from --spec, or else the full set of release specs. this command uses the
process exit code to indicate its result, specifically, if the exit code is non-zero, then at
least one of the indicated specs needs to be rebuilt
"""Check specs (either a single spec from --spec, or else the full set
of release specs) against remote binary mirror(s) to see if any need
to be rebuilt. This command uses the process exit code to indicate
its result, specifically, if the exit code is non-zero, then at least
one of the indicated specs needs to be rebuilt.
"""
if args.spec or args.spec_file:
specs = [_concrete_spec_from_args(args)]
@@ -456,12 +460,10 @@ def check_fn(args):
def download_fn(args):
"""download buildcache entry from a remote mirror to local folder
this command uses the process exit code to indicate its result, specifically, a non-zero exit
code indicates that the command failed to download at least one of the required buildcache
components
"""
"""Download buildcache entry from a remote mirror to local folder. This
command uses the process exit code to indicate its result, specifically,
a non-zero exit code indicates that the command failed to download at
least one of the required buildcache components."""
if not args.spec and not args.spec_file:
tty.msg("No specs provided, exiting.")
return
@@ -478,18 +480,19 @@ def download_fn(args):
def get_buildcache_name_fn(args):
"""get name (prefix) of buildcache entries for this spec"""
"""Get name (prefix) of buildcache entries for this spec"""
spec = _concrete_spec_from_args(args)
buildcache_name = bindist.tarball_name(spec, "")
print("{0}".format(buildcache_name))
def save_specfile_fn(args):
"""get full spec for dependencies and write them to files in the specified output directory
uses exit code to signal success or failure. an exit code of zero means the command was likely
successful. if any errors or exceptions are encountered, or if expected command-line arguments
are not provided, then the exit code will be non-zero
"""Get full spec for dependencies, relative to root spec, and write them
to files in the specified output directory. Uses exit code to signal
success or failure. An exit code of zero means the command was likely
successful. If any errors or exceptions are encountered, or if expected
command-line arguments are not provided, then the exit code will be
non-zero.
"""
if not args.root_spec and not args.root_specfile:
tty.msg("No root spec provided, exiting.")
@@ -543,17 +546,17 @@ def copy_buildcache_file(src_url, dest_url, local_path=None):
def sync_fn(args):
"""sync binaries (and associated metadata) from one mirror to another
"""Syncs binaries (and associated metadata) from one mirror to another.
Requires an active environment in order to know which specs to sync.
requires an active environment in order to know which specs to sync
Args:
src (str): Source mirror URL
dest (str): Destination mirror URL
"""
if args.manifest_glob:
manifest_copy(glob.glob(args.manifest_glob))
return 0
if args.src_mirror is None or args.dest_mirror is None:
tty.die("Provide mirrors to sync from and to.")
src_mirror = args.src_mirror
dest_mirror = args.dest_mirror
@@ -633,7 +636,7 @@ def update_index(mirror: spack.mirror.Mirror, update_keys=False):
def update_index_fn(args):
"""update a buildcache index"""
"""Update a buildcache index."""
update_index(args.mirror, update_keys=args.keys)

View File

@@ -3,6 +3,8 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import print_function
import argparse
import sys

View File

@@ -18,7 +18,6 @@
import spack.environment as ev
import spack.hash_types as ht
import spack.mirror
import spack.util.gpg as gpg_util
import spack.util.url as url_util
import spack.util.web as web_util
@@ -48,36 +47,40 @@ def setup_parser(subparser):
generate.add_argument(
"--output-file",
default=None,
help="pathname for the generated gitlab ci yaml file\n\n"
"path to the file where generated jobs file should be written. "
"default is .gitlab-ci.yml in the root of the repository",
help="""pathname for the generated gitlab ci yaml file
Path to the file where generated jobs file should
be written. Default is .gitlab-ci.yml in the root of
the repository.""",
)
generate.add_argument(
"--copy-to",
default=None,
help="path to additional directory for job files\n\n"
"this option provides an absolute path to a directory where the generated "
"jobs yaml file should be copied. default is not to copy",
help="""path to additional directory for job files
This option provides an absolute path to a directory
where the generated jobs yaml file should be copied.
Default is not to copy.""",
)
generate.add_argument(
"--optimize",
action="store_true",
default=False,
help="(experimental) optimize the gitlab yaml file for size\n\n"
"run the generated document through a series of optimization passes "
"designed to reduce the size of the generated file",
help="""(Experimental) optimize the gitlab yaml file for size
Run the generated document through a series of
optimization passes designed to reduce the size
of the generated file.""",
)
generate.add_argument(
"--dependencies",
action="store_true",
default=False,
help="(experimental) disable DAG scheduling (use 'plain' dependencies)",
help="(Experimental) disable DAG scheduling; use " ' "plain" dependencies.',
)
generate.add_argument(
"--buildcache-destination",
default=None,
help="override the mirror configured in the environment\n\n"
"allows for pushing binaries from the generated pipeline to a different location",
help="Override the mirror configured in the environment (spack.yaml) "
+ "in order to push binaries from the generated pipeline to a "
+ "different location.",
)
prune_group = generate.add_mutually_exclusive_group()
prune_group.add_argument(
@@ -85,37 +88,45 @@ def setup_parser(subparser):
action="store_true",
dest="prune_dag",
default=True,
help="skip up-to-date specs\n\n"
"do not generate jobs for specs that are up-to-date on the mirror",
help="""skip up-to-date specs
Do not generate jobs for specs that are up-to-date
on the mirror.""",
)
prune_group.add_argument(
"--no-prune-dag",
action="store_false",
dest="prune_dag",
default=True,
help="process up-to-date specs\n\n"
"generate jobs for specs even when they are up-to-date on the mirror",
help="""process up-to-date specs
Generate jobs for specs even when they are up-to-date
on the mirror.""",
)
generate.add_argument(
"--check-index-only",
action="store_true",
dest="index_only",
default=False,
help="only check spec state from buildcache indices\n\n"
"Spack always checks specs against configured binary mirrors, regardless of the DAG "
"pruning option. if enabled, Spack will assume all remote buildcache indices are "
"up-to-date when assessing whether the spec on the mirror, if present, is up-to-date. "
"this has the benefit of reducing pipeline generation time but at the potential cost of "
"needlessly rebuilding specs when the indices are outdated. if not enabled, Spack will "
"fetch remote spec files directly to assess whether the spec on the mirror is up-to-date",
help="""only check spec state from buildcache indices
Spack always checks specs against configured binary
mirrors, regardless of the DAG pruning option.
If enabled, Spack will assume all remote buildcache
indices are up-to-date when assessing whether the spec
on the mirror, if present, is up-to-date. This has the
benefit of reducing pipeline generation time but at the
potential cost of needlessly rebuilding specs when the
indices are outdated.
If not enabled, Spack will fetch remote spec files
directly to assess whether the spec on the mirror is
up-to-date.""",
)
generate.add_argument(
"--artifacts-root",
default=None,
help="path to the root of the artifacts directory\n\n"
"if provided, concrete environment files (spack.yaml, spack.lock) will be generated under "
"this directory. their location will be passed to generated child jobs through the "
"SPACK_CONCRETE_ENVIRONMENT_PATH variable",
help="""path to the root of the artifacts directory
If provided, concrete environment files (spack.yaml,
spack.lock) will be generated under this directory.
Their location will be passed to generated child jobs
through the SPACK_CONCRETE_ENVIRONMENT_PATH variable.""",
)
generate.set_defaults(func=ci_generate)
@@ -139,13 +150,13 @@ def setup_parser(subparser):
"--tests",
action="store_true",
default=False,
help="run stand-alone tests after the build",
help="""run stand-alone tests after the build""",
)
rebuild.add_argument(
"--fail-fast",
action="store_true",
default=False,
help="stop stand-alone tests after the first failure",
help="""stop stand-alone tests after the first failure""",
)
rebuild.set_defaults(func=ci_rebuild)
@@ -155,10 +166,10 @@ def setup_parser(subparser):
description=deindent(ci_reproduce.__doc__),
help=spack.cmd.first_line(ci_reproduce.__doc__),
)
reproduce.add_argument("job_url", help="URL of job artifacts bundle")
reproduce.add_argument("job_url", help="Url of job artifacts bundle")
reproduce.add_argument(
"--working-dir",
help="where to unpack artifacts",
help="Where to unpack artifacts",
default=os.path.join(os.getcwd(), "ci_reproduction"),
)
@@ -166,12 +177,12 @@ def setup_parser(subparser):
def ci_generate(args):
"""generate jobs file from a CI-aware spack file
"""Generate jobs file from a CI-aware spack file.
if you want to report the results on CDash, you will need to set the SPACK_CDASH_AUTH_TOKEN
before invoking this command. the value must be the CDash authorization token needed to create
a build group and register all generated jobs under it
"""
If you want to report the results on CDash, you will need to set
the SPACK_CDASH_AUTH_TOKEN before invoking this command. The
value must be the CDash authorization token needed to create a
build group and register all generated jobs under it."""
env = spack.cmd.require_active_env(cmd_name="ci generate")
output_file = args.output_file
@@ -212,11 +223,10 @@ def ci_generate(args):
def ci_reindex(args):
"""rebuild the buildcache index for the remote mirror
"""Rebuild the buildcache index for the remote mirror.
use the active, gitlab-enabled environment to rebuild the buildcache index for the associated
mirror
"""
Use the active, gitlab-enabled environment to rebuild the buildcache
index for the associated mirror."""
env = spack.cmd.require_active_env(cmd_name="ci rebuild-index")
yaml_root = env.manifest[ev.TOP_LEVEL_KEY]
@@ -232,11 +242,10 @@ def ci_reindex(args):
def ci_rebuild(args):
"""rebuild a spec if it is not on the remote mirror
"""Rebuild a spec if it is not on the remote mirror.
check a single spec against the remote mirror, and rebuild it from source if the mirror does
not contain the hash
"""
Check a single spec against the remote mirror, and rebuild it from
source if the mirror does not contain the hash."""
env = spack.cmd.require_active_env(cmd_name="ci rebuild")
# Make sure the environment is "gitlab-enabled", or else there's nothing
@@ -265,19 +274,13 @@ def ci_rebuild(args):
signing_key = os.environ.get("SPACK_SIGNING_KEY")
job_spec_pkg_name = os.environ.get("SPACK_JOB_SPEC_PKG_NAME")
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")
remote_mirror_override = os.environ.get("SPACK_REMOTE_MIRROR_OVERRIDE")
remote_mirror_url = os.environ.get("SPACK_REMOTE_MIRROR_URL")
spack_ci_stack_name = os.environ.get("SPACK_CI_STACK_NAME")
shared_pr_mirror_url = os.environ.get("SPACK_CI_SHARED_PR_MIRROR_URL")
rebuild_everything = os.environ.get("SPACK_REBUILD_EVERYTHING")
require_signing = os.environ.get("SPACK_REQUIRE_SIGNING")
# Fail early if signing is required but we don't have a signing key
sign_binaries = require_signing is not None and require_signing.lower() == "true"
if sign_binaries and not spack_ci.can_sign_binaries():
gpg_util.list(False, True)
tty.die("SPACK_REQUIRE_SIGNING=True => spack must have exactly one signing key")
# Construct absolute paths relative to current $CI_PROJECT_DIR
ci_project_dir = os.environ.get("CI_PROJECT_DIR")
@@ -292,6 +295,7 @@ def ci_rebuild(args):
tty.debug("pipeline_artifacts_dir = {0}".format(pipeline_artifacts_dir))
tty.debug("remote_mirror_url = {0}".format(remote_mirror_url))
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
# CDash instance, and if so, gather some information from the manifest to
@@ -407,6 +411,14 @@ def ci_rebuild(args):
if 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
# 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))
@@ -604,7 +616,7 @@ def ci_rebuild(args):
)
reports_dir = fs.join_path(os.getcwd(), "cdash_report")
if args.tests and broken_tests:
tty.warn("Unable to run stand-alone tests since listed in ci's 'broken-tests-packages'")
tty.warn("Unable to run stand-alone tests since listed in " "ci's 'broken-tests-packages'")
if cdash_handler:
msg = "Package is listed in ci's broken-tests-packages"
cdash_handler.report_skipped(job_spec, reports_dir, reason=msg)
@@ -647,7 +659,7 @@ def ci_rebuild(args):
tty.warn("No recognized test results reporting option")
else:
tty.warn("Unable to run stand-alone tests due to unsuccessful installation")
tty.warn("Unable to run stand-alone tests due to unsuccessful " "installation")
if cdash_handler:
msg = "Failed to install the package"
cdash_handler.report_skipped(job_spec, reports_dir, reason=msg)
@@ -663,7 +675,7 @@ def ci_rebuild(args):
input_spec=job_spec,
buildcache_mirror_url=buildcache_mirror_url,
pipeline_mirror_url=pipeline_mirror_url,
sign_binaries=sign_binaries,
pr_pipeline=spack_is_pr_pipeline,
):
msg = tty.msg if result.success else tty.warn
msg(
@@ -726,11 +738,10 @@ def ci_rebuild(args):
def ci_reproduce(args):
"""generate instructions for reproducing the spec rebuild job
"""Generate instructions for reproducing the spec rebuild job.
artifacts of the provided gitlab pipeline rebuild job's URL will be used to derive
instructions for reproducing the build locally
"""
Artifacts of the provided gitlab pipeline rebuild job's URL will be
used to derive instructions for reproducing the build locally."""
job_url = args.job_url
work_dir = args.working_dir

View File

@@ -115,7 +115,7 @@ def clean(parser, args):
tty.msg("Removing all temporary build stages")
spack.stage.purge()
# Temp directory where buildcaches are extracted
extract_tmp = os.path.join(spack.store.STORE.layout.root, ".tmp")
extract_tmp = os.path.join(spack.store.layout.root, ".tmp")
if os.path.exists(extract_tmp):
tty.debug("Removing {0}".format(extract_tmp))
shutil.rmtree(extract_tmp)

View File

@@ -48,7 +48,7 @@ def get_origin_info(remote):
)
except ProcessError:
origin_url = _SPACK_UPSTREAM
tty.warn("No git repository found; using default upstream URL: %s" % origin_url)
tty.warn("No git repository found; " "using default upstream URL: %s" % origin_url)
return (origin_url.strip(), branch.strip())
@@ -69,7 +69,7 @@ def clone(parser, args):
files_in_the_way = os.listdir(prefix)
if files_in_the_way:
tty.die(
"There are already files there! Delete these files before boostrapping spack.",
"There are already files there! " "Delete these files before boostrapping spack.",
*files_in_the_way,
)

View File

@@ -3,17 +3,17 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import print_function
import argparse
import copy
import os
import re
import sys
from argparse import ArgumentParser, Namespace
from typing import IO, Any, Callable, Dict, Iterable, List, Optional, Sequence, Set, Tuple, Union
import llnl.util.filesystem as fs
import llnl.util.tty as tty
from llnl.util.argparsewriter import ArgparseRstWriter, ArgparseWriter, Command
from llnl.util.argparsewriter import ArgparseCompletionWriter, ArgparseRstWriter, ArgparseWriter
from llnl.util.tty.colify import colify
import spack.cmd
@@ -27,46 +27,28 @@
#: list of command formatters
formatters: Dict[str, Callable[[Namespace, IO], None]] = {}
formatters = {}
#: standard arguments for updating completion scripts
#: we iterate through these when called with --update-completion
update_completion_args: Dict[str, Dict[str, Any]] = {
update_completion_args = {
"bash": {
"aliases": True,
"format": "bash",
"header": os.path.join(spack.paths.share_path, "bash", "spack-completion.in"),
"update": os.path.join(spack.paths.share_path, "spack-completion.bash"),
},
"fish": {
"aliases": True,
"format": "fish",
"header": os.path.join(spack.paths.share_path, "fish", "spack-completion.in"),
"update": os.path.join(spack.paths.share_path, "spack-completion.fish"),
},
}
}
def formatter(func: Callable[[Namespace, IO], None]) -> Callable[[Namespace, IO], None]:
"""Decorator used to register formatters.
Args:
func: Formatting function.
Returns:
The same function.
"""
def formatter(func):
"""Decorator used to register formatters"""
formatters[func.__name__] = func
return func
def setup_parser(subparser: ArgumentParser) -> None:
"""Set up the argument parser.
Args:
subparser: Preliminary argument parser.
"""
def setup_parser(subparser):
subparser.add_argument(
"--update-completion",
action="store_true",
@@ -109,34 +91,18 @@ class SpackArgparseRstWriter(ArgparseRstWriter):
def __init__(
self,
prog: str,
out: IO = sys.stdout,
aliases: bool = False,
documented_commands: Set[str] = set(),
rst_levels: Sequence[str] = ["-", "-", "^", "~", ":", "`"],
prog,
out=None,
aliases=False,
documented_commands=[],
rst_levels=["-", "-", "^", "~", ":", "`"],
):
"""Initialize a new SpackArgparseRstWriter instance.
Args:
prog: Program name.
out: File object to write to.
aliases: Whether or not to include subparsers for aliases.
documented_commands: Set of commands with additional documentation.
rst_levels: List of characters for rst section headings.
"""
super().__init__(prog, out, aliases, rst_levels)
out = sys.stdout if out is None else out
super(SpackArgparseRstWriter, self).__init__(prog, out, aliases, rst_levels)
self.documented = documented_commands
def usage(self, usage: str) -> str:
"""Example usage of a command.
Args:
usage: Command usage.
Returns:
Usage of a command.
"""
string = super().usage(usage)
def usage(self, *args):
string = super(SpackArgparseRstWriter, self).usage(*args)
cmd = self.parser.prog.replace(" ", "-")
if cmd in self.documented:
@@ -146,21 +112,11 @@ def usage(self, usage: str) -> str:
class SubcommandWriter(ArgparseWriter):
"""Write argparse output as a list of subcommands."""
def format(self, cmd: Command) -> str:
"""Return the string representation of a single node in the parser tree.
Args:
cmd: Parsed information about a command or subcommand.
Returns:
String representation of this subcommand.
"""
def format(self, cmd):
return " " * self.level + cmd.prog + "\n"
_positional_to_subroutine: Dict[str, str] = {
_positional_to_subroutine = {
"package": "_all_packages",
"spec": "_all_packages",
"filter": "_all_packages",
@@ -179,76 +135,10 @@ def format(self, cmd: Command) -> str:
}
class BashCompletionWriter(ArgparseWriter):
class BashCompletionWriter(ArgparseCompletionWriter):
"""Write argparse output as bash programmable tab completion."""
def format(self, cmd: Command) -> str:
"""Return the string representation of a single node in the parser tree.
Args:
cmd: Parsed information about a command or subcommand.
Returns:
String representation of this subcommand.
"""
assert cmd.optionals # we should always at least have -h, --help
assert not (cmd.positionals and cmd.subcommands) # one or the other
# We only care about the arguments/flags, not the help messages
positionals: Tuple[str, ...] = ()
if cmd.positionals:
positionals, _, _, _ = zip(*cmd.positionals)
optionals, _, _, _, _ = zip(*cmd.optionals)
subcommands: Tuple[str, ...] = ()
if cmd.subcommands:
_, subcommands, _ = zip(*cmd.subcommands)
# Flatten lists of lists
optionals = [x for xx in optionals for x in xx]
return (
self.start_function(cmd.prog)
+ self.body(positionals, optionals, subcommands)
+ self.end_function(cmd.prog)
)
def start_function(self, prog: str) -> str:
"""Return the syntax needed to begin a function definition.
Args:
prog: Program name.
Returns:
Function definition beginning.
"""
name = prog.replace("-", "_").replace(" ", "_")
return "\n_{0}() {{".format(name)
def end_function(self, prog: str) -> str:
"""Return the syntax needed to end a function definition.
Args:
prog: Program name
Returns:
Function definition ending.
"""
return "}\n"
def body(
self, positionals: Sequence[str], optionals: Sequence[str], subcommands: Sequence[str]
) -> str:
"""Return the body of the function.
Args:
positionals: List of positional arguments.
optionals: List of optional arguments.
subcommands: List of subcommand parsers.
Returns:
Function body.
"""
def body(self, positionals, optionals, subcommands):
if positionals:
return """
if $list_options
@@ -278,15 +168,7 @@ def body(
self.optionals(optionals)
)
def positionals(self, positionals: Sequence[str]) -> str:
"""Return the syntax for reporting positional arguments.
Args:
positionals: List of positional arguments.
Returns:
Syntax for positional arguments.
"""
def positionals(self, positionals):
# If match found, return function name
for positional in positionals:
for key, value in _positional_to_subroutine.items():
@@ -296,439 +178,22 @@ def positionals(self, positionals: Sequence[str]) -> str:
# If no matches found, return empty list
return 'SPACK_COMPREPLY=""'
def optionals(self, optionals: Sequence[str]) -> str:
"""Return the syntax for reporting optional flags.
Args:
optionals: List of optional arguments.
Returns:
Syntax for optional flags.
"""
def optionals(self, optionals):
return 'SPACK_COMPREPLY="{0}"'.format(" ".join(optionals))
def subcommands(self, subcommands: Sequence[str]) -> str:
"""Return the syntax for reporting subcommands.
Args:
subcommands: List of subcommand parsers.
Returns:
Syntax for subcommand parsers
"""
def subcommands(self, subcommands):
return 'SPACK_COMPREPLY="{0}"'.format(" ".join(subcommands))
# Map argument destination names to their complete commands
# Earlier items in the list have higher precedence
_dest_to_fish_complete = {
("activate", "view"): "-f -a '(__fish_complete_directories)'",
("bootstrap root", "path"): "-f -a '(__fish_complete_directories)'",
("mirror add", "mirror"): "-f",
("repo add", "path"): "-f -a '(__fish_complete_directories)'",
("test find", "filter"): "-f -a '(__fish_spack_tests)'",
("bootstrap", "name"): "-f -a '(__fish_spack_bootstrap_names)'",
("buildcache create", "key"): "-f -a '(__fish_spack_gpg_keys)'",
("build-env", r"spec \[--\].*"): "-f -a '(__fish_spack_build_env_spec)'",
("checksum", "package"): "-f -a '(__fish_spack_packages)'",
(
"checksum",
"versions",
): "-f -a '(__fish_spack_package_versions $__fish_spack_argparse_argv[1])'",
("config", "path"): "-f -a '(__fish_spack_colon_path)'",
("config", "section"): "-f -a '(__fish_spack_config_sections)'",
("develop", "specs?"): "-f -k -a '(__fish_spack_specs_or_id)'",
("diff", "specs?"): "-f -a '(__fish_spack_installed_specs)'",
("gpg sign", "output"): "-f -a '(__fish_complete_directories)'",
("gpg", "keys?"): "-f -a '(__fish_spack_gpg_keys)'",
("graph", "specs?"): "-f -k -a '(__fish_spack_specs_or_id)'",
("help", "help_command"): "-f -a '(__fish_spack_commands)'",
("list", "filter"): "-f -a '(__fish_spack_packages)'",
("mirror", "mirror"): "-f -a '(__fish_spack_mirrors)'",
("pkg", "package"): "-f -a '(__fish_spack_pkg_packages)'",
("remove", "specs?"): "-f -a '(__fish_spack_installed_specs)'",
("repo", "namespace_or_path"): "$__fish_spack_force_files -a '(__fish_spack_repos)'",
("restage", "specs?"): "-f -k -a '(__fish_spack_specs_or_id)'",
("rm", "specs?"): "-f -a '(__fish_spack_installed_specs)'",
("solve", "specs?"): "-f -k -a '(__fish_spack_specs_or_id)'",
("spec", "specs?"): "-f -k -a '(__fish_spack_specs_or_id)'",
("stage", "specs?"): "-f -k -a '(__fish_spack_specs_or_id)'",
("test-env", r"spec \[--\].*"): "-f -a '(__fish_spack_build_env_spec)'",
("test", r"\[?name.*"): "-f -a '(__fish_spack_tests)'",
("undevelop", "specs?"): "-f -k -a '(__fish_spack_specs_or_id)'",
("verify", "specs_or_files"): "$__fish_spack_force_files -a '(__fish_spack_installed_specs)'",
("view", "path"): "-f -a '(__fish_complete_directories)'",
("", "comment"): "-f",
("", "compiler_spec"): "-f -a '(__fish_spack_installed_compilers)'",
("", "config_scopes"): "-f -a '(__fish_complete_directories)'",
("", "extendable"): "-f -a '(__fish_spack_extensions)'",
("", "installed_specs?"): "-f -a '(__fish_spack_installed_specs)'",
("", "job_url"): "-f",
("", "location_env"): "-f -a '(__fish_complete_directories)'",
("", "pytest_args"): "-f -a '(__fish_spack_unit_tests)'",
("", "package_or_file"): "$__fish_spack_force_files -a '(__fish_spack_packages)'",
("", "package_or_user"): "-f -a '(__fish_spack_packages)'",
("", "package"): "-f -a '(__fish_spack_packages)'",
("", "PKG"): "-f -a '(__fish_spack_packages)'",
("", "prefix"): "-f -a '(__fish_complete_directories)'",
("", r"rev\d?"): "-f -a '(__fish_spack_git_rev)'",
("", "specs?"): "-f -k -a '(__fish_spack_specs)'",
("", "tags?"): "-f -a '(__fish_spack_tags)'",
("", "virtual_package"): "-f -a '(__fish_spack_providers)'",
("", "working_dir"): "-f -a '(__fish_complete_directories)'",
("", r"(\w*_)?env"): "-f -a '(__fish_spack_environments)'",
("", r"(\w*_)?dir(ectory)?"): "-f -a '(__fish_spack_environments)'",
("", r"(\w*_)?mirror_name"): "-f -a '(__fish_spack_mirrors)'",
}
def _fish_dest_get_complete(prog: str, dest: str) -> Optional[str]:
"""Map from subcommand to autocompletion argument.
Args:
prog: Program name.
dest: Destination.
Returns:
Autocompletion argument.
"""
s = prog.split(None, 1)
subcmd = s[1] if len(s) == 2 else ""
for (prog_key, pos_key), value in _dest_to_fish_complete.items():
if subcmd.startswith(prog_key) and re.match("^" + pos_key + "$", dest):
return value
return None
class FishCompletionWriter(ArgparseWriter):
"""Write argparse output as bash programmable tab completion."""
def format(self, cmd: Command) -> str:
"""Return the string representation of a single node in the parser tree.
Args:
cmd: Parsed information about a command or subcommand.
Returns:
String representation of a node.
"""
assert cmd.optionals # we should always at least have -h, --help
assert not (cmd.positionals and cmd.subcommands) # one or the other
# We also need help messages and how arguments are used
# So we pass everything to completion writer
positionals = cmd.positionals
optionals = cmd.optionals
subcommands = cmd.subcommands
return (
self.prog_comment(cmd.prog)
+ self.optspecs(cmd.prog, optionals)
+ self.complete(cmd.prog, positionals, optionals, subcommands)
)
def _quote(self, string: str) -> str:
"""Quote string and escape special characters if necessary.
Args:
string: Input string.
Returns:
Quoted string.
"""
# Goal here is to match fish_indent behavior
# Strings without spaces (or other special characters) do not need to be escaped
if not any([sub in string for sub in [" ", "'", '"']]):
return string
string = string.replace("'", r"\'")
return f"'{string}'"
def optspecs(
self,
prog: str,
optionals: List[Tuple[Sequence[str], List[str], str, Union[int, str, None], str]],
) -> str:
"""Read the optionals and return the command to set optspec.
Args:
prog: Program name.
optionals: List of optional arguments.
Returns:
Command to set optspec variable.
"""
# Variables of optspecs
optspec_var = "__fish_spack_optspecs_" + prog.replace(" ", "_").replace("-", "_")
if optionals is None:
return "set -g %s\n" % optspec_var
# Build optspec by iterating over options
args = []
for flags, dest, _, nargs, _ in optionals:
if len(flags) == 0:
continue
required = ""
# Because nargs '?' is treated differently in fish, we treat it as required.
# Because multi-argument options are not supported, we treat it like one argument.
required = "="
if nargs == 0:
required = ""
# Pair short options with long options
# We need to do this because fish doesn't support multiple short
# or long options.
# However, since we are paring options only, this is fine
short = [f[1:] for f in flags if f.startswith("-") and len(f) == 2]
long = [f[2:] for f in flags if f.startswith("--")]
while len(short) > 0 and len(long) > 0:
arg = "%s/%s%s" % (short.pop(), long.pop(), required)
while len(short) > 0:
arg = "%s/%s" % (short.pop(), required)
while len(long) > 0:
arg = "%s%s" % (long.pop(), required)
args.append(arg)
# Even if there is no option, we still set variable.
# In fish such variable is an empty array, we use it to
# indicate that such subcommand exists.
args = " ".join(args)
return "set -g %s %s\n" % (optspec_var, args)
@staticmethod
def complete_head(
prog: str, index: Optional[int] = None, nargs: Optional[Union[int, str]] = None
) -> str:
"""Return the head of the completion command.
Args:
prog: Program name.
index: Index of positional argument.
nargs: Number of arguments.
Returns:
Head of the completion command.
"""
# Split command and subcommand
s = prog.split(None, 1)
subcmd = s[1] if len(s) == 2 else ""
if index is None:
return "complete -c %s -n '__fish_spack_using_command %s'" % (s[0], subcmd)
elif nargs in [argparse.ZERO_OR_MORE, argparse.ONE_OR_MORE, argparse.REMAINDER]:
head = "complete -c %s -n '__fish_spack_using_command_pos_remainder %d %s'"
else:
head = "complete -c %s -n '__fish_spack_using_command_pos %d %s'"
return head % (s[0], index, subcmd)
def complete(
self,
prog: str,
positionals: List[Tuple[str, Optional[Iterable[Any]], Union[int, str, None], str]],
optionals: List[Tuple[Sequence[str], List[str], str, Union[int, str, None], str]],
subcommands: List[Tuple[ArgumentParser, str, str]],
) -> str:
"""Return all the completion commands.
Args:
prog: Program name.
positionals: List of positional arguments.
optionals: List of optional arguments.
subcommands: List of subcommand parsers.
Returns:
Completion command.
"""
commands = []
if positionals:
commands.append(self.positionals(prog, positionals))
if subcommands:
commands.append(self.subcommands(prog, subcommands))
if optionals:
commands.append(self.optionals(prog, optionals))
return "".join(commands)
def positionals(
self,
prog: str,
positionals: List[Tuple[str, Optional[Iterable[Any]], Union[int, str, None], str]],
) -> str:
"""Return the completion for positional arguments.
Args:
prog: Program name.
positionals: List of positional arguments.
Returns:
Completion command.
"""
commands = []
for idx, (args, choices, nargs, help) in enumerate(positionals):
# Make sure we always get same order of output
if isinstance(choices, dict):
choices = sorted(choices.keys())
elif isinstance(choices, (set, frozenset)):
choices = sorted(choices)
# Remove platform-specific choices to avoid hard-coding the platform.
if choices is not None:
valid_choices = []
for choice in choices:
if spack.platforms.host().name not in choice:
valid_choices.append(choice)
choices = valid_choices
head = self.complete_head(prog, idx, nargs)
if choices is not None:
# If there are choices, we provide a completion for all possible values.
commands.append(head + " -f -a %s" % self._quote(" ".join(choices)))
else:
# Otherwise, we try to find a predefined completion for it
value = _fish_dest_get_complete(prog, args)
if value is not None:
commands.append(head + " " + value)
return "\n".join(commands) + "\n"
def prog_comment(self, prog: str) -> str:
"""Return a comment line for the command.
Args:
prog: Program name.
Returns:
Comment line.
"""
return "\n# %s\n" % prog
def optionals(
self,
prog: str,
optionals: List[Tuple[Sequence[str], List[str], str, Union[int, str, None], str]],
) -> str:
"""Return the completion for optional arguments.
Args:
prog: Program name.
optionals: List of optional arguments.
Returns:
Completion command.
"""
commands = []
head = self.complete_head(prog)
for flags, dest, _, nargs, help in optionals:
# Make sure we always get same order of output
if isinstance(dest, dict):
dest = sorted(dest.keys())
elif isinstance(dest, (set, frozenset)):
dest = sorted(dest)
# Remove platform-specific choices to avoid hard-coding the platform.
if dest is not None:
valid_choices = []
for choice in dest:
if spack.platforms.host().name not in choice:
valid_choices.append(choice)
dest = valid_choices
# To provide description for optionals, and also possible values,
# we need to use two split completion command.
# Otherwise, each option will have same description.
prefix = head
# Add all flags to the completion
for f in flags:
if f.startswith("--"):
long = f[2:]
prefix += " -l %s" % long
elif f.startswith("-"):
short = f[1:]
assert len(short) == 1
prefix += " -s %s" % short
# Check if option require argument.
# Currently multi-argument options are not supported, so we treat it like one argument.
if nargs != 0:
prefix += " -r"
if dest is not None:
# If there are choices, we provide a completion for all possible values.
commands.append(prefix + " -f -a %s" % self._quote(" ".join(dest)))
else:
# Otherwise, we try to find a predefined completion for it
value = _fish_dest_get_complete(prog, dest)
if value is not None:
commands.append(prefix + " " + value)
if help:
commands.append(prefix + " -d %s" % self._quote(help))
return "\n".join(commands) + "\n"
def subcommands(self, prog: str, subcommands: List[Tuple[ArgumentParser, str, str]]) -> str:
"""Return the completion for subcommands.
Args:
prog: Program name.
subcommands: List of subcommand parsers.
Returns:
Completion command.
"""
commands = []
head = self.complete_head(prog, 0)
for _, subcommand, help in subcommands:
command = head + " -f -a %s" % self._quote(subcommand)
if help is not None and len(help) > 0:
help = help.split("\n")[0]
command += " -d %s" % self._quote(help)
commands.append(command)
return "\n".join(commands) + "\n"
@formatter
def subcommands(args: Namespace, out: IO) -> None:
"""Hierarchical tree of subcommands.
args:
args: Command-line arguments.
out: File object to write to.
"""
def subcommands(args, out):
parser = spack.main.make_argument_parser()
spack.main.add_all_commands(parser)
writer = SubcommandWriter(parser.prog, out, args.aliases)
writer.write(parser)
def rst_index(out: IO) -> None:
"""Generate an index of all commands.
Args:
out: File object to write to.
"""
def rst_index(out):
out.write("\n")
index = spack.main.index_commands()
@@ -756,19 +221,13 @@ def rst_index(out: IO) -> None:
@formatter
def rst(args: Namespace, out: IO) -> None:
"""ReStructuredText documentation of subcommands.
args:
args: Command-line arguments.
out: File object to write to.
"""
def rst(args, out):
# create a parser with all commands
parser = spack.main.make_argument_parser()
spack.main.add_all_commands(parser)
# extract cross-refs of the form `_cmd-spack-<cmd>:` from rst files
documented_commands: Set[str] = set()
documented_commands = set()
for filename in args.rst_files:
with open(filename) as f:
for line in f:
@@ -786,13 +245,7 @@ def rst(args: Namespace, out: IO) -> None:
@formatter
def names(args: Namespace, out: IO) -> None:
"""Simple list of top-level commands.
args:
args: Command-line arguments.
out: File object to write to.
"""
def names(args, out):
commands = copy.copy(spack.cmd.all_commands())
if args.aliases:
@@ -802,13 +255,7 @@ def names(args: Namespace, out: IO) -> None:
@formatter
def bash(args: Namespace, out: IO) -> None:
"""Bash tab-completion script.
args:
args: Command-line arguments.
out: File object to write to.
"""
def bash(args, out):
parser = spack.main.make_argument_parser()
spack.main.add_all_commands(parser)
@@ -816,22 +263,7 @@ def bash(args: Namespace, out: IO) -> None:
writer.write(parser)
@formatter
def fish(args, out):
parser = spack.main.make_argument_parser()
spack.main.add_all_commands(parser)
writer = FishCompletionWriter(parser.prog, out, args.aliases)
writer.write(parser)
def prepend_header(args: Namespace, out: IO) -> None:
"""Prepend header text at the beginning of a file.
Args:
args: Command-line arguments.
out: File object to write to.
"""
def prepend_header(args, out):
if not args.header:
return
@@ -839,14 +271,10 @@ def prepend_header(args: Namespace, out: IO) -> None:
out.write(header.read())
def _commands(parser: ArgumentParser, args: Namespace) -> None:
def _commands(parser, args):
"""This is the 'regular' command, which can be called multiple times.
See ``commands()`` below for ``--update-completion`` handling.
Args:
parser: Argument parser.
args: Command-line arguments.
"""
formatter = formatters[args.format]
@@ -868,15 +296,12 @@ def _commands(parser: ArgumentParser, args: Namespace) -> None:
formatter(args, sys.stdout)
def update_completion(parser: ArgumentParser, args: Namespace) -> None:
def update_completion(parser, args):
"""Iterate through the shells and update the standard completion files.
This is a convenience method to avoid calling this command many
times, and to simplify completion update for developers.
Args:
parser: Argument parser.
args: Command-line arguments.
"""
for shell, shell_args in update_completion_args.items():
for attr, value in shell_args.items():
@@ -884,20 +309,14 @@ def update_completion(parser: ArgumentParser, args: Namespace) -> None:
_commands(parser, args)
def commands(parser: ArgumentParser, args: Namespace) -> None:
"""Main function that calls formatter functions.
Args:
parser: Argument parser.
args: Command-line arguments.
"""
def commands(parser, args):
if args.update_completion:
if args.format != "names" or any([args.aliases, args.update, args.header]):
tty.die("--update-completion can only be specified alone.")
# this runs the command multiple times with different arguments
update_completion(parser, args)
return update_completion(parser, args)
else:
# run commands normally
_commands(parser, args)
return _commands(parser, args)

View File

@@ -36,10 +36,7 @@ def shell_init_instructions(cmd, equivalent):
" source %s/setup-env.fish" % spack.paths.share_path,
"",
color.colorize("@*c{For Windows batch:}"),
" %s\\spack_cmd.bat" % spack.paths.bin_path,
"",
color.colorize("@*c{For PowerShell:}"),
" %s\\setup-env.ps1" % spack.paths.share_path,
" source %s/spack_cmd.bat" % spack.paths.share_path,
"",
"Or, if you do not want to use shell support, run "
+ ("one of these" if shell_specific else "this")
@@ -53,7 +50,6 @@ def shell_init_instructions(cmd, equivalent):
equivalent.format(sh_arg="--csh ") + " # csh/tcsh",
equivalent.format(sh_arg="--fish") + " # fish",
equivalent.format(sh_arg="--bat ") + " # batch",
equivalent.format(sh_arg="--pwsh") + " # powershell",
]
else:
msg += [" " + equivalent]

View File

@@ -82,12 +82,12 @@ def _specs(self, **kwargs):
# return everything for an empty query.
if not qspecs:
return spack.store.STORE.db.query(**kwargs)
return spack.store.db.query(**kwargs)
# Return only matching stuff otherwise.
specs = {}
for spec in qspecs:
for s in spack.store.STORE.db.query(spec, **kwargs):
for s in spack.store.db.query(spec, **kwargs):
# This is fast for already-concrete specs
specs[s.dag_hash()] = s
@@ -265,7 +265,7 @@ def recurse_dependents():
"--dependents",
action="store_true",
dest="dependents",
help="also uninstall any packages that depend on the ones given via command line",
help="also uninstall any packages that depend on the ones given " "via command line",
)
@@ -286,7 +286,7 @@ def deptype():
"--deptype",
action=DeptypeAction,
default=dep.all_deptypes,
help="comma-separated list of deptypes to traverse\n\ndefault=%s"
help="comma-separated list of deptypes to traverse\ndefault=%s"
% ",".join(dep.all_deptypes),
)
@@ -350,9 +350,9 @@ def install_status():
"--install-status",
action="store_true",
default=True,
help="show install status of packages\n\npackages can be: "
help="show install status of packages. packages can be: "
"installed [+], missing and needed by an installed package [-], "
"installed in an upstream instance [^], "
"installed in and upstream instance [^], "
"or not installed (no annotation)",
)
@@ -393,23 +393,24 @@ def add_cdash_args(subparser, add_help):
cdash_help = {}
if add_help:
cdash_help["upload-url"] = "CDash URL where reports will be uploaded"
cdash_help["build"] = (
"name of the build that will be reported to CDash\n\n"
"defaults to spec of the package to operate on"
)
cdash_help["site"] = (
"site name that will be reported to CDash\n\n" "defaults to current system hostname"
)
cdash_help["track"] = (
"results will be reported to this group on CDash\n\n" "defaults to Experimental"
)
cdash_help["buildstamp"] = (
"use custom buildstamp\n\n"
"instead of letting the CDash reporter prepare the "
"buildstamp which, when combined with build name, site and project, "
"uniquely identifies the build, provide this argument to identify "
"the build yourself. format: %%Y%%m%%d-%%H%%M-[cdash-track]"
)
cdash_help[
"build"
] = """The name of the build that will be reported to CDash.
Defaults to spec of the package to operate on."""
cdash_help[
"site"
] = """The site name that will be reported to CDash.
Defaults to current system hostname."""
cdash_help[
"track"
] = """Results will be reported to this group on CDash.
Defaults to Experimental."""
cdash_help[
"buildstamp"
] = """Instead of letting the CDash reporter prepare the
buildstamp which, when combined with build name, site and project,
uniquely identifies the build, provide this argument to identify
the build yourself. Format: %%Y%%m%%d-%%H%%M-[cdash-track]"""
else:
cdash_help["upload-url"] = argparse.SUPPRESS
cdash_help["build"] = argparse.SUPPRESS
@@ -478,7 +479,7 @@ def __init__(
# substituting '_' for ':'.
dest = dest.replace(":", "_")
super().__init__(
super(ConfigSetAction, self).__init__(
option_strings=option_strings,
dest=dest,
nargs=0,
@@ -541,16 +542,16 @@ def add_s3_connection_args(subparser, add_help):
"--s3-access-key-id", help="ID string to use to connect to this S3 mirror"
)
subparser.add_argument(
"--s3-access-key-secret", help="secret string to use to connect to this S3 mirror"
"--s3-access-key-secret", help="Secret string to use to connect to this S3 mirror"
)
subparser.add_argument(
"--s3-access-token", help="access token to use to connect to this S3 mirror"
"--s3-access-token", help="Access Token to use to connect to this S3 mirror"
)
subparser.add_argument(
"--s3-profile", help="S3 profile name to use to connect to this S3 mirror", default=None
)
subparser.add_argument(
"--s3-endpoint-url", help="endpoint URL to use to connect to this S3 mirror"
"--s3-endpoint-url", help="Endpoint URL to use to connect to this S3 mirror"
)

View File

@@ -2,6 +2,8 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import print_function
import argparse
import os
@@ -106,7 +108,7 @@ def emulate_env_utility(cmd_name, context, args):
visitor = AreDepsInstalledVisitor(context=context)
# Mass install check needs read transaction.
with spack.store.STORE.db.read_transaction():
with spack.store.db.read_transaction():
traverse.traverse_breadth_first_with_visitor([spec], traverse.CoverNodesVisitor(visitor))
if visitor.has_uninstalled_deps:

View File

@@ -3,6 +3,8 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import print_function
import argparse
import sys

View File

@@ -14,16 +14,18 @@
def setup_parser(subparser):
subparser.add_argument(
"-f", "--force", action="store_true", help="re-concretize even if already concretized"
"-f", "--force", action="store_true", help="Re-concretize even if already concretized."
)
subparser.add_argument(
"--test",
default=None,
choices=["root", "all"],
help="concretize with test dependencies of only root packages or all packages",
help="""Concretize with test dependencies. When 'root' is chosen, test
dependencies are only added for the environment's root specs. When 'all' is
chosen, test dependencies are enabled for all packages in the environment.""",
)
subparser.add_argument(
"-q", "--quiet", action="store_true", help="don't print concretized specs"
"-q", "--quiet", action="store_true", help="Don't print concretized specs"
)
spack.cmd.common.arguments.add_concretizer_args(subparser)

View File

@@ -2,6 +2,8 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import print_function
import collections
import os
import shutil
@@ -42,7 +44,7 @@ def setup_parser(subparser):
get_parser = sp.add_parser("get", help="print configuration values")
get_parser.add_argument(
"section",
help="configuration section to print\n\noptions: %(choices)s",
help="configuration section to print. " "options: %(choices)s",
nargs="?",
metavar="section",
choices=spack.config.section_schemas,
@@ -53,7 +55,7 @@ def setup_parser(subparser):
)
blame_parser.add_argument(
"section",
help="configuration section to print\n\noptions: %(choices)s",
help="configuration section to print. " "options: %(choices)s",
metavar="section",
choices=spack.config.section_schemas,
)
@@ -61,7 +63,7 @@ def setup_parser(subparser):
edit_parser = sp.add_parser("edit", help="edit configuration file")
edit_parser.add_argument(
"section",
help="configuration section to edit\n\noptions: %(choices)s",
help="configuration section to edit. " "options: %(choices)s",
metavar="section",
nargs="?",
choices=spack.config.section_schemas,
@@ -76,7 +78,7 @@ def setup_parser(subparser):
add_parser.add_argument(
"path",
nargs="?",
help="colon-separated path to config that should be added, e.g. 'config:default:true'",
help="colon-separated path to config that should be added," " e.g. 'config:default:true'",
)
add_parser.add_argument("-f", "--file", help="file from which to set all config values")
@@ -88,7 +90,7 @@ def setup_parser(subparser):
"--local",
action="store_true",
default=False,
help="set packages preferences based on local installs, rather than upstream",
help="Set packages preferences based on local installs, rather " "than upstream.",
)
remove_parser = sp.add_parser("remove", aliases=["rm"], help="remove configuration parameters")
@@ -157,7 +159,7 @@ def config_get(args):
tty.die("environment has no %s file" % ev.manifest_name)
else:
tty.die("`spack config get` requires a section argument or an active environment.")
tty.die("`spack config get` requires a section argument " "or an active environment.")
def config_blame(args):
@@ -180,7 +182,7 @@ def config_edit(args):
# If we aren't editing a spack.yaml file, get config path from scope.
scope, section = _get_scope_and_section(args)
if not scope and not section:
tty.die("`spack config edit` requires a section argument or an active environment.")
tty.die("`spack config edit` requires a section argument " "or an active environment.")
config_file = spack.config.config.get_config_filename(scope, section)
if args.print_file:
@@ -374,7 +376,7 @@ def config_revert(args):
proceed = True
if not args.yes_to_all:
msg = "The following scopes will be restored from the corresponding backup files:\n"
msg = "The following scopes will be restored from the corresponding" " backup files:\n"
for entry in to_be_restored:
msg += "\t[scope={0.scope}, bkp={0.bkp}]\n".format(entry)
msg += "This operation cannot be undone."
@@ -399,8 +401,8 @@ def config_prefer_upstream(args):
if scope is None:
scope = spack.config.default_modify_scope("packages")
all_specs = set(spack.store.STORE.db.query(installed=True))
local_specs = set(spack.store.STORE.db.query_local(installed=True))
all_specs = set(spack.store.db.query(installed=True))
local_specs = set(spack.store.db.query_local(installed=True))
pref_specs = local_specs if args.local else all_specs - local_specs
conflicting_variants = set()

View File

@@ -10,7 +10,7 @@
import spack.container
import spack.container.images
description = "creates recipes to build images for different container runtimes"
description = "creates recipes to build images for different" " container runtimes"
section = "container"
level = "long"

View File

@@ -3,6 +3,8 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import print_function
import os
import re
import urllib.parse
@@ -69,7 +71,7 @@ class {class_name}({base_class_name}):
'''
class BundlePackageTemplate:
class BundlePackageTemplate(object):
"""
Provides the default values to be used for a bundle package file template.
"""
@@ -120,7 +122,7 @@ def install(self, spec, prefix):
url_line = ' url = "{url}"'
def __init__(self, name, url, versions):
super().__init__(name, versions)
super(PackageTemplate, self).__init__(name, versions)
self.url_def = self.url_line.format(url=url)
@@ -198,7 +200,7 @@ def __init__(self, name, url, *args, **kwargs):
# Make it more obvious that we are renaming the package
tty.msg("Changing package name from {0} to lua-{0}".format(name))
name = "lua-{0}".format(name)
super().__init__(name, url, *args, **kwargs)
super(LuaPackageTemplate, self).__init__(name, url, *args, **kwargs)
class MesonPackageTemplate(PackageTemplate):
@@ -306,7 +308,7 @@ def __init__(self, name, url, *args, **kwargs):
tty.msg("Changing package name from {0} to rkt-{0}".format(name))
name = "rkt-{0}".format(name)
self.body_def = self.body_def.format(name[4:])
super().__init__(name, url, *args, **kwargs)
super(RacketPackageTemplate, self).__init__(name, url, *args, **kwargs)
class PythonPackageTemplate(PackageTemplate):
@@ -325,7 +327,6 @@ class PythonPackageTemplate(PackageTemplate):
# FIXME: Add a build backend, usually defined in pyproject.toml. If no such file
# exists, use setuptools.
# depends_on("py-setuptools", type="build")
# depends_on("py-hatchling", type="build")
# depends_on("py-flit-core", type="build")
# depends_on("py-poetry-core", type="build")
@@ -333,11 +334,17 @@ class PythonPackageTemplate(PackageTemplate):
# depends_on("py-foo", type=("build", "run"))"""
body_def = """\
def config_settings(self, spec, prefix):
# FIXME: Add configuration settings to be passed to the build backend
def global_options(self, spec, prefix):
# FIXME: Add options to pass to setup.py
# FIXME: If not needed, delete this function
settings = {}
return settings"""
options = []
return options
def install_options(self, spec, prefix):
# FIXME: Add options to pass to setup.py install
# FIXME: If not needed, delete this function
options = []
return options"""
def __init__(self, name, url, *args, **kwargs):
# If the user provided `--name py-numpy`, don't rename it py-py-numpy
@@ -393,7 +400,7 @@ def __init__(self, name, url, *args, **kwargs):
+ self.url_line
)
super().__init__(name, url, *args, **kwargs)
super(PythonPackageTemplate, self).__init__(name, url, *args, **kwargs)
class RPackageTemplate(PackageTemplate):
@@ -432,7 +439,7 @@ def __init__(self, name, url, *args, **kwargs):
if bioc:
self.url_line = ' url = "{0}"\n' ' bioc = "{1}"'.format(url, r_name)
super().__init__(name, url, *args, **kwargs)
super(RPackageTemplate, self).__init__(name, url, *args, **kwargs)
class PerlmakePackageTemplate(PackageTemplate):
@@ -459,7 +466,7 @@ def __init__(self, name, *args, **kwargs):
tty.msg("Changing package name from {0} to perl-{0}".format(name))
name = "perl-{0}".format(name)
super().__init__(name, *args, **kwargs)
super(PerlmakePackageTemplate, self).__init__(name, *args, **kwargs)
class PerlbuildPackageTemplate(PerlmakePackageTemplate):
@@ -492,7 +499,7 @@ def __init__(self, name, *args, **kwargs):
tty.msg("Changing package name from {0} to octave-{0}".format(name))
name = "octave-{0}".format(name)
super().__init__(name, *args, **kwargs)
super(OctavePackageTemplate, self).__init__(name, *args, **kwargs)
class RubyPackageTemplate(PackageTemplate):
@@ -520,7 +527,7 @@ def __init__(self, name, *args, **kwargs):
tty.msg("Changing package name from {0} to ruby-{0}".format(name))
name = "ruby-{0}".format(name)
super().__init__(name, *args, **kwargs)
super(RubyPackageTemplate, self).__init__(name, *args, **kwargs)
class MakefilePackageTemplate(PackageTemplate):
@@ -565,7 +572,7 @@ def __init__(self, name, *args, **kwargs):
tty.msg("Changing package name from {0} to py-{0}".format(name))
name = "py-{0}".format(name)
super().__init__(name, *args, **kwargs)
super(SIPPackageTemplate, self).__init__(name, *args, **kwargs)
templates = {
@@ -607,7 +614,7 @@ def setup_parser(subparser):
"--template",
metavar="TEMPLATE",
choices=sorted(templates.keys()),
help="build system template to use\n\noptions: %(choices)s",
help="build system template to use. options: %(choices)s",
)
subparser.add_argument(
"-r", "--repo", help="path to a repository where the package should be created"
@@ -615,7 +622,7 @@ def setup_parser(subparser):
subparser.add_argument(
"-N",
"--namespace",
help="specify a namespace for the package\n\nmust be the namespace of "
help="specify a namespace for the package. must be the namespace of "
"a repository registered with Spack",
)
subparser.add_argument(
@@ -873,7 +880,7 @@ def get_build_system(template, url, guesser):
# Use whatever build system the guesser detected
selected_template = guesser.build_system
if selected_template == "generic":
tty.warn("Unable to detect a build system. Using a generic package template.")
tty.warn("Unable to detect a build system. " "Using a generic package template.")
else:
msg = "This package looks like it uses the {0} build system"
tty.msg(msg.format(selected_template))

View File

@@ -3,6 +3,8 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import print_function
import os
import platform
import re
@@ -60,16 +62,16 @@ def create_db_tarball(args):
tarball_name = "spack-db.%s.tar.gz" % _debug_tarball_suffix()
tarball_path = os.path.abspath(tarball_name)
base = os.path.basename(str(spack.store.STORE.root))
base = os.path.basename(str(spack.store.root))
transform_args = []
if "GNU" in tar("--version", output=str):
transform_args = ["--transform", "s/^%s/%s/" % (base, tarball_name)]
else:
transform_args = ["-s", "/^%s/%s/" % (base, tarball_name)]
wd = os.path.dirname(str(spack.store.STORE.root))
wd = os.path.dirname(str(spack.store.root))
with working_dir(wd):
files = [spack.store.STORE.db._index_path]
files = [spack.store.db._index_path]
files += glob("%s/*/*/*/.spack/spec.json" % base)
files += glob("%s/*/*/*/.spack/spec.yaml" % base)
files = [os.path.relpath(f) for f in files]

View File

@@ -26,8 +26,8 @@ def setup_parser(subparser):
"--installed",
action="store_true",
default=False,
help="list installed dependencies of an installed spec "
"instead of possible dependencies of a package",
help="List installed dependencies of an installed spec, "
"instead of possible dependencies of a package.",
)
subparser.add_argument(
"-t",
@@ -60,7 +60,7 @@ def dependencies(parser, args):
format_string = "{name}{@version}{%compiler}{/hash:7}"
if sys.stdout.isatty():
tty.msg("Dependencies of %s" % spec.format(format_string, color=True))
deps = spack.store.STORE.db.installed_relatives(
deps = spack.store.db.installed_relatives(
spec, "children", args.transitive, deptype=args.deptype
)
if deps:

View File

@@ -25,15 +25,15 @@ def setup_parser(subparser):
"--installed",
action="store_true",
default=False,
help="list installed dependents of an installed spec "
"instead of possible dependents of a package",
help="List installed dependents of an installed spec, "
"instead of possible dependents of a package.",
)
subparser.add_argument(
"-t",
"--transitive",
action="store_true",
default=False,
help="show all transitive dependents",
help="Show all transitive dependents.",
)
arguments.add_common_arguments(subparser, ["spec"])
@@ -96,7 +96,7 @@ def dependents(parser, args):
format_string = "{name}{@version}{%compiler}{/hash:7}"
if sys.stdout.isatty():
tty.msg("Dependents of %s" % spec.cformat(format_string))
deps = spack.store.STORE.db.installed_relatives(spec, "parents", args.transitive)
deps = spack.store.db.installed_relatives(spec, "parents", args.transitive)
if deps:
spack.cmd.display_specs(deps, long=True)
else:

View File

@@ -13,6 +13,8 @@
It is up to the user to ensure binary compatibility between the deprecated
installation and its deprecator.
"""
from __future__ import print_function
import argparse
import os
@@ -26,7 +28,7 @@
from spack.database import InstallStatuses
from spack.error import SpackError
description = "replace one package with another via symlinks"
description = "Replace one package with another via symlinks"
section = "admin"
level = "long"
@@ -46,7 +48,7 @@ def setup_parser(sp):
action="store_true",
default=True,
dest="dependencies",
help="deprecate dependencies (default)",
help="Deprecate dependencies (default)",
)
deps.add_argument(
"-D",
@@ -54,7 +56,7 @@ def setup_parser(sp):
action="store_false",
default=True,
dest="dependencies",
help="do not deprecate dependencies",
help="Do not deprecate dependencies",
)
install = sp.add_mutually_exclusive_group()
@@ -64,7 +66,7 @@ def setup_parser(sp):
action="store_true",
default=False,
dest="install",
help="concretize and install deprecator spec",
help="Concretize and install deprecator spec",
)
install.add_argument(
"-I",
@@ -72,7 +74,7 @@ def setup_parser(sp):
action="store_false",
default=False,
dest="install",
help="deprecator spec must already be installed (default)",
help="Deprecator spec must already be installed (default)",
)
sp.add_argument(
@@ -81,7 +83,7 @@ def setup_parser(sp):
type=str,
default="soft",
choices=["soft", "hard"],
help="type of filesystem link to use for deprecation (default soft)",
help="Type of filesystem link to use for deprecation (default soft)",
)
sp.add_argument(
@@ -130,7 +132,7 @@ def deprecate(parser, args):
already_deprecated = []
already_deprecated_for = []
for spec in all_deprecate:
deprecated_for = spack.store.STORE.db.deprecator(spec)
deprecated_for = spack.store.db.deprecator(spec)
if deprecated_for:
already_deprecated.append(spec)
already_deprecated_for.append(deprecated_for)

View File

@@ -25,14 +25,14 @@ def setup_parser(subparser):
"--source-path",
dest="source_path",
default=None,
help="path to source directory (defaults to the current directory)",
help="path to source directory. defaults to the current directory",
)
subparser.add_argument(
"-i",
"--ignore-dependencies",
action="store_true",
dest="ignore_deps",
help="do not try to install dependencies of requested packages",
help="don't try to install dependencies of requested packages",
)
arguments.add_common_arguments(subparser, ["no_checksum", "deprecated"])
subparser.add_argument(
@@ -55,13 +55,16 @@ def setup_parser(subparser):
type=str,
dest="shell",
default=None,
help="drop into a build environment in a new shell, e.g., bash",
help="drop into a build environment in a new shell, e.g. bash, zsh",
)
subparser.add_argument(
"--test",
default=None,
choices=["root", "all"],
help="run tests on only root packages or all packages",
help="""If 'root' is chosen, run package tests during
installation for top-level packages (but skip tests for dependencies).
if 'all' is chosen, run package tests during installation for all
packages. If neither are chosen, don't run tests for any packages.""",
)
arguments.add_common_arguments(subparser, ["spec"])

View File

@@ -20,7 +20,7 @@
def setup_parser(subparser):
subparser.add_argument("-p", "--path", help="source location of package")
subparser.add_argument("-p", "--path", help="Source location of package")
clone_group = subparser.add_mutually_exclusive_group()
clone_group.add_argument(
@@ -28,18 +28,18 @@ def setup_parser(subparser):
action="store_false",
dest="clone",
default=None,
help="do not clone, the package already exists at the source path",
help="Do not clone. The package already exists at the source path",
)
clone_group.add_argument(
"--clone",
action="store_true",
dest="clone",
default=None,
help="clone the package even if the path already exists",
help="Clone the package even if the path already exists",
)
subparser.add_argument(
"-f", "--force", help="remove any files or directories that block cloning source code"
"-f", "--force", help="Remove any files or directories that block cloning source code"
)
arguments.add_common_arguments(subparser, ["spec"])
@@ -66,7 +66,8 @@ def develop(parser, args):
# Both old syntax `spack develop pkg@x` and new syntax `spack develop pkg@=x`
# are currently supported.
spec = spack.spec.parse_with_version_concrete(entry["spec"])
env.develop(spec=spec, path=path, clone=True)
pkg_cls = spack.repo.path.get_pkg_class(spec.name)
pkg_cls(spec).stage.steal_source(abspath)
if not env.dev_specs:
tty.warn("No develop specs to download")

View File

@@ -29,7 +29,7 @@ def setup_parser(subparser):
action="store_true",
default=False,
dest="dump_json",
help="dump json output instead of pretty printing",
help="Dump json output instead of pretty printing.",
)
subparser.add_argument(
"--first",

View File

@@ -62,7 +62,7 @@ def setup_parser(subparser):
dest="path",
action="store_const",
const=spack.paths.build_systems_path,
help="edit the build system with the supplied name",
help="Edit the build system with the supplied name.",
)
excl_args.add_argument(
"-c",

View File

@@ -86,13 +86,6 @@ def env_activate_setup_parser(subparser):
const="bat",
help="print bat commands to activate the environment",
)
shells.add_argument(
"--pwsh",
action="store_const",
dest="shell",
const="pwsh",
help="print powershell commands to activate environment",
)
view_options = subparser.add_mutually_exclusive_group()
view_options.add_argument(
@@ -102,7 +95,7 @@ def env_activate_setup_parser(subparser):
dest="with_view",
const=True,
default=True,
help="update PATH, etc., with associated view",
help="update PATH etc. with associated view",
)
view_options.add_argument(
"-V",
@@ -111,7 +104,7 @@ def env_activate_setup_parser(subparser):
dest="with_view",
const=False,
default=True,
help="do not update PATH, etc., with associated view",
help="do not update PATH etc. with associated view",
)
subparser.add_argument(
@@ -161,7 +154,7 @@ def env_activate(args):
# Error out when -e, -E, -D flags are given, cause they are ambiguous.
if args.env or args.no_env or args.env_dir:
tty.die("Calling spack env activate with --env, --env-dir and --no-env is ambiguous")
tty.die("Calling spack env activate with --env, --env-dir and --no-env " "is ambiguous")
env_name_or_dir = args.activate_env or args.dir
@@ -250,7 +243,7 @@ def env_deactivate(args):
# Error out when -e, -E, -D flags are given, cause they are ambiguous.
if args.env or args.no_env or args.env_dir:
tty.die("Calling spack env deactivate with --env, --env-dir and --no-env is ambiguous")
tty.die("Calling spack env deactivate with --env, --env-dir and --no-env " "is ambiguous")
if ev.active_environment() is None:
tty.die("No environment is currently active.")
@@ -290,7 +283,7 @@ def env_create_setup_parser(subparser):
"envfile",
nargs="?",
default=None,
help="either a lockfile (must end with '.json' or '.lock') or a manifest file",
help="either a lockfile (must end with '.json' or '.lock') or a manifest file.",
)
@@ -418,7 +411,7 @@ def env_list(args):
colify(color_names, indent=4)
class ViewAction:
class ViewAction(object):
regenerate = "regenerate"
enable = "enable"
disable = "disable"
@@ -608,16 +601,16 @@ def env_depfile_setup_parser(subparser):
"--make-target-prefix",
default=None,
metavar="TARGET",
help="prefix Makefile targets (and variables) with <TARGET>/<name>\n\nby default "
help="prefix Makefile targets (and variables) with <TARGET>/<name>. By default "
"the absolute path to the directory makedeps under the environment metadata dir is "
"used. can be set to an empty string --make-prefix ''",
"used. Can be set to an empty string --make-prefix ''.",
)
subparser.add_argument(
"--make-disable-jobserver",
default=True,
action="store_false",
dest="jobserver",
help="disable POSIX jobserver support",
help="disable POSIX jobserver support.",
)
subparser.add_argument(
"--use-buildcache",
@@ -625,8 +618,8 @@ def env_depfile_setup_parser(subparser):
type=arguments.use_buildcache,
default="package:auto,dependencies:auto",
metavar="[{auto,only,never},][package:{auto,only,never},][dependencies:{auto,only,never}]",
help="when using `only`, redundant build dependencies are pruned from the DAG\n\n"
"this flag is passed on to the generated spack install commands",
help="When using `only`, redundant build dependencies are pruned from the DAG. "
"This flag is passed on to the generated spack install commands.",
)
subparser.add_argument(
"-o",
@@ -640,7 +633,7 @@ def env_depfile_setup_parser(subparser):
"--generator",
default="make",
choices=("make",),
help="specify the depfile type\n\ncurrently only make is supported",
help="specify the depfile type. Currently only make is supported.",
)
subparser.add_argument(
metavar="specs",

View File

@@ -22,7 +22,7 @@
def setup_parser(subparser):
subparser.epilog = (
"If called without argument returns the list of all valid extendable packages"
"If called without argument returns " "the list of all valid extendable packages"
)
arguments.add_common_arguments(subparser, ["long", "very_long"])
subparser.add_argument(
@@ -91,7 +91,7 @@ def extensions(parser, args):
if args.show in ("installed", "all"):
# List specs of installed extensions.
installed = [s.spec for s in spack.store.STORE.db.installed_extensions_for(spec)]
installed = [s.spec for s in spack.store.db.installed_extensions_for(spec)]
if args.show == "all":
print

View File

@@ -2,6 +2,8 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import print_function
import argparse
import errno
import os
@@ -42,7 +44,7 @@ def setup_parser(subparser):
"--path",
default=None,
action="append",
help="one or more alternative search paths for finding externals",
help="Alternative search paths for finding externals. May be repeated",
)
find_parser.add_argument(
"--scope",
@@ -66,8 +68,10 @@ def setup_parser(subparser):
read_cray_manifest = sp.add_parser(
"read-cray-manifest",
help="consume a Spack-compatible description of externally-installed packages, including "
"dependency relationships",
help=(
"consume a Spack-compatible description of externally-installed "
"packages, including dependency relationships"
),
)
read_cray_manifest.add_argument(
"--file", default=None, help="specify a location other than the default"
@@ -90,7 +94,7 @@ def setup_parser(subparser):
read_cray_manifest.add_argument(
"--fail-on-error",
action="store_true",
help="if a manifest file cannot be parsed, fail and report the full stack trace",
help=("if a manifest file cannot be parsed, fail and report the " "full stack trace"),
)
@@ -109,14 +113,14 @@ def external_find(args):
# For most exceptions, just print a warning and continue.
# Note that KeyboardInterrupt does not subclass Exception
# (so CTRL-C will terminate the program as expected).
skip_msg = "Skipping manifest and continuing with other external checks"
skip_msg = "Skipping manifest and continuing with other external " "checks"
if (isinstance(e, IOError) or isinstance(e, OSError)) and e.errno in [
errno.EPERM,
errno.EACCES,
]:
# The manifest file does not have sufficient permissions enabled:
# print a warning and keep going
tty.warn("Unable to read manifest due to insufficient permissions.", skip_msg)
tty.warn("Unable to read manifest due to insufficient " "permissions.", skip_msg)
else:
tty.warn("Unable to read manifest, unexpected error: {0}".format(str(e)), skip_msg)
@@ -166,7 +170,7 @@ def external_find(args):
)
if new_entries:
path = spack.config.config.get_config_filename(args.scope, "packages")
msg = "The following specs have been detected on this system and added to {0}"
msg = "The following specs have been detected on this system " "and added to {0}"
tty.msg(msg.format(path))
spack.cmd.display_specs(new_entries)
else:
@@ -234,7 +238,7 @@ def _collect_and_consume_cray_manifest_files(
if fail_on_error:
raise
else:
tty.warn("Failure reading manifest file: {0}\n\t{1}".format(path, str(e)))
tty.warn("Failure reading manifest file: {0}" "\n\t{1}".format(path, str(e)))
def external_list(args):

View File

@@ -10,7 +10,6 @@
import spack.config
import spack.environment as ev
import spack.repo
import spack.traverse
description = "fetch archives for packages"
section = "build"
@@ -37,12 +36,6 @@ def setup_parser(subparser):
def fetch(parser, args):
if args.no_checksum:
spack.config.set("config:checksum", False, scope="command_line")
if args.deprecated:
spack.config.set("config:deprecated", True, scope="command_line")
if args.specs:
specs = spack.cmd.parse_specs(args.specs, concretize=True)
else:
@@ -58,21 +51,24 @@ def fetch(parser, args):
else:
specs = env.all_specs()
if specs == []:
tty.die("No uninstalled specs in environment. Did you run `spack concretize` yet?")
tty.die(
"No uninstalled specs in environment. Did you " "run `spack concretize` yet?"
)
else:
tty.die("fetch requires at least one spec argument")
if args.dependencies or args.missing:
to_be_fetched = spack.traverse.traverse_nodes(specs, key=spack.traverse.by_dag_hash)
else:
to_be_fetched = specs
if args.no_checksum:
spack.config.set("config:checksum", False, scope="command_line")
for spec in to_be_fetched:
if args.missing and spec.installed:
continue
if args.deprecated:
spack.config.set("config:deprecated", True, scope="command_line")
pkg = spec.package
for spec in specs:
if args.missing or args.dependencies:
for s in spec.traverse(root=False):
# Skip already-installed packages with --missing
if args.missing and s.installed:
continue
pkg.stage.keep = True
with pkg.stage:
pkg.do_fetch()
s.package.do_fetch()
spec.package.do_fetch()

View File

@@ -3,6 +3,8 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import print_function
import copy
import sys
@@ -30,14 +32,6 @@ def setup_parser(subparser):
default=None,
help="output specs with the specified format string",
)
format_group.add_argument(
"-H",
"--hashes",
action="store_const",
dest="format",
const="{/hash}",
help="same as '--format {/hash}'; use with xargs or $()",
)
format_group.add_argument(
"--json",
action="store_true",

View File

@@ -20,7 +20,7 @@ def setup_parser(subparser):
def gc(parser, args):
specs = spack.store.STORE.db.unused_specs
specs = spack.store.db.unused_specs
# Restrict garbage collection to the active environment
# speculating over roots that are yet to be installed

View File

@@ -68,7 +68,7 @@ def setup_parser(subparser):
metavar="DEST",
type=str,
dest="secret",
help="export the private key to a file",
help="export the private key to a file.",
)
create.set_defaults(func=gpg_create)
@@ -86,7 +86,7 @@ def setup_parser(subparser):
export = subparsers.add_parser("export", help=gpg_export.__doc__)
export.add_argument("location", type=str, help="where to export keys")
export.add_argument(
"keys", nargs="*", help="the keys to export (all public keys if unspecified)"
"keys", nargs="*", help="the keys to export; " "all public keys if unspecified"
)
export.add_argument("--secret", action="store_true", help="export secret keys")
export.set_defaults(func=gpg_export)
@@ -99,29 +99,29 @@ def setup_parser(subparser):
"--directory",
metavar="directory",
type=str,
help="local directory where keys will be published",
help="local directory where keys will be published.",
)
output.add_argument(
"-m",
"--mirror-name",
metavar="mirror-name",
type=str,
help="name of the mirror where keys will be published",
help="name of the mirror where " + "keys will be published.",
)
output.add_argument(
"--mirror-url",
metavar="mirror-url",
type=str,
help="URL of the mirror where keys will be published",
help="URL of the mirror where " + "keys will be published.",
)
publish.add_argument(
"--rebuild-index",
action="store_true",
default=False,
help="regenerate buildcache key index after publishing key(s)",
help=("Regenerate buildcache key index " "after publishing key(s)"),
)
publish.add_argument(
"keys", nargs="*", help="keys to publish (all public keys if unspecified)"
"keys", nargs="*", help="the keys to publish; " "all public keys if unspecified"
)
publish.set_defaults(func=gpg_publish)
@@ -146,7 +146,7 @@ def gpg_create(args):
def gpg_export(args):
"""export a gpg key, optionally including secret key"""
"""export a gpg key, optionally including secret key."""
keys = args.keys
if not keys:
keys = spack.util.gpg.signing_keys()
@@ -168,7 +168,7 @@ def gpg_sign(args):
elif not keys:
raise RuntimeError("no signing keys are available")
else:
raise RuntimeError("multiple signing keys are available; please choose one")
raise RuntimeError("multiple signing keys are available; " "please choose one")
output = args.output
if not output:
output = args.spec[0] + ".asc"
@@ -216,7 +216,7 @@ def gpg_publish(args):
url = spack.util.url.path_to_file_url(args.directory)
mirror = spack.mirror.Mirror(url, url)
elif args.mirror_name:
mirror = spack.mirror.MirrorCollection(binary=True).lookup(args.mirror_name)
mirror = spack.mirror.MirrorCollection().lookup(args.mirror_name)
elif args.mirror_url:
mirror = spack.mirror.Mirror(args.mirror_url, args.mirror_url)

View File

@@ -63,7 +63,7 @@ def graph(parser, args):
if env:
specs = env.all_specs()
else:
specs = spack.store.STORE.db.query()
specs = spack.store.db.query()
else:
specs = spack.cmd.parse_specs(args.specs, concretize=not args.static)

View File

@@ -3,6 +3,8 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import print_function
import textwrap
from itertools import zip_longest
@@ -71,7 +73,7 @@ def variant(s):
return spack.spec.enabled_variant_color + s + plain_format
class VariantFormatter:
class VariantFormatter(object):
def __init__(self, variants):
self.variants = variants
self.headers = ("Name [Default]", "When", "Allowed values", "Description")

View File

@@ -75,9 +75,10 @@ def setup_parser(subparser):
default="package,dependencies",
dest="things_to_install",
choices=["package", "dependencies"],
help="select the mode of installation\n\n"
"default is to install the package along with all its dependencies. "
"alternatively, one can decide to install only the package or only the dependencies",
help="""select the mode of installation.
the default is to install the package along with all its dependencies.
alternatively one can decide to install only the package or only
the dependencies""",
)
subparser.add_argument(
"-u",
@@ -142,11 +143,12 @@ def setup_parser(subparser):
type=arguments.use_buildcache,
default="package:auto,dependencies:auto",
metavar="[{auto,only,never},][package:{auto,only,never},][dependencies:{auto,only,never}]",
help="select the mode of buildcache for the 'package' and 'dependencies'\n\n"
"default: package:auto,dependencies:auto\n\n"
"- `auto` behaves like --use-cache\n"
"- `only` behaves like --cache-only\n"
"- `never` behaves like --no-cache",
help="""select the mode of buildcache for the 'package' and 'dependencies'.
Default: package:auto,dependencies:auto
- `auto` behaves like --use-cache
- `only` behaves like --cache-only
- `never` behaves like --no-cache
""",
)
subparser.add_argument(
@@ -154,8 +156,8 @@ def setup_parser(subparser):
action="store_true",
dest="include_build_deps",
default=False,
help="include build deps when installing from cache, "
"useful for CI pipeline troubleshooting",
help="""include build deps when installing from cache,
which is useful for CI pipeline troubleshooting""",
)
subparser.add_argument(
@@ -184,7 +186,7 @@ def setup_parser(subparser):
dest="install_verbose",
help="display verbose build output while installing",
)
subparser.add_argument("--fake", action="store_true", help="fake install for debug purposes")
subparser.add_argument("--fake", action="store_true", help="fake install for debug purposes.")
subparser.add_argument(
"--only-concrete",
action="store_true",
@@ -197,13 +199,14 @@ def setup_parser(subparser):
"--add",
action="store_true",
default=False,
help="(with environment) add spec to the environment as a root",
help="""(with environment) add spec to the environment as a root.""",
)
updateenv_group.add_argument(
"--no-add",
action="store_false",
dest="add",
help="(with environment) do not add spec to the environment as a root",
help="""(with environment) do not add spec to the environment as a
root (the default behavior).""",
)
subparser.add_argument(
@@ -213,7 +216,7 @@ def setup_parser(subparser):
default=[],
dest="specfiles",
metavar="SPEC_YAML_FILE",
help="read specs to install from .yaml files",
help="install from file. Read specs to install from .yaml files",
)
cd_group = subparser.add_mutually_exclusive_group()
@@ -224,12 +227,19 @@ def setup_parser(subparser):
"--test",
default=None,
choices=["root", "all"],
help="run tests on only root packages or all packages",
help="""If 'root' is chosen, run package tests during
installation for top-level packages (but skip tests for dependencies).
if 'all' is chosen, run package tests during installation for all
packages. If neither are chosen, don't run tests for any packages.""",
)
arguments.add_common_arguments(subparser, ["log_format"])
subparser.add_argument("--log-file", default=None, help="filename for the log file")
subparser.add_argument(
"--help-cdash", action="store_true", help="show usage instructions for CDash reporting"
"--log-file",
default=None,
help="filename for the log file. if not passed a default will be used",
)
subparser.add_argument(
"--help-cdash", action="store_true", help="Show usage instructions for CDash reporting"
)
arguments.add_cdash_args(subparser, False)
arguments.add_common_arguments(subparser, ["yes_to_all", "spec"])
@@ -266,11 +276,11 @@ def require_user_confirmation_for_overwrite(concrete_specs, args):
if args.yes_to_all:
return
installed = list(filter(lambda x: x, map(spack.store.STORE.db.query_one, concrete_specs)))
installed = list(filter(lambda x: x, map(spack.store.db.query_one, concrete_specs)))
display_args = {"long": True, "show_flags": True, "variants": True}
if installed:
tty.msg("The following package specs will be reinstalled:\n")
tty.msg("The following package specs will be " "reinstalled:\n")
spack.cmd.display_specs(installed, **display_args)
not_installed = list(filter(lambda x: x not in installed, concrete_specs))

View File

@@ -3,6 +3,8 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import print_function
import os
import re
from collections import defaultdict
@@ -100,7 +102,7 @@ def list_files(args):
]
class LicenseError:
class LicenseError(object):
def __init__(self):
self.error_counts = defaultdict(int)

View File

@@ -3,6 +3,8 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import division, print_function
import argparse
import fnmatch
import json

View File

@@ -66,9 +66,10 @@ def setup_parser(subparser):
default="package,dependencies",
dest="things_to_load",
choices=["package", "dependencies"],
help="select whether to load the package and its dependencies\n\n"
"the default is to load the package and all dependencies. alternatively, "
"one can decide to load only the package or only the dependencies",
help="""select whether to load the package and its dependencies
the default is to load the package and all dependencies
alternatively one can decide to load only the package or only
the dependencies""",
)
subparser.add_argument(
@@ -101,7 +102,7 @@ def load(parser, args):
)
return 1
with spack.store.STORE.db.read_transaction():
with spack.store.db.read_transaction():
if "dependencies" in args.things_to_load:
include_roots = "package" in args.things_to_load
specs = [

View File

@@ -3,6 +3,8 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import print_function
import os
import llnl.util.tty as tty
@@ -55,13 +57,13 @@ def setup_parser(subparser):
directories.add_argument(
"--source-dir",
action="store_true",
help="source directory for a spec (requires it to be staged first)",
help="source directory for a spec " "(requires it to be staged first)",
)
directories.add_argument(
"-b",
"--build-dir",
action="store_true",
help="build directory for a spec (requires it to be staged first)",
help="build directory for a spec " "(requires it to be staged first)",
)
directories.add_argument(
"-e",
@@ -162,7 +164,7 @@ def location(parser, args):
# source dir remains, which requires the spec to be staged
if not pkg.stage.expanded:
tty.die(
"Source directory does not exist yet. Run this to create it:",
"Source directory does not exist yet. " "Run this to create it:",
"spack stage " + " ".join(args.spec),
)

View File

@@ -3,6 +3,8 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import print_function
import argparse
from collections import defaultdict

View File

@@ -39,7 +39,7 @@ def line_to_rtf(str):
def setup_parser(subparser):
spack_source_group = subparser.add_mutually_exclusive_group(required=True)
spack_source_group.add_argument(
"-v", "--spack-version", default="", help="download given spack version"
"-v", "--spack-version", default="", help="download given spack version e.g. 0.16.0"
)
spack_source_group.add_argument(
"-s", "--spack-source", default="", help="full path to spack source"
@@ -49,8 +49,9 @@ def setup_parser(subparser):
"-g",
"--git-installer-verbosity",
default="",
choices=["SILENT", "VERYSILENT"],
help="level of verbosity provided by bundled git installer (default is fully verbose)",
choices=set(["SILENT", "VERYSILENT"]),
help="Level of verbosity provided by bundled Git Installer.\
Default is fully verbose",
required=False,
action="store",
dest="git_verbosity",

View File

@@ -3,6 +3,8 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import print_function
import sys
from llnl.util import tty
@@ -35,7 +37,10 @@ def setup_parser(subparser):
"--all",
action="store_true",
dest="all",
help="mark ALL installed packages that match each supplied spec",
help="Mark ALL installed packages that match each "
"supplied spec. If you `mark --all libelf`,"
" ALL versions of `libelf` are marked. If no spec is "
"supplied, all installed packages will be marked.",
)
exim = subparser.add_mutually_exclusive_group(required=True)
exim.add_argument(
@@ -43,14 +48,14 @@ def setup_parser(subparser):
"--explicit",
action="store_true",
dest="explicit",
help="mark packages as explicitly installed",
help="Mark packages as explicitly installed.",
)
exim.add_argument(
"-i",
"--implicit",
action="store_true",
dest="implicit",
help="mark packages as implicitly installed",
help="Mark packages as implicitly installed.",
)
@@ -71,7 +76,7 @@ def find_matching_specs(specs, allow_multiple_matches=False):
for spec in specs:
install_query = [InstallStatuses.INSTALLED]
matching = spack.store.STORE.db.query_local(spec, installed=install_query)
matching = spack.store.db.query_local(spec, installed=install_query)
# For each spec provided, make sure it refers to only one package.
# Fail and ask user to be unambiguous if it doesn't
if not allow_multiple_matches and len(matching) > 1:
@@ -102,7 +107,7 @@ def do_mark(specs, explicit):
explicit (bool): whether to mark specs as explicitly installed
"""
for spec in specs:
spack.store.STORE.db.update_explicit(spec, explicit)
spack.store.db.update_explicit(spec, explicit)
def mark_specs(args, specs):

View File

@@ -21,6 +21,7 @@
import spack.util.path
import spack.util.web as web_util
from spack.error import SpackError
from spack.util.spack_yaml import syaml_dict
description = "manage mirrors (source and binary)"
section = "config"
@@ -54,13 +55,13 @@ def setup_parser(subparser):
)
create_parser.add_argument(
"--exclude-specs",
help="specs which Spack should not try to add to a mirror (specified on command line)",
help="specs which Spack should not try to add to a mirror" " (specified on command line)",
)
create_parser.add_argument(
"--skip-unstable-versions",
action="store_true",
help="don't cache versions unless they identify a stable (unchanging) source code",
help="don't cache versions unless they identify a stable (unchanging)" " source code",
)
create_parser.add_argument(
"-D", "--dependencies", action="store_true", help="also fetch all dependencies"
@@ -103,15 +104,6 @@ def setup_parser(subparser):
default=spack.config.default_modify_scope(),
help="configuration scope to modify",
)
add_parser.add_argument(
"--type",
action="append",
choices=("binary", "source"),
help=(
"specify the mirror type: for both binary "
"and source use `--type binary --type source` (default)"
),
)
arguments.add_s3_connection_args(add_parser, False)
# Remove
remove_parser = sp.add_parser("remove", aliases=["rm"], help=mirror_remove.__doc__)
@@ -128,12 +120,8 @@ def setup_parser(subparser):
set_url_parser = sp.add_parser("set-url", help=mirror_set_url.__doc__)
set_url_parser.add_argument("name", help="mnemonic name for mirror", metavar="mirror")
set_url_parser.add_argument("url", help="url of mirror directory from 'spack mirror create'")
set_url_push_or_fetch = set_url_parser.add_mutually_exclusive_group(required=False)
set_url_push_or_fetch.add_argument(
"--push", action="store_true", help="set only the URL used for uploading"
)
set_url_push_or_fetch.add_argument(
"--fetch", action="store_true", help="set only the URL used for downloading"
set_url_parser.add_argument(
"--push", action="store_true", help="set only the URL used for uploading new packages"
)
set_url_parser.add_argument(
"--scope",
@@ -144,35 +132,6 @@ def setup_parser(subparser):
)
arguments.add_s3_connection_args(set_url_parser, False)
# Set
set_parser = sp.add_parser("set", help=mirror_set.__doc__)
set_parser.add_argument("name", help="mnemonic name for mirror", metavar="mirror")
set_parser_push_or_fetch = set_parser.add_mutually_exclusive_group(required=False)
set_parser_push_or_fetch.add_argument(
"--push", action="store_true", help="modify just the push connection details"
)
set_parser_push_or_fetch.add_argument(
"--fetch", action="store_true", help="modify just the fetch connection details"
)
set_parser.add_argument(
"--type",
action="append",
choices=("binary", "source"),
help=(
"specify the mirror type: for both binary "
"and source use `--type binary --type source`"
),
)
set_parser.add_argument("--url", help="url of mirror directory from 'spack mirror create'")
set_parser.add_argument(
"--scope",
choices=scopes,
metavar=scopes_metavar,
default=spack.config.default_modify_scope(),
help="configuration scope to modify",
)
arguments.add_s3_connection_args(set_parser, False)
# List
list_parser = sp.add_parser("list", help=mirror_list.__doc__)
list_parser.add_argument(
@@ -185,85 +144,105 @@ def setup_parser(subparser):
def mirror_add(args):
"""add a mirror to Spack"""
"""Add a mirror to Spack."""
if (
args.s3_access_key_id
or args.s3_access_key_secret
or args.s3_access_token
or args.s3_profile
or args.s3_endpoint_url
or args.type
):
connection = {"url": args.url}
if args.s3_access_key_id and args.s3_access_key_secret:
connection["access_pair"] = [args.s3_access_key_id, args.s3_access_key_secret]
connection["access_pair"] = (args.s3_access_key_id, args.s3_access_key_secret)
if args.s3_access_token:
connection["access_token"] = args.s3_access_token
if args.s3_profile:
connection["profile"] = args.s3_profile
if args.s3_endpoint_url:
connection["endpoint_url"] = args.s3_endpoint_url
if args.type:
connection["binary"] = "binary" in args.type
connection["source"] = "source" in args.type
mirror = spack.mirror.Mirror(connection, name=args.name)
mirror = spack.mirror.Mirror(fetch_url=connection, push_url=connection, name=args.name)
else:
mirror = spack.mirror.Mirror(args.url, name=args.name)
spack.mirror.add(mirror, args.scope)
def mirror_remove(args):
"""remove a mirror by name"""
"""Remove a mirror by name."""
spack.mirror.remove(args.name, args.scope)
def _configure_mirror(args):
def mirror_set_url(args):
"""Change the URL of a mirror."""
url = args.url
mirrors = spack.config.get("mirrors", scope=args.scope)
if not mirrors:
mirrors = syaml_dict()
if args.name not in mirrors:
tty.die(f"No mirror found with name {args.name}.")
tty.die("No mirror found with name %s." % args.name)
entry = spack.mirror.Mirror(mirrors[args.name], args.name)
direction = "fetch" if args.fetch else "push" if args.push else None
changes = {}
if args.url:
changes["url"] = args.url
if args.s3_access_key_id and args.s3_access_key_secret:
changes["access_pair"] = [args.s3_access_key_id, args.s3_access_key_secret]
if args.s3_access_token:
changes["access_token"] = args.s3_access_token
if args.s3_profile:
changes["profile"] = args.s3_profile
if args.s3_endpoint_url:
changes["endpoint_url"] = args.s3_endpoint_url
entry = mirrors[args.name]
key_values = ["s3_access_key_id", "s3_access_token", "s3_profile"]
# argparse cannot distinguish between --binary and --no-binary when same dest :(
# notice that set-url does not have these args, so getattr
if getattr(args, "type", None):
changes["binary"] = "binary" in args.type
changes["source"] = "source" in args.type
if any(value for value in key_values if value in args):
incoming_data = {
"url": url,
"access_pair": (args.s3_access_key_id, args.s3_access_key_secret),
"access_token": args.s3_access_token,
"profile": args.s3_profile,
"endpoint_url": args.s3_endpoint_url,
}
try:
fetch_url = entry["fetch"]
push_url = entry["push"]
except TypeError:
fetch_url, push_url = entry, entry
changed = entry.update(changes, direction)
changes_made = False
if changed:
mirrors[args.name] = entry.to_dict()
spack.config.set("mirrors", mirrors, scope=args.scope)
if args.push:
if isinstance(push_url, dict):
changes_made = changes_made or push_url != incoming_data
push_url = incoming_data
else:
changes_made = changes_made or push_url != url
push_url = url
else:
if isinstance(push_url, dict):
changes_made = changes_made or push_url != incoming_data or push_url != incoming_data
fetch_url, push_url = incoming_data, incoming_data
else:
changes_made = changes_made or push_url != url
fetch_url, push_url = url, url
items = [
(
(n, u)
if n != args.name
else (
(n, {"fetch": fetch_url, "push": push_url})
if fetch_url != push_url
else (n, {"fetch": fetch_url, "push": fetch_url})
)
)
for n, u in mirrors.items()
]
mirrors = syaml_dict(items)
spack.config.set("mirrors", mirrors, scope=args.scope)
if changes_made:
tty.msg(
"Changed%s url or connection information for mirror %s."
% ((" (push)" if args.push else ""), args.name)
)
else:
tty.msg("No changes made to mirror %s." % args.name)
def mirror_set(args):
"""configure the connection details of a mirror"""
_configure_mirror(args)
def mirror_set_url(args):
"""change the URL of a mirror"""
_configure_mirror(args)
def mirror_list(args):
"""print out available mirrors to the console"""
"""Print out available mirrors to the console."""
mirrors = spack.mirror.MirrorCollection(scope=args.scope)
if not mirrors:
@@ -416,7 +395,9 @@ def process_mirror_stats(present, mirrored, error):
def mirror_create(args):
"""create a directory to be used as a spack mirror, and fill it with package archives"""
"""Create a directory to be used as a spack mirror, and fill it with
package archives.
"""
if args.specs and args.all:
raise SpackError(
"cannot specify specs on command line if you chose to mirror all specs with '--all'"
@@ -489,7 +470,7 @@ def create_mirror_for_all_specs_inside_environment(path, skip_unstable_versions,
def mirror_destroy(args):
"""given a url, recursively delete everything under it"""
"""Given a url, recursively delete everything under it."""
mirror_url = None
if args.mirror_name:
@@ -509,7 +490,6 @@ def mirror(parser, args):
"remove": mirror_remove,
"rm": mirror_remove,
"set-url": mirror_set_url,
"set": mirror_set,
"list": mirror_list,
}

View File

@@ -11,7 +11,6 @@
import sys
from llnl.util import filesystem, tty
from llnl.util.tty import color
import spack.cmd
import spack.cmd.common.arguments as arguments
@@ -32,7 +31,7 @@ def setup_parser(subparser):
action="store",
dest="module_set_name",
default="default",
help="named module set to use from modules configuration",
help="Named module set to use from modules configuration.",
)
sp = subparser.add_subparsers(metavar="SUBCOMMAND", dest="subparser_name")
@@ -348,20 +347,14 @@ def refresh(module_type, specs, args):
spack.modules.common.generate_module_index(
module_type_root, writers, overwrite=args.delete_tree
)
errors = []
for x in writers:
try:
x.write(overwrite=True)
except spack.error.SpackError as e:
msg = f"{x.layout.filename}: {e.message}"
errors.append(msg)
except Exception as e:
msg = f"{x.layout.filename}: {str(e)}"
errors.append(msg)
if errors:
errors.insert(0, color.colorize("@*{some module files could not be written}"))
tty.warn("\n".join(errors))
tty.debug(e)
msg = "Could not write module file [{0}]"
tty.warn(msg.format(x.layout.filename))
tty.warn("\t--> {0} <--".format(str(e)))
#: Dictionary populated with the list of sub-commands.
@@ -375,9 +368,7 @@ def refresh(module_type, specs, args):
def modules_cmd(parser, args, module_type, callbacks=callbacks):
# Qualifiers to be used when querying the db for specs
constraint_qualifiers = {
"refresh": {"installed": True, "known": lambda x: not spack.repo.path.exists(x)}
}
constraint_qualifiers = {"refresh": {"installed": True, "known": True}}
query_args = constraint_qualifiers.get(args.subparser_name, {})
# Get the specs that match the query from the DB

View File

@@ -30,7 +30,7 @@ def add_command(parser, command_dict):
def setdefault(module_type, specs, args):
"""set the default module file, when multiple are present"""
"""Set the default module file, when multiple are present"""
# For details on the underlying mechanism see:
#
# https://lmod.readthedocs.io/en/latest/060_locating.html#marking-a-version-as-default

View File

@@ -29,7 +29,7 @@ def add_command(parser, command_dict):
def setdefault(module_type, specs, args):
"""set the default module file, when multiple are present"""
"""Set the default module file, when multiple are present"""
# Currently, accepts only a single matching spec
spack.cmd.modules.one_spec_or_raise(specs)
spec = specs[0]

View File

@@ -7,11 +7,7 @@
import spack.cmd
import spack.cmd.common.arguments as arguments
import spack.config
import spack.environment as ev
import spack.package_base
import spack.repo
import spack.traverse
description = "patch expanded archive sources in preparation for install"
section = "build"
@@ -25,10 +21,7 @@ def setup_parser(subparser):
def patch(parser, args):
if not args.specs:
env = ev.active_environment()
if not env:
tty.die("`spack patch` requires a spec or an active environment")
return _patch_env(env)
tty.die("patch requires at least one spec argument")
if args.no_checksum:
spack.config.set("config:checksum", False, scope="command_line")
@@ -36,19 +29,6 @@ def patch(parser, args):
if args.deprecated:
spack.config.set("config:deprecated", True, scope="command_line")
specs = spack.cmd.parse_specs(args.specs, concretize=False)
specs = spack.cmd.parse_specs(args.specs, concretize=True)
for spec in specs:
_patch(spack.cmd.matching_spec_from_env(spec).package)
def _patch_env(env: ev.Environment):
tty.msg(f"Patching specs from environment {env.name}")
for spec in spack.traverse.traverse_nodes(env.concrete_roots()):
_patch(spec.package)
def _patch(pkg: spack.package_base.PackageBase):
pkg.stage.keep = True
with pkg.stage:
pkg.do_patch()
tty.msg(f"Patched {pkg.name} in {pkg.stage.path}")
spec.package.do_patch()

View File

@@ -3,6 +3,8 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import print_function
import argparse
import itertools
import os
@@ -58,7 +60,7 @@ def setup_parser(subparser):
"--type",
action="store",
default="C",
help="types of changes to show (A: added, R: removed, C: changed); default is 'C'",
help="Types of changes to show (A: added, R: removed, " "C: changed); default is 'C'",
)
rm_parser = sp.add_parser("removed", help=pkg_removed.__doc__)
@@ -81,7 +83,7 @@ def setup_parser(subparser):
"--canonical",
action="store_true",
default=False,
help="dump canonical source as used by package hash",
help="dump canonical source as used by package hash.",
)
arguments.add_common_arguments(source_parser, ["spec"])

View File

@@ -17,7 +17,9 @@
def setup_parser(subparser):
subparser.epilog = "If called without argument returns the list of all valid virtual packages"
subparser.epilog = (
"If called without argument returns " "the list of all valid virtual packages"
)
subparser.add_argument(
"virtual_package", nargs="*", help="find packages that provide this virtual package"
)

View File

@@ -3,6 +3,8 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import print_function
import argparse
import code
import os

Some files were not shown because too many files have changed in this diff Show More