How to trigger update of ExternalProject when UPDATE_DISCONNECTED set to ON - cmake

I have following CMakeLists.txt file in external directory in my project root. It is supposed fetch Catch (header only unit test library) for me.
include(ExternalProject)
ExternalProject_Add(
Catch
# I want to have it downloaded only once,
# therefore CMAKE_CURRENT_SOURCE_DIR which is projectRoot/external
PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/Catch
GIT_REPOSITORY https://github.com/philsquared/Catch.git
# disables auto update on every build
UPDATE_DISCONNECTED 1
# disable following, since it is not needed
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_DIR ""
INSTALL_COMMAND ""
)
It works well for me, except one thing.
I have set UPDATE_DISCONNECTED to 1 since I do not want to check for updates in every build I make (checking for updates takes some time).
But I would still like to have the opportunity to update external project from CMake itself. Eg. by doing make Catch_update or make external_update_all or whatever.
Is there better way to do that than writing custom target calling git pull in external project directory? If yes, then how? Thanks!

The ExternalProject module has a Target Option, STEP_TARGETS, to which you can add, for instance, "update". In your call to ExternalProject_Add, simply add a line, STEP_TARGETS update. That will automatically create a target (in your case, Catch-update), which calls a pretty sophisticated CMake script (in your case, probably projectRoot/external/Catch/tmp/Catch-gitupdate.cmake). The ExternalProject module itself creates this gitupdate script. The script does a lot more than a simple pull; it will stash local changes, as needed, and pop them back, for instance. I just used this about a week ago and was pretty happy with it.
From the CMake documentation for the UPDATE_DISCONNECTED option (boldface added by me):
When enabled, this option causes the update step to be skipped. It does not, however, prevent the download step. The update step can still be added as a step target (see ExternalProject_Add_StepTargets()) and called manually. This is useful if you want to allow developers to build the project when disconnected from the network (the network may still be needed for the download step though).
When you call ExternalProject_Add with a STEP_TARGETS option, however, ExternalProject_Add_StepTargets is called for you automatically.
One final thing: You don't need to call find_package(Git REQUIRED), as you did in the first answer. By virtue of setting GIT_REPOSITORY in your call to ExternalProject_Add, the module itself will attempt to find git (and will issue a FATAL_ERROR message if it can't).

OK, so after additional searching, this seems to be the only way to do this. Add custom target with an update. It is not pretty, but it is working.
#we need git executable
find_package(Git REQUIRED)
# update Catch target
add_custom_target(external-Catch-update
COMMENT "Updated Catch"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Catch/src/Catch
COMMAND ${GIT_EXECUTABLE} pull
DEPENDS Catch)

Related

Cmake is failing to call a custom command [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?

Is it possible to force CMake to run add_compile_definitions() each time?

I have an embedded project (using ESP-IDF which builds projects with CMake), where I have a props.json file that contains several settings (e.g. "device type"). For example based on the actual value of "deviceType" the CMake open and read props.json by calling execute_process() and jq, then defines C preprocessor macros, such as: DEVICE_TYPE_A by using add_compile_definitions().
The problem is that, this will run only when I modify the CMakeLists.txt or clean the whole project, but I don't want to recompile each components when I change the props.json only the files that I wrote (so, depend on the settings). I'd like to make CMake read the file each time I build the project without cleaning it.
I did my research, so I know there are add_custom_target() and add_custom_command() that behave that way, however add_compile_definitions() cannot be called in a script. Is there a solution to achieve this or should I just use a header file configured by configure_file() and leave add_compile_definitions() alone?
This is actually pretty easy and you don't need to manually reconfigure CMake. Just add the following to the CMakeLists.txt in the directory containing your props.json file:
set_property(DIRECTORY . APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS props.json)
This will add props.json to the list of files that the CMake-generated build scans when determining whether to re-run the CMake configure step. See the docs on CMAKE_CONFIGURE_DEPENDS for more detail.
In general, you should never need to manually re-run CMake1 after the first configure. If you do, it is an indication that you have not communicated all of the necessary information for CMake to generate a correct build system.
1 There is one notable exception: Xcode is known to be buggy when re-running the CMake configure step automatically.

How do I use ExternalProject_Add to get a simple make invocation?

I need ExternalProject_Add to invoke just one command for an external project whose makefile does all the needed steps. I need to have that command be "make WITH_OPTION1=no WITH_OPTION2=no" in the directory at the top of the external project's source tree. This is one of a few dozen external projects we use. Most of them fit the model of 'configure; make; make install' but a good third don't and this is one that I thought would be easy.
If I try to have the make command invoked like this:
ExternalProject_Add(build-example
PREFIX "${CMAKE_CURRENT_BINARY_DIR}/example"
DEPENDS ""
SOURCE_DIR "${PROJECT_TOPDIR}/External/example"
CONFIGURE_COMMAND ""
BUILD_COMMAND COMMAND make WITH_OPTION1=no WITH_OPTION2=no
INSTALL_COMMAND "")
The resulting make step is done from the wrong directory and therefore the Makefile is not found.
If I add a -C option to the make command it seems to use the right directory, but it ignores the WITH_OPTION stuff. That doesn't compile properly, because those features use things I don't want to provide, and if it did work, I still don't want those features.
If I then add quotes around the entire desired make command, it goes wrong. Apparently the command is passed to 'sh' in such a way that sh fails.
If I use a mechanism to pass the command into a 'configure-file' step and invoke that file from the BUILD_COMMAND, then CMake actually tries to use a broken CMakeLists.txt file that's in the external project, and I wonder what makes CMake think that it should do so.
I just want to invoke the makefile properly, using CMake to organize that build within the dozens of other builds that have to be done to build the complete project.

cmake third party project step

We are using a vendor code as third party project in our source code. The Vendor code uses Makefile, for which we wrote new CMake add_custom_target for vendor source code.
To copy library from vendor specific build/lib dir to Our CMAKE binary/library dir, I coded Step to copy all the libs as
ExternalProject_Add_Step(CopyStep)
However I see that whenever I build, CopyStep is executed all the time. Is there any way to control the CopyStep to exec only if there is change in library (something similar as Make, whenever there is not code change, source code is not rebuilt).
Let me know if there is any other way I could do copy etc.
For make an ExternalProject's step to be re-executed only when some file(s) are changed, add DEPENDS option to it:
ExternalProject_Add_Step(extLibrary CopyStep
COMMAND cp <BINARY_DIR>/lib/libext.a <...>
DEPENDS <BINARY_DIR>/lib/libext.a
)
Alternatively (e.g. if you don't want to list all files you depends on), you may make the step to be a part of the build step. For that, modify ExternalProject_Add by adding appropriate command:
ExternalProject_Add(extLibrary
...
BUILD_COMMAND make # Need to explicitely specify build command.
COMMAND cp -r <BINARY_DIR>/lib <...> # Additional action for the build step
)

CMake run command during generation

I'm trying to have cmake download some files. Is it possible to do this once, when the "Generate" button is pressed? I can only set it up to run each time the configure button is pressed or each time the project is built.
CMakeLists are processed at configure time, so you can't have it do things at generate time. You could, however, set up a cache variable and use it as a flag to determine if the download should happen or not. Something like:
if(NOT DOWNLOAD_HAPPENED)
execute_process( ... do the downloading stuff ... )
set(DOWNLOAD_HAPPENED TRUE CACHE BOOL "Has the download happened?" FORCE)
endif()
This will execute the download on first configure and never again (unless the user manually resets the DOWNLOAD_HAPPENED) variable. However, if you really need the download to happen at the last configure, you're out of luck, AFAIK.
Something like this should help:
add_custom_command(
OUTPUT myfile.txt
COMMAND wget http://myurl.com/myfile.txt
)
EDIT 1
It's require to make it as a dependency of the main command:
add_dependencies(<myprogram> wget)