I've written a cmake module for finding QCustomPlot. However, to use the shared library, one needs to #define QCUSTOMPLOT_USE_LIBRARY. I'd like to provide this define through cmake, automatically adding the definition to any project that uses QCustomPlot.
Here is a snippet of my cmake module and my current attempted solution:
SET(QCP_FOUND "NO")
IF(QCP_LIBRARY AND QCP_INCLUDE_DIR)
SET(QCP_FOUND "YES")
SET_PROPERTY(
GLOBAL
APPEND
PROPERTY COMPILE_DEFINITIONS QCUSTOMPLOT_USE_LIBRARY
)
ENDIF(QCP_LIBRARY AND QCP_INCLUDE_DIR)
However, no linkers append the -DQCUSTOMPLOT_USE_LIBRARY flag in my compilations. What's the right way to approach this problem?
There is no global property COMPILE_DEFINITIONS. But there are such properties for directory, target and source file (see documentation). So probably the closest commands for you are:
set(QCP_FOUND "NO")
if(QCP_LIBRARY AND QCP_INCLUDE_DIR)
set(QCP_FOUND "YES")
set_directory_properties(
PROPERTIES COMPILE_DEFINITIONS QCUSTOMPLOT_USE_LIBRARY
)
endif()
But I think that this kind of job is for imported targets:
add_library(QCP::qcp UNKNOWN IMPORTED)
set_target_properties(
QCP::qcp
PROPERTIES
IMPORTED_LOCATION "${QCP_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${QCP_INCLUDE_DIR}"
INTERFACE_COMPILE_DEFINITIONS QCUSTOMPLOT_USE_LIBRARY
)
and usage:
add_executable(foo ...)
target_link_libraries(foo PUBLIC QCP::qcp)
Related
I'm not familar with cmake but in CMakeLists.txt we set the target shared library name like this.
add_library( mylib SHARED ${source_list} )
This generates libmylib.so and other settings in CMakeLists.txt are defined for mylib like
about the mylib
and also we can use shell environment variable to do some selective settings like
target_compile_definitions( mylib PRIVATE -DQQQ -D... )
Also it is possible to use shell environment variable to do some selective things.
if(defined env{MYVAR})
set(CMAKE_C_FLAGS "-g -DXYZ")
else()
set(CMAKE_C_FLAGS "-DXYZ")
endif()
I would be happy if I could set the target shared library name as a variable according to the environment variable and use that selected name variable as the shared library name in all other settings. In other words, is it possible to do things like below?
if (defined ENV{FOR_QEMU})
set_name(target_name "simlib_qemu")
else ()
set_name(target_name "simlib")
endif ()
add_library(target_name SHARED ${source_list} )
target_compile_definitions( target_name PRIVATE -DQQQ -D... )
...
You can set the output name of a target to anything you like via:
set_target_properties(target_name PROPERTIES OUTPUT_NAME "whatever")
Then instead of libtarget_name.so, you'll get libwhatever.so. You would continue to refer to the target as target_name in your CMakeLists.txt.
However, since this will only work during configure time anyway, I strongly urge you to use a normal CMake variable instead. You may initialize it from the environment if it is not set, like so:
option(FOR_QEMU "Enable if building with Qemu support" "$ENV{FOR_QEMU}")
add_library(simlib SHARED ${source_list})
target_compile_definitions(simlib PRIVATE -DQQQ -D...)
if (FOR_QEMU)
set_target_properties(target_name PROPERTIES OUTPUT_NAME "simlib_qemu")
endif ()
This way, the CMake variable FOR_QEMU is the de-facto control and it is initialized on the first execution if the matching env-var is set. It will also appear with documentation in the cache, so other developers may query the build system directly for all its configuration points. Bear in mind: CMake is not Make and reading from the environment on every configure is a surprising behavior and generally bad practice.
My project contains a lot of libraries and executables. On Windows a .rc file is used to embed meta informations (version, original file name, ...). One of these meta informations is FILETYPE (VFT_DLL, VFT_APP). So I add a -D TYPE definition to the resource compiler (which I evaluate inside the .rc) to distinguish between dll/exe.
Example:
add_library (myLib SHARED "src/myLib.cpp"
"src/myLib.rc")
target_include_directories (myLib PUBLIC "include")
set_source_files_properties("src/myLib.rc" APPEND_STRING PROPERTY COMPILE_FLAGS "-DDLL")
I won't repeat myself in dozens of CMakeLists, is there a possibility to create a generic rule (e.g. in a .cmake include) that .rc files will be compiled with a target depending flag (-DDLL in case of SHARED library)?
I would write a small wrapper function:
# Or maybe not object, depending on what you want
add_library(rc_for_shared OBJECT src/myLib.rc)
target_compile_definitions(rc_for_shared PRIVATE -DDLL)
add_library(rc_for_executable OBJECT src/myLib.rc)
# This is a draft from my memory
function(target_add_the_rc_file target)
get_target_properties(target PROPERTIES TYPE type)
if (type STREQUAL "SHARED")
target_link_libraries(${target} rc_for_shared)
# or maybe target_source(${target} $<TARGET_OBJECTS:rc_for_shared>)
# depending on what you want
elseif(type STREQUAL "EXECUTABLE")
target_link_libraries(${target} rc_for_executable)
endif()
endfunction()
then you would:
add_library(myLib SHARED src/myLib.cpp)
target_include_directories(myLib PUBLIC)
target_add_the_rc_file(myLib)
Now, I know how the question sounds... however, target_link_libraries does more than just linking. And I want this "more" but not the linking. How to do it?
My current setup looks (in a simplified view) like this:
The "3rd Party" target is a PkgConfig created target. The rest is my own.
However, this setup creates a problem in tests. In "Application UTs" I don't want to use the "3rd Party" library. I want to use its stub. To put it in a picture, I would like to have the following instead:
The dotted lines are "something" - it could be targets dependency (to avoid repeating the same information, like include paths), however, I consider it an implementation detail as long as it works.
If I would be in control of the "3rd Party" targets I would just do them like that. But I'm not. I'm receiving .pc files and I have the target created by PkgConfig. While the created target is this single target having it all.
For now, I have solved it by creating a custom function target_nonlink_libraries which propagates the following target properties:
INTERFACE_COMPILE_DEFINITIONS,
INTERFACE_COMPILE_FEATURES,
INTERFACE_COMPILE_OPTIONS,
INTERFACE_INCLUDE_DIRECTORIES,
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES.
(Note, that this does not include all INTERFACE_... properties.)
The code looks like this:
function(target_nonlink_libraries TARGET_NAME SCOPE SOURCE_TARGET)
foreach(source_target IN ITEMS ${SOURCE_TARGET} ${ARGN})
get_target_property(interface_compile_definitions ${source_target} INTERFACE_COMPILE_DEFINITIONS)
if(interface_compile_definitions)
target_compile_definitions(${TARGET_NAME}
${SCOPE}
${interface_compile_definitions}
)
endif()
get_target_property(interface_compile_features ${source_target} INTERFACE_COMPILE_FEATURES)
if(interface_compile_features)
target_compile_definitions(${TARGET_NAME}
${SCOPE}
${interface_compile_features}
)
endif()
get_target_property(interface_compile_options ${source_target} INTERFACE_COMPILE_OPTIONS)
if(interface_compile_options)
target_compile_options(${TARGET_NAME}
${SCOPE}
${interface_compile_options}
)
endif()
get_target_property(interface_include_directories ${source_target} INTERFACE_INCLUDE_DIRECTORIES)
if(interface_include_directories)
target_include_directories(${TARGET_NAME}
${SCOPE}
${interface_include_directories}
)
endif()
get_target_property(interface_system_include_directories ${source_target} INTERFACE_SYSTEM_INCLUDE_DIRECTORIES)
if(interface_system_include_directories)
target_include_directories(${TARGET_NAME} SYSTEM
${SCOPE}
${interface_system_include_directories}
)
endif()
endforeach()
endfunction()
and is later used like this:
target_nonlink_libraries(ApplicationCommon
INTERFACE
PkgConfig::Library
)
target_link_libraries(Application
PRIVATE
PkgConfig::Library
)
The problem here, however, is that I'm not sure that I'm copying all properties (which I care about) and doing it in the correct way.
How to solve this in CMake? Is there any build-in to link targets explicitly skipping actual linking?
I've been told it's bad practice to do things like seting CFLAGS directly in CMake, and that, instead, I should use the target_compile_definitions() command.
Ok, but - what if I want to use similar/identical definitions for multiple (independent) targets? I don't want to repeat myself over and over again.
I see three possible ways:
The preferred one using target_compile_definitions(... INTERFACE/PUBLIC ...) which would self-propagate the compiler definitions to targets depending on it via target_link_libraries() command.
Using the set_property(TARGET target1 target2 ... APPEND PROPERTY COMPILE_DEFINITIONS ...) to set the same definitions to multiple targets.
You may still use the "old commands" of add_definitions() and remove_definitions() to modify COMPILE_DEFINITIONS directory property (which would pre-set all COMPILE_DEFINITIONS target properties in this directories scope).
References
Is Cmake set variable recursive?
CMake: Is there a difference between set_property(TARGET ...) and set_target_properties?
tl;dr: You can iterate the targets in a loop.
If you have a bunch of targets with some common/similar features, you may want to simply manipulate them all in a loop! Remember - CMake is not like GNU Make, it's a full-fledged scripting language (well, sort of). So you could write:
set(my_targets
foo
bar
baz)
foreach(TARGET ${my_targets})
add_executable(${TARGET} "${TARGET}.cu")
target_compile_options(${TARGET} PRIVATE "--some_option=some_value")
target_link_libraries(${TARGET} PRIVATE some_lib)
# and so on
set_target_properties(
${TARGET}
PROPERTIES
C_STANDARD 99
C_STANDARD_REQUIRED YES
C_EXTENSIONS NO )
endforeach(TARGET)
And you could also initialize an empty list of targets, then add to it here-and-there, and only finally apply your common options and settings to all of them, centrally.
Note: In this example I added PRIVATE compile options, but if you need some of them to propagate to targets using your targets, you can make them PUBLIC).
another neat solution is to define an interface library target (a fake target that does not produce any binaries) with all required properties and compiler definitions, then link the other existing targets against it
example:
add_library(myfakelib INTERFACE)
target_compile_definitions(myfakelib INTERFACE MY_NEEDED_DEFINITION)
add_executable(actualtarget1 main1.cpp)
add_executable(actualtarget2 main2.cpp)
set_property(
TARGET actualtarget1 actualtarget2
APPEND PROPERTY LINK_LIBRARIES myfakelib
)
refs:
https://cmake.org/cmake/help/latest/command/add_library.html#interface-libraries
I'm trying to set multiple compile definitions for one of the executables I'm trying to compile in CMake (in order to activate macros used for debugging). Here's what I tried:
add_executable (trie_io_test trie_io_test.c trie.c word_list.c)
set_target_properties(
trie_io_test
PROPERTIES
COMPILE_DEFINITIONS UNIT_TESTING=1)
set_target_properties(
trie_io_test
PROPERTIES
COMPILE_DEFINITIONS IO_TEST=1)
Unfortunately this causes only the IO_TEST to be defined.
I also tried the following:
add_executable (trie_io_test trie_io_test.c trie.c word_list.c)
set_target_properties(
trie_io_test
PROPERTIES
COMPILE_DEFINITIONS UNIT_TESTING=1 IO_TEST=1)
But this, on the other hand, causes CMake error.
How to set both of these definitions for the executable I'm trying to build?
You want target_compile_definitions instead of set_target_properties:
target_compile_definitions(trie_io_test PRIVATE UNIT_TESTING=1 IO_TEST=1)
I find this can work for you:
add_executable (trie_io_test trie_io_test.c trie.c word_list.c)
set_target_properties(
trie_io_test
PROPERTIES
COMPILE_DEFINITIONS UNIT_TESTING=1
COMPILE_DEFINITIONS IO_TEST=1
)
Just by adding another COMPILE_DEFINITIONS :P