Jiri Kralovec
Jiri Kralovec

Reputation: 1627

CMake - Library with example subdirectory running as separate project

Edit 2 - Fixed!

The issue was fixed by correctly using absolute paths rather than relative paths, and by adding add_subdirectory to example/CMakelists.txt.

I have updated the provided code (and will leave the repository in case somebody wants to use it as a starting point.

Edit:

Original post:

I am trying to write a library, that contains example project, and can be added as a Git submodule. The desired structure would be this:

- source
  - MyLib
    lib.cpp
  - CMakelists.txt
- include
  - MyLib
    lib.h
- example
  main.cpp
  CMakelists.txt
CMakelists.txt (main CMake for library)

What am I trying to achieve:

What is my problem: My biggest problem is linking the example to the library which lives in a sibling folder, and make sure it compiles. I managed to write a CMakelists.txt in a way that my IDE understands #include statements correctly, but during compilation the function definitions are not found. Could anyone provide some pointers, please?

Exact compilation error message:

C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/10.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -lMY_LIB
collect2.exe: error: ld returned 1 exit status
mingw32-make[3]: *** [CMakeFiles\MY_LIB_EXAMPLE.dir\build.make:95: MY_LIB_EXAMPLE.exe] Error 1
mingw32-make[2]: *** [CMakeFiles\Makefile2:82: CMakeFiles/MY_LIB_EXAMPLE.dir/all] Error 2
mingw32-make[1]: *** [CMakeFiles\Makefile2:89: CMakeFiles/MY_LIB_EXAMPLE.dir/rule] Error 2
mingw32-make: *** [Makefile:123: MY_LIB_EXAMPLE] Error 2

CMakelists.txt files CMakelists.txt:

cmake_minimum_required(VERSION 3.20)
set(CMAKE_CXX_STANDARD 20)
project(MY_LIB)
add_subdirectory(source)

source/CMakelists.txt

add_library(MY_LIB MyLib/library.cpp ../include/MyLib/library.h)

target_include_directories(MY_LIB PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include/MyLib)
target_include_directories(MY_LIB PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/source/MyLib)

example/CMakelists.txt

cmake_minimum_required(VERSION 3.20)

set(CMAKE_CXX_STANDARD 20)

#--------------------------------------------------------------------
# Get solution root
#--------------------------------------------------------------------
cmake_path(GET CMAKE_CURRENT_SOURCE_DIR PARENT_PATH LIB_PATH)
cmake_path(SET LIB_INCLUDE_PATH "${LIB_PATH}/include")

message(${LIB_PATH})
message(${LIB_INCLUDE_PATH})

#--------------------------------------------------------------------
# Set project name
#--------------------------------------------------------------------
project(MY_LIB_EXAMPLE)

#--------------------------------------------------------------------
# Add source
#--------------------------------------------------------------------
add_executable(MY_LIB_EXAMPLE main.cpp)
add_subdirectory(${LIB_PATH} library-build)

#--------------------------------------------------------------------
# Link libraries
#--------------------------------------------------------------------
target_link_libraries(MY_LIB_EXAMPLE MY_LIB)

#--------------------------------------------------------------------
# Link include directories
#--------------------------------------------------------------------
target_include_directories(MY_LIB_EXAMPLE PUBLIC ${LIB_INCLUDE_PATH})

I created a sample repository with my setup: https://github.com/jiriKralovec/cmake-library

Upvotes: 1

Views: 1965

Answers (1)

KamilCuk
KamilCuk

Reputation: 141940

he example project should be capable of running on its own by loading the CMakelists.txt in the directory, and should be able to use the library

Just add:

add_subdirectory(./../ some_unique_name_here)

I think I would remove source/CMakelists.txt and write it all in root CMakelists.txt. It's odd to use ../ to refer to include directories.


I suggest doing options and/or unit tests:

root CMakeLists.txt:

include(CTest)
add_library(MY_LIB ....)

# one design
if (BUILD_TESTING)
   add_subdirectory(example)
endif()

# another design
add_subdirectory(utilities)

example/CMakeLists.txt:

# if it is something simple, add unit test:
add_executable(MY_LIB_EXAMPLE1 <maybe EXCLUDE_FROM_ALL?> ...)
add_test(NAME MY_LIB_EXAMPLE1 COMMAND MY_LIB_EXAMPLE1)

utilities/CMakeLists.txt:

# if it is a utility, optionally build it
add_executable(MY_LIB_UTILITY_TO_DO_SMTH ...)
option(... BUILD_MY_LIB_UTILITY_TO_DO_SMTH OFF)
if(NOT BUILD_MY_LIB_UTILITY_TO_DO_SMTH)
   set_target_properties(
       MY_LIB_UTILITY_TO_DO_SMTH
       PROPERTIES EXCLUDE_FROM_ALL ON
   )
endif()

Either way, do one CMake build. Then if user wants to build the utility, he will do cmake --build <builddir> --target MY_LIB_UTILITY_TO_DO_SMTH (or make MY_LIB_UTILITY_TO_DO_SMTH). If you would want to build unit tests, you would do cmake ... -D BUILD_TESTING=1.

Some people add all unit tests executables as EXCLUDE_FROM_ALL and make special add_custom_target(build_tests) add_target_dependencies(build_tests MY_LIB_EXAMPLE1 etc. etc.) and build that custom target before testing.

Upvotes: 3

Related Questions