Reputation: 6275
I'm learning cmake
, coming from a make
/makefile
background.
I have the following directory structure:
.
├── CMakeLists.txt
├── src
│ ├── CMakeLists.txt
│ ├── foo.cpp
│ └── foo.h
└── test
├── CMakeLists.txt
└── unit
├── CMakeLists.txt
└── unit_test.cpp
3 directories, 7 files
Intention/goal:
./src/
directory to be compiled into libfoo.a
, and I want foo.h
and libfoo.a
installed to /tmp/foo/include/foo/
and /tmp/foo/lib/
, respectively.unit_test.cpp
to #include <foo/foo.h>
and link with -lfoo
.Question:
In my top-level CMakeLists.txt
, I have add_subdirectory
statements for both src
and test
directories. When I run make install
(which uses the cmake
-generated Makefiles
), it looks like it's descending into the test/
directory before the install
statements from src/CMakeLists.txt
have been executed.
How can I achieve the stated goal?
I think what I am looking for is a way to create another make target...so that I can run "make install" first, which installs the files at their respective destinations, then - as a separate step - run another make target, which compiles the unit test files and makes use of libfoo.a and foo.h at their installed locations.
Problem:
As-is (files below), when I run make install
, compilation of unit_test.cpp
fails because the file foo/foo.h
isn't found because it hasn't been "installed" yet:
cd /home/user/projects/scratch/cmake/build/test/unit && /usr/bin/c++ -I/tmp/foo/include -o CMakeFiles/unit_test.dir/unit_test.cpp.o -c /home/user/projects/scratch/cmake/test/unit/unit_test.cpp
/home/user/projects/scratch/cmake/test/unit/unit_test.cpp:1:21: fatal error: foo/foo.h: No such file or directory
#include <foo/foo.h>
^
compilation terminated.
What I have tried:
If I comment-out the add_subdirectory( test )
in the top-level CMakeLists.txt
, then foo.h
and libfoo.a
are installed in the desired locations per goal #1.
The Files:
Apologies - I know this is an awkward way to distribute my project content, but I'm not aware of any easier/better way to share this. I've tried, though, to keep file content minimal.
# <root>/CMakeLists.txt
cmake_minimum_required(VERSION 3.7)
set(CMAKE_INSTALL_PREFIX /tmp/foo/)
add_subdirectory(src)
add_subdirectory(test) # If I comment this out, then "make install" succeeds
# <root>/src/CMakeLists.txt
add_library( foo foo.cpp )
install(TARGETS foo DESTINATION lib)
install(FILES foo.h DESTINATION include/foo)
// foo.cpp
#include "foo.h"
#include <iostream>
void foo() {
std::cout << __FUNCTION__ << std::endl;
}
// foo.h
#ifndef FOO_H
#define FOO_H
void foo();
#endif
# <root>/test/CMakeLists.txt
add_subdirectory(unit)
# <root>/test/unit/CMakeLists.txt
add_executable( unit_test unit_test.cpp )
target_include_directories( unit_test PUBLIC /tmp/foo/include )
target_link_libraries( unit_test PUBLIC /tmp/foo/lib )
// unit_test.cpp
#include <foo/foo.h>
int main( int argc, char* argv[] ) {
foo();
return 0;
}
Upvotes: 0
Views: 490
Reputation: 141190
Don't install it. Just compile it as you want as it is and create unit tests as you want. Use target_link_libraries
to link between cmake targets. With the following directory structure:
.
├── CMakeLists.txt
├── src
│ ├── CMakeLists.txt
│ └── foo
| └── foo.cpp
| └── foo.h
└── test
├── CMakeLists.txt
└── unit
└── unit_test.cpp
# CMakeLists.txt
cmake_minimum_required(VERSION 3.11)
include(CTest) # From memory, not sure
add_subdirectory(src)
if (BUILD_TESTING)
add_subdirectory(unit)
endif()
# src/CMakeLists.txt
add_library(foo foo/foo.cpp foo/foo.h)
target_include_directories(foo PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
# unit/CMakeLists.txt
add_executable(unit_test unit/unit_test.cpp)
target_link_libraries(unit_test PUBLIC foo)
add_test(NAME unit_test COMMAND unit_test)
Then configure&build&unit-test with:
cd the_project
cmake -S. -B_build
cmake --build _build
( cd _build && ctest )
Then if you want to let users install foo
, add the whole install(
stuff to src/CMakeLists.txt
. There's little need to install when unit testing - cmake
will manage dependencies and linking by itself. Installing is only used when you really want to install the stuff - I do not see the need to install anything to unit_test (unless you want to test the installation itself).
Upvotes: 1