Reworking of lapack_shared_libs and similar properties#1682
Reworking of lapack_shared_libs and similar properties#1682tgamblin merged 8 commits intospack:developfrom
lapack_shared_libs and similar properties#1682Conversation
| from llnl.util.filesystem import LibraryList | ||
|
|
||
|
|
||
| class LibraryListTest(unittest.TestCase): |
There was a problem hiding this comment.
@davydden Can you take a look at this test file and tell me if there are other operations you would like to perform on a list of libraries ?
There was a problem hiding this comment.
sure, will have a look tomorrow...
There was a problem hiding this comment.
as far as i can tell,
- get list of names with defined separator, i.e.
foo bar baz - get list of directories where libraries are (i.e.
/path/to/openblas/lib) - complete link flags
-L/path/to/dir -lfoo -lbar -lbaz - list of full paths with separator, i.e.
path/to/libfoo.so path/to/libbar.so
Looks like this should be enough.
|
there are two functions in the utilities |
| self.libraries = list(libraries) | ||
|
|
||
| @property | ||
| def directories(self): |
There was a problem hiding this comment.
donno what's the convention, but maybe one-line doc-string for properties? Especially for things like libnames vs names.
|
Looks good! |
|
@davydden Thanks for the thorough review! I'll start working on the PR + cleaning and documenting the code.I'll come back to you when all this will be ready for a test-drive 😄 |
| ]) | ||
| options.extend([ | ||
| '--with-blas={0}'.format( | ||
| ' '.join(spec['blas'].blas_libs.ld_flags) |
There was a problem hiding this comment.
Before anybody says anything : since ' '.join(ld_flags) seems a common idiom I'll modify the PR to avoid having to write the join in the package 😄
fce956b to
b27a904
Compare
| def blas_libs(self): | ||
| shared = True if '+shared' in self.spec else False | ||
| suffix = dso_suffix if '+shared' in self.spec else 'a' | ||
| mkl_integer = ['libmkl_intel_lp64'] if '+ilp64' in self.spec else ['libmkl_intel_ilp64'] # NOQA: ignore=E501 |
lapack_shared_libs and similar propertieslapack_shared_libs and similar properties
|
@tgamblin @adamjstewart ready to be reviewed |
|
@alalazo is it rebased on top of |
|
@davydden Yes, it should be |
fix : failing unit tests due to missing `self`
@adamjstewart I would do it in another PR, as it requires some thinking on the searching logic |
39df558 to
be14686
Compare
|
@KineticTheory: if this does not clobber your changes from #1539, I think it's ready to merge. Can you take a look? |
|
This PR looks pretty good. There's only a couple of things I think it's missing. I would like |
|
@adamjstewart let's get it merged and then fix minor issues in follow up PRs. |
|
NOTE: this is not a request for changes. This is a proposal for how this PR should evolve because I think it's heading in the right direction and it solves a problem that other packages have. One thing I thought about when looking at this PR: I don't really like it that some dependents still search for their dependencies libraries, though I realize that it can be necessary. Basically there are two guiding principles here:
(2) breaks down for external packages, which Spack didn't install and which may even be laid out differently than Spack would lay them out (e.g. a install of boost might have al the weird lib suffixes that boost supports, and I think Spack wouldn't currently know what to do with that). Even better is that This PR follows both of these principles for I think this PR makes it clearer to me what a good convention for passing args among packages in a build should look like. You want to ask the package for a property, and the package needs to know a) the property name and b) the I guess the question is whether this should be a convention or a more formal interface. The convention currently looks like: spec['blas'].blas_libs
spec['lapack'].lapack_libsSlightly annoying things about it:
So, think we could have a convention like this: def provider_property(function):
"""Decorator for provider properties that get info from dependencies."""
def __init__(self, name):
self.name = name
def __call__(self, provider):
"""Get provider-specific property if available, or default to generic property."""
specific = provider.replace('-', '_') + '_' + self.name
dep = spec[provider]
if hasattr(dep, specific):
return getattr(dep, specific)
else:
return getattr(dep, self.name) # default to generic attribute
class Spec(object):
...
libs = provider_property('libs')
includes = provider_property('includes')Now the interface looks like this: spec.libs('blas')
spec.libs('lapack')And you've got a convention for properties to add to packages ( class Package(object):
...
def libs(self):
# requiring installation allows us to be more generic b/c we can search,
# but it does restrict when you can call the method.
assert(self.installed)
return find_libraries(... something that returns all the libs in `lib` or `lib64`...)And packages that need to can add things like I guess the question is whether the interface above is so much better than the simple convention we've got that it's worthwhile. The current convention is not so bad. I think it boils down which is simpler to document and teach to package authors. The other question is whether this convention is worthwhile for non-virtuals. Sometimes the client needs to be specific about which lib names it needs, in which case find_libraries seems like the right thing to do. What do you all think? |
one have to do that because a single package can provide different virtual packages (e.g. mkl provides blas and lapack and scalapack). Would the convention you suggested work here as well?
i would say for most packages the library names are simple ( So i would not adopt this approach to every package, but only to a few virtuals ( What should be systematically addressed is |
|
@tgamblin p.s. maybe you copy-paste your comment into a separate issue to continue the discussion? |
|
@alalazo @tgamblin I just updated to the latest develop and now I'm seeing infinite error messages: If I checkout the commit before this PR was merged, everything works fine. There must be some infinite recursion going on here. |
|
To reproduce the bug, just try installing any package. Even ones that don't depend on blas/lapack/etc. Maybe a getattribute problem? |
| # This line is to avoid infinite recursion in case package is | ||
| # not present among self attributes | ||
| package = super(Spec, self).__getattribute__('package') | ||
| return getattr(package, item) |
There was a problem hiding this comment.
I've confirmed that these changes caused the bug I'm seeing. When I comment this function out, Spack works again. What was this added for?
There was a problem hiding this comment.
Today I succeeded installing cp2k before pushing changes and right now I correctly installed hdf5 on a fresh develop checkout... sorry I can't reproduce the problem...
The two lines are needed to forward requests for non-existing attributes from Spec to Package. It's what makes :
blas = spec['blas'].blas_libspossible instead of :
blas = spec['blas'].package.blas_libsThere was a problem hiding this comment.
Ok, this is weird. Spack works on my laptop but not on our cluster. Going to investigate further.
There was a problem hiding this comment.
Ouch... could reproduce with [email protected]. I wonder why Travis never fired up a warning about that
EDIT : Travis seems to be running with 2.6.9 and no issues. Python 2.6.6 instead shows the problem.
There was a problem hiding this comment.
On my cluster, Spack works with Python 2.7 but crashes with the system Python 2.6 Can you try 2.6 on your end to see if you can reproduce the problem?
There was a problem hiding this comment.
Yeah, I'm running Python 2.6.6 too.
There was a problem hiding this comment.
@alalazo: if you're working on a fix I'll merge it asap, otherwise I'll work on it.
There was a problem hiding this comment.
EDIT : Travis seems to be running with 2.6.9 and no issues. Python 2.6.6 instead shows the problem.
If only we could just drop 2.6. Sigh.
There was a problem hiding this comment.
@tgamblin Sorry for the trouble. I couldn't find a solution that works for both python versions right now. If you couldn't solve it right away, feel free to revert the merge and I'll work it out without impacting everyone...
| suffix = dso_suffix if '+shared' in self.spec else 'a' | ||
| mkl_integer = ['libmkl_intel_ilp64'] if '+ilp64' in self.spec else ['libmkl_intel_lp64'] # NOQA: ignore=E501 | ||
| mkl_threading = ['libmkl_sequential'] | ||
| if '+openmp' in spec: |
There was a problem hiding this comment.
@alalazo : this should be self.spec ?
if '+openmp' in spec:
There was a problem hiding this comment.
@pramodk True! Feel free to submit a PR to fix this. Otherwise, if you prefer, I'll take care of that.
There was a problem hiding this comment.
I am testing now Atlas and MKL and i will create a PR today, need to fix a few extra minor things in MKL.
| to package. In order to make the ``install()`` method independent of the | ||
| choice of ``Blas`` implementation, each package which provides it | ||
| sets up ``self.spec.blas_shared_lib`` and ``self.spec.blas_static_lib`` to | ||
| point to the shared and static ``Blas`` libraries, respectively. The same |
| choice of ``Blas`` implementation, each package which provides it | ||
| sets up ``self.spec.blas_shared_lib`` and ``self.spec.blas_static_lib`` to | ||
| point to the shared and static ``Blas`` libraries, respectively. The same | ||
| applies to packages which provide ``Lapack``. Package developers are advised to |
Fixes #1670
Modifications
LibraryList+ unit testsfind_librariesanddedupesetup_dependent_packageand define properties insteadintel-parallel-studioandmklatlas