So I'm trying to convert a make-based project to cmake, and having trouble wrapping my head around how it works. I've figured out how to get custom commands to generate a header file, and how to compile source file for the target executable, but I can't seem to link them together -- I can't figure out how to trigger the custom command to generate the header file. Here's a trivial example of what I'm trying to do:
CMakeLists.txt:
add_executable(test test.c)
add_custom_command(OUTPUT foo.h
COMMAND echo "/* test */" > foo.h
)
test.c:
#include "foo.h"
int main() { return 0; }
However, when I run cmake and make, it gives me:
/home/cdodd/test/test.c:1:17: fatal error: foo.h: No such file or directory
compilation terminated.
It can't seem to figure out that it needs to create foo.h first before compiling test.c. With make I'd just add a dependency; how do I do that with cmake?
Add the generated file foo.h to the executable target as a dependency:
add_executable(testexe test.c foo.h)
This will make CMake add a file level dependency of the target testexe to the file foo.h in the generated build system.
Related
I am working on an embedded project managed with CMake and I would like to do different actions on the same executable.
Consider I have the following project:
/cmake/foo.cmake
/CMakeLists.txt
/main.cpp
Irrelevant main.cpp for this question:
#include <iostream>
int main() {
std::cout << "hello world" << std::endl;
return 0;
}
The foo.cmake file contains a function:
function(print_executable_size TARGET_NAME)
add_custom_command(
TARGET ${TARGET_NAME}
POST_BUILD
COMMAND size ${TARGET_NAME}.exe
)
endfunction()
The main CMakeLists.txt has the following content:
cmake_minimum_required(VERSION 3.15)
project(proj)
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
include(foo)
add_executable(pgm-dev main.cpp)
add_executable(pgm-prod ALIAS pgm-dev)
# this line works but this is not needed when compiling the "dev" program.
print_executable_size(pgm-dev)
# Uncomment this line leads to TARGET 'pgm-prod' was not created in this directory.
# print_executable_size(pgm-prod)
My ideal build process would be to have:
a cmake target "pgm-dev" that just build the executable,
a cmake target "pgm-prod" that re-build the "pgm-dev" executable if necessary AND do some POST_BUILD actions.
Why having two targets "pgm-dev" and "pgm-prod"?
Consider the "pgm-prod" does some extra build actions like cipher the produced binary so, no need to do it in everyday development.
Why use a cmake file with a function instead of add_custom_command() right after executable?
I have multiple executables concerned in my whole project and I would like to avoid code duplications.
Why not creating another executable, with a different name?
"pgm-dev" and "pgm-prod" are exactly compiled the same way, only post build actions differ.
I thought using add_executable(.. ALIAS ..) would be great for this but it seems I'm not understanding some key points here. What would be the best CMake approach to do what I want?
Don't create an alias. Instead create a custom target that executes the commands and add a dependency to the executable target
add_executable(pgm-dev main.cpp)
add_custom_target(pgm-prod COMMAND size $<TARGET_FILE:pgm-dev>)
add_dependency(pgm-prod pgm-dev)
This way cmake makes sure that pgm-dev is built before the command is executed.
If you need to more than one of those commands to all be executed, you could introduce intermediate targets that execute the command that depend on the original target and create a target for executing all those commands that depends on all of those:
function(pgm_add_custom_postbuild_command ORIGINAL_TARGET NEW_TARGET SUFFIX)
if(NOT TARGET ${ORIGINAL_TARGET})
add_custom_target(${ORIGINAL_TARGET})
endif()
add_custom_target(pgm_internal_${ORIGINAL_TARGET}_${SUFFIX} ${ARGN})
# hide away target in a dedicated folder, if folders are activated in the IDE
set_target_properties(pgm_internal_${ORIGINAL_TARGET}_${SUFFIX} PROPERTIES FOLDER PgmInternal)
add_dependencies(pgm_internal_${ORIGINAL_TARGET}_${SUFFIX} ${ORIGINAL_TARGET})
add_dependencies(${NEW_TARGET} pgm_internal_${ORIGINAL_TARGET}_${SUFFIX})
endfunction()
function(pgm_print_size ORIGINAL_TARGET NEW_TARGET)
pgm_add_custom_postbuild_command(${ORIGINAL_TARGET} ${NEW_TARGET} size
COMMAND size $<TARGET_FILE:${ORIGINAL_TARGET}>)
endfunction()
function(pgm_print_md5sum ORIGINAL_TARGET NEW_TARGET)
pgm_add_custom_postbuild_command(${ORIGINAL_TARGET} ${NEW_TARGET} md5sum
COMMAND ${CMAKE_COMMAND} -E md5sum $<TARGET_FILE:${ORIGINAL_TARGET}>)
endfunction()
pgm_print_size(pgm-dev pgm-prod)
pgm_print_md5sum(pgm-dev pgm-prod)
Newbee here on QT creator and CMake and apologies for the basic question but struggling to link simple external library to my main project. Here are the project paths:
build-QMLTest-Desktop_Qt_6_3_1_MinGW_64_bit-Debug // application binary path
QMLLib
build-mylib-Desktop_Qt_6_3_1_MinGW_64_bit-Debug // lib binary path
mylib
CMakeLists.txt
mylib.cpp // lib source path
mylib.h
QMLTest
CMakeLists.txt
main.cpp // application source path
Here is my how I am trying to link the static library (snipped of QMLTest/CMakeLists.txt). I am using this thread as reference:
How do I explicitly specify an out-of-tree source in CMake?
add_subdirectory(../QMLLib/mylib ../QMLLib/build-mylib-Desktop_Qt_6_3_1_MinGW_64_bit-Debug)
target_link_libraries(mylib)
set(PROJECT_SOURCES
main.cpp
qml.qrc
)
main.cpp
#include "../QMLLib/mylib/mylib.h"
int main(int argc, char *argv[])
{
Mylib mylib;
Yet, it won't resolve, here is the output
C:/Qt/Tools/mingw1120_64/bin/../lib/gcc/x86_64-w64-mingw32/11.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: CMakeFiles/QMLTest.dir/main.cpp.obj: in function `qMain(int, char**)':
C:/myroot/UITest/QMLTest/main.cpp:13: undefined reference to `Mylib::Mylib()'
collect2.exe: error: ld returned 1 exit status
I could not get an answer, so 50 points for grabs, since I cannot resolve this issue.
Ideally if you can point me out to recent project using CMake and QT Creator 8.0 making a simple project and library and link them together, it would be great.
At first, try to separate the directories to add in the add_subdirectory command arguments to be sure that everything within the library subdirectories is compiled. Then use target_link_libraries command properly:
add_subdirectory(../QMLLib/mylib)
set(PROJECT_SOURCES
main.cpp
qml.qrc
)
include_directories(../QMLLib/mylib/include)
# Link subdir target AFTER definition of the project target
add_library(myproject ${PROJECT_SOURCES})
target_link_libraries(myproject mylib)
I suggest you refer to the actual CMakeLists.txt file I used when importing my library into the existing project (despite being ROS-based, CMake works the same way). Just extract the sequence of commands from there.
I ended up re-arranging the folders and the solution is here:
Cannot add library source to cmake project on QT
where I just include the sources from a separate library project
I'm building a dynamic library that is meant to be loaded dynamically like a plugin. When present, the library is loaded. When not present, it can't.
Naturally, I made a test app...and it doesn't work.
CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
PROJECT(MYLIB)
enable_testing()
INCLUDE(CPack)
SET(SRCS
src/source1.cpp
src/source2.cpp
)
ADD_LIBRARY(mylib SHARED ${SRCS})
ADD_SUBDIRECTORY(test)
test/CMakeLists.txt
ADD_EXECUTABLE(test_loader main.c)
TARGET_LINK_LIBRARIES(test_loader dl)
ADD_TEST(NAME test_loader COMMAND test_loader)
test/main.c
#include <dlfcn.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
void* handle;
handle = dlopen("./mylib.so", RTLD_LAZY);
if (handle == 0)
{
fprintf(stderr, "%s\n", dlerror());
}
return 1;
}
and then to build
mkdir build
cd build
cmake ..
make
The result is that there is a mylib.so file in /build. There is a test_loader executable in /build/test.
And this doesn't work out.
What I need is for there to be a copy of mylib.so under /build/test/ so that I can dynamically load it with the test app.
While the answers given helped me greatly (and anyone reading this should review those answers as well as the comments), I found an even better solution:
add_custom_command(TARGET test_loader POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE:mylib>" $<TARGET_FILE_DIR:test_loader>)
This solution results in two copies of the library. One copy is next to the test_loader, so that the unit tests can work. The other is in the default output location. Since it's still in the default output location, other projects that depend on mylib know where to find them, by default.
There are cmake variables that let you control where libraries, shared libraries, and executables are placed. See cmake documentation Variables that Control the Build. I do not think it is a good idea to mix executables and libraries. (Of course this is my opinion--you can use whatever layout you want.) I use the following:
# Directory for output files
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
CACHE PATH "Output directory for static libraries.")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
CACHE PATH "Output directory for shared libraries.")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
CACHE PATH "Output directory for executables and DLL's.")
If you used this, then you would need to adjust your dlopen statement to handle = dlopen("../lib/libmylib.so", RTLD_LAZY); Note that I adjusted the name of mylib because I believe cmake will, by default on Linux, use a lib prefix when naming libraries.
I am new to ROS (Indigo), and I want to write a C++ program to use the MoveIt! package.
However, I am confused as to what I need to have in my CMakeLists.txt file, such that the header and library files can be found.
For example, here is the file so far:
cmake_minimum_required(VERSION 2.8.1)
project(MicoMoveIt)
set(CMAKE_CXX_FLAGS "-std=c++11")
file(GLOB srcs *.cpp)
include_directories(/opt/ros/indigo/include)
link_directories(/opt/ros/indigo/lib)
add_executable(${PROJECT_NAME} ${srcs})
And my main.cpp file is:
#include <ros/ros.h>
#include <moveit/move_group_interface/move_group.h>
int main()
{
moveit::planning_interface::MoveGroup group("right_arm");
return 0;
}
But compiling this gives me the error:
/opt/ros/indigo/include/moveit/robot_model/joint_model.h:47: error: Eigen/Geometry: No such file or directory
#include <Eigen/Geometry>
^
So, I need to specify where to find these Eigen header files. But given that Eigen is a dependency of MoveIt!, then I shouldn't have to manually include all these header file directories.
How should I be writing the CMakeLists.txt file so that all header and library directories of dependencies are included?
Try with this changes:
cmake_minimum_required(VERSION 2.8.3)
project(<project_name>)
set(CMAKE_CXX_FLAGS "-std=c++11")
find_package(catkin REQUIRED COMPONENTS
<other_packages_you_need>
moveit_ros_planning_interface
)
catkin_package(CATKIN_DEPENDS)
include_directories(include ${catkin_INCLUDE_DIRS})
set(BINNAME <executable_name>)
set(SOURCES <cpp_source_files>)
add_executable (${BINNAME} ${SOURCES})
target_link_libraries(${BINNAME} ${catkin_LIBRARIES})
The key factor for your problem is the inclusion of package moveit_ros_planning_interface in the find_package() directive. Everytime you have to build some code, you have to add every package dependancies to your project (in this case you need the moveit interface package). Notice that the package.xml file plays a crucial role too.
Lastly you have to tell the linker to retrieve and link the libraries you are using in your project. To do so use target_link_libraries() and the useful catkin_LIBRARIES variable which almost does what you expected (you could manually add libraries, but it could be messy in big projects).
Have a look here and here for more info.
EDIT: in this case you do not need the link_directories() directive, which has to be used only when the linker has to know where to retrieve the external library.
How can I get a CMake generated build system to retrieve source files using an arbitrary shell command and update the build properly?
The motivation for doing this is to migrate from another build system to CMake. The intermediate goal is to have a single list of source files shared between the build systems. The list of source files might actually live inside of the other build system but in general suppose some shell command can retrieve the list of source files. This problem is very similar to the problem of using globbing to get source file names but in this case manually listing all the source files in a CMakeLists.txt file is not a reasonable alternative.
Even if there's no way to get CMake itself to do this, an answer that provides any automated solution would be fine (e.g. a script that wraps the generated build system).
Very simple concrete example of the problem
We have an app that consists of two source files, main.cpp and foo.cpp. For some reason it is desirable to acquire the names of sources files with some shell command instead of listing them in a CMakeLists.txt file. For this example, the files are listed one per line in files.txt and we cat this file. In general, the shell command is some script that retrieves a list of source files in mysterious ways.
main.cpp (ver 1)
#include "foo.h"
int main() {
foo();
}
foo.h
#ifndef FOO_H
#define FOO_H
void foo();
#endif
foo.cpp
#include "foo.h"
#include <iostream>
void foo() {
std::cout << "foo()" << std::endl;
}
files.txt (ver 1)
main.cpp
foo.cpp
CMakeLists.txt
cmake_minimum_required(VERSION 2.8.1)
project(SourcesFromCommand)
# run some external command that retrieves our source files
execute_process(COMMAND cat ${CMAKE_CURRENT_SOURCE_DIR}/files.txt OUTPUT_VARIABLE files)
# turn newline separated relative filenames into cmake list of absolute filenames
string(REPLACE "\n" ";${CMAKE_CURRENT_SOURCE_DIR}/" file_list ${files})
# print filenames to make sure the list came out right
foreach(file ${file_list})
message(${file})
endforeach()
add_executable(main ${file_list})
CMake generates a working build system for a project that consists of the above files. Later, our app is successful and popular so we decide to add new functionality for ver. 2.0. main.cpp now calls bar() which lives in bar.h and bar.cpp. We update files.txt accordingly.
main.cpp (ver 2)
#include "foo.h"
#include "bar.h"
int main() {
foo();
bar();
}
bar.h
#ifndef BAR_H
#define BAR_H
void bar();
#endif
bar.cpp
#include "bar.h"
#include <iostream>
void bar() {
std::cout << "bar()" << std::endl;
}
files.txt (ver 2)
main.cpp
foo.cpp
bar.cpp
The build system previously generated by CMake no longer works -- trying to use it results in linker errors because it doesn't know about bar.cpp. This can be solved manually by touching the CMakeLists.txt file or rerunning the cmake command but the point of a build system is to free us from such arduous and easily forgotten manual labor. How can this be automated?
You can solve this problem with a makefile that touchs the relevant CMakeLists.txt files.
I'll continue from the OP's example and also add a directory, glob_us/ from which we want to glob all filenames matching *.cpp. It contains baz.cpp which is similar to the OP's bar.cpp.
Relevant files:
Makefile
CMakeLists.txt
files.txt
main.cpp
main.h
foo.cpp
foo.h
bar.cpp
bar.h
glob_us/baz.h
glob_us/baz.cpp
The bottom of CMakeLists.txt becomes:
file(GLOB globbed_files glob_us/*.cpp)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/glob_us)
add_executable(main ${file_list} ${globbed_files})
Makefile contains the following:
MAKEFILE_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
GLOBBED := $(wildcard $(MAKEFILE_DIR)/glob_us/*cpp)
# phony target that depends on whatever files we need to touch
cmake: $(MAKEFILE_DIR)/CMakeLists.txt
$(MAKEFILE_DIR)/CMakeLists.txt: $(MAKEFILE_DIR)/files.txt $(GLOBBED)
#touch $(MAKEFILE_DIR)/CMakeLists.txt
.PHONY: cmake
Generate a build system:
mkdir build
cd build
cmake -G $MY_FAVORITE_GENERATOR ..
Then build:
make -f ../Makefile && $MY_FAVORITE_BUILD_SYSTEM_COMMAND
Files can be added to and globbed from glob_us or as in the OP's example, to files.txt, without manual intervention.