Developer support for CMake-based Projects#474
Developer support for CMake-based Projects#474citibeth wants to merge 3 commits intospack:developfrom
Conversation
…one place (this code was previously included inline).
…t breaking backwards compatibility:
1. Boilerplate code in CMake projects can now be eliminated (see everytrace/package.py for details on how it can be done now).
2. The CMakeProject understands a special version 'local', to enlist Spack to help configure CMake properly when developing projects. Consider the following example for usage, in which Spack is used to set up a build and a module, but not acutally DO the build.
git clone https://github.com/citibeth/everytrace.git
cd everytrace
spack diy --skip-patch everytrace@local
mkdir build
cd build
/usr/bin/python ../spconfig.py .. # Runs cmake with Spack-supplied configuration
make
make install # Installs into spack directory
spack load everytrace@local # Spack even makes a module
Once you're happy with your project, you can add appropriate version() commands to your package.py, and use Spack normally with it. To make this work, your project has to cooperate with Spack, as follows:
a) You need to use CMake (of course).
b) Your CMakeLists.txt should use the following line, which will ensure that all TRANSITIVE dependencies are added to the include path. If you're not running with Spack, then this line will do nothing.
include_directories($ENV{CMAKE_TRANSITIVE_INCLUDE_PATH})
|
I added a new commit to my branch that should fix the failure. I'm led to believe that if I just do nothing, eventually Travis-CI will re-run and the PR will turn green. But that hasn't happened yet... |
| homepage = "https://github.com/citibeth/everytrace" | ||
| url = "https://github.com/citibeth/everytrace/tarball/dev" | ||
|
|
||
| version('devel', '0123456789abcdef0123456789abcdef') |
There was a problem hiding this comment.
does this fetch? I guess not -- but what if someone wants to build every trace non-local?
There was a problem hiding this comment.
The URL I put in there (line 12) should fetch; GitHub can give you a tarball directly, if you tell it which branch/tag you want to fetch. Of course, fetching the "dev" branch would be bad practice for anyone other than the developer. Since the "spack diy" usage I outlined above doesn't try to fetch anyway, it would be more useful to put a real version tag in that URL in case someone is running Spack in regular mode. But... if a project is early in development, there won't be such a tag yet; so might as well just fetch master or dev.
The checksum won't match yet, but that can be fixed easily enough.
|
Ok this looks pretty cool. Comments:
|
|
Looks like it failed again despite the new commit (which you can see above). Not sure why. Edit: oh wait, it looks like it just hasn't run again. Not sure how it decides that... |
I agree, the UI needs to be fixed and a new PR submitted. No need to debug Travis-CI at this time. (In the meantime, I have something that at least gets the job done). How about the following usage, assuming a "spack cmake" command: Notes/Questions here:
In general: suppose our project uses netcdf-cxx4. And when you #include something from that project, it in turn #includes something from netcdf. Then we need -I.../netcdf/include to be provided to the compiler, even though netcdf is not a (direct) dependency of our project. In my experience, we don't need this transitive behavior with libraries, at least as long as RPATH is used. But maybe I'm wrong... I can't tell for sure from your description... Are Spack wrappers already providing this transitive functionlity? Or do they just provide -I for direct dependencies?
I believe that Spack only sets CMAKE_PREFIX_PATH for direct dependencies. That is the correct behavior, since only direct dependencies should be found via the CMake find_package commands.
They add include directories for packages that are NOT direct dependencies of your project. In the old days, that was not such a problem, since most packages were in one big tree as well: chances are, netcdf would be in the same place as netcdf-cxx4, even if you didn't specify it as a dependency. When every project has its own tree, it becomes important to enumerate all transitive depenencies.
I like the Spack wrapper approach better than mine because it requires no Spack-specific elements in the CMakeLists.txt file. But... Spack's wrappers complain if you try to use them outside of Spack. Spack is not currently set up to just run "make" a zillion times, which is what you do when developing software (after running "cmake" once). I see some possible solutions to this problem, I'd appreciate your thoughts:
I think it's similar but different. "spack env" sets up the environment for one package. If I'm developing package A that depends on X, Y and Z, then I really want "spack env X Y Z", or "spack env dependencies-of-A". Which is the same as what you get if you do (assuming recursive modules such as lmod): |
This is part of the thing that is supposed to be solved by pkg-config and CMake's exported target information scripts. I certainly don't think spack should require a patch to all CMake projects. Libraries are generally handled by the linker (but if you use a symbol directly, linkers have been requiring that you link directly in which case you need to add it to your project anyways. Transitive header includes leaking out are usually a bug in the middle project. |
|
Oh, and for static libraries, you need to link the transitive dependencies for executables so that the symbols can be found. |
Hmmm... What about a thin C++ header-only wrapper around a C library? |
|
You're going to need to link the C library directly anyways, so you'll need to know where its bits live to link successfully. |
|
Spack RPath-ifies everything, so I don't need to link the C library Overall, I'd like a system in which you only have to list as dependencies On Tue, Mar 8, 2016 at 3:00 PM, Ben Boeckel [email protected]
|
|
Ben,
I agree, putting something into the CMake file is icky. But it's not the
end of the world, since it's a no-op without Spack.
However... Todd pointed out above that this is not really necessary. The
Spack wrappers already add a lot of -I flags to the command line. If I can
make Spack-like wrappers that work outside of Spack, then the
TRANSITIVE_INCLUDE_PATH stuff inside the CMakeLists.txt wouldn't be needed
at all.
Do you have any thoughts on the overall idea of a "spack cmake" command?
The one problem I have with it is the following scenario:
mkdir build
cd build
spack cmake ..
make
...modify CMakeLists.txt...
make
That second make command will fail because it will run the (unwrapped)
CMake automatically, instead of the "spack cmake" command. I'm wondering
if there's any way to tell CMake what CMake command you want it to use on
subsequent invocations of itself.
Thank you,
-- Elizabeth
|
|
Just a thought FYI: one thing we are thinking very strongly of implementing is a "profile" capability, which would work kind of like virtualenv or conda environments, but for anything, not just python packages. It would likely look sort of like the existing python support in Spack, where you can activate/deactivate packages and they are symlinked into a common prefix (in the current Python case, that is the interpreter prefix). You could do something like: This would make it easy to create and save environments and stacks of packages for different teams, and to get them in/out of your environment. I would likely also want to version the profiles. This feature is in package managers like nix and guix. Another nice feature is that it would allow external apps to RPATH the profile directory (or a symlink to it, to allow transactional updates), but you could do seamless upgrades of packages linked into the profile by removing them and adding a new version. If you had something like this, would you still need |
What library are you linking to given a header-only C++ library?
No, there isn't CMake sets FWIW, I think that a CMake-specific subcommand is probably not the way to go (What about scons? waf? autotools?). As much as it'd be nice if CMake were the only way to build C and C++ code, it is not. |
|
I'm not opposed to a cmake-specific subcommand, although speaking (in #506) about clutter, we probably need to get argparse to spit out something shorter and more helpful for arg-less |
|
Todd, I really like the spack profile idea.
I think that's a harder question to answer. I like the idea of "spack IF Spack profiles are to serve this purpose, then there would need to be a So in the end... I think that "spack cmake" would be better at what I want -- Elizabeth On Tue, Mar 8, 2016 at 3:20 PM, Todd Gamblin [email protected]
|
|
On Tue, Mar 8, 2016 at 3:22 PM, Ben Boeckel [email protected]
See, for example, this wrapper of the PROJ.4 library:
But point taken... maybe I should think about this in a layered approach. |
|
I guess I am still not clear on how That could be made more concise, but most packages don't have to do a lot of it so far. With The nice thing about |
And what library provides |
if '+foo' in spec:
std_cmake_args.append('-Dfoo')
cmake (*std_cmake_args)Modifying |
|
No I'm not. Every package build in spack gets its own process, which was a very intentional design decision to prevent mayhem like this. Package authors are free to do whatever they want in that sandbox -- this is why they're allowed to, e.g., set whatever env vars they want. The root spack process stays clean, and it forks before it enters any package code. I was actually debating changing |
|
Ah, I had missed that detail (I was even in the code right above that section of code). That's going to make Windows support interesting… |
|
@citibeth: Since it's not particularly well documented (sorry!) maybe an example will help. Try this on your command line with the latest This is both a decent way to test out how a spack package will work, and a way to generate lots of dev versions of something. Spack will auto-fetch and install deps before it runs the DIY build, so you basically get the env you want and it runs the same package you would use with spack. Note that you must provide a version after the |
|
@mathstuf: it's not a |
Other auto-build systems (MacPorts, EasyBuild) separate a build into an I do not see such a structure in Spack, which seems to be both good and Suppose we have a hypothetical stage-separated Spack with the following
If Spack were structured in this way, then what I want in "spack cmake" Similarly, "spack diy" can be described as running stages 1, 2, 4, 5, 6, 7, The simple answer to why "spack diy" doesn't do what I want is, I want to a) Spack is slow to startup, concretize, etc. I don't want to incur that b) In practice, Spack involves long, complex command lines with lots of The downside of (b) is that now, the two-stage build (configure, Rather than talking about "spack cmake" vs "spack diy," maybe we should With that change, one would write a version of what I want with "spack On Tue, Mar 8, 2016 at 3:44 PM, Ben Boeckel [email protected]
Modifying std_cmake_args directly isn't a good idea since you're modifying
I use the following pattern, of concatenating std_cmake_args with Package authors are free to do whatever they want in that sandbox -- this
I've been wondering why packages set os.environ at all. Since it's quite |
|
Adding stages seems like a good idea to me. We've got internal feature request to separate out For the environment question, the reason is that there are builds that really want particular environment variables set, either for makefiles, or for whatever. The Python Mac OS X build requires an env var set during build. |
The linker in newer GNU binutils require that if you use a symbol that is provided by a library, it must be explicitly listed on the command line (either by |
|
In that case, we won't be able to build reliably without something that On Tue, Mar 8, 2016 at 5:59 PM, Ben Boeckel [email protected]
|
|
On Tue, Mar 8, 2016 at 5:50 PM, Todd Gamblin [email protected]
I can take a go at it, of separating the current install() method into For the environment question, the reason is that there are builds that
with something like this: |
The folks at the Apache mirrors were so nice to purge old versions.
New CMakePackage subclass of Package. This has two advantages, without breaking backwards compatibility:
Once you're happy with your project, you can add appropriate version() commands to your package.py, and use Spack normally with it. To make this work, your project has to cooperate with Spack, as follows:
a) You need to use CMake (of course).
b) Your CMakeLists.txt should use the following line, which will ensure that all TRANSITIVE dependencies are added to the include path. If you're not running with Spack, then this line will do nothing.
include_directories($ENV{CMAKE_TRANSITIVE_INCLUDE_PATH})
children d2e5234
on branches efischer/develop, origin/efischer/develop