CMake collect and process all source files in top directory - cmake

I am trying to collect and process all source files in the top directory, to deal with the fact that variables are not passed to sub directories. I have cobbled together the following CMakeLists.txt files:
cmake_minimum_required(VERSION 2.8)
project(main)
enable_language(Fortran)
enable_testing()
set (CMAKE_Fortran_COMPILER ifort)
set (CMAKE_Fortran_FLAGS " -g -C -fixed")
set (CMAKE_Fortran95_FLAGS " -openmp ")
# function to collect all the sources from sub-directories
# into a single list
function(add_sources)
get_property(is_defined GLOBAL PROPERTY SRCS_LIST DEFINED)
if(NOT is_defined)
define_property(GLOBAL PROPERTY SRCS_LIST
BRIEF_DOCS "List of source files"
FULL_DOCS "List of source files to be compiled in one library")
endif()
# make absolute paths
set(SRCS)
foreach(s IN LISTS ARGN)
if(NOT IS_ABSOLUTE "${s}")
get_filename_component(s "${s}" ABSOLUTE)
endif()
list(APPEND SRCS "${s}")
endforeach()
# append to global list
set_property(GLOBAL APPEND PROPERTY SRCS_LIST "${SRCS}")
endfunction(add_sources)
add_sources(SRCS main.f95)
add_subdirectory(sub)
# preprocess sources
set(PREP_SRCS)
get_property(SRCS GLOBAL PROPERTY SRCS_LIST)
foreach(s IN LISTS SRCS)
file(RELATIVE_PATH rs "${CMAKE_CURRENT_SOURCE_DIR}" "${s}")
string(REGEX REPLACE "f95$" "f" o "${CMAKE_CURRENT_BINARY_DIR}/${rs}")
add_custom_command(
OUTPUT "${o}"
COMMAND ${CMAKE_COMMAND} -E copy "${s}" "${o}"
DEPENDS "${s}"
if(${s} MATCHES "f95$")
set_source_files_properties( ${o} PROPERTIES
COMPILE_FLAGS ${CMAKE_Fortran95_FLAGS})
endif(${s} MATCHES "f95$")
COMMENT "Creating ${o}"
VERBATIM
)
list(APPEND PREP_SRCS "${o}")
endforeach()
message(${PREP_SRCS})
add_executable(main ${PREP_SRCS})
SET_TARGET_PROPERTIES(main PROPERTIES LINKER_LANGUAGE Fortran)
SET_TARGET_PROPERTIES(main PROPERTIES
LINK_FLAGS " ")
and in sub/
add_sources(SRCS saxpy2.f95 saxpy_noomp.f)
which cmakes correctly:
[baron#ray:1034]$ cmake ..
-- The C compiler identification is AppleClang 7.0.2.7000181
-- The CXX compiler identification is AppleClang 7.0.2.7000181
-- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc
-- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- The Fortran compiler identification is Intel 15.0.1.20141022
-- Check for working Fortran compiler: /opt/intel/composer_xe_2015.1.108/bin/intel64/ifort
-- Check for working Fortran compiler: /opt/intel/composer_xe_2015.1.108/bin/intel64/ifort -- works
-- Detecting Fortran compiler ABI info
-- Detecting Fortran compiler ABI info - done
-- Checking whether /opt/intel/composer_xe_2015.1.108/bin/intel64/ifort supports Fortran 90
-- Checking whether /opt/intel/composer_xe_2015.1.108/bin/intel64/ifort supports Fortran 90 -- yes
/Users/baron/teach/comps/ompstuff/new_new_cmake_test/build/SRCS/Users/baron/teach/comps/ompstuff/new_new_cmake_test/build/main.f/Users/baron/teach/comps/ompstuff/new_new_cmake_test/build/sub/SRCS/Users/baron/teach/comps/ompstuff/new_new_cmake_test/build/sub/saxpy2.f/Users/baron/teach/comps/ompstuff/new_new_cmake_test/build/sub/saxpy_noomp.f
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/baron/teach/comps/ompstuff/new_new_cmake_test/build
[baron#ray:1035]$ make
make[2]: *** No rule to make target `../)', needed by `sub/saxpy_noomp.f'. Stop.
make[1]: *** [CMakeFiles/main.dir/all] Error 2
make: *** [all] Error 2
[baron#ray:1036]$
but obviously something subtle is missing. TIA

Related

Why the `unset` does not work as expected?

Here is the shell script which invokes the cmake
rm -rf build && mkdir build && cd build && cmake .. -DPULISH_VERSION=ON
Here is the CMakeLists.txt:
cmake_minimum_required(VERSION 3.13)
option(WITH_FOO "whether FOO is enabled or not" on)
option(PULISH_VERSION "whether is the pulished verion or not" on)
if(PULISH_VERSION)
if(WITH_FOO)
message("FOO should not be enabled for the published version.")
unset(WITH_FOO) # It seems `WITH_FOO` does not work, what a surprise!
endif()
endif()
if(WITH_FOO)
message("Attention! FOO is enabled!")
add_compile_definitions(WITH_FOO_ENABLED)
endif()
add_executable(demo main.c)
Here is the content of the main.c:
int main(){return 0;}
Here is the output when the aforementioned shell script is called:
cmake .. -DPULISH_VERSION=ON
-- The C compiler identification is GNU 4.9.4
-- The CXX compiler identification is GNU 4.9.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
-- Detecting C compile features
-- Detecting C compile features - 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
-- Detecting CXX compile features
-- Detecting CXX compile features - done
FOO should not be enabled for the published version.
Attention! FOO is enabled!
-- Configuring done
-- Generating done
-- Build files have been written to: /home/John/demo/build
As per the output above, it seems the unset does not work as expected. What a surprise!
WITH_FOO is the option, a cache variable, not a scope (local) variable. unset(WITH_FOO) tries to unset the scope variable WITH_FOO, that does not exits. The scope variable does not exist and if(WITH_FOO) fallbacks to the value of the option WITH_FOO. You might want to set the variable set(WITH_FOO OFF) that will hide the option value.
There is a better choice - cmake dependent option:
cmake_dependent_option(WITH_FOO "whether FOO is enabled or not" ON "NOT PULISH_VERSION" OFF)
Makes WITH_FOO option ON if PULISH_VERSION is OFF. Otherwise, a local variable named WITH_FOO is set to OFF.
Thanks to #273k
cmake_minimum_required(VERSION 3.13)
include(CMakeDependentOption)
option(PULISH_VERSION "whether is the pulished verion or not" on)
cmake_dependent_option(WITH_FOO "whether FOO is enabled or not" ON "NOT PULISH_VERSION" OFF)
if(WITH_FOO)
message("Attention! FOO is enabled!")
add_compile_definitions(WITH_FOO_ENABLED)
endif()
add_executable(demo main.c)

Setting RPATH of Static Library in Executable

I have the following setup for my CMake project:
add_executable(exeA ${SOURCES})
target_link_libraries(exeA PRIVATE libB)
libB is a static library built separately in a different project and depends on shared libraries libC OR libD which provide equivalent API's but implemented differently and have different performance profiles.
In exeA's build I want to be able to link against either of libC or libD depending on a condition.
From reading RPATH handling, it seems I could use RPATH related CMake properties, however its unclear if I can use these properties to set the RPATH of an existing static library
Unfortunately there is no easy and fast way to change the RPATH of an
existing executable or shared library.
Is something like this possible to do in CMake?
Appreciate any recommendations to handle this use case.
Create a STATIC IMPORTED library for libB and SHARED IMPORTED libraries for libC and libD. Put the relevant one in the INTERFACE_LINK_LIBRARIES property of libB. CMake will set the RPATH correctly when exeA is built.
cmake_minimum_required(VERSION 3.23)
project(test)
# --------------------------------------------------
# The following code should probably be factored out
# into a proper Find module.
find_library(LIBB_LIBRARY NAMES B REQUIRED)
find_library(LIBC_LIBRARY NAMES C REQUIRED)
find_library(LIBD_LIBRARY NAMES D REQUIRED)
add_library(third-party::libC SHARED IMPORTED)
set_target_properties(
third-party::libC
PROPERTIES
IMPORTED_LOCATION "${LIBC_LIBRARY}"
)
add_library(third-party::libD SHARED IMPORTED)
set_target_properties(
third-party::libD
PROPERTIES
IMPORTED_LOCATION "${LIBD_LIBRARY}"
)
add_library(third-party::libB STATIC IMPORTED)
set_target_properties(
third-party::libB
PROPERTIES
IMPORTED_LOCATION "${LIBB_LIBRARY}"
)
option(LIBB_USES_LIBC "dummy option for demo" ON)
if (LIBB_USES_LIBC)
target_link_libraries(third-party::libB INTERFACE third-party::libC)
else ()
target_link_libraries(third-party::libB INTERFACE third-party::libD)
endif ()
# --------------------------------------------------
# --------------------------------------------------
# Project code
add_executable(exeA main.cpp)
target_link_libraries(exeA PRIVATE third-party::libB)
You can test this out with dummy files like so:
$ ls
CMakeLists.txt
$ mkdir -p prefix{1,2}/lib
$ touch prefix1/lib/lib{C,D}.so prefix2/lib/libB.a main.cpp
$ cmake -G Ninja -S . -B build "-DCMAKE_PREFIX_PATH=$PWD/prefix1;$PWD/prefix2"
-- The C compiler identification is GNU 9.4.0
-- The CXX compiler identification is GNU 9.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/alex/test/build
$ cmake --build build -- -nv # dry run (n), verbose (v)
[1/2] /usr/bin/c++ -MD -MT CMakeFiles/exeA.dir/main.cpp.o -MF CMakeFiles/exeA.dir/main.cpp.o.d -o CMakeFiles/exeA.dir/main.cpp.o -c /home/alex/test/main.cpp
[2/2] : && /usr/bin/c++ CMakeFiles/exeA.dir/main.cpp.o -o exeA -Wl,-rpath,/home/alex/test/prefix1/lib ../prefix2/lib/libB.a ../prefix1/lib/libC.so && :
As you can see, the RPATH is set correctly to the directory containing libC.so.

Is it possible to call a custom CMake function when library is linked?

Let's say I have a library A and a project B which uses A.
project(libA)
function(calledWhenLink target)
message(STATUS Hi ${target}) # Should print "Hi exeB"
endfunction()
add_library(libA INTERFACE)
project(exeB)
find_package(A REQUIRED)
target_link_library(exeB libA)
Is it possible to automatically call calledWhenLink() when the executable links to the library ?
Reasoning: As said in a comment, the original problem is that VTK needs to call CMake vtk_module_autoinit. My library uses VTK, and it seems better for all the samples to automatically call this when linked to the lib instead of copy paste the code in each CMakeLists. The problem is that vtk_module_autoinit is not working when called with the lib target instead ot the exe in my tests.
Yes, it is possible to run arbitrary CMake code at build time. However, it is not at all clear what you're really trying to do, so this might not be a good approach.
Here's a minimal example:
cmake_minimum_required(VERSION 3.21)
project(example)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/post-link.cmake" [[
message(STATUS "Hi ${target}")
]])
add_executable(exeB main.cpp)
add_custom_command(
TARGET exeB POST_BUILD
COMMAND "${CMAKE_COMMAND}"
-Dtarget=exeB
-P "${CMAKE_CURRENT_BINARY_DIR}/post-link.cmake"
)
Test interaction:
alex#alex-ubuntu:~/test$ cmake -G Ninja -S . -B build
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/alex/test/build
alex#alex-ubuntu:~/test$ cmake --build build/ --verbose
[1/2] /usr/bin/c++ -MD -MT CMakeFiles/exeB.dir/main.cpp.o -MF CMakeFiles/exeB.dir/main.cpp.o.d -o CMakeFiles/exeB.dir/main.cpp.o -c /home/alex/test/main.cpp
[2/2] : && /usr/bin/c++ CMakeFiles/exeB.dir/main.cpp.o -o exeB && cd /home/alex/test/build && /usr/bin/cmake -Dtarget=exeB -P /home/alex/test/build/post-link.cmake
-- Hi exeB

add_custom_command not working

I am trying to understand add_custom_command usage, I wrote a minimal make file (CMakeLists.txt)
like shown below
When I run the make file using
$cmake.
$make
I don't see perfecthash.cpp being created and the build breaks
cmake_minimum_required(VERSION 2.8)
# Call add_custom_command() with appropriate arguments for generate output file
# Note, that *gperf* will work in the build tree,
# so for file in the source tree full path should be used.
function(gperf_generate_new input output)
MESSAGE("debugging function")
add_custom_command(
OUTPUT ${output}
COMMAND gperf -L c++ ${input} > ${output}
DEPENDS ${input}
COMMENT "printing ${output}" # Just for nice message during build
)
endfunction()
# Generate *example.hpp* file ...
gperf_generate_new(command_options.new.gperf pefecthash.hpp)
# ... for use it in executable
add_custom_target(my_target
ALL # Force target to be built with default build target.
DEPENDS perfecthash.hpp
)
$cmake .
$make
give the below error
-- The C compiler identification is GNU 4.9.2
-- The CXX compiler identification is GNU 4.9.2
-- 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
debugging function
-- Configuring done
-- Generating done
-- Build files have been written to: /home/srinivas
Scanning dependencies of target my_target
make[2]: *** No rule to make target `perfecthash.hpp', needed by `CMakeFiles/my_target'. Stop.
make[1]: *** [CMakeFiles/my_target.dir/all] Error 2
make: *** [all] Error 2
Just a typo:
gperf_generate_new(command_options.new.gperf pefecthash.hpp)
...
DEPENDS perfecthash.hpp

CMake link an external library

First of all, I am newbie in CMake. I've just started work with it. I want to link an external library to my project. I use code which I take from CMake wiki (at the end of article). Here is my CMakeLists.txt:
cmake_minimum_required(VERSION 2.8)
project(hello_world)
set(SOURCE_EXE main.cpp)
include_directories(foo)
add_library(foo STATIC IMPORTED)
set_property(TARGET foo PROPERTY IMPORTED_LOCATION /usr/lib/libfoo.a)
target_link_libraries(main foo)
And here is a text of error:
-- The C compiler identification is GNU 4.7.3
-- The CXX compiler identification is GNU 4.7.3
-- 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
CMake Error at CMakeLists.txt:24 (target_link_libraries):
Cannot specify link libraries for target "main" which is not built by this
project.
-- Configuring incomplete, errors occurred!
How can I do it correctly?
It looks like you're just missing out an add_executable call. You need to add main as an executable target in your CMakeLists.txt:
set(SOURCE_EXE main.cpp)
add_executable(main ${SOURCE_EXE})
...
target_link_libraries(main foo)