CMake: How to execute a command before make install? - linux

This is the way I install the config files:
file(GLOB ConfigFiles ${CMAKE_CURRENT_SOURCE_DIR}/configs/*.xml
${CMAKE_CURRENT_SOURCE_DIR}/configs/*.xsd
${CMAKE_CURRENT_SOURCE_DIR}/configs/*.conf)
install(FILES ${ConfigFiles} DESTINATION ${INSTDIR})
But I need to convert one of the xml files before installing it. There is an executable that can do this job for me:
./Convertor a.xml a-converted.xml
How can I automatically convert the xml file before installing it? It should be a custom command or target that installing depends on it, I just don't know how to make the install command depend on it though. Any advice would be appreciated!

Take a look at the SCRIPT version of install:
The SCRIPT and CODE signature:
install([[SCRIPT <file>] [CODE <code>]] [...])
The SCRIPT form will invoke the given CMake script files during
installation. If the script file name is a relative path it will be
interpreted with respect to the current source directory. The CODE
form will invoke the given CMake code during installation. Code is
specified as a single argument inside a double-quoted string.
For example:
install(CODE "execute_process(\"./Convertor a.xml a-converted.xml\")")
install(FILES a-converted.xml DESTINATION ${INSTDIR})
Be sure to checkout the entry for execute_process in the manual. Also be aware that macro expansion inside the CODE parameter can be a bit tricky to get right. Check the generated cmake_install.cmake in your build directory where the generated code will be placed.

I think that your specific case would work better if you were to use a custom command and target like so:
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/a-converted.xml
COMMAND ./Convertor a.xml a-converted.xml
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Convertor
)
add_custom_target(run ALL
DEPENDS ${CMAKE_BINARY_DIR}/a-converted.xml
COMMENT "Generating a-converted.xml" VERBATIM
)
install(
FILES ${CMAKE_BINARY_DIR}/a-converted.xml
DESTINATION ${INSTDIR}
)
Note: I don't have all the details, so the directories are probably
not exactly what you'd want in your environment, although it's
a good idea to generate files in the ${CMAKE_BINARY_DIR} area.
That way you can be sure that the file a-converted.xml is built at the time you want to install it. Especially, these two rules make sure that if you make changes to the file, it gets recompiled.

Related

how to add creating protobuf python files [duplicate]

I'm trying to use add_custom_command to generate a file during the build. The command never seemed to be run, so I made this test file.
cmake_minimum_required( VERSION 2.6 )
add_custom_command(
OUTPUT hello.txt
COMMAND touch hello.txt
DEPENDS hello.txt
)
I tried running:
cmake .
make
And hello.txt was not generated. What have I done wrong?
The add_custom_target(run ALL ... solution will work for simple cases when you only have one target you're building, but breaks down when you have multiple top level targets, e.g. app and tests.
I ran into this same problem when I was trying to package up some test data files into an object file so my unit tests wouldn't depend on anything external. I solved it using add_custom_command and some additional dependency magic with set_property.
add_custom_command(
OUTPUT testData.cpp
COMMAND reswrap
ARGS testData.src > testData.cpp
DEPENDS testData.src
)
set_property(SOURCE unit-tests.cpp APPEND PROPERTY OBJECT_DEPENDS testData.cpp)
add_executable(app main.cpp)
add_executable(tests unit-tests.cpp)
So now testData.cpp will generated before unit-tests.cpp is compiled, and any time testData.src changes. If the command you're calling is really slow you get the added bonus that when you build just the app target you won't have to wait around for that command (which only the tests executable needs) to finish.
It's not shown above, but careful application of ${PROJECT_BINARY_DIR}, ${PROJECT_SOURCE_DIR} and include_directories() will keep your source tree clean of generated files.
Add the following:
add_custom_target(run ALL
DEPENDS hello.txt)
If you're familiar with makefiles, this means:
all: run
run: hello.txt
The problem with two existing answers is that they either make the dependency global (add_custom_target(name ALL ...)), or they assign it to a specific, single file (set_property(...)) which gets obnoxious if you have many files that need it as a dependency. Instead what we want is a target that we can make a dependency of another target.
The way to do this is to use add_custom_command to define the rule, and then add_custom_target to define a new target based on that rule. Then you can add that target as a dependency of another target via add_dependencies.
# this defines the build rule for some_file
add_custom_command(
OUTPUT some_file
COMMAND ...
)
# create a target that includes some_file, this gives us a name that we can use later
add_custom_target(
some_target
DEPENDS some_file
)
# then let's suppose we're creating a library
add_library(some_library some_other_file.c)
# we can add the target as a dependency, and it will affect only this library
add_dependencies(some_library some_target)
The advantages of this approach:
some_target is not a dependency for ALL, which means you only build it when it's required by a specific target. (Whereas add_custom_target(name ALL ...) would build it unconditionally for all targets.)
Because some_target is a dependency for the library as a whole, it will get built before all of the files in that library. That means that if there are many files in the library, we don't have to do set_property on every single one of them.
If we add DEPENDS to add_custom_command then it will only get rebuilt when its inputs change. (Compare this to the approach that uses add_custom_target(name ALL ...) where the command gets run on every build regardless of whether it needs to or not.)
For more information on why things work this way, see this blog post: https://samthursfield.wordpress.com/2015/11/21/cmake-dependencies-between-targets-and-files-and-custom-commands/
This question is pretty old, but even if I follow the suggested recommendations, it does not work for me (at least not every time).
I am using Android Studio and I need to call cMake to build C++ library. It works fine until I add the code to run my custom script (in fact, at the moment I try to run 'touch', as in the example above).
First of,
add_custom_command
does not work at all.
I tried
execute_process (
COMMAND touch hello.txt
)
it works, but not every time!
I tried to clean the project, remove the created file(s) manually, same thing.
Tried cMake versions:
3.10.2
3.18.1
3.22.1
when they work, they produce different results, depending on cMake version, one file or several. This is not that important as long as they work, but that's the issue.
Can somebody shed light on this mystery?

Why does CMake generator for Unix Makefiles delete files?

I have a rather complex CMake configuration, which contains several execute_process commands which create files during the configuration stage. Sometimes after some changes in the CMake configuration the generation stage deletes these files. I can reproduce that.
I've checked that the files exist after the configuration stage but are gone after the Makefile generation and before actually calling make.
The files are created in some cases simply by ${CMAKE_COMMAND} -E copy and in other cases by calling a script with ${CMAKE_COMMAND} -P which contains a configure_file call to replace some placeholders in a template.
The files are created in the source tree. Their purpose is to provide the developer with some initial code. When the developer has edited the files, they should be committed to version control and not be re-created again unless they are missing. I have add_custom_commands to recreate the files if they're missing, but those are not the culprit.
I know, you'd prefer a simple test example, but unfortunately this is not so easy to create, so my question is:
What might be the cause and how can I debug that?
Unfortunately, the --trace options of cmake do not give any log data about the generation stage.
Versions
OS: Ubuntu 16.04
CMake 3.5.1 (belonging to Ubuntu 16.04)
Update
I've compiled CMake itself with the current master commit (696b2d4) and the behavior is still the same.
By running CMake under a debugger I've discovered that the line
cmSystemTools::RemoveFile(fname);
in function cmGlobalGenerator::CheckRuleHashes(std::string const& pfile, std::string const& home) actually deletes the files. It is called from cmGlobalGenerator::Generate().
With further debugging (see update in the question) I've found the cause of my problem.
CMake's behavior
CMake's global generator creates the file ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeRuleHashes.txt. It contains a line for every custom command with outputs. Each line contains a hash and the path to the first output of the custom command.
The hash is made over the current binary dir of the custom command and the entire content of the COMMAND arguments of add_custom_command.
On generation CMake executes cmGlobalGenerator::CheckRuleHashes to check if the hashes are still up to date. If a hash is not up to date the corresponding output file will be removed, obviously to trigger re-execution of the custom command in the build stage.
Cause of my problem
As mentioned in the question I execute the file creation command with execute_process during configuration and call add_custom_command with the same file creation command to trigger re-creation of the file in case it was missing.
So, CMake creates a hash line in its CMakeRuleHashes.txt for the file to be created.
Some of my add_custom_command calls are contained in a CMake file included from different CMakeLists.txt belonging to different sub-directories of the source. Depending on my actual configuration only specific sub-directories are added with add_subdirectory.
Thus, because the sub-directory is part of the hash of the custom command, the hash changes depending on the sub-directory added which includes the custom command. The changed hash then causes the deletion of the output file.
Conclusion
I have to redesign my CMake configuration to resolve the dependency of the custom command on the CMake binary directory.
Debugging generation stage
This could be done by compiling CMake itself as Debug build type and run it under a debugger.

CMAKE install command post process the file

I am installing static file using CMake's INSTALL command.
I want to post process the output file using CMake.
For example, static files have a string like
v={{VERSION}}
I want to replace {{VERSION}} in the output files.
Is this possible with CMake?
Yes, but preprocessing is usually done at cmake invocation step, not during install. This is done using configure_file() command.
Note that configure_file() supports substituting values only in ${} or ##, so if you really need to configure a file with {{}}, you might end up writing your own function using the CMake command file().
Finally, you need to install your configured file. Be sure to
install(FILES ${CMAKE_BINARY_DIR}/your.file)
and not just
install(FILES your.file)
since the latter command would install your source file.

Running a program from the source tree

Should it generally be possible to run a program from the source directory (src) after having invoked ./configure and make (but not make install)? I'm trying to fix a bug in an application and it seems unnecessary to run make install after each code change. Unfortunately I can't run the application in the source directory since it tries to access files in the lib installation directory (which do not exist before make install). Is the application wrongly configured or do I have to reinstall it after each change to the source code?
It all depends on the application and what components or files it expects to be visible and where. But assuming no required configuration or dependencies, then yes, you can run the program in-place.
To add a directory to your lib search path, add to the environment variable LD_LIBRARY_PATH. Like so:
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/home/user/myproject/lib" ./someprogram
Note that specifiying a variable assignment on the command line in front of the program you run sets that variable for that run only. (Note, no semicolon -- this is a single command.) If you want to set the variable for the entire session, use
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/home/user/myproject/lib"
I'd recommend against this, though. It can lead to problems and confusion.

Where to put helper-scripts with GNU autoconf/automake?

I'm working on a project that will be distributed with GNU autoconf/automake, and I have a set of bash scripts which call awk scripts. I would like the bash scripts to end up in the $PATH, but not the awk scripts. How should I insert these into the project? Should they be put in with other binaries?
Also, is there a way to determine the final location of the file after installation? I presume that /usr/local/bin isn't always where the executables end up...
You can just list the scripts that you want to be installed in Makefile.am:
bin_SCRIPTS = foo bar
This will cause foo and bar to be installed during make install. To get the path to their final location, you can use #bindir# in foo.in and let configure build foo for you. For example, in configure.ac:
AC_CONFIG_FILES([foo bar])
and then in foo.in:
#!/bin/sh
prefix=#prefix#
exec_prefix=#exec_prefix#
bindir=#bindir#
echo bindir = $bindir
Keep in mind that the person running configure may specify any of --prefix, --exec_prefix, or
--bindir, and the installation may be redirected with a DESTDIR. Using the technique described here, DESTDIR will not be taken into account and the script will be installed in a location other than the path that it will echo. This is by design, and is the correct behavior, as usually a DESTDIR installation is used to create a tarball that will eventually be unpacked into the filesystem in such a way that the bindir in the script becomes valid.
Add something like this to Makefile.am
scriptsdir = $(prefix)/bin
scripts_DATA = awkscript1 awkscript2
In this case it will install awkscript in $(prefix)/bin (you can also use $(bindir)).
Note: Dont forget that the first should be named name + dir (scripts -> scriptsdir) and the second should be name + _DATA (scripts -> scripts_DATA).
Jonathan, in response to your additional question: if you want to replace the value of prefix at the time of build, you will need to:
rename your script 'myscript' to 'myscript.in'
add a rule to configure.ac to generate it at the bottom
use a macro I made called AS_AC_EXPAND
use it like this:
AS_AC_EXPAND(BINDIR, $bindir)
in your 'myscript.in', you can now use #BINDIR# and it will get expanded to the full path where the script will end up being installed.
Note that you shouldn't use PREFIX directly, any of the installation directories can potentially be changed so you really want to use the value passed to configure for bindir and expand that.
If the awk scripts won't go into the main bin directory (prefix/bin), then you need to place them in an appropriate sub-directory - probably of lib but possibly libexec or share (since the awk scripts are probably platform neutral).
Correct: software won't necessarily end up in /usr/local/bin; on my machine, /usr/local/bin is managed by MIS and all the software I install therefore goes under /usr/gnu/. I use: ./configure --prefix=/usr/gnu to get the software installed where I want it.
You can embed the value of PREFIX in the bash scripts -- effectively, you will 'compile' the scripts to include the install location. Be aware of problems during the build testing - you may need to locate the scripts relative to the current directory first and relative to PREFIX later.

Resources