I currently try to write a custom_target to print out properites of a target (e.g. COMPILE_DEFINITIONS).
I've placed the invocation of this custom_target creation almost at the end of my Top-Level-CMakeLists.txt to make sure all modules have been invoked.
The goal is to print out all properties of a target including properties given by dependencies via target_link_libraries.
Simplified example:
add_library(libA STATIC)
add_library(libB STATIC)
target_compile_definitions(libA
PRIVATE
PRIV_A
PUBLIC
PUB_A
INTERFACE
INT_A
)
target_compile_definitions(libB
PRIVATE
PRIV_B
PUBLIC
PUB_B
INTERFACE
INT_B
)
# create dependency from A -> B,
# this should compile A with all PUBLIC and INTERFACE defintions from B
target_link_libraries(libA libB)
get_target_property(compile_defs libA COMPILE_DEFINITIONS)
get_target_property(compile_defs_intf libA INTERFACE_COMPILE_DEFINITIONS)
message("compile_defs: ${compile_defs}")
message("compile_defs_intf: ${compile_defs_intf}")
This will print:
compile_defs: PRIV_A; PUB_A
compile_defs_intf: PUB_A; INT_A
Actually I would like to get:
compile_defs: PRIV_A; PUB_A; PUB_B; INT_B
But obviously at this stage, the dependencies are not yet resolved / included in the properties.
A possible workaround would be to iterate over all dependencies of target A and collect all the INTERFACE_PROPERTIES of the dependency target. But this would require quiet some recursion to resolve all dependencies in the tree (e.g. requires resolving of all dependencies...).
Is it possible to get properties of a target incl. its dependencies (PUBLIC, INTERFACE properties) in a more easy way?
First get all dependent libraries:
# target_get_linked_libraries.cmake
#
function(list_add_if_not_present list elem)
list(FIND "${list}" "${elem}" exists)
if(exists EQUAL -1)
list(APPEND "${list}" "${elem}")
set("${list}" "${${list}}" PARENT_SCOPE)
endif()
endfunction()
macro(_target_get_linked_libraries_in _target _outlist)
list_add_if_not_present("${_outlist}" "${_target}")
# get libraries
get_target_property(target_type "${_target}" TYPE)
if (${target_type} STREQUAL "INTERFACE_LIBRARY")
get_target_property(libs "${_target}" INTERFACE_LINK_LIBRARIES)
else()
get_target_property(libs "${_target}" LINK_LIBRARIES)
endif()
foreach(lib IN LISTS libs)
if(NOT TARGET "${lib}")
continue()
endif()
list(FIND "${_outlist}" "${lib}" exists)
if(NOT exists EQUAL -1)
continue()
endif()
_target_get_linked_libraries_in("${lib}" "${_outlist}")
endforeach()
endmacro()
function(target_get_linked_libraries _target _outlist)
set(${_outlist} "${_target}")
_target_get_linked_libraries_in(${_target} ${_outlist})
set(${_outlist} ${${_outlist}} PARENT_SCOPE)
endfunction()
Then just iterate over all dependent libraries and get the interface definitions:
cmake_minimum_required(VERSION 3.11)
project(A)
add_library(libB STATIC)
target_compile_definitions(libB
PRIVATE PRIV_B
PUBLIC PUB_B
INTERFACE INT_B
)
add_library(libA STATIC)
target_compile_definitions(libA
PRIVATE PRIV_A
PUBLIC PUB_A
INTERFACE INT_A
)
target_link_libraries(libA PUBLIC libB)
include(target_get_linked_libraries.cmake)
target_get_linked_libraries(libA libraries)
set(compile_defs)
foreach(i IN LISTS libraries)
if("${i}" STREQUAL libA)
get_target_property(tmp "${i}" COMPILE_DEFINITIONS)
else()
get_target_property(tmp "${i}" INTERFACE_COMPILE_DEFINITIONS)
endif()
list(APPEND compile_defs "${tmp}")
endforeach()
message("compile_defs: ${compile_defs}")
message(FATAL_ERROR "")
would output:
$ cmake .
compile_defs: PRIV_A;PUB_A;PUB_B;INT_B
Related
I'm trying to understand targets and properties in CMake. When using GTest in a C++ project, one needs to link to the GTest static libraries, which in my case have the default paths /usr/lib/libgtest.a and /usr/lib/libgtest_main.a. These paths are contained in variables (or macros?) GTEST_LIBRARIES and GTEST_MAIN_LIBRARIES, which are set when you run find_package(GTest REQUIRED).
Another way (seemingly more "modern CMake"?) is to use GTest::Main as a target in target_link_libraries. This also works but I don't understand why. Where are the library paths in terms of properties of this target?
To find out, I tried to list all the target properties using code from 1. Below code shows how these properties are listed and the output.
// tests_main.cpp
// Google Test framework
#include <gtest/gtest.h>
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
TEST(ExampleTests, example1) {
EXPECT_EQ(1, 1);
}
# list_target_properties.cmake
# Taken from https://stackoverflow.com/q/32183975/9988487
# Defines function `print_target_properties` that lists all properties of a target.
# Get all propreties that cmake supports
execute_process(COMMAND cmake --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTY_LIST)
# Convert command output into a CMake list
STRING(REGEX REPLACE ";" "\\\\;" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
STRING(REGEX REPLACE "\n" ";" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
function(print_properties)
message ("CMAKE_PROPERTY_LIST = ${CMAKE_PROPERTY_LIST}")
endfunction(print_properties)
function(print_target_properties tgt)
if(NOT TARGET ${tgt})
message("There is no target named '${tgt}'")
return()
endif()
foreach (prop ${CMAKE_PROPERTY_LIST})
string(REPLACE "<CONFIG>" "${CMAKE_BUILD_TYPE}" prop ${prop})
# Fix https://stackoverflow.com/questions/32197663/how-can-i-remove-the-the-location-property-may-not-be-read-from-target-error-i
if(prop STREQUAL "LOCATION" OR prop MATCHES "^LOCATION_" OR prop MATCHES "_LOCATION$")
continue()
endif()
# message ("Checking ${prop}")
get_property(propval TARGET ${tgt} PROPERTY ${prop} SET)
if (propval)
get_target_property(propval ${tgt} ${prop})
message ("${tgt} ${prop} = ${propval}")
endif()
endforeach(prop)
endfunction(print_target_properties)
# CMakeLists.txt
cmake_minimum_required(VERSION 3.12)
find_package(GTest REQUIRED)
add_executable(unit_tests
tests_main.cpp
)
target_compile_features(unit_tests
PUBLIC
cxx_std_17
)
target_include_directories(unit_tests
PUBLIC
)
if(USE_GTEST_MACROS_TO_FIND_LIBRARY_PATHS)
# I was originally using this way to link to GTest
target_link_libraries(unit_tests
${GTEST_LIBRARIES} # macro provided by find_package(GTest REQUIRED) earlier
${GTEST_MAIN_LIBRARIES} # macro provided by find_package(GTest REQUIRED) earlier
pthread
)
else()
# Then I started using this way
target_link_libraries(unit_tests
GTest::Main
pthread
)
endif(USE_GTEST_MACROS_TO_FIND_LIBRARY_PATHS)
# Both ways work but I don't understand why the second way works. What property of `GTest::Main` contains the library
# paths that are contained in ${GTEST_LIBRARIES} and ${GTEST_MAIN_LIBRARIES}?
# Code to print the static library paths for GTest
message(STATUS "Value of GTEST_LIBRARIES: ${GTEST_LIBRARIES}")
message(STATUS "Value of GTEST_MAIN_LIBRARIES: ${GTEST_MAIN_LIBRARIES}")
# Code that (I hope) prints all properties of the GTest::Main target and the GTest::GTest target, which is specified as
# a dependency of GTest::GTest.
include(list_target_properties.cmake) # provides function `print_target_properties`
print_target_properties(GTest::Main)
message(STATUS "---------------------")
print_target_properties(GTest::GTest)
CMake Output:
...
...
-- Found GTest: /usr/lib/libgtest.a
-- Value of GTEST_LIBRARIES: /usr/lib/libgtest.a
-- Value of GTEST_MAIN_LIBRARIES: /usr/lib/libgtest_main.a
GTest::Main AUTOMOC_COMPILER_PREDEFINES = ON
GTest::Main AUTOMOC_MACRO_NAMES = Q_OBJECT;Q_GADGET;Q_NAMESPACE;Q_NAMESPACE_EXPORT
GTest::Main BINARY_DIR = /home/alice/CLionProjects/UnderstandCMakeTargets/cmake-build-debug
GTest::Main BINARY_DIR = /home/alice/CLionProjects/UnderstandCMakeTargets/cmake-build-debug
GTest::Main BUILD_WITH_INSTALL_RPATH = OFF
GTest::Main IMPORTED = TRUE
GTest::Main IMPORTED_LINK_INTERFACE_LANGUAGES = CXX
GTest::Main INSTALL_RPATH =
GTest::Main INSTALL_RPATH_USE_LINK_PATH = OFF
GTest::Main INTERFACE_LINK_LIBRARIES = GTest::GTest
GTest::Main NAME = GTest::Main
GTest::Main SKIP_BUILD_RPATH = OFF
GTest::Main SOURCE_DIR = /home/alice/CLionProjects/UnderstandCMakeTargets
GTest::Main SOURCE_DIR = /home/alice/CLionProjects/UnderstandCMakeTargets
GTest::Main TYPE = UNKNOWN_LIBRARY
GTest::Main TYPE = UNKNOWN_LIBRARY
-- ---------------------
GTest::GTest AUTOMOC_COMPILER_PREDEFINES = ON
GTest::GTest AUTOMOC_MACRO_NAMES = Q_OBJECT;Q_GADGET;Q_NAMESPACE;Q_NAMESPACE_EXPORT
GTest::GTest BINARY_DIR = /home/alice/CLionProjects/UnderstandCMakeTargets/cmake-build-debug
GTest::GTest BINARY_DIR = /home/alice/CLionProjects/UnderstandCMakeTargets/cmake-build-debug
GTest::GTest BUILD_WITH_INSTALL_RPATH = OFF
GTest::GTest IMPORTED = TRUE
GTest::GTest IMPORTED_LINK_INTERFACE_LANGUAGES = CXX
GTest::GTest INSTALL_RPATH =
GTest::GTest INSTALL_RPATH_USE_LINK_PATH = OFF
GTest::GTest INTERFACE_INCLUDE_DIRECTORIES = /usr/include
GTest::GTest INTERFACE_LINK_LIBRARIES = Threads::Threads
GTest::GTest NAME = GTest::GTest
GTest::GTest SKIP_BUILD_RPATH = OFF
GTest::GTest SOURCE_DIR = /home/alice/CLionProjects/UnderstandCMakeTargets
GTest::GTest SOURCE_DIR = /home/alice/CLionProjects/UnderstandCMakeTargets
GTest::GTest TYPE = UNKNOWN_LIBRARY
GTest::GTest TYPE = UNKNOWN_LIBRARY
-- Configuring done
-- Generating done
-- Build files have been written to: /home/alice/CLionProjects/UnderstandCMakeTargets/cmake-build-debug
[Finished]
lib1-CMakeLists.txt
add_library(lib1_objs OBJECT
hello.cpp
hello.h
)
target_include_directories(lib1_objs
PUBLIC ../third
)
add_library(lib1 STATIC $<TARGET_OBJECTS:lib1_objs>)
hello.h
#include<test.h> //Path: ../third/test.h
...
exe-CMakeLists.txt
add_executable(exe
main.cpp
)
target_link_libraries(exe
PRIVATE lib1
)
target_include_directories(lib1_objs
PRIVATE ../lib1
)
main.cpp
#include "hello.h"
...
When I build 'exe', it would show an error
../lib1/hello.h:2 fatal error test.h No such file or directory
I already include '../third' directory PUBLIC in lib1-CMakeLists.txt. But it didn't propaganda it to exe-CMakeLists.txt. How can I fix this?
Just actually link with the object library:
lib1/CMakeLists.txt:
add_library(lib1 OBJECT
hello.cpp
hello.h
)
target_include_directories(lib1
PUBLIC ../third
)
CMakeLists.txt:
add_subdirectory(lib1)
add_executable(exe
main.cpp
)
target_link_libraries(exe
PRIVATE lib1
)
$<TARGET_OBJECTS> are just object files - they do not know about include directories and such.
You could also:
lib1/CMakeLists.txt:
add_library(lib1_objs OBJECT
hello.cpp
hello.h
)
target_include_directories(lib1_objs
PUBLIC ../third
)
add_library(lib1 STATIC) # I think it works, if not create an empty source file
target_link_libraries(lib1 PUBLIC lib1_objs)
CMakeLists.txt:
add_subdirectory(lib1)
add_executable(exe
main.cpp
)
target_link_libraries(exe
PRIVATE lib1
)
but then lib1_objs could just be removed.
I am a beginner, I was trying to get SDL2 to work in clion but I failed.
I've searched on youtube and google but nothing worked. I have SDL2.dll in the same folder as main.cpp
My cmake file looks like this:
cmake_minimum_required(VERSION 3.8)
project(sdlTest)
set(CMAKE_CXX_STANDARD 17)
set(SOURCE_FILES main.cpp)
add_executable(sdlTest ${SOURCE_FILES})
add_library(SDL2.dll SHARED main.cpp)
set_target_properties(SDL2.dll PROPERTIES LINKER_LANGUAGE CXX)
I don't know what I'm doing wrong.
I tried running this example code from sdl:
#include "SDL2/SDL.h"
#include <stdio.h>
int main(int argc, char* argv[]) {
SDL_Window *window; // Declare a pointer
SDL_Init(SDL_INIT_VIDEO); // Initialize SDL2
// Create an application window with the following settings:
window = SDL_CreateWindow(
"An SDL2 window", // window title
SDL_WINDOWPOS_UNDEFINED, // initial x position
SDL_WINDOWPOS_UNDEFINED, // initial y position
640, // width, in pixels
480, // height, in pixels
SDL_WINDOW_OPENGL // flags - see below
);
// Check that the window was successfully created
if (window == NULL) {
// In the case that the window could not be made...
printf("Could not create window: %s\n", SDL_GetError());
return 1;
}
// The window is open: could enter program loop here (see SDL_PollEvent())
SDL_Delay(3000); // Pause execution for 3000 milliseconds, for example
// Close and destroy the window
SDL_DestroyWindow(window);
// Clean up
SDL_Quit();
return 0;
}
and I get this error:
CMakeFiles\sdlTest.dir/objects.a(main.cpp.obj): In function `SDL_main':
C:/Users/Dddsasul/CLionProjects/sdlTest/main.cpp:8: undefined reference to
`SDL_Init'
C:/Users/Dddsasul/CLionProjects/sdlTest/main.cpp:18: undefined reference to
`SDL_CreateWindow'
C:/Users/Dddsasul/CLionProjects/sdlTest/main.cpp:23: undefined reference to
`SDL_GetError'
C:/Users/Dddsasul/CLionProjects/sdlTest/main.cpp:29: undefined reference to
`SDL_Delay'
C:/Users/Dddsasul/CLionProjects/sdlTest/main.cpp:32: undefined reference to
`SDL_DestroyWindow'
C:/Users/Dddsasul/CLionProjects/sdlTest/main.cpp:35: undefined reference to
`SDL_Quit'
c:/mingw/bin/../lib/gcc/mingw32/5.3.0/../../../libmingw32.a(main.o):
(.text.startup+0xa0): undefined reference to `WinMain#16'
collect2.exe: error: ld returned 1 exit status
mingw32-make.exe[3]: *** [sdlTest.exe] Error 1
CMakeFiles\sdlTest.dir\build.make:95: recipe for target 'sdlTest.exe' failed
CMakeFiles\Makefile2:103: recipe for target 'CMakeFiles/sdlTest.dir/all'
failed
CMakeFiles\Makefile2:115: recipe for target 'CMakeFiles/sdlTest.dir/rule'
failed
mingw32-make.exe[2]: *** [CMakeFiles/sdlTest.dir/all] Error 2
mingw32-make.exe[1]: *** [CMakeFiles/sdlTest.dir/rule] Error 2
mingw32-make.exe: *** [sdlTest] Error 2
Makefile:130: recipe for target 'sdlTest' failed
I've tried both x64 and x86 and it's the same result but at the same time clion doesn't red-text the code, it thinks it's ok:
Any ideas what I'm doing wrong and how to fix? THANKS!
EDIT:
Cmake looks like this
cmake_minimum_required(VERSION 3.8)
project(sdlTest)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
find_package(SDL2 REQUIRED)
set(SOURCE_FILES main.cpp)
add_executable(sdlTest ${SOURCE_FILES})
set(SDL2_LIBRARY SDL2.dll)
target_link_libraries(sdlTest ${SDL2_LIBRARYS})
I get this error:
Found package configuration file:
C:/MinGW/lib/cmake/SDL2/sdl2-config.cmake
but it set SDL2_FOUND to FALSE so package "SDL2" is considered to be NOT
FOUND.
SECOND EDIT:
sdl2-config.cmake:
# sdl2 cmake project-config input for ./configure scripts
set(prefix "/usr/local/x86_64-w64-mingw32")
set(exec_prefix "${prefix}")
set(libdir "${exec_prefix}/lib")
set(SDL2_PREFIX "/usr/local/x86_64-w64-mingw32")
set(SDL2_EXEC_PREFIX "/usr/local/x86_64-w64-mingw32")
set(SDL2_LIBDIR "${exec_prefix}/lib")
set(SDL2_INCLUDE_DIRS "${prefix}/include/SDL2")
set(SDL2_LIBRARIES "-L${SDL2_LIBDIR} -lmingw32 -lSDL2main -lSDL2 -mwindows")
string(STRIP "${SDL2_LIBRARIES}" SDL2_LIBRARIES)
I tried to compile a simple POSIX example in CLIon ide, but it doesn`t know about pthread library, I think...
Here is the code:
void *func1()
{
int i;
for (i=0;i<10;i++) { printf("Thread 1 is running\n"); sleep(1); }
}
void *func2()
{
int i;
for (i=0;i<10;i++) { printf("Thread 2 is running\n"); sleep(1); }
}
int result, status1, status2;
pthread_t thread1, thread2;
int main()
{
result = pthread_create(&thread1, NULL, func1, NULL);
result = pthread_create(&thread2, NULL, func2, NULL);
pthread_join(thread1, &status1);
pthread_join(thread2, &status2);
printf("\nПотоки завершены с %d и %d", status1, status2);
getchar();
return 0;
}
It is known, that this code is correct, because it's taken from the example in the book. So Clion marks second arguments of pthread_join function as a mistake, giving this error:
error: invalid conversion from ‘void* (*)()’ to ‘void* (*)(void*)’
I suppose, thet the problem is in the CmakeList. Here is my current CMakeList:
cmake_minimum_required(VERSION 3.3)
project(hello_world C CXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pthread")
set(SOURCE_FILES main.cpp)
add_executable(hello_world ${SOURCE_FILES})
Your function signature is wrong for the callback to pthread.
func1 and func2 have the signature void* (*)(). This means returns a void* and has no parameters
But pthread wants void* (*)(void*) Here you also have a void* as parameter.
so your functions should look like this:
void *func1(void* param) ...
You don't have to use the parameter but it has to be there in the declaration.
Note:
To tell cmake to link against pthread you should use this:
find_package( Threads REQUIRED )
add_executable(hello_world ${SOURCE_FILES})
target_link_libraries( hello_world Threads::Threads )
See here: How do I force cmake to include "-pthread" option during compilation?
when I compile my code in Eclipse it works fine. But the process fails if I try to compile it with CMake. It says:
error: ‘default_random_engine’ does not name a type
default_random_engine generator;
error: ‘uniform_int_distribution’ does not name a type
uniform_int_distribution distribution;
and some more errors, which I believe are the consequences of these two.
class randomInt
{
private:
int m_max;
default_random_engine generator;
uniform_int_distribution<int> distribution;
public:
randomInt(int max = 0) :
m_max(max),
generator(time(0)),
distribution(0, m_max)
{}
int operator ()()
{
return distribution(generator);
}
};
int main(int argc, char **argv)
{
vector<int> vec(100);
generate(vec.begin(), vec.end(), randomInt(100));
ostream_iterator<int> streamIt(cout, ",\n");
copy(vec.begin(), vec.end(), streamIt);
return 0;
}
There is my CMakeLists.txt
project(TEST)
# States that CMake required version must be greater than 2.6
cmake_minimum_required(VERSION 2.8)
# Setup sources
set(TEST_SOURCES
aufgabe2_1.cpp
aufgabe2_2.cpp
aufgabe2_3.cpp
aufgabe2_4.cpp)
set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -std=c++11")
# Build executable
add_executable(main ${TEST_SOURCES})
These are symbols from the C++ standard library's pseudo-random number generation, but you haven't qualified the symbols with the namespace std, so use std::default_random_engine and std::uniform_int_distribution.