What does the CPack preinstall target do - cmake

I have a fairly complicated project that builds using CMake. The project uses CPack to generate .deb packages. When I just build the project with make (i.e. not building a .deb) a clean build takes roughly 2 minutes. However when I build a package using make package the build takes roughly 12 minutes, with most of the the extra 10 minutes spent during CPack: - Run preinstall target for....
Building a .deb "manually" using dpkg-deb takes a couple seconds at most, so I'm wondering what CPack is doing when it's running the preinstall target.
I'm not necessarily interested in why it takes a long for my project specifically. I'm more curious about how the preinstall target fits into CPack's process of building a Debian package and how CPack chooses what actions will be taken when running the preinstall target.

As far as I can tell from the CMake Mailing List and this related question, the 'preinstall target' tries to rebuild your entire project, except without logging any build output to stdout. (And likely without any flags to enable parallelism either)
Apparently, this only happens if the CMake Generator / CPACK_CMAKE_GENERATOR you are using is Unix Makefiles, and is the result of CMake install commands not carrying any target dependency information with them.
One possible workaround is lying to CPack about what generator you're using, for example by setting CPACK_CMAKE_GENERATOR to Ninja.

Related

How do I stop CMake from searching for a file I removed from my dependencies entirely?

I used to have a file called game/test.dae in my current CMake project. It was used in an add_custom_command call as a dependency.
I removed this file as a dependency from my CMakeLists.txt quite some time ago, but for some reason, my windows build environment hasn't gotten the memo (it works on macOS, though):
NMAKE : fatal error U1073: don't know how to make '..\game\test.dae'
I keep running into this for old dependencies whenever I remove them in this one particular project, on windows only, and it seems the only 'fix' is to delete my build directory and start over - which is incredibly inconvenient, as I use some third-party libraries that can take up to 10 mins. to build.
Is there any way to force CMake to forget this file was ever a dependency without purging all my cmake files, cache, binaries, etc?
While it looks like a bug in CMake, it can be worked around by deleting every Makefile in the build directory.
On Unix-like it can be achieved with find build -name Makefile -delete.

How do you make CMake skip an ExternalProject on subsequent runs?

I'm using CMake for a project where I want to bundle Clang. I use ExternalProject_Add to build clang from source. However, since Clang and LLVM is huge, a make with nothing changed takes 45 seconds.
Is there a way to make CMake just build the ExternalProject once, and then not even check if anything has changed on subsequent runs if it has already been built successfully?
The best way to use ExternalProject_Add() is to structure your project as a superbuild. This means that your top-level project (the "superbuild") does not build any actual code and instead consists only of ExternalProject_Add calls. Your "real" project is added as one of these "external" projects. This allows you to set up the superbuild with all dependencies, ordering, etc.
The workflow is then as follows:
Generate the superbuild project.
Build the superbuild project. This will build and install all dependencies, and also generate (and build) your real project.
Switch to the buildsystem generated for your real project and start doing further development using that. Your dependencies are already correctly set up and installed by the build of the superbuild project in the previous step, and will never be checked for out-of-dateness by the build.
If you ever need to change the setup of your dependencies, do it and build the superbuild again.

CMake force install after add_subdirectory

We are converting a large Makefile based project to a CMake based system. I have numerous dependencies that I need to build prior to building our code. The first three dependencies are build using the following:
add_subdirectory(dependencies/libexpat/expat)
add_subdirectory(dependencies/libuuid-1.0.3)
add_subdirectory(dependencies/log4c-1.2.4)
expat has it's own CMakeLists.txt file and build with no problems. I would like to force expat to install to the staging directory before continuing. For libuuid I am using a ExternalProject_Add and as part of that process it does install into the staging directory.
Then when I build log4c, which needs expat, I can point it to the location of expat. Otherwise I would need to someone get access to the absolutely path for the temporary build location of expat.
I've tried to add the following after add_subdirectory:
add_subdirectory(dependencies/libexpat/expat)
add_subdirectory(dependencies/libuuid-1.0.3)
install(TARGETS expat LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/usr/lib)
add_subdirectory(dependencies/log4c-1.2.4)
Unfortunately CMake will not run expat's install code. How do I force expat to install after building but before it builds the rest of the project?
This looks like the primary use case for ExternalProject_Add, which is best used as a superbuild setup. This means that your top-level project (the "superbuild") does not build any actual code and instead consists only of ExternalProject_Add calls. Your "real" project is added as one of these "external" projects. This allows you to set up the superbuild with all dependencies, ordering, etc.
The workflow is then as follows:
Generate the superbuild project.
Build the superbuild project. This will build and install all dependencies, and also generate (and build) your real project.
Switch to the buildsystem generated for your real project and start doing further development using that. Your dependencies are already correctly set up and installed by the build of the superbuild project in the previous step.

CMake workflow?

I am learning CMake and I am having problems in understanding its multi-step workflow. So far, my understanding is that you:
write a CMakeLists.txt
run cmake from a subdirectory to generate a build file (a Makefile, in my case)
run make
However, I don't understand how you should handle different targets (Release vs Debug). By running CMake in two different subdirectories?
Also, I don't understand why you would edit CMakeCache.txt (there is also a GUI tool for that). To emulate what you would accomplish with ./configure by passing different options?
You got it pretty much right. The write CMakeLists.txt > cmake > make sequence is correct.
Regarding different configurations (Debug vs. Release), you have to differentiate between multi-config generators (Visual Studio, XCode), and single-config generators (everything else). With the multi-config generators, you generate one buildsystem (e.g. solution file) which contains all configurations, and choosing between them happens at build time.
With single-config generators, different configurations are obtained by generating different buildsystems, that is, by running CMake multiple times in different directories (and with a different value of the CMAKE_BUILD_TYPE CMake variable).
So you'd do something like this:
> cd my_project/bld/debug
> cmake ../../src -DCMAKE_BUILD_TYPE=Debug
> cd ../release
> cmake ../../src -DCMAKE_BUILD_TYPE=Release
Regarding editing the cache (usually through CMake GUI or ccmake): you're right again, this largely corresponds to passing options to ./configure from AutoMake world. This would be the typical workflow with a freshly downloaded project (using CMake GUI):
Run CMake GUI, point it to the source directory (input) and binary directory (output) you want
Configure. This will fill the cache with project-specified defaults. If CMake cannot find some dependencies of the project automatically, this will end with an error.
Inspect the cache, change any values you don't like (such as compilation options), fill in any missing options (paths to libraries CMake couldn't find etc.)
Repeat steps 2 & 3 until you're satisfied with the project's setup.
Generate the buildsystem.
Exit CMake GUI and build using the appropriate build tool.
What #Angew said. Plus here's an image of the cmake-gui:
Also note that you install it (the CMake GUI) on Ubuntu with sudo apt install cmake-qt-gui, and you run it with cmake-gui.
Source: Where is the CMake GUI for Linux?
Here's my cmake-gui image:

What is the CMake install time?

A quote from the official documentation:
"Specify rules to run at install time."
What exactly is install time?
The problem for me: I am on Linux, software is installed from packages that are just dependencies and data. There is no CMake that can do anything here. So installation time of software is out of scope from CMake. So what exactly do they mean?
Building a CMake project can roughly be divided into three phases:
Configure time. This includes everything that happens while running cmake itself. This phase is concerned with inspecting certain properties of the host system and generating the specific build files for that platform under the selected configuration.
Build time. This includes everything that happens while actually building your project from the files generated by CMake (like, when running cmake --build or make). This is where all of the actual compilation and linking happens, so at the end of the build phase, you have a usable binary.
Install time. This includes everything that happens when running the INSTALL target generated by CMake (like, when running cmake --build --target install or make install). This takes care of copying the binaries that were generated into the build tree to a different directory. Note that the build tree contains a lot of stuff that is no longer needed once the build is completed if you are only interested in running the binary. Examples include all intermediate build artifacts, like the build files generated during the configure phase or the intermediate object files created during the build phase. Furthermore, the install phase might include additional steps to ensure that the binaries produced during the build are portable. For instance, on Linux systems you might want to remove the build directory from the shared library search path in the binary and replace it with a portable equivalent. So the install phase might do more than just copy all the important files to a new directory. It could also include additional steps that change the binaries to make them more portable.
Note that the last phase is optional. If you do not want to support calling make install but prefer another deployment mechanism, you simply don't use the install command in your CMake script and no INSTALL target will be generated.
I'd like to expand the answer, which ComicSansMS gave you, a little bit.
As he mentioned - CMake generates an extra target called install for the make tool (when you use a Makefile-based generator).
It may look weird for you as a package system is used for Linux. However the install target is still useful or even necessary:
When you develop your application you may need to install (move binaries and possibly some include files) to a certain location so some of your projects may see each other. For example, you may develop a library and a set of non-related applications which use it. Then this library must be installed somewhere to be visible. It doesn't mean you need to put it to the /usr directory; you may use your /home.
The process of Linux package preparation requires an install step. For example, the RPM packaging system does three main steps when the rpm package file is being built: the project is configured, then is compiled and linked and finally is being installed to a certain location. All files from this location are being packed to the rpm file.