Introduction
Using CMake, I am trying to build a library using a special make target. To this end, I use the EXCLUDE_FROM_ALL argument to ensure that the library is not built by running make (which defaults to make all). Unfortunately, the EXCLUDE_FROM_ALL option seems to be ignored by cmake.
Consider the following file tree:
.
├── CMakeLists.txt
├── foo
│ ├── CMakeLists.txt
│ ├── headers
│ │ └── foo.h
│ └── src
│ └── foo.cpp
└── src
└── main.cpp
4 directories, 5 files
In this file tree, you can see that I basically have two parts:
The foo library, for which all files are located in ./foo/.
The main executable, for which the source code is located in ./src/main.cpp.
Below, I will further explain these two parts and the behaviour I am observing.
The foo library
First of all, the header code located in ./foo/headers/foo.h is:
#ifndef _FOO_H_
#define _FOO_H_
#include <iostream>
void foo(void);
#endif // _FOO_H_
The corresponding source code, located in ./foo/src/foo.cpp, is:
#include "foo.h"
void foo(void) {
std::printf("Hello foo!\n");
}
As you can see, the foo library only contains the function foo(). This function simply prints Hello, foo! to the terminal. To build the foo library, I have the following CMakeLists.txt file defined (in ./foo/CMakeLists.txt):
cmake_minimum_required(VERSION 3.10)
project(foo
LANGUAGES CXX
)
# Create a foo library target
add_library(foo EXCLUDE_FROM_ALL
"src/foo.cpp"
)
target_include_directories(foo
PUBLIC "headers"
)
Note that I use the EXCLUDE_FROM_ALL argument in the add_library() function.
To test the behaviour of the EXCLUDE_FROM_ALL argument, I will now build the foo library as a stand-alone (using the previously shown CMakeLists.txt). Starting from the top directory (.), I use the following workflow:
$ cd foo
$ mkdir build
$ cd build
$ cmake ..
After this is finished, I run the make command:
$ make
(nothing happens)
As expected, nothing happens (since the foo library is excluded from the all target). Now, when I use make foo, I get the following output:
$ make foo
Scanning dependencies of target foo
[ 50%] Building CXX object CMakeFiles/foo.dir/src/foo.cpp.o
[100%] Linking CXX static library libfoo.a
[100%] Built target foo
So far, so good. Before moving on to building the main executable, I completely remove the ./foo/build directory and go back to the top-level directory:
$ cd ..
$ rm -rf build
$ cd ..
The main executable
The source code for the main executable is located in ./src/main.cpp:
#include "foo.h"
int main() {
foo();
return 0;
}
The top-level CMakeLists.txt contains:
cmake_minimum_required(VERSION 3.10)
project(TestProject
LANGUAGES CXX
)
# Include the foo library
add_subdirectory("foo" EXCLUDE_FROM_ALL)
# Create the main executable
add_executable(TestProject
"src/main.cpp"
)
target_link_libraries(TestProject
PRIVATE foo
)
Note that I again use the EXCLUDE_FROM_ALL argument, but now for the add_subdirectory() function.
To generate a makefile for the main executable, I use the following workflow:
$ mkdir build
$ cd build
$ cmake ..
When I now run make, I get the following output:
$ make
Scanning dependencies of target foo
[ 25%] Building CXX object foo/CMakeFiles/foo.dir/src/foo.cpp.o
[ 50%] Linking CXX static library libfoo.a
[ 50%] Built target foo
Scanning dependencies of target TestProject
[ 75%] Building CXX object CMakeFiles/TestProject.dir/src/main.cpp.o
[100%] Linking CXX executable TestProject
[100%] Built target TestProject
As you can see, it first builds the foo library. However, this is not what I expected! I explicitly stated to exclude the foo library from the all target. Hence, I would expect the need to first run make foo before running make.
This brings me to my main question(s): does someone know why it ignores the EXCLUDE_FROM_ALL option in the add_subdirectory() function in the top-level CMakeLists.txt? It did work when I built the foo library as a stand-alone. Is this a bug in cmake? If not, how do I achieve my expected behaviour (i.e. the need to run make foo before running make)?
This is not a bug. CMake does not include the foo lib in the all target directly. It does however add the foo target as dependency to TestProject and since TestProject is included in the all target, foo gets built too, if you build all.
This needs to be done this way. How else would you be able to link the foo function into TestProject?
If you want to use a lib that is not built as part of all, you need to use a imported lib. Doing this is and including the lib in the project seems a bit nonsensical though, but here you go.
add_library(foo STATIC EXCLUDE_FROM_ALL
"src/foo.cpp"
)
cmake_minimum_required(VERSION 3.10)
project(TestProject
LANGUAGES CXX
)
# Include the foo library
add_subdirectory("foo" EXCLUDE_FROM_ALL)
# Create the main executable
add_executable(TestProject
"src/main.cpp"
)
# add copy of foo as imported lib
add_library(foo_imported STATIC IMPORTED)
target_include_directories(foo_imported INTERFACE $<TARGET_PROPERTY:foo,INTERFACE_INCLUDE_DIRECTORIES>)
set_target_properties(foo_imported PROPERTIES IMPORTED_LOCATION $<TARGET_FILE:foo>)
target_link_libraries(TestProject
PRIVATE foo_imported
)
Related
I have a cmake project somewhere that I want to use in several other projects. Let's call it projA located at path /projA. I have built it in /projA/build. In this build folder there is some library /projA/build/lib.a.
Now if I want to create a new project B using project A in the folder /projB I know two options for the CMakeLists:
Solution A
cmake_minimum_required(VERSION 3.0)
project(projB)
add_executable(${PROJECT_NAME} projB.cpp)
add_subdirectory(/projA /projA/build)
target_link_libraries(${PROJECT_NAME} projA)
The problem is that this solution will create new make files in /projA/build and the project A will be built again. Furthermore each time I will switch to a new project using projA, projA will be built again. So that's not a good solution. I would like to not overwrite all the build folder each time I switch between two project using projA.
Solution B
cmake_minimum_required(VERSION 3.0)
project(projB)
add_executable(${PROJECT_NAME} projB.cpp)
link_libraries(${PROJECT_NAME} /projA/build/lib.a)
target_include_directories(${PROJECT_NAME} PRIVATE /projA/include)
# Include directories
target_include_directories(${PROJECT_NAME} PRIVATE /projA/deps/depA/include)
target_include_directories(${PROJECT_NAME} PRIVATE /projA/deps/depB/include)
...
This solution works, but it's not very beautiful. I have to add a line for each include directory of all dependencies of project A.
So my question is: Is there a way to do it properly?
If you want to build them entirely separately, you will need to go through the find_package infrastructure. Here's a complete example:
Project A
lib.h
#ifndef LIB_H
#define LIB_H
int lib();
#endif
lib.c
#include "lib.h"
int lib() { return 42; }
CMakeLists.txt
cmake_minimum_required(VERSION 3.21)
project(projA)
add_library(lib lib.c)
target_include_directories(lib PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>")
# Export the target named lib for use in other projects
# from the build tree of this project.
export(TARGETS lib FILE projA-config.cmake)
Project B
main.c
#include <stdio.h>
#include <lib.h>
int main () {
printf("%d\n", lib());
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.21)
project(projB)
# Import the targets defined by projA
find_package(projA REQUIRED)
add_executable(projB main.c)
target_link_libraries(projB PRIVATE lib)
Compilation steps:
I'm imagining the two directories are next to each other, like so:
$ tree
.
├── projA
│ ├── CMakeLists.txt
│ ├── lib.c
│ └── lib.h
└── projB
├── CMakeLists.txt
└── main.c
Now we can build project A:
$ cmake -S projA -B projA/build -DCMAKE_BUILD_TYPE=Release
...
-- Configuring done
-- Generating done
-- Build files have been written to: /path/to/projA/build
$ cmake --build projA/build/ -- -v
[1/2] /usr/bin/cc -I/path/to/projA -O3 -DNDEBUG -MD -MT CMakeFiles/lib.dir/lib.c.o -MF CMakeFiles/lib.dir/lib.c.o.d -o CMakeFiles/lib.dir/lib.c.o -c /path/to/projA/lib.c
[2/2] : && /usr/bin/cmake -E rm -f liblib.a && /usr/bin/ar qc liblib.a CMakeFiles/lib.dir/lib.c.o && /usr/bin/ranlib liblib.a && :
And now we'll build project B, and set the projA_ROOT variable to /path/to/projA/build so that find_package(projA) will succeed.
$ cmake -S projB -B projB/build -DCMAKE_BUILD_TYPE=Release -DprojA_ROOT=$PWD/projA/build
...
-- Configuring done
-- Generating done
-- Build files have been written to: /path/to/projB/build
$ cmake --build projB/build
$ cmake --build projB/build/ -- -v
[1/2] /usr/bin/cc -isystem /path/to/projA -O3 -DNDEBUG -MD -MT CMakeFiles/projB.dir/main.c.o -MF CMakeFiles/projB.dir/main.c.o.d -o CMakeFiles/projB.dir/main.c.o -c /path/to/projB/main.c
[2/2] : && /usr/bin/cc -O3 -DNDEBUG CMakeFiles/projB.dir/main.c.o -o projB /path/to/projA/build/liblib.a && :
Clearly, this is non-obvious, and it's also sort of minimal. To build your own packages correctly, you should carefully read these CMake documentation pages:
https://cmake.org/cmake/help/latest/guide/importing-exporting/index.html
https://cmake.org/cmake/help/latest/command/install.html
https://cmake.org/cmake/help/latest/command/export.html
I would also suggest watching Craig Scott's talk "Deep CMake for Library Authors", here: https://youtu.be/m0DwB4OvDXk
file structure
├── a_lib
│ ├── a.cpp
│ └── a.h
├── CMakeLists.txt
└── main.cpp
CmakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(untitled)
set(CMAKE_CXX_STANDARD 11)
add_library(a_lib SHARED a_lib/a.cpp)
target_include_directories(a_lib PUBLIC a_lib/)
add_executable(untitled main.cpp)
target_link_libraries(untitled a_lib)
But every time I build the a_lib will be rebuilt when a_lib is not changed.
cmake --build /home/autocar/Workspace/untitled/cmake-build-debug --target untitled -- -j 4
[ 50%] Built target a_lib
[100%] Built target untitled
So what should I do, if I do not want to rebuild the linked library every time. It takes a long time when a_lib builds every time even without any changes.
Just one addition to arrowd's answer:
If you want to completely avoid check if any target is up to date, you can issue the following command:
cmake --build /home/autocar/Workspace/untitled/cmake-build-debug --target untitled/fast -- -j 4
Note that --target untitled is changed into --target untitled/fast.
You can add "/fast" to any target mentioned in CMakeLists.txt to trigger compilation without any additional checks. On the other hand, be extremely cautious, since this allows you create inconsistent results, in case when sources for a_lib are changed, and target a_lib should be compiled/linked as well.
Judging from this output
cmake --build /home/autocar/Workspace/untitled/cmake-build-debug --target untitled -- -j 4
[ 50%] Built target a_lib
nothing's actually gets rebuilt. CMake just checks that this target is up to date.
The compilation takes place when CMake prints Building CXX object a.cpp.o and linking is performed when Linking CXX library a_lib.so pops out. If you don't see these messages, the library doesn't get rebuilt.
I have a simple shared library libfool2.so with installed header fool2.h which are not from a CMake project. My project my_temp1 depends on fool2 so I write a FindFool2.cmake to make an imported target:
find_path(Fool2_INCLUDE_DIR fool2.h PATH_SUFFIXES fool2)
find_library(Fool2_LIB fool2)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Fool2
REQUIRED_VARS Fool2_INCLUDE_DIR Fool2_LIB
)
if(Fool2_FOUND AND NOT TARGET Fool2::Fool2)
add_library(Fool2::Fool2 SHARED IMPORTED)
set_target_properties(Fool2::Fool2 PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${Fool2_INCLUDE_DIR}"
INTERFACE_LINK_LIBRARIES "${Fool2_LIB}"
)
endif()
The CMakeLists.txt for my_temp1 project is:
cmake_minimum_required(VERSION 3.3)
project(my_temp1)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake/cmake_modules)
# FindFool2.cmake is in ${CMAKE_CURRENT_LIST_DIR}/cmake/cmake_modules
find_package(Fool2 REQUIRED)
if (TARGET Fool2::Fool2)
message(STATUS "target found")
endif()
add_executable(my_temp1 main.cpp)
target_link_libraries(my_temp1 Fool2::Fool2)
Now
$ tree ../__install
../__install/
├── include
│ └── fool2
│ ├── fool2.h
│ └── version.h
└── lib
└── libfool2.so
$ tree .
.
├── cmake
│ └── cmake_modules
│ └── FindFool2.cmake
├── CMakeLists.txt
└── main.cpp
$ cmake -H. -B_builds -DCMAKE_INSTALL_PREFIX=../__install
# some output omitted
-- target found
-- Configuring done
-- Generating done
-- Build files have been written to: /OMITTED/my_temp1/_builds
$ cmake --build _builds
CMakeFiles/my_temp1.dir/build.make:82: *** target pattern contains no '%'. Stop.
CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/my_temp1.dir/all' failed
make[1]: *** [CMakeFiles/my_temp1.dir/all] Error 2
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2
$ head -n 85 _builds/CMakeFiles/my_temp1.dir/build.make | tail -n 10
# External object files for target my_temp1
my_temp1_EXTERNAL_OBJECTS =
my_temp1: CMakeFiles/my_temp1.dir/main.cpp.o
my_temp1: CMakeFiles/my_temp1.dir/build.make
my_temp1: Fool2::Fool2-NOTFOUND
my_temp1: CMakeFiles/my_temp1.dir/link.txt
#$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --bold --progress-dir=/OMITTED/my_temp1/_builds/CMakeFiles --progress-num=$(CMAKE_PROGRESS_2) "Linking CXX executable my_temp1"
$(CMAKE_COMMAND) -E cmake_link_script CMakeFiles/my_temp1.dir/link.txt --verbose=$(VERBOSE)
The command $ cmake -H. -B_builds -DCMAKE_INSTALL_PREFIX=../__install finds fool2 because find_* commands searches in the CMAKE_INSTALL_PREFIX as well.
But why is there weird output my_temp1: Fool2::Fool2-NOTFOUND in build.make?
CMake version is 3.11.3
For IMPORTED library target value -NOTFOUND corresponds to absent IMPORTED_LOCATION property, corresponded to the library's path. You need to set that property for correctly work with IMPORTED target.
If you want CMake target to be a placeholder just for link with other libraries, use INTERFACE library target instead: such library target doesn't have library location.
I'm encountering an annoying error in CMake where the following conditions hold:
A target name is identical to name of the directory defining the target.
I am using the RUNTIME_OUTPUT_DIRECTORY property for the target.
Under these conditions, I get the errors:
Linking CXX executable .
/usr/bin/ld: cannot open output file .: Is a directory
CMake seems to be trying to build a target named . , apparently trying to refer to the current directory name rather than the desired target name.
Here's a trivial example. My file tree is:
/tmp/example$ tree
.
├── build
└── src
├── CMakeLists.txt
└── hello_world
├── CMakeLists.txt
└── HelloWorld.cpp
src/CMakeLists.txt:
set (ARBITRARY_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR})
add_subdirectory(hello_world)
src/hello_world:
add_executable(hello_world HelloWorld.cpp)
set_property(TARGET hello_world PROPERTY RUNTIME_OUTPUT_DIRECTORY ${ARBITRARY_OUTPUT_DIR})
...and HelloWorld.cpp itself is a trivial Hello World program, with a main() method.
I run:
/tmp/example/build$ cmake ../src/ -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=gcc-4.8 -DCMAKE_CXX_COMPILER=g++-4.8 -G"Unix Makefiles"
/tmp/example/build$ make VERBOSE=1
And I get:
[ 50%] Building CXX object hello_world/CMakeFiles/hello_world.dir/HelloWorld.o
cd /tmp/example/build/hello_world && /usr/bin/g++-4.8 -O3 -DNDEBUG -o CMakeFiles/hello_world.dir/HelloWorld.o -c /tmp/example/src/hello_world/HelloWorld.cpp
[100%] Linking CXX executable .
cd /tmp/example/build/hello_world && /usr/bin/cmake -E cmake_link_script CMakeFiles/hello_world.dir/link.txt --verbose=1
/usr/bin/g++-4.8 -O3 -DNDEBUG CMakeFiles/hello_world.dir/HelloWorld.o -o . -rdynamic
/usr/bin/ld: cannot open output file .: Is a directory
collect2: error: ld returned 1 exit status
As you can see, CMakeFiles/hello_world.dir/link.txt sets the linking target as -o . , which obviously won't work.
Is this a bug in CMake, or am I doing something wrong? Is there some workaround for this?
My tools are:
cmake version 3.5.1
Ubuntu 16.04 LTS (Xenial Xerus)
g++-4.8
Your top-level build directory contains hello_world directory because of command add_subdirectory(hello_world).
By setting RUNTIME_OUTPUT_DIRECTORY property to top-level build directory, you want to create executable file with name hello_world there.
But in single directory it is impossible to have both file and directory with the same name.
You need to rename either subdirectory or executable or change directory to place executable.
I have a project with a structure
├── CMakeLists.txt
├── mzl.c
├── mzl.h
└── tests
├── CMakeLists.txt
├── mzl-communication-test.c
├── mzl-setup-test.c
├── mzl-test-errors.c
└── mzl-test-errors.h
Where the top directory CMakeLists.txt file is
project(mzl)
cmake_minimum_required(VERSION 2.8)
add_subdirectory(tests)
# Enable testing for the project
enable_testing()
# Find zmq
find_library(ZMQ_LIB zmq REQUIRED)
message(STATUS "ZMQ Library: ${ZMQ_LIB}")
# Find threading library
set(CMAKE_THREAD_PREFER_PTHREAD ON)
find_package(Threads REQUIRED)
message(STATUS "Threading Library: ${CMAKE_THREAD_LIBS_INIT}")
# Set include directories for headers
set (
MZL_INCLUDE_DIRS
${CMAKE_SOURCE_DIR}
CACHE STRING "MZL Include Directories"
)
include_directories(${MZL_INCLUDE_DIRS})
# Set source files
set (
MZL_SRC_FILES
mzl.c
)
# Add library
add_library(${PROJECT_NAME} STATIC ${MZL_SRC_FILES})
# Link to zmq
target_link_libraries(${PROJECT_NAME} ${ZMQ_LIB} ${CMAKE_THREAD_LIBS_INIT})
and tests/CMakeLists.txt is
# Use MZL Source Directories (mzl headers are in top directory)
include_directories(${CMAKE_SOURCE_DIR})
# Variable from here is empty
message(STATUS "MZL Include Directories: ${MZL_INCLUDE_DIRS}")
# Files common to all tests
set ( TEST_COMMON_SOURCES
mzl-test-errors.c
)
# Library setup/shutdown testing
set(SETUP_TEST_NAME mzl-setup-test)
add_executable(${SETUP_TEST_NAME} ${TEST_COMMON_SOURCES} ${SETUP_TEST_NAME}.c)
target_link_libraries(${SETUP_TEST_NAME} ${PROJECT_NAME} ${ZMQ_LIB})
add_test(${SETUP_TEST_NAME} ${SETUP_TEST_NAME})
# Communcations test
set(COMMUNICATION_TEST_NAME mzl-communication-test)
add_executable(${COMMUNICATION_TEST_NAME} ${TEST_COMMON_SOURCES}
${COMMUNICATION_TEST_NAME}.c)
target_link_libraries(${COMMUNICATION_TEST_NAME} ${PROJECT_NAME}
${CMAKE_THREAD_LIBS_INIT} ${ZMQ_LIB})
add_test(${COMMUNICATION_TEST_NAME} ${COMMUNICATION_TEST_NAME})
Everything worked fine before I added the second test mzl-communication-test. After adding it and the lines in the tests/CMakeLists.txt after # Communications test, running ctest did nothing extra -- it still only ran the first test.
After deleting the build directory and running cmake again, I get no errors for the initial CMake run, but running make runs CMake again, resulting in an error with CMake:
CMake Error: Parse error in cache file build/CMakeCache.txt. Offending entry: include
followed by a Make error:
Makefile:203: recipe for target 'cmake_check_build_system' failed
make: *** [cmake_check_build_system] Error 1
Running make again results in everything being built, including the tests; however, running ctest results in
Test project build
No tests were found!!!
The issue seems to be with something that I am doing with CMake, but can't figure out what to do from here as I can't see anything that I'm doing differently than when it was originally working. Even my last commit to git, which was working when I committed it, is no longer working.
You need to move the enable_testing() call to be before you do add_subdirectory(tests)
# Enable testing for the project
enable_testing()
add_subdirectory(tests)