summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/spack/docs/build_settings.rst22
-rw-r--r--lib/spack/spack/repo.py35
-rw-r--r--lib/spack/spack/schema/packages.py9
-rw-r--r--lib/spack/spack/test/concretize_preferences.py47
4 files changed, 113 insertions, 0 deletions
diff --git a/lib/spack/docs/build_settings.rst b/lib/spack/docs/build_settings.rst
index de580a3289..3ab38dd4da 100644
--- a/lib/spack/docs/build_settings.rst
+++ b/lib/spack/docs/build_settings.rst
@@ -531,3 +531,25 @@ directories inside the install prefix. This will ensure that even
manually placed files within the install prefix are owned by the
assigned group. If no group is assigned, Spack will allow the OS
default behavior to go as expected.
+
+----------------------------
+Assigning Package Attributes
+----------------------------
+
+You can assign class-level attributes in the configuration:
+
+.. code-block:: yaml
+
+ packages:
+ mpileaks:
+ # Override existing attributes
+ url: http://www.somewhereelse.com/mpileaks-1.0.tar.gz
+ # ... or add new ones
+ x: 1
+
+Attributes set this way will be accessible to any method executed
+in the package.py file (e.g. the ``install()`` method). Values for these
+attributes may be any value parseable by yaml.
+
+These can only be applied to specific packages, not "all" or
+virtual packages.
diff --git a/lib/spack/spack/repo.py b/lib/spack/spack/repo.py
index 123f34320e..11370f7b56 100644
--- a/lib/spack/spack/repo.py
+++ b/lib/spack/spack/repo.py
@@ -1362,6 +1362,41 @@ class Repo(object):
if not inspect.isclass(cls):
tty.die("%s.%s is not a class" % (pkg_name, class_name))
+ new_cfg_settings = (
+ spack.config.get("packages").get(pkg_name, {}).get("package_attributes", {})
+ )
+
+ overridden_attrs = getattr(cls, "overridden_attrs", {})
+ attrs_exclusively_from_config = getattr(cls, "attrs_exclusively_from_config", [])
+ # Clear any prior changes to class attributes in case the config has
+ # since changed
+ for key, val in overridden_attrs.items():
+ setattr(cls, key, val)
+ for key in attrs_exclusively_from_config:
+ delattr(cls, key)
+
+ # Keep track of every class attribute that is overridden by the config:
+ # if the config changes between calls to this method, we make sure to
+ # restore the original config values (in case the new config no longer
+ # sets attributes that it used to)
+ new_overridden_attrs = {}
+ new_attrs_exclusively_from_config = set()
+ for key, val in new_cfg_settings.items():
+ if hasattr(cls, key):
+ new_overridden_attrs[key] = getattr(cls, key)
+ else:
+ new_attrs_exclusively_from_config.add(key)
+
+ setattr(cls, key, val)
+ if new_overridden_attrs:
+ setattr(cls, "overridden_attrs", dict(new_overridden_attrs))
+ elif hasattr(cls, "overridden_attrs"):
+ delattr(cls, "overridden_attrs")
+ if new_attrs_exclusively_from_config:
+ setattr(cls, "attrs_exclusively_from_config", new_attrs_exclusively_from_config)
+ elif hasattr(cls, "attrs_exclusively_from_config"):
+ delattr(cls, "attrs_exclusively_from_config")
+
return cls
def __str__(self):
diff --git a/lib/spack/spack/schema/packages.py b/lib/spack/spack/schema/packages.py
index 92fe9e0ba8..eed8db3e73 100644
--- a/lib/spack/spack/schema/packages.py
+++ b/lib/spack/spack/schema/packages.py
@@ -82,6 +82,15 @@ properties = {
},
},
},
+ # If 'get_full_repo' is promoted to a Package-level
+ # attribute, it could be useful to set it here
+ "package_attributes": {
+ "type": "object",
+ "additionalProperties": False,
+ "patternProperties": {
+ r"\w+": {},
+ },
+ },
"providers": {
"type": "object",
"default": {},
diff --git a/lib/spack/spack/test/concretize_preferences.py b/lib/spack/spack/test/concretize_preferences.py
index 7bae90cb2c..eb5b073444 100644
--- a/lib/spack/spack/test/concretize_preferences.py
+++ b/lib/spack/spack/test/concretize_preferences.py
@@ -179,6 +179,53 @@ class TestConcretizePreferences(object):
spec = concretize("mpileaks")
assert "zmpi" in spec
+ def test_config_set_pkg_property_url(self, mutable_mock_repo):
+ """Test setting an existing attribute in the package class"""
+ update_packages(
+ "mpileaks",
+ "package_attributes",
+ {"url": "http://www.somewhereelse.com/mpileaks-1.0.tar.gz"},
+ )
+ spec = concretize("mpileaks")
+ assert spec.package.fetcher[0].url == "http://www.somewhereelse.com/mpileaks-2.3.tar.gz"
+
+ update_packages("mpileaks", "package_attributes", {})
+ spec = concretize("mpileaks")
+ assert spec.package.fetcher[0].url == "http://www.llnl.gov/mpileaks-2.3.tar.gz"
+
+ def test_config_set_pkg_property_new(self, mutable_mock_repo):
+ """Test that you can set arbitrary attributes on the Package class"""
+ conf = syaml.load_config(
+ """\
+mpileaks:
+ package_attributes:
+ v1: 1
+ v2: true
+ v3: yesterday
+ v4: "true"
+ v5:
+ x: 1
+ y: 2
+ v6:
+ - 1
+ - 2
+"""
+ )
+ spack.config.set("packages", conf, scope="concretize")
+
+ spec = concretize("mpileaks")
+ assert spec.package.v1 == 1
+ assert spec.package.v2 is True
+ assert spec.package.v3 == "yesterday"
+ assert spec.package.v4 == "true"
+ assert dict(spec.package.v5) == {"x": 1, "y": 2}
+ assert list(spec.package.v6) == [1, 2]
+
+ update_packages("mpileaks", "package_attributes", {})
+ spec = concretize("mpileaks")
+ with pytest.raises(AttributeError):
+ spec.package.v1
+
def test_preferred(self):
""" "Test packages with some version marked as preferred=True"""
spec = Spec("python")