This question already has answers here:
How to always run command when building regardless of any dependency?
(4 answers)
Closed 1 year ago.
I wanna run a bash script using cmake. And I am partially successful. I am not understanding the workflow properly.
main.cxx
# include <iostream>
int main()
{
std::cout << "hello\n";
}
script.sh
#!/bin/bash
touch file;
CMakeLists.txt
cmake_minimum_required(VERSION 3.21.1)
project(testdir)
add_executable(mainbin "main.cxx")
add_custom_command(TARGET mainbin
POST_BUILD
COMMAND /bin/sh /path/to/testdir/script.sh
)
So, this is my directory tree.
testdir
|
|---CMakeLists.txt
|
|---main.cxx
|
|---script.sh
When I do "cmake . ; make ;", cmake runs the bash script and except the cmake build related files, the directory tree looks like this:
testdir
|
|---file
|
|---CMakeLists.txt
|
|---main.cxx
|
|---script.sh
So it worked.
But now I would want to rename "file" to "renamed_file". So I would edit script.sh. New script.sh looks like this:
#!/bin/bash
mv file renamed_file;
Now when I do "cmake . ; make ;" again, it doesn't work. I mean file "file" doesn't change to "renamed_file". I wanna run script.sh everytime I call "cmake . ; make ;".
Maybe I am missing a very simple thing/basic understanding. What should I do to achieve that?
As you want to keep track of the content of the script, adding it as a source of a custom target will keep track of its mtime and copy it to the build directory if changed. Something like this will help you to achieve that.
add_custom_target(myscript
COMMAND /bin/sh /path/to/testdir/script.sh
SOURCES /path/to/testdir/script.sh
)
Disclaimer, I have not tried it, you night need to iterate on this.
More info: https://cmake.org/cmake/help/latest/command/add_custom_target.html
Related
Is that true that you can't customize the name of your CMakeLists.txt file? I read in a few places that make suffers from the same problem, but that's completely not true, you sure can:
~$ make -f whatever_name_you_feel_like
Can't you do this with CMake?
My situation is as follows: The project leader wants to have a certain CMakeLists.txt file run in the CI workflow and another when developing. I thought it would be possible to just keep 2 CMake files and tell cmake which one to execute.
It's not possible to use file with a name different to CMakeLists.txt, but I'm almost certain that's not actually what you want to do anyways.
I assume the cl version and the development version are mostly similar and only some details change. In this case you should not duplicate the logic. Instead add one or multiple options to your cmake project that can set when you set up the build dir and can even be changed without reconfiguring the whole project from scratch. Basically you add a cache variable to CMakeLists.txt which allows the user to overwrite the default value via -D command line option. The value can also be modified after the initial configuration using cmake-gui.
cmake_mimimum_required(VERSION 3.0.2)
project(MyProject)
# option set to true by default
set(MY_PROJECT_COMMAND_LINE_BUILD 1 CACHE BOOL "Use the command line configuration for MyProject")
#logic common to both configurations
add_executable(MyProg foo.cpp bar.cpp)
if(MY_PROJECT_COMMAND_LINE_BUILD)
#logic only for command line build
target_compile_definitions(MyProg PRIVATE COMMAND_LINE_BUILD)
else()
# logic only for non-command line build
target_compile_definitions(MyProg PRIVATE DEVELOPMENT_BUILD)
endif()
Ironically you could set up both from the command line:
Command line build
cmake -S sourceDir -B buildDir
Development build
cmake -D MY_PROJECT_COMMAND_LINE_BUILD:BOOL=0 -S sourceDir -B buildDir
If you don't want to enter the cache values in the command line every time you set up the project, you could also use a cmake script file to initialize the cache values using the -C command line option.
cmake -C developmentVersion.cmake -S sourceDir -B buildDir
developmentVersion.cmake:
set(MY_PROJECT_COMMAND_LINE_BUILD 0 CACHE BOOL "Use the command line configuration for MyProject")
Theoretically you could the whole CMakeLists.txt file in an if else endif structure and use include in one of the alternatives to competely replace the standard logic in the CMakeLists.txt file, but imho this is not a good idea.
Can't you do this with CMake?
No, it's not possible.
The project leader wants to have a certain CMakeLists.txt file run in the CI workflow and another when developing.
One way: copy or symlink proper CMakeLists.txt before executing cmake.
Preferably one would use cmake scripting language:
# CMakeLists.txt
if (MODE STREUQAL "CI_WORKFLOW")
include(CMakeLists-ci-workflow.txt)
elseif (MODE STREQUAL "DEVELOPING")
include(CMakeLists-developing.txt)
else()
message("SUPER ERROR")
fi()
and then separate CMakeLists-ci-workflow.txt and separate CMakeLists-developing.txt and do cmake -D MODE=DEVELOPING or -D MODE=CI_WORKFLOW.
But overall, the idea of "separate CMakeLists.txt" sounds bad to me. Instead use CMAKE_BUILD_TYPE=Debug for developing and CMAKE_BUILD_TYPE=Release for release builds, and use other cmake variables to differentiate settings, instead of duplicating configuration.
tl;dr;
When using ADD_LIBRARY in CMake it will check during cmake .. that all the source files exist. I have some that are generated during compile time. Is there a way to force CMake to ignore the missing files since they will exist when they are needed?
Full version:
I am trying to build Sqlite3 with CMake, and I can get it to compile, however, I need to comment out a few lines in CMake and run it, then uncomment them and run it again. I would like to do everything in one pass.
The main issue I'm having is that Sqlite3 using a code generator called "lemon". So to properly build everything, I need to compile lemon. Then I need to run lemon. Once lemon is done running there is a new file called "parse.c". "parse.c" needs to be included with the source to build sqlite3.
I will strip down the code to show the applicable bits.
set(SourceFiles
#Lots of Files are here
build/parse.c
)
add_executable(lemon sqlite/tool/lemon.c)
add_custom_target(CreateParse ALL COMMAND
cp ../sqlite/tool/lempar.c .
&& ./lemon -s ../sqlite/src/parse.y
&& mv ../sqlite/src/parse.h .
&& mv ../sqlite/src/parse.c .
&& mv ../sqlite/src/parse.out .)
add_library(sqlite ${SourceFiles})
add_dependencies(CreateParse lemon)
add_dependencies(sqlite CreateParse)
So there are 3 steps that happen here:
The executable lemon is created.
lemon is used to create parse.c (among other things)
parse.c is compiled with a ton of other files to create the sqlite3 library.
With the add_dependencies this should work. But it doesn't. The reason it doesn't work is that build/parse.c doesn't exist on the first pass. I've been able to work around it by commenting out parse.c. After it is done half building. Then I can uncomment it and it will compile successfully.
I've looked into using ADD_CUSTOM_COMMAND, which I believe will run lemon when cmake .. is run rather than ninja is run. However, when cmake .. is run, "lemon" doesn't exist yet so the custom command is unable to run.
Edit 1
So I came up with a hack...
execute_process(COMMAND touch ${CMAKE_SOURCE_DIR}/build/parse.c)
So when cmake .. is run it will create an empty file in the correct spot. However, there has to a be a better way. Help is appreciated.
I have a project laid out like so:
CMakeLists.txt
|
|--------subdir1
| |--CMakeLists.txt
| |--sourcefiles
| |--filetocopy1
|
|--------subdir2
|--CMakeLists.txt
|--sourcefiles
|--filetocopy2
I want to copy filetocopy1 and filetocopy2 to a specified output directory in the build folder. So in both, I have something like
add_custom_command(
TARGET nameoftargetinsubdir1
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"${CMAKE_CURRENT_SOURCE_DIR}/filetocopy1"
"${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
)
The problem is that if the filetocopy1 or filetocopy2 change, but the source files don't, then invoking make in the build folder doesn't copy the files. Is there some way to force it to copy those files? I'm getting the feeling I might have to put the copy commands in the top level CMakeLists.txt file.
The main purpose of POST_BUILD custom commands is being run whenever target executable/library is rebuilt. If you don't need such behavior, use common custom commands with OUTPUT and DEPENDS option, combined with add_custom_target:
# If someone needs file "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/filetocopy1",
# this command will create it if the file doesn't exist or is older than
# "${CMAKE_CURRENT_SOURCE_DIR}/filetocopy1".
add_custom_command(
OUTPUT "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/filetocopy1"
COMMAND ${CMAKE_COMMAND} -E copy
"${CMAKE_CURRENT_SOURCE_DIR}/filetocopy1"
"${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/filetocopy1"
)
# Custom target for activate the custom command above
add_custom_target(copy_file1 DEPENDS "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/filetocopy1")
# Whenever target 'nameoftargetinsubdir1' is requested, the custom target will be evaluated.
add_dependencies(nameoftargetinsubdir1 copy_file1)
If you simply want to copy these files to another place, I recommend to use the following command:
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/filetocopy1 ${CMAKE_BINARY_DIR}/ COPYONLY)
https://cmake.org/cmake/help/v3.10/command/configure_file.html
This command is simple and effective. And it will copy your file to the destination, whenever cmake is invoked.
Is there something equivalent to qmake -project that will automatically create a CMake project from a directory of source and header files?
Ideally, this should work recursively.
No, but it's an easy project to set up:
project(myProject)
enable_language(CXX)
file(GLOB SRC_FILES *.cpp)
include_directories(${PROJECT_SOURCE_DIR})
add_executable(myExe ${SRC_FILES})
Assuming you are making an executable. add_library should be used if you are making a library. And just change the paths if your project things in subdirectories like src and include.
(I know this was asked quite a while ago, but I'll post my answer anyway, for future reference.)
I think the better idea is not to have a CMakeLists.txt that automatically adds all sources with a glob every time it is run, but a static one instead. I guess that what was originally meant was a script that scans the current directory (recursively) for source files and adds them to the CMake file. This could possibly save a lot of time copying the name of every source file to the CMake file. Therefore: Let's automate it!
Create a file named cmake-project.sh with the following content:
#!/bin/bash
# use the first argument as project name
PROJECT_NAME=$1
# find source files, but exclude the build directory
PROJECT_SOURCES=$(find . -iname "*.cpp" -not -path "./build/*")
# find header files, but exclude the build directory;
# only print the name of the directory; only print unique names
PROJECT_SOURCE_DIR=$(find . -iname "*.h" -not -path "./build/*" \
-printf "%h\n" | sort -u)
# The standard content of the CMakeLists.txt can be edited here
cat << EOF > CMakeLists.txt
cmake_minimum_required (VERSION 2.8)
set(PROJ_NAME $PROJECT_NAME )
set(PROJ_SOURCES $PROJECT_SOURCES )
project(\${PROJ_NAME})
include_directories(${PROJECT_SOURCE_DIR})
add_executable(\${PROJ_NAME} \${PROJ_SOURCES})
EOF
Then, make the file executable with chmod +x cmake-project.sh. Now you can run ./cmake-project.sh [your_project_name] in the root directory to create a static (i.e. without glob) CMakeLists.txt automatically.
Of course you'll have to adapt things if necessary (e.g. use .cc instead of .cpp), but you get the idea.
I am trying to write a CMakeLists.txt to speed up compilation.
The executable depends on a script generated .cpp file: I use the cppcms web application library which has a templating system where .tmpl must be converted to .cpp files during the compilation like this:
cppcms_tmpl_cc page.tmpl -o page.cpp
There are related questions that cover the use of bash commands within cmake:
How to run a command at compile time within Makefile generated by CMake?
CMake : how to use bash command in CMakeLists.txt
These questions cover most of my needs.
What I want to know, now, is how to tell cmake to run the above command and re-generate page.cpp every time page.tmpl itself has changed, and only then?
The goal obviously is to improve the compile time and have an up to date binary with the latest template.
(can a moderator add the cppcms tag?)
[Edit: I am actually trying to convert the following Makefile to cmake:
LIBS=-lcppcms -lconfig++ -lboost_filesystem-mt
all: clean gitbrowser
gitbrowser: main.cpp view.cpp content.hpp gitbrowser.cpp
$(CXX) -Wall main.cpp gitbrowser.cpp view.cpp -o run ${LIBS}
view.cpp: page.tmpl content.hpp
cppcms_tmpl_cc page.tmpl -o view.cpp
[Edit2: I added a note about the solution in the official cppcms wiki:
http://art-blog.no-ip.info/wikipp/en/page/cppcms_1x_howto#How.to.compile.the.templates.with.cmake.
now = get_now_time()
time = get_last_upd_time()
if (now > time)
set (LAST_UPD_TIME time CACHE INTERNAL "Defines last update time of the file" FORCE)
# run bash command here
endif (now > time)
get_now_time and get_last_upd_time are fictional functions, returning timestamps (I guess you can use bash commands to get those timestamps). Then you can compare them and store last modification timestamp into cache.
However, this solution looks ugly for me, as I know if you properly define targets and dependencies between them CMake itself will take care of rebuilding only modified files, doesn't it? Could you show me target definitions?
edit
You can use following CMakeLists.txt (thougn I'm not sure, it's based on my project):
# add main target, the executable file
ADD_EXECUTABLE(gitbrowser main.cpp view.cpp content.hpp gitbrowser.cpp)
# linking it with necessary libraries
TARGET_LINK_LIBRARIES(gitbrowser "cppcms config++ boost_filesystem-mt")
# add page.cpp target
ADD_CUSTOM_COMMAND(
OUTPUT page.cpp
COMMAND "cppcms_tmpl_cc page.tmpl -o view.cpp"
DEPENDS page.tmpl content.hpp
)
# and finally add dependency of the main target
ADD_DEPENDENCIES(gitbrowser page.cpp)
Good luck
Take a look on this CMake file of Wikipp lines 66-72
You basically need something like this:
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/view.cpp
COMMAND cppcms_tmpl_cc
view.tmpl
-o ${CMAKE_CURRENT_BINARY_DIR}/view.cpp
DEPENDS view.tmpl)
Edit: Also if you want to improve compilation speed you may compile
the view into shared object and load it dynamically.
This would also allow you not to restart application if you only changed the view, the
shared object after recompilation would be automatically reloaded.
See: http://art-blog.no-ip.info/wikipp/en/page/cppcms_1x_config#views for more details.