I’m integrating a Makefile’s functionality into CMake. The Makefile builds and installs a target. The install copies from a target’s install directory into the toolchain sysroot thus making it available for dependent targets. When building all targets, the install is performed for the dependency chain automatically.
I’m trying have this same functionality with CMake. Except ‘cmake --install’/‘make install’/‘ninja install’ performs a build of dependencies but doesn’t install them before the dependent target build starts. This causes a build failure because dependent files are not found.
I’m using ExternalProject to build open source packages. It performs all of the necessary steps to for ‘cmake --install’/‘make install’/‘ninja install’ to function.
Here is the install function for each open source package:
For example, openssl depends upon zlib. The openssl/CMakeLists.txt has this line:
add_dependencies(${PROJECT_NAME} zlib)
This causes zlib to be built before openssl by ‘ninja install’. But zlib isn’t installed.
This forces me to perform builds and installs manually which isn’t following the functionality of the original Makefile:
cmake --build . --target zlib
cmake --install . --component zlib
cmake --build . --target openssl
cmake --install . --component openssl
Is there something I’m missing or is this a limitation with CMake?
I don’t understand this response. I’m am working with targets not files or directories.
In this case, the problem is the ‘–install’ option doesn’t perform an install on dependencies.
Building ExternalProjects in a specific order and manage dependencies between them is called a superbuild. Only ExternalProjects builds are done there.
You can only integrate ExternalProjects in a normal build if you do not rely on installed items at configure time. I guess that building openssl also configures it. You sometimes can work around this a bit by specifying the install target as build target in ExternalProject with a custom BUILD_COMMAND (or both commands if the install does not depend on build).
The other option is to tightly integrate the build using FetchContent which can bring its own set of problems.
It is in any case easier to have a tool that properly builds a sysroot that you can rely on instead of using cmake as a hammer to handle the screw.
In the openssl code there an add_dependencies line for zlib. This assures that zlib is built before openssl. But, as I’ve pointed out, zlib doesn’t get installed when using ‘ninja install’ or ‘cmake --install .’
Why perform the install during the BUILD_COMMAND? The INSTALL_COMMAND works just fine in that the package files are installed to the install directory after BUILD_COMMAND completes.
The issue is not with the ExternalProject_Add INSTALL_COMMAND. The issue is that CMake, or rather CPack, doesn’t perform an install of dependencies during ‘ninja install’. It performs a build of dependencies but doesn’t install them.
I think this captures the heart of your problem. The INSTALL_COMMAND part of ExternalProject_Add() defines how to install the external project into a place that your main project can read it. It has nothing to do with installing the main project. If you want anything from the external project to be installed along with your main project, the main project is responsible for defining how that should happen.
Ok, but that doesn’t answer my question. Putting that in the dependency sub builds won’t cause it to be installed as part of installing the main project (at least, not on its own). If you put it in the main project, that might achieve what you want, as long as each sub build installed things to wherever ${CMAKE_CURRENT_BINARY_DIR}/install/ points to in the main project.
When you do a ninja install this is for the main project. I agree with Craig with his statement
I think this captures the heart of your problem. The INSTALL_COMMAND part of ExternalProject_Add() defines how to install the external project into a place that your main project can read it. It has nothing to do with installing the main project . If you want anything from the external project to be installed along with your main project, the main project is responsible for defining how that should happen.
When you build your dependencies via external project, these are built and installed in one rush. You need to make sure this implicit install is done to the right place. This is either locally in your build such that your main project finds the artefacts or probably already to the system. In the first case (what I do myself) you might want to add the artefacts from the dependencies by the install() of the main projection.
What is the output when your external projects are build? I have
[ 0%] Creating directories for 'zlib'
[ 0%] Performing download step (verify and extract) for 'zlib'
...
[ 50%] No update step for 'zlib'
[ 50%] No patch step for 'zlib'
[100%] Performing configure step for 'zlib'
...
[100%] Performing build step for 'zlib'
...
[100%] Performing install step for 'zlib'
...
[100%] Completed 'zlib'
Zlib is a cmake package and when built to the directory zlib-build, a file install_manifest.txt is created, containing the files to be installed within the external_project.
And again, to have the dependency zlib to be resolved, you need to call your external project as zlib - at least I did not see it in your quoted code that you do so.