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]
Related
I don't know how to mix HIP compilation and Fortran using CMake. The follow is a demo.
I have 3 files:
fcode.f90
SUBROUTINE fcode()
implicit double precision (a-h, o-z)
parameter (M=64)
dimension x(M),y(M),z(M)
do k=1, M
x(k) = 1.0
y(k) = 2.0
z(k) = 0.0
end do
call hipcode(x,y,z,M)
do k = 1, M
if ( z(k) .ne. 3.0 ) then
write(6,*) 'u fail !'
return
endif
end do
write(6,*)' PASSED !'
return
end
hipcode.cpp
#include <hip/hip_runtime.h>
#define HIP_ASSERT(status) assert(status == hipSuccess)
__global__ void add(double *x, double *y, double *z, const unsigned int M) {
z[threadIdx.x] = x[threadIdx.x] + y[threadIdx.x];
}
extern "C" void hipcode_(double *h_x, double *h_y, double *h_z, int &M) {
HIP_ASSERT(hipSetDevice(0));
double *d_x, *d_y, *d_z;
HIP_ASSERT(hipMalloc((void **)&d_x, M * sizeof(double)));
HIP_ASSERT(hipMalloc((void **)&d_y, M * sizeof(double)));
HIP_ASSERT(hipMalloc((void **)&d_z, M * sizeof(double)));
HIP_ASSERT(hipMemcpy(d_x, h_x, M * sizeof(double), hipMemcpyHostToDevice));
HIP_ASSERT(hipMemcpy(d_y, h_y, M * sizeof(double), hipMemcpyHostToDevice));
HIP_ASSERT(hipMemcpy(d_z, h_z, M * sizeof(double), hipMemcpyHostToDevice));
hipLaunchKernelGGL(add, 1, 64, 0, 0, d_x, d_y, d_z, M);
HIP_ASSERT(hipMemcpy(h_z, d_z, M * sizeof(double), hipMemcpyDeviceToHost));
hipFree(d_x);
hipFree(d_y);
hipFree(d_z);
}
main.f90
call fcode()
stop
end
and I write a Makefile to compile it, it works. But I don't know how to use cmake to do this.
OBJS=main.o fcode.o hipcode.o
FC=gfortran
HIPCC=hipcc
FCFLAGS=-c
HIPCCFLAGS=-c
LDFLAGS=-lgfortran
all :
$(HIPCC) $(HIPCCFLAGS) hipcode.cpp
$(FC) $(FCFLAGS) fcode.f90
$(FC) $(FCFLAGS) main.f90
$(HIPCC) $(OBJS) $(LDFLAGS) -o test
Here is my CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(test LANGUAGES Fortran CXX)
# source file: hipcode.cpp fcode.f90 main.f90
# target:
# hipcc -c hipcode.cpp
# gfortran -c fcode.f90
# gfortran -c main.f90
# hipcc hipcode.o fcode.o main.o -lgfortran -o test
set(sources_list hipcode.cpp)
set(raw_sources_list_f90 fcode.f90)
# find hip
find_package(HIP QUIET)
set(CMAKE_HIP_FLAGS "${CMAKE_CXX_FLAGS} -D__HIP_PLATFORM_HCC__ --offload-arch=gfx906")
set(HIP_CLANG_FLAGS "${HIP_CLANG_FLAG} --hip-link")
set_source_files_properties(${sources_list} PROPERTIES HIP_SOURCE_PROPERTY_FORMAT 1)
set(MY_SOURCE_FILES ${sources_list})
set(MY_TARGET_NAME hipcode)
set(MY_HIPCC_OPTIONS "--hip-link")
set(HIP_TARGET_LINK_LIB "rocm/hip/lib/libamdhip64.so" )
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -x hip -fgpu-rdc --hip-link -std=c++14 -g")
set(CMAKE_CXX_COMPILER "hipcc")
set(CMAKE_HIP_FLAFS "${CMAKE_HIP_FLAGS} --hip-link")
set(CMAKE_HIP_LINKER_WRAPPER_FLAG "--hip-link")
set(CMAKE_CXX_LINK_FLAGS " -fgpu-rdc --hip-link -std=c++14 ")
set(HIP_HIPCC_CMAKE_LINKER_HELPER "hipcc")
set(HIP_CLANG_PATH " ")
set(HIP_CLANG_PARALLEL_BUILD_LINK_OPTIONS " ")
add_library(${MY_TARGET_NAME} ${MY_SOURCE_FILES})
target_link_libraries(${MY_TARGET_NAME} ${HIP_TARGET_LINK_LIB} )
add_library(fcodef90 STATIC ${raw_sources_list_f90})
target_link_libraries(fcodef90 hipcode)
I use CXX=hipcc cmake .. && make -j to build the demo, and it passed.
But then I got an error: "undefined reference to `hipcode_'", so how to modify the CMakeLists.txt?
First of all thank you again, after use nm, I realized that maybe I need to add separate compilation options, then I found answer here, all right, thank you very much!
And the final compiled command is
gfortran -c main.f90
hipcc -fgpu-rdc --hip-link main.o libfcode.a libhipcode.a -lgfortran
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
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)
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.