New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow packages with multiple build-systems (inheritance) #30411
Conversation
This comment was marked as off-topic.
This comment was marked as off-topic.
7c2b441
to
2b66f8d
Compare
2b66f8d
to
87c2098
Compare
4805d10
to
c0e675f
Compare
20da7c9
to
98a5f2f
Compare
e7f1c84
to
b5abd75
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to start following this PR more closely because many of the packages I maintain are dependent on these changes. Here are a couple of use cases to consider while working on this:
Python
Right now, all of our Python libraries are built with pip. However, pip is a full-fledged package manager, and we currently go out of our way to disable most of its features like dependency management and automatic download. I would really like to use build and installer instead. However, build and installer will likely be difficult to use for Python 2 due to additional dependencies or dropping Python 2 support a long time ago. What I would really like is for ^python@3.6:
packages to be built with build/installer and ^python@:3.5
packages to be built with pip. Ideally, this would work without changing every python package in Spack. Is it possible to support this within PythonPackage
?
GDAL
See #30668 for details. Basically, the GDAL package is becoming quite complicated, and depending on which version you are trying to build, the entire build system and every single variant will change. What I would really like is to be able to do something like:
@when('@3.5:')
class Gdal(CMakePackage):
# driver variants
@when('@3.0:3.4')
class Gdal(AutotoolsPackage):
# driver variants
@when('@:2')
class Gdal(AutotoolsPackage):
# dependency variants
In this case, different versions of GDAL are so different that I want to store them separately. This allows me to ignore them (if they worked before then they should hopefully continue to work) but also keep the class for the latest versions as simple and easy to maintain as possible. I would also be fine with storing different classes in different files. Would something like this be possible? From what I've seen so far in this PR you can only do:
class Gdal(CMakePackage, AutotoolsPackage):
with when('@3:'):
# driver variants
with when('@3.5:'):
# cmake installation steps
with when('@3.0:3.4'):
# autotools installation steps
with when('@:2'):
# dependency variants
# autotools installation steps
As you can imagine, this doubly nested structure (is this even possible?) is kinda of a nightmare to maintain and wrap your head around.
@@ -717,6 +718,17 @@ def _execute_resource(pkg): | |||
return _execute_resource | |||
|
|||
|
|||
def buildsystem(*values, **kwargs): | |||
default = kwargs.get('default', None) or values[0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this is used heavily through Spack, but we should really avoid the use of *args
and **kwargs
. It makes type checking impossible, and incorrect parameter spellings go unnoticed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is doable when we drop Python 2.7. Until then we cannot do:
def buildsystem(*values, default=None):
...
I think it is. The way it would be done is:
In buildsystem(
conditional('pip', when='^python@:3.5'),
conditional('buildinstaller', when='^python@3.6:'),
default='buildinstaller'
) |
This commit reworks build-systems to move default build methods to a separate class, in preparation to add multiple build-systems for a single package.
b5abd75
to
8532e67
Compare
This PR extends the DSL that can be used in packages to allow declaring that a package uses different build-systems under different conditions. The main design decision both in here and in #30738 is to require each and every spec to have a
build_system
single valued variant. This variant can be used in many contexts to manipulate (force, query, display etc.) the build-system associated with a concrete spec.Expected backward incompatibilities
phases
need to be converted to some existing build-systemPros
Cons
The PR maintain the same single class approach for coding recipes. To do so, it complicates the MRO logic by injecting the appropriate base class at build-time. This might be beneficial in the short term (less breakages in package recipes), but might not be in the long term (single blob class for packages).
As the package class responsibilities grow in numbers, so do the name clashes in different methods and attributes. In case an attribute defined for multiple build-systems used by the package needs customization, it's responsibility of the packager to distinguish all the different cases and return the appropriate value in each. For instance, a package buildable with both
cmake
andautotools
that needs a custombuild_directory
for the latter, need to return in a property the default value for the former explicitly.The
buildsystem
directiveTo make it easy to declare the build-system, a new directive has been created. This directive is essentially a wrapper around
variant
:For packages with a single buildsystem, the directive is already used in base classes - so no modifications are needed for packages in that sense.
Decorators to customize phases
To be able to run certain functions before or after a phase, or to override a phase, there is one decorator for each build-system:
The choice of the name to use, like
cmakelists
, have sometime been driven by the impossibility to use the most obvious choice (e.g.cmake
) due to name clashes at the class scope.Injection of the base class
A package object in this implementation lacks the base class to call any of the methods needed during build. That base class is injected when the builder is retrieved, by using an object wrapper:
In the implementation above the relevant part is that
type(self)
will evaluate to specialized bases from different build-systems, and each builder must define aPackageWrapper
which is effectively implementing the base class methods.Example of packages with multiple build-systems
arpack-ng
nasm
uncrustify