How to choose which CMake executable target will be the default one? - build

I have written a CMakeLists.txt file including 2 executables (target1 and target2):
ADD_EXECUTABLE(target1 ${CXX_FILES})
TARGET_LINK_LIBRARIES(target1 ${requiredlibs})
ADD_EXECUTABLE(target2 ${CXX_FILES} ${OTHER_CXX_FILES})
TARGET_LINK_LIBRARIES(target2 ${requiredlibs})
Now every time when I run make without any parameters both targets are rebuilt. But I want to define target1 as default executable so that running make without any parameters only builds target1. For building target2 I would run make target2.
Is this possible?
In the Makefile created by CMake there is the following definition:
default_target: all
I think I need a way to set this default_target to target1.
Another problem I have is that make always rebuilds the targets, even if no source file has been changed.

An example CMakeLists.txt that does what you requested:
ADD_EXECUTABLE(target1 a.c)
ADD_EXECUTABLE(target2 EXCLUDE_FROM_ALL b.c)
For me it does not rebuild the target if the source files are not changed (or modification time did not change). Output I get:
$ make -f Makefile
Scanning dependencies of target target1
[100%] Building C object CMakeFiles/target1.dir/a.c.o
Linking C executable target1
[100%] Built target target1
[$ make -f Makefile
[100%] Built target target1
Note that the second make does not rebuild anything.
(you could read the CMake manual for this type of information)

Related

Best practice to run a prebuild step with CMake

Suppose you have a repository with a folder (named dataset) with several .csv files and a python script (named csvcut.py) that takes all the .csv in dataset and generates corresponding .h files.
Those .h files are included in some .cpp files to build an executable (add_executable(testlib...) used for testing.
Suppose you use add_custom_target(test_pattern... to make a target (named test_pattern) that runs csvcut.py, and add_dependencies(testlib test_pattern) to run the script before building testlib.
This works, but it would be better if:
the script was run only when the files in dataset folder or the script itself changes (not when .cpp changes);
the .h files was generated in a subfolder of the build folder (i.e. build/tests/dataset/), and included in the .cpp files like so #include <tests/dataset/generated.h>.
Do you have any suggestions for making these improvements / optimizations?
Thanks, Alberto
This requires multiple steps, but can all be handled with standard CMake. First, we'll use add_custom_command to actually generate the files. I'm also adding a custom target, but only since I couldn't figure out how to make an INTERFACE library work without it.
add_custom_command(
OUTPUT
"${CMAKE_CURRENT_BINARY_DIR}/include/foo.h"
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/gen.py"
DEPENDS
gen.py
foo.h.in
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include"
)
add_custom_target(gen_files
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/include/foo.h"
)
For my case, gen.py just spits out a basic header file, but it shouldn't matter. List whatever files you need as output, and your csv file should be under DEPENDS (for me, foo.h.in tries to simulate this).
Since you only mentioned generating header files, I created an INTERFACE library that depends on the gen_files target. I also added the appropriate include directory.
add_library(foo INTERFACE)
target_include_directories(foo
INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/include"
)
add_dependencies(foo
gen_files
)
If building a STATIC/SHARED library, I was able to add the generated files directly as sources and dependencies worked, but the INTERFACE library required the extra target (even when I tried listing the files under add_dependencies). Since you already have a custom target, I assume this won't be a huge issue.
Lastly, I have an executable that links against foo.
add_executable(main
main.c
)
target_link_libraries(main
PRIVATE
foo
)
Demo:
$ make clean
$ ls
CMakeCache.txt CMakeFiles cmake_install.cmake include Makefile
$ make
[ 33%] Generating include/foo.h
[ 33%] Built target gen_files
[ 66%] Building C object CMakeFiles/main.dir/main.c.o
[100%] Linking C executable main
[100%] Built target main
$ make clean
$ make main
[ 33%] Generating include/foo.h
[ 33%] Built target gen_files
[ 66%] Building C object CMakeFiles/main.dir/main.c.o
[100%] Linking C executable main
[100%] Built target main
$ make
[ 33%] Built target gen_files
[100%] Built target main
$ touch ../foo.h.in
$ make
[ 33%] Generating include/foo.h
[ 33%] Built target gen_files
[100%] Built target main
$ touch ../gen.py
$ make
[ 33%] Generating include/foo.h
[ 33%] Built target gen_files
[100%] Built target main
$ ls include/
foo.h
If either the input (foo.h.in) or generation script (gen.py) change, the targets are rebuilt.

How can I convert a configure script to a CMakeLists file?

I'm wanting to use this library in my cmake project, however it comes with a configure file instead of CMakeLists.txt
#! /bin/sh
# waf configure wrapper
# based on http://code.google.com/p/waf/source/browse/configure
CUR_DIR=$PWD
#possible relative path
WORKINGDIR=`dirname $0`
cd $WORKINGDIR
#abs path
WORKINGDIR=`pwd`
cd $CUR_DIR
WAF=$WORKINGDIR/tools/waf
# Generates a Makefile. Requires that $WAF is set.
#
generateMakefile()
{
cat > Makefile << EOF
#!/usr/bin/make -f
# Waf Makefile wrapper
WAF_HOME=$CUR_DIR
all:
#$WAF build
all-debug:
#$WAF -v build
all-progress:
#$WAF -p build
install:
$WAF install --yes;
uninstall:
$WAF uninstall
clean:
#$WAF clean
distclean:
#$WAF distclean
#-rm -rf build
#-rm -f Makefile
check:
#$WAF check
dist:
#$WAF dist
.PHONY: clean dist distclean check uninstall install all
EOF
}
generateMakefile
"${WAF}" configure $*
exit $?
Are there automated tools for the conversion? Does CMake supoprt the use of configure files? Is there a rule of thumb for conversion - i.e. replace ... with add_library?
You don't need to convert an upstream library to cmake to be able to use it in cmake projects. As long as you're able to install and/or link to that library, you can configure your cmake project to use it.
A common pattern to consume third-party libraries with cmake is to use cmake's find_package() function by supplying your own special-purpose cmake module files to find and configure the library.
Say, you're hoping to load libfoo. Here are the steps:
create a directory within your project tree to store your custom cmake modules (say, ./cmake/modules)
in that directory create a text file named FindFoo.cmake.
Within FindFoo.cmake add checks to determine if foo is actually present in the system. If it is then set the relevant variables. Otherwise, throw an error. The ideal thing is to create a build target for that library, which you can simply add as dependencies to other cmake targets.
Configure your cmake project to use your local modules by adding set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules/"),
Finally, configure your cmake project to include your Foo library by calling find_package(Foo REQUIRED).
The way to do the conversion is to read the configure script and understand what it does and how/why. Then write a CMakeLists.txt file that achieves the same.
There are no shortcuts.

How to find my generated shared library from cmake?

I want to create a shared library from cmake . I have a simple test.cpp .
My CMakeLists.txt looks like below
cmake_minimum_required(VERSION 2.8)
project (test)
set(CMAKE_BUILD_TYPE Release)
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(test SHARED /home/tuhin/test/test1/test.cpp)
But I am not able to find "test" which my .so, I have seen test.dir folder created but not .so
Please help me to understand the issue.
(I suppose you read the comments and acted accordingly...)
(I also suppose you need a way to find out where your library will be placed, from within CMake build system)
The disk location of any target does not depend on CMakeLists.txt only, but also on the choice of the generator. Multi config generators like Visual Studio something, or Xcode might append configuration name as an additional directory, so you may get different results just by choosing a different generator.
This means that there is no easy way to uniquely identify disk location during configure stage. On the other hand, you may very easily check that information during the build stage:
cmake_minimum_required(VERSION 3.15)
project (lib_file_name)
add_library(my_test_lib SHARED my_test_lib.cpp)
add_custom_target(output_lib_name
ALL
COMMAND ${CMAKE_COMMAND} -E echo "my_test_lib location: $<TARGET_FILE:my_test_lib>"
)
note add_custom_target line:
new target was added, named output_lib_name
it will be executed as a part of building the default target (-> ALL)
command to build this target is asking cmake to output the file name of the target in question, using CMAke generator expressions (--> COMMAND ${CMAKE_COMMAND} -E echo "my_test_lib location: $<TARGET_FILE:my_test_lib>")
If you run it with makefile generator:
$ cmake -S /tmp -B /tmp/make-build -G "Unix Makefiles" ; cmake --build /tmp/make-build
-- Configuring done
-- Generating done
-- Build files have been written to: /tmp/make-build
Scanning dependencies of target my_test_lib
[ 50%] Building CXX object CMakeFiles/my_test_lib.dir/my_test_lib.cpp.o
[100%] Linking CXX shared library libmy_test_lib.dylib
[100%] Built target my_test_lib
Scanning dependencies of target output_lib_name
my_test_lib location: /tmp/make-build/libmy_test_lib.dylib
[100%] Built target output_lib_name
Note the line
my_test_lib location: /tmp/make-build/libmy_test_lib.dylib
if you run it with Xcode generator:
configure:
$ cmake -S /tmp -B /tmp/xcode-build -G Xcode
-- Configuring done
-- Generating done
-- Build files have been written to: /tmp/xcode-build
build release configuration:
$ cmake --build /tmp/xcode-build --config Release
........... lot of output deleted ...........
my_test_lib location: /tmp/xcode-build/Release/libmy_test_lib.dylib
** BUILD SUCCEEDED **
build debug configuration:
$ cmake --build /tmp/xcode-build --config Debug
........... lot of output deleted ...........
my_test_lib location: /tmp/xcode-build/Debug/libmy_test_lib.dylib
** BUILD SUCCEEDED **
Note how the location is different for different configuration builds, without any change in CMake build system.
At the end, this is the cmake documentation about add_custom_command, cmake generator expressions.

recursive Makefile: want to run phony target then run the all target

Given this Makefile snippet:
TARGETS = ${SHARED_LIB_A} ${SHARED_LIB_B}
.PHONY: subdirs $(SUBDIRS)
subdirs: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $#
all: $(TARGETS)
I want to modify this Makefile so that TARGETS get built after the PHONY target is run. Ie. TARGETS depends on the code in SUBDIRS being built - TARGETS needs to be run after SUBDIRS. Right now the way this works is that the PHONY target gets run (the subdirs are built), but not the all target (unless I specifically run it like 'make all' - but I want to avoid doing that if possible, I just want to run 'make' and have it build the subdirectories and then build TARGETS.
First, you should have all as the first rule, not the last one. Unless you specify a specific target on the make command line, it will run the first target in the makefile (here, subdirs).
Second, you should declare subdirs as a prerequisite of $(TARGETS); that will ensure that they are not built until after subdirs is complete:
all: $(TARGETS)
$(TARGETS): subdirs
Now, because subdirs is PHONY it means that $(TARGETS) will always be considered out of date and always be built, even if invoking subdirs didn't change any files. However, there's no other alternative as you've written your makefile here, because make cannot know what files (created by the submakes) you want to use to see if the library is out of date.
Alternatively you can specify that subdirs is an order-only prerequisite:
$(TARGETS): | subdirs

Have makefile based cmake projects automatically run make rebuild_cache when an environment variable changes

In my cmake configuration several variables depend on an environment variable in order to be set correctly. This environment variable can change and it means that the cache for cmake should be rebuilt.
My configuration can detect the need for this reconfigure and update the appropriate cache entries when either another call to "cmake " is called or "make rebuild_cache" is called.
However, I would like whenever I run make it to be checked automatically for changes and have the rebuild_cache target run if necessary.
Is this possible?
Make does not have a memory. There is no way for make to "remember" what a given environment variable was set to the last time make was run.
Unless you write the environment variable to a file.
I've never used CMake, so I don't know how to best implement it. But on the "raw" make level, the general idea would be to:
1) write a rule (say, envir_cache) which writes the environment variable to file (named, not so coincidentially, envir_cache), if that file does not yet exist, or the file exists but its contents are different from the value of the environment variable. (Something along the lines of if [ -f envir_cache ] and read cached_var < envir_cache and if [ "${myvar}" != "${cached_var}" ].)
2) make the target rebuild_cache depend on envir_cache.
That way, the rebuild_cache rule will be executed on the first run, and whenever the variable changed between runs.
This is not easily done with CMake, but an example of how to do this as a CMake include modules is shown below. The solution depends on using a custom target that outputs the variables in question to a file and calls the cmake compare_files option to compare the previous file to the verify file and calls cmake to rebuild the cache in the event they don't match.
The solution involves a carefully crafted CMake include module that will recursively call itself to verify the cached values haven't been changed by an Environment variable. If it has, it will perform the rebuild cache step by calling cmake with appropriate arguments as shown below. It is expected that you will call the add_option macro for every variable you want to be able to override using an Environment variable (see examples below):
# Capture the full path to this CMake module file
if(NOT _option_cmake_file)
set(_option_cmake_file ${CMAKE_CURRENT_LIST_FILE})
endif()
# When this CMake module is called as a script include the option file
if(_option_verify)
include(${_option_file})
endif()
# add_option macro for adding cached values you want to be able to
# override with an environment variable of the same name
# _name - variable name to use for the cached value
# _type - type of cached variable
# _description - description of cached variable for CMake GUI
# _default - default value if no variable with same name is defined
macro(add_option _name _type _description _default)
# Define _option_file to be created if not in verify mode
if(NOT _option_verify)
set(_option_file ${CMAKE_BINARY_DIR}/${_name}.cmake)
endif()
# Determine the source for the alue of the cached variable
set(_option_output "set(_name ${_name})")
list(APPEND _option_output "\nset(_type ${_type})")
list(APPEND _option_output "\nset(_description \"${_description}\")")
if(DEFINED ENV{${_name}})
set(${_name} $ENV{${_name}} CACHE ${_type} "${_description}" FORCE)
list(APPEND _option_output "\nset(${_name} $ENV{${_name}})")
elseif(${_name})
set(${_name} ${${_name}} CACHE ${_type} "${_description}" FORCE)
set(ENV{${_name}} ${${_name}}) # needed to pass from verify back to rebuild_cache
list(APPEND _option_output "\nset(${_name} ${${_name}})")
else()
set(${_name} ${_default} CACHE ${_type} "${_description}" FORCE)
list(APPEND _option_output "\nset(${_name} ${_default})")
endif()
# Create the _option_file (or verify file) containing the values
# defined above
execute_process(
COMMAND ${CMAKE_COMMAND} -E echo ${_option_output}
OUTPUT_FILE ${_option_output}${_option_verify})
# If not in verify mode create check target to verify value
if(NOT _option_verify)
# Only create parent check-variables target once
if(NOT TARGET check-variables)
add_custom_target(check-variables ALL)
endif()
# Use this file as custom CMake target to verify variable value
add_custom_target(check-${_name}
COMMAND ${CMAKE_COMMAND}
-D_option_verify:String=-verify
-D_option_file:Filepath=${_option_file}
-D_option_sdir:Path=${CMAKE_SOURCE_DIR}
-D_option_bdir:Path=${CMAKE_BINARY_DIR}
-P ${_option_cmake_file}
COMMENT "Checking variable '${_name}' for changes"
VERBATIM)
# Add custom target as dependency for parent check-variables target
add_dependencies(check-variables check-${_name})
else()
# Use cmake to compare options file and verify file created above
execute_process(
COMMAND ${CMAKE_COMMAND} -E compare_files
${_option_file} ${_option_file}${_option_verify}
OUTPUT_VARIABLE COMPARE_OUTPUT
ERROR_VARIABLE COMPARE_ERROR
RESULT_VARIABLE COMPARE_RESULT)
# Remove verify file
file(REMOVE ${_option_file}${_option_verify})
# If compare failed, then call CMAKE to rebuild_cache
if(NOT COMPARE_RESULT EQUAL 0)
# Perform the rebuild_cache step
execute_process(
COMMAND ${CMAKE_COMMAND} -H${_option_sdir} -B${_option_bdir})
endif()
endif()
endmacro()
# In verify mode? then call add_option macro to initiate the process
if(_option_verify)
# The values below come from the include(_option_file) above
add_option(${_name} ${_type} "${_description}" ${${_name}})
endif()
If the above CMake module was named add_option.cmake you could use it as follows:
cmake_minimum_required(VERSION 2.8)
project(Example)
include(${PROJECT_SOURCE_DIR}/add_option.cmake)
add_option(MYVAR
BOOL
"A boolean cached value that can be overridden by Environment variable"
ON)
add_option(MYSTR
STRING
"A string cached value that can be overridden by Environment variable"
"some string")
message(STATUS "MYVAR=${MYVAR}")
message(STATUS "MYSTR=${MYSTR}")
With the above CMakeLists.txt file do the following (using Unix Makefiles):
mkdir build
cd build
The following example demonstrates the initial creation of the Unix Makefiles. Note the variables use their default values in this case.
cmake .. -G "Unix Makefiles"
-- The C compiler identification is GNU 4.8.4
-- The CXX compiler identification is GNU 4.8.4
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- MYVAR=ON
-- MYSTR=some string
-- Configuring done
-- Generating done
-- Build files have been written to: /home/user/example/build
The following example shows how make can invoke the check-variables target and its dependent targets created by the add_option.cmake module above. Notice how no rebuild cache occurs.
make
Scanning dependencies of target check-MYVAR
[ 50%] Checking variable 'MYVAR' for changes
[ 50%] Built target check-MYVAR
Scanning dependencies of target check-MYSTR
[100%] Checking variable 'MYSTR' for changes
[100%] Built target check-MYSTR
Scanning dependencies of target check-variables
[100%] Built target check-variables
The following example shows how an environment variable causes one of the check-variables step to fail and triggers a rebuild cache event. Note the change in the MYVAR value during the cache rebuild process.
make MYVAR=off
[ 50%] Checking variable 'MYVAR' for changes
-- MYVAR=off
-- MYSTR=some string
-- Configuring done
-- Generating done
-- Build files have been written to: /home/user/example/build
[ 50%] Built target check-MYVAR
[100%] Checking variable 'MYSTR' for changes
[100%] Built target check-MYSTR
[100%] Built target check-variables
The following example shows how the variable changed temporarily above is reverted back to the defaults and a different variable is triggered to change its value. Note the MYVAR variable revert back to its default value while the MYSTR variable obtains a new value provided.
make MYSTR="hi mom"
[ 50%] Checking variable 'MYSTR' for changes
-- MYVAR=ON
-- MYSTR=hi mom
-- Configuring done
-- Generating done
-- Build files have been written to: /home/user/example/build
[ 50%] Built target check-MYSTR
[100%] Checking variable 'MYVAR' for changes
[100%] Built target check-MYVAR
[100%] Built target check-variables