diff --git a/dedal/docs/resources/dedal_UML.png b/dedal/docs/resources/dedal_UML.png
index 6e970d08690dc6f53f95c5088ad3933b9bae6f15..65d9f15828d9c6e19f8bffee41d5db3e02307b3f 100644
Binary files a/dedal/docs/resources/dedal_UML.png and b/dedal/docs/resources/dedal_UML.png differ
diff --git a/dedal/error_handling/exceptions.py b/dedal/error_handling/exceptions.py
index 85398df060dc8248372d273c2f6f35d65cf16696..055768f82555a38b16ce87f96d5d441b11b6be45 100644
--- a/dedal/error_handling/exceptions.py
+++ b/dedal/error_handling/exceptions.py
@@ -66,3 +66,8 @@ class SpackConfigException(BashCommandException):
"""
To be thrown when the spack config command fails
"""
+
+class SpackFindException(BashCommandException):
+ """
+ To be thrown when the spack find command fails
+ """
\ No newline at end of file
diff --git a/dedal/spack_factory/SpackOperation.py b/dedal/spack_factory/SpackOperation.py
index 8eaf3ef54a584147a81377ac5d0f20f9aedf97e0..e190b6ef6e6742a4cdca237cfff887bec7031e30 100644
--- a/dedal/spack_factory/SpackOperation.py
+++ b/dedal/spack_factory/SpackOperation.py
@@ -6,7 +6,7 @@ from dedal.configuration.SpackConfig import SpackConfig
from dedal.enum.SpackConfigCommand import SpackConfigCommand
from dedal.error_handling.exceptions import BashCommandException, NoSpackEnvironmentException, \
SpackInstallPackagesException, SpackConcertizeException, SpackMirrorException, SpackGpgException, \
- SpackRepoException, SpackReindexException, SpackSpecException, SpackConfigException
+ SpackRepoException, SpackReindexException, SpackSpecException, SpackConfigException, SpackFindException
from dedal.logger.logger_builder import get_logger
from dedal.tests.testing_variables import SPACK_VERSION
from dedal.utils.utils import run_command, git_clone_repo, log_command, set_bashrc_variable, get_first_word
@@ -363,6 +363,33 @@ class SpackOperation:
log_command(install_result, str(Path(os.getcwd()).resolve() / ".generate_cache.log"))
return install_result
+ @check_spack_env
+ def find_packages(self):
+ """Returns a dictionary of installed Spack packages in the current environment.
+ Each key is the name of a Spack package, and the corresponding value is a list of
+ installed versions for that package.
+ Raises:
+ NoSpackEnvironmentException: If the spack environment is not set up.
+ """
+ packages = run_command("bash", "-c",
+ f'{self.spack_command_on_env} && spack find -c',
+ check=True,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ text=True,
+ logger=self.logger,
+ info_msg=f'Listing installed packages.',
+ exception_msg=f'Failed to list installed packages',
+ exception=SpackFindException).stdout
+ dict_packages = {}
+ for package in packages.strip().splitlines():
+ if package.startswith('[+]'):
+ package = package.replace('@', ' ').split()
+ if len(package) == 3:
+ _, name, version = package
+ dict_packages.setdefault(name, []).append(version)
+ return dict_packages
+
def install_spack(self, spack_version=f'{SPACK_VERSION}', spack_repo='https://github.com/spack/spack',
bashrc_path=os.path.expanduser("~/.bashrc")):
"""Install spack.
diff --git a/dedal/tests/integration_tests/helper.py b/dedal/tests/integration_tests/helper.py
new file mode 100644
index 0000000000000000000000000000000000000000..58f9e0b1968e7f83afb289aa6bc3df050a809e97
--- /dev/null
+++ b/dedal/tests/integration_tests/helper.py
@@ -0,0 +1,12 @@
+from pathlib import Path
+
+from dedal.spack_factory.SpackOperation import SpackOperation
+from dedal.utils.spack_utils import extract_spack_packages
+from dedal.utils.variables import test_spack_env_name
+
+
+def check_installed_spack_packages(spack_operation: SpackOperation, install_dir: Path):
+ to_install_spack_packages = extract_spack_packages(str(install_dir / test_spack_env_name / 'spack.yaml'))
+ installed_spack_packages = list(spack_operation.find_packages().keys())
+ for spack_pacakge in to_install_spack_packages:
+ assert spack_pacakge in installed_spack_packages
diff --git a/dedal/tests/integration_tests/spack_create_cache_test.py b/dedal/tests/integration_tests/spack_create_cache_test.py
index 2ae70502e0db3548ba08ded83645bec7957ea773..1da51128a873222796753bfe2d81cbd5506777f6 100644
--- a/dedal/tests/integration_tests/spack_create_cache_test.py
+++ b/dedal/tests/integration_tests/spack_create_cache_test.py
@@ -8,6 +8,7 @@ from dedal.configuration.SpackConfig import SpackConfig
from dedal.model.SpackDescriptor import SpackDescriptor
from dedal.spack_factory.SpackOperationCreateCache import SpackOperationCreateCache
from dedal.spack_factory.SpackOperationCreator import SpackOperationCreator
+from dedal.tests.integration_tests.helper import check_installed_spack_packages
from dedal.tests.testing_variables import test_spack_env_git, ebrains_spack_builds_git
"""
@@ -43,3 +44,4 @@ def test_spack_create_cache_installation(tmp_path):
spack_operation = test_spack_create_cache_concretization(tmp_path)
spack_operation.install_packages()
assert len(spack_operation.build_cache.list_tags()) > 0
+ check_installed_spack_packages(spack_operation, tmp_path)
diff --git a/dedal/tests/integration_tests/spack_from_cache_test.py b/dedal/tests/integration_tests/spack_from_cache_test.py
index 42ecf3b7bf52c478299e41f270fa0d58000f5ec6..9bf2991a240e94f5b1ee2b3e67248a67735b07c8 100644
--- a/dedal/tests/integration_tests/spack_from_cache_test.py
+++ b/dedal/tests/integration_tests/spack_from_cache_test.py
@@ -4,6 +4,7 @@ from dedal.configuration.SpackConfig import SpackConfig
from dedal.model.SpackDescriptor import SpackDescriptor
from dedal.spack_factory.SpackOperationCreator import SpackOperationCreator
from dedal.spack_factory.SpackOperationUseCache import SpackOperationUseCache
+from dedal.tests.integration_tests.helper import check_installed_spack_packages
from dedal.utils.utils import file_exists_and_not_empty, count_files_in_folder
from dedal.utils.variables import test_spack_env_git, ebrains_spack_builds_git
@@ -41,9 +42,11 @@ def test_spack_from_cache_install_1(tmp_path):
spack_operation = test_spack_from_cache_concretize(tmp_path)
install_result = spack_operation.install_packages(jobs=2, signed=True, debug=False)
assert install_result.returncode == 0
+ check_installed_spack_packages(spack_operation, tmp_path)
def test_spack_from_cache_install_2(tmp_path):
spack_operation = test_spack_from_cache_concretize(tmp_path)
install_result = spack_operation.install_packages(jobs=2, signed=True, debug=False, test='root')
assert install_result.returncode == 0
+ check_installed_spack_packages(spack_operation, tmp_path)
diff --git a/dedal/tests/integration_tests/spack_from_scratch_test.py b/dedal/tests/integration_tests/spack_from_scratch_test.py
index 50b19faab603232f0ebc9d19503b5303e16604ff..bed34df1d7cce9121e9263b92f22f06d6294fe81 100644
--- a/dedal/tests/integration_tests/spack_from_scratch_test.py
+++ b/dedal/tests/integration_tests/spack_from_scratch_test.py
@@ -4,6 +4,7 @@ from dedal.configuration.SpackConfig import SpackConfig
from dedal.error_handling.exceptions import BashCommandException, NoSpackEnvironmentException
from dedal.spack_factory.SpackOperationCreator import SpackOperationCreator
from dedal.model.SpackDescriptor import SpackDescriptor
+from dedal.tests.integration_tests.helper import check_installed_spack_packages
from dedal.tests.testing_variables import test_spack_env_git, ebrains_spack_builds_git
from dedal.utils.utils import file_exists_and_not_empty
from dedal.spack_factory.SpackOperation import SpackOperation
@@ -49,6 +50,7 @@ def test_spack_reindex(tmp_path):
spack_operation.install_spack(bashrc_path=str(tmp_path / Path('.bashrc')))
spack_operation.reindex()
+
@pytest.mark.skip(reason="It does nopt work on bare metal operating systems")
def test_spack_spec(tmp_path):
install_dir = tmp_path
@@ -216,19 +218,20 @@ def test_spack_from_scratch_concretize_7(tmp_path):
concretization_file_path = spack_operation.env_path / 'spack.lock'
assert file_exists_and_not_empty(concretization_file_path) == True
- def test_spack_from_scratch_concretize_8(tmp_path):
- install_dir = tmp_path
- env = SpackDescriptor('test-spack-env', install_dir, test_spack_env_git)
- repo = SpackDescriptor('ebrains-spack-builds', install_dir, ebrains_spack_builds_git)
- config = SpackConfig(env=env, install_dir=install_dir)
- config.add_repo(repo)
- spack_operation = SpackOperationCreator.get_spack_operator(config)
- assert isinstance(spack_operation, SpackOperation)
- spack_operation.install_spack(bashrc_path=str(tmp_path / Path('.bashrc')))
- spack_operation.setup_spack_env()
- spack_operation.concretize_spack_env(force=True, test='root')
- concretization_file_path = spack_operation.env_path / 'spack.lock'
- assert file_exists_and_not_empty(concretization_file_path) == True
+
+def test_spack_from_scratch_concretize_8(tmp_path):
+ install_dir = tmp_path
+ env = SpackDescriptor('test-spack-env', install_dir, test_spack_env_git)
+ repo = SpackDescriptor('ebrains-spack-builds', install_dir, ebrains_spack_builds_git)
+ config = SpackConfig(env=env, install_dir=install_dir)
+ config.add_repo(repo)
+ spack_operation = SpackOperationCreator.get_spack_operator(config)
+ assert isinstance(spack_operation, SpackOperation)
+ spack_operation.install_spack(bashrc_path=str(tmp_path / Path('.bashrc')))
+ spack_operation.setup_spack_env()
+ spack_operation.concretize_spack_env(force=True, test='root')
+ concretization_file_path = spack_operation.env_path / 'spack.lock'
+ assert file_exists_and_not_empty(concretization_file_path) == True
def test_spack_from_scratch_install(tmp_path):
@@ -246,6 +249,7 @@ def test_spack_from_scratch_install(tmp_path):
assert file_exists_and_not_empty(concretization_file_path) == True
install_result = spack_operation.install_packages(jobs=2, signed=False, fresh=True, debug=False)
assert install_result.returncode == 0
+ check_installed_spack_packages(spack_operation, install_dir)
def test_spack_from_scratch_install_2(tmp_path):
@@ -263,6 +267,7 @@ def test_spack_from_scratch_install_2(tmp_path):
assert file_exists_and_not_empty(concretization_file_path) == True
install_result = spack_operation.install_packages(jobs=2, signed=False, fresh=True, debug=False, test='root')
assert install_result.returncode == 0
+ check_installed_spack_packages(spack_operation, install_dir)
def test_spack_mirror_env(tmp_path):
diff --git a/dedal/tests/integration_tests/spack_utils_test.py b/dedal/tests/integration_tests/spack_utils_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..d0fb09a76f33476b000897f238eacef73a6a8ce7
--- /dev/null
+++ b/dedal/tests/integration_tests/spack_utils_test.py
@@ -0,0 +1,10 @@
+from pathlib import Path
+
+from dedal.utils.spack_utils import extract_spack_packages
+
+
+def test_extract_spack_packages():
+ test_file_path = Path(__file__).parent
+ assert sorted(extract_spack_packages(test_file_path.parent / 'spack_files' / 'spack1.yaml')) == sorted(
+ ['python', 'numpy',
+ 'py-scipy', 'cmake'])
diff --git a/dedal/tests/spack_files/__init__.py b/dedal/tests/spack_files/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/dedal/tests/spack_files/spack1.yaml b/dedal/tests/spack_files/spack1.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..c8a0bbe798063370c45c919bbe044ce5edb87b99
--- /dev/null
+++ b/dedal/tests/spack_files/spack1.yaml
@@ -0,0 +1,11 @@
+spack:
+ view: false
+ specs:
+ - python@3.10.4+optimizations%gcc@11.2.0
+ - numpy+blas
+ - py-scipy@1.9.3 ^openblas threads=openmp
+ - cmake
+ concretizer:
+ unify: true
+ repos: [ ]
+ mirrors: { }
diff --git a/dedal/utils/spack_utils.py b/dedal/utils/spack_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..90566f219b99e2353947fcbd1eedf25a1eae9810
--- /dev/null
+++ b/dedal/utils/spack_utils.py
@@ -0,0 +1,24 @@
+import yaml
+import re
+
+
+def extract_spack_packages(spack_yaml_path: str):
+ """
+ Extracts only the package names from a spack.yaml file, ignoring versions, variants, compilers, and dependencies.
+ Args:
+ spack_yaml_path (str): Path to the spack.yaml file.
+ Returns:
+ List[str]: Clean list of package names.
+ """
+ with open(spack_yaml_path, 'r') as f:
+ data = yaml.safe_load(f)
+ specs = data.get('spack', {}).get('specs', [])
+ package_names = []
+ for spec in specs:
+ if isinstance(spec, str):
+ # Match the first word-like token before any of: @ + % ^ whitespace
+ match = re.match(r'^([\w-]+)', spec.strip())
+ if match:
+ package_names.append(match.group(1))
+
+ return package_names
diff --git a/dedal/utils/variables.py b/dedal/utils/variables.py
index 553ccf97992ca2dcd06970f82fdbbb26f5f1db23..63dd6d637c2d263e82890623e39e5bc8e565eb39 100644
--- a/dedal/utils/variables.py
+++ b/dedal/utils/variables.py
@@ -2,4 +2,5 @@ import os
SPACK_ENV_ACCESS_TOKEN = os.getenv("SPACK_ENV_ACCESS_TOKEN")
test_spack_env_git = f'https://oauth2:{SPACK_ENV_ACCESS_TOKEN}@gitlab.ebrains.eu/ri/projects-and-initiatives/virtualbraintwin/tools/test-spack-env.git'
+test_spack_env_name = 'test-spack-env'
ebrains_spack_builds_git = 'https://gitlab.ebrains.eu/ri/tech-hub/platform/esd/ebrains-spack-builds.git'
diff --git a/pyproject.toml b/pyproject.toml
index 7b82909769b161d8777cadd560899ba827cea941..1885e60e88421cd8572defa4f20ef062c88f49bc 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -19,6 +19,7 @@ dependencies = [
"ruamel.yaml",
"click",
"jsonpickle",
+ "pyyaml",
]
[project.scripts]