Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

cmake: how to reference and build separate cmake project dependency?

Tags:

c++

cmake

I have a cross-compiler cmake project that depends on libraries from a separate project that happens to also use cmake:

/myProject/CMakeLists.txt (uses cross-compiler)
/anotherProject/CMakeLists.txt (platform-agnostic)

anotherProject can be built completely separately on its own. It has no knowledge of myProject at all.

Now, anotherProject has numerous modules that I need, like:

anotherProject/A/CMakeLists.txt (produces static lib A.a)
anotherProject/B/CMakeLists.txt (produces static lib B.a)
etc

When I build myProject, I want to build and link against anotherProject/A and anotherProject/B, to produce shared lib myproject.so. I'd like to leverage the existing cmake-ness of anotherProject if possible, as opposed to manually globbing its various source sets from myProject.

What's the correct way to achieve this with cmake? I feel like I'm missing something obvious.

It would be straightforward if, say, myProject were just a subdirectory under anotherProject, or if there were a top-level CMakeLists.txt that could reference both myProject and anotherProject; but neither is what I'm after. I know I could build anotherProject and export its libraries to a well-known location, and then reference the export directory from myProject - but I would like to avoid that setup as well.

like image 775
Kevin Avatar asked Feb 01 '18 05:02

Kevin


People also ask

Can CMake download dependencies?

CPM is the new kid on the block, its first version was released on April 2019, it's a CMake script that gives CMake the capacity to make version control, caching, and install the dependencies of your project, created by Lars Melchior.

How do I build from source CMake?

In order to build CMake from a source tree on Windows, you must first install the latest binary version of CMake because it is used for building the source tree. Once the binary is installed, run it on CMake as you would any other project.

What does Add_subdirectory do in CMake?

Add a subdirectory to the build. Adds a subdirectory to the build. The source_dir specifies the directory in which the source CMakeLists.


2 Answers

A solution is to use CMake packages.

Basically, in anotherProject, you craft a CMake configuration file where you set variables to be used by myProject (eg. include directory, list of libraries, compilation flags...), or even targets.

Then, in myProject, you use the find_package() mechanism so that CMake finds this configuration file and imports the variables/targets in your current project.

There is a tutorial on the CMake wiki.

like image 104
piwi Avatar answered Sep 28 '22 20:09

piwi


The only alternative setup that I can think of based on your requirements is to allow your main (dependent) project to discover the other (dependee) project using find_package.

In your main project CMakeLists.txt you should add something like this:

find_package(anotherProject CONFIG)

if(anotherProject_FOUND)
  message(STATUS "Found project dependency: anotherProject")
else
  # change WARNING to FATAL_ERROR if the dependency is NOT optional
  message(WARNING "package anotherProject was not found")
endif()

On the differences between CONFIG and MODULE modes, check the documentation and this link.

Then assuming that your main project creates an executable, you could hook up the discovered dependency like this:

add_executable(myProject ${SOURCES})
[...]

if(anotherProject_FOUND)
   target_link_libraries(myProject PUBLIC anotherProject)
endif()

This should take care of the required include files and definitions as well.


Now in the dependee project CMakeLists.txt you should do something like this:

set(PRJ_NAME "anotherProject")
string(TOLOWER ${PRJ_NAME} PRJ_NAME_LOWER)
set(ANOTHERPROJECT_EXPORT_NAME "${PRJ_NAME}")

install(TARGETS ${PRJ_NAME} EXPORT ${ANOTHERPROJECT_EXPORT_NAME}
  RUNTIME DESTINATION .)
install(EXPORT ${ANOTHERPROJECT_EXPORT_NAME} DESTINATION "share/cmake")

This associates an export with a target and then installs the export.

Now, if you check that export file, it expects certain things to be found and included, that could be specific for your project. To make this as supple as possible, you can use the configure feature to generate them from a template and then install from the build directory.
So, in the project under a subdir named share/cmake you could have a file named config.cmake.in with contents:

include(${CMAKE_CURRENT_LIST_DIR}/@[email protected])

In the main project's CMakeLists.txt you need to add the following for generating the file from that template:

configure_file(${CMAKE_CURRENT_SOURCE_DIR}/share/cmake/config.cmake
  ${CMAKE_CURRENT_BINARY_DIR}/share/cmake/${PRJ_NAME_LOWER}-config.cmake)

install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/share/
  DESTINATION share)

Notice that I used PRJ_NAME, because you could potentially reuse that to name the actual executable at the add_executable command. It mentally helps if the exported target has the same name with produced one.

This is a more versatile version to accommodate multiple subprojects of this tutorial.

like image 36
compor Avatar answered Sep 28 '22 21:09

compor