Im trying to build a vala-application together with an own library in the same project using CMake. Building the application works fine, so I've added a new "lib" directory to my project with the following CMakeLists.txt:
# Configure precompile
vala_precompile (LIB_VALA_C ${LIB_NAME}
Session.vala
PACKAGES
gio-2.0
gee-0.8
OPTIONS
--thread
--vapidir=${CMAKE_SOURCE_DIR}/vapi
--target-glib 2.32
GENERATE_VAPI
${LIB_NAME}
GENERATE_HEADER
${CMAKE_PROJECT_NAME}
)
# Add executable
add_library (${LIB_NAME} SHARED ${LIB_VALA_C})
# Set library properties
set_target_properties (${LIB_NAME} PROPERTIES
OUTPUT_NAME ${LIB_NAME}
VERSION ${LIB_SOVERSION}.${LIB_VERSION}
SOVERSION ${LIB_SOVERSION}
)
target_link_libraries (${LIB_NAME} ${LIB_LIBRARIES})
# Installation
install (TARGETS ${LIB_NAME} DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}/)
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/${LIB_NAME}.pc DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}/pkgconfig/)
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/${LIB_NAME}.vapi DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/vala/vapi/)
install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/${LIB_NAME}.deps DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/vala/vapi/)
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.h DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR}/${LIB_NAME}/)
The CMakeLists.txt in the project's root contains the following:
# Project name
project (drop)
# Minimum requirements of build-system
cmake_minimum_required (VERSION 2.8)
cmake_policy (VERSION 2.6)
# Global configuration
set (DATADIR "${CMAKE_INSTALL_PREFIX}/share")
set (PKGDATADIR "${DATADIR}/${CMAKE_PROJECT_NAME}")
set (GETTEXT_PACKAGE "${CMAKE_PROJECT_NAME}")
set (RELEASE_NAME "${CMAKE_PROJECT_NAME}")
set (VERSION "0.1")
set (VERSION_INFO "Release")
set (PREFIX ${CMAKE_INSTALL_PREFIX})
set (DOLLAR "$")
# Library configuration
set (LIB_VERSION 1.0)
set (LIB_SOVERSION 0)
set (LIB_NAME ${CMAKE_PROJECT_NAME}-${LIB_VERSION})
# Cmake-files
list (APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
# Configuration file
configure_file (${CMAKE_SOURCE_DIR}/dropd/config.vala.cmake ${CMAKE_SOURCE_DIR}/dropd/config.vala)
# Check for vala
find_package (Vala REQUIRED)
include (ValaVersion)
ensure_vala_version ("0.18" MINIMUM)
include (ValaPrecompile)
# Disable C compiler warnings
add_definitions (-w)
# Set gettext-package
add_definitions (-DGETTEXT_PACKAGE="${CMAKE_PROJECT_NAME}")
# Check for required dependencies
find_package (PkgConfig)
pkg_check_modules (DEPS REQUIRED granite gthread-2.0 gio-2.0 gee-0.8 avahi-gobject avahi-client)
# Link the avahi library
add_definitions (-lavahi)
# Link dependencies
add_definitions (${DEPS_CFLAGS})
add_definitions (${LIB_CFLAGS})
link_libraries (${DEPS_LIBRARIES})
link_directories (${DEPS_LIBRARY_DIRS})
link_directories (${LIB_LIBRARY_DIRS})
# Load directories
add_subdirectory (dropd)
add_subdirectory (lib)
add_subdirectory (po)
add_subdirectory (data)
add_subdirectory (schemas)
When building the project now the main application still builds without problems, but when the build of the library begins this error appears (translated):
make[2]: *** No rule to make »../lib/drop-1.0«,
required by »lib/drop-1.0«. End.
make[1]: *** [lib/CMakeFiles/drop-1.0.dir/all] Errors 2
make: *** [all] Errors 2
Badly Im not that familar with CMake and don't get what that error message means, neither what is causing it.
I have already compared my CMakeLists.txt files with them in another project that's also built out of a 'normal' application and one library, but I couldn't find the difference that makes my code not working: http://bazaar.launchpad.net/~wingpanel-devs/wingpanel/trunk/files/head:/
It would be very nice if you could give me some tips what I could have missed in the CMake-Files.
For the ones with the same problem: I've now solved the issue myself with removing the ${LIB_NAME} from the line vala_precompile (LIB_VALA_C ${LIB_NAME}. Im still not sure what's happening here, but this "solution" seemed to work.
Better ways to solve this issue are still appreciated. ;)
Related
I am learning how to use ExternalProject to download header only libaries and link to my executable.
My workflow is following:
I download header library Eigen using ExtenalProject:
cmake -DGET_LIBS=ON -DBUILD_SHARED_LIBS=ON -DBUILD_MY_PROJECTS=OFF -G
"Visual Studio 17 2022" -A x64 .. && cmake --build . --config Release
Then I run the same CMakeLists a second time, but this time I disable ExternalProject and compile the executable that links the Eigen:
cmake -DGET_LIBS=OFF -DBUILD_SHARED_LIBS=OFF -DBUILD_MY_PROJECTS=ON
-G "Visual Studio 17 2022" -A x64 .. && cmake --build . --config Release
Question
Why I need to use both of these commands since in target_include_directories command I specify the same path as in include_directories?
In the code below I need two commands include_directories and target_include_directories.
I thought that it would be enough to use only target_include_directories, but without include_directories it wont work.
if (BUILD_MY_PROJECTS)
add_executable(my_exe main.cpp)
include_directories("${CMAKE_BINARY_DIR}/install/eigen/") #add directory of the header-only library without this the next line wort work
target_include_directories(my_exe INTERFACE "${CMAKE_BINARY_DIR}/install/eigen/")# link exe to exectable
endif ()
My full CMakeLists.txt is following:
project(superbuild LANGUAGES CXX)
cmake_minimum_required(VERSION 3.19)
########################################################################
# EIGEN
########################################################################
SET(GET_LIBS "" CACHE STRING "Set option to download dependencies")
if (GET_LIBS)
message(AUTHOR_WARNING ${GET_LIBS})
ExternalProject_Add(eigen
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
GIT_TAG 3.4.0
CMAKE_ARGS
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/install/eigen #this does nothing...
SOURCE_DIR "${CMAKE_BINARY_DIR}/install/eigen" #install in my local build dir
#SOURCE_DIR ${CMAKE_INSTALL_PREFIX}
BUILD_COMMAND "" #do not build
INSTALL_COMMAND "" #do not install
)
endif ()
###############################################################################
#EXECUTABLES
###############################################################################
if (BUILD_MY_PROJECTS)
add_executable(my_exe main.cpp)
include_directories("${CMAKE_BINARY_DIR}/install/eigen/") #add directory of the header-only library without this the next line wort work
target_include_directories(my_exe INTERFACE "${CMAKE_BINARY_DIR}/install/eigen/")# link exe to exectable
endif ()
Tl;dr never use include_directories. Ever. Learn how property visibility works in CMake instead.
include_directories("${CMAKE_BINARY_DIR}/install/eigen/") #add directory of the header-only library without this the next line wort work
target_include_directories(my_exe INTERFACE "${CMAKE_BINARY_DIR}/install/eigen/")# link exe to exectable
In the code you supplied, include_directories is implicitly setting the INCLUDE_DIRECTORIES property on your my_exe target. Then the second line sets the INTERFACE_INCLUDE_DIRECTORIES property.
INCLUDE_DIRECTORIES is a list of include directories that must be passed to the compiler when building the target. INTERFACE_INCLUDE_DIRECTORIES is a list of include directories that must be passed to the compiler when building targets that link to this one. This doesn't make much sense for an executable
Slightly better would be to write:
target_include_directories(my_exe PRIVATE "${CMAKE_BINARY_DIR}/install/eigen/")
This will just populate INCLUDE_DIRECTORIES without touching the INTERFACE_ version. If you wanted both you could write PUBLIC instead of PRIVATE or INTERFACE. By definition, PUBLIC is just both of the others.
But this isn't a great dependency management strategy anyway... digging into the source tree guts of a project isn't scalable. It's also difficult to try different versions of Eigen without editing your build files.
I would just write:
cmake_minimum_required(VERSION 3.23)
project(example)
find_package(Eigen3 REQUIRED)
add_executable(my_exe main.cpp)
target_link_libraries(my_exe PRIVATE Eigen3::Eigen)
Use a proper package manager like vcpkg or Conan to handle downloading Eigen when it isn't available on the system.
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:
Adjusted CMakelists.txt files to use absolute paths only (as per recommendation from #Tsyvarev
Added exact compilation error message
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:
The structure should probably more or less stay, so one can install the library just by adding a gitmodule
The 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
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
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.
How can I link OpenNI (libOpenNI2.so) at run time to my C++ program? This question/answer is most relevant to my question. I followed it and prepared the following CMakeLists.txt but still it cannot link the .so file and generates an error /usr/bin/ld: cannot find -lOpenNI2
I use cmake .. && cmake --build . --config Release to compile the program.
I tried $ORIGIN, $$ORIGIN, \$ORIGIN and I noticed that ORIGIN is empty string.
What am I doing wrong?
cmake_minimum_required(VERSION 3.1)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
project(rgbd)# project name
# to link OpenNI2 at runtime
SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
SET(CMAKE_INSTALL_RPATH "")
add_executable(rgbd rgbd.cpp)
message(STATUS ${ORIGIN})# display ORIGIN
set_target_properties(rgbd PROPERTIES LINK_FLAGS "-Wl,-rpath,$ORIGIN/../OpenNI-Linux-x64-2.3.0.66/Redist")
target_link_libraries(rgbd libOpenNI2.so)
The error you get isn't at runtime but at link time. ld cannot find the specified libOpenNI2.so because you haven't provided any search path to the linker.
You shouldn't have to do anything special as CMake will use build rpath by default (that gets removed during installation, but this is not a step that you've configured anyway).
This should be enough:
cmake_minimum_required(VERSION 3.13)
project(rgbd)
add_executable(${PROJECT_NAME} rgbd.cpp)
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_11)
target_link_directories(${PROJECT_NAME} PRIVATE ../OpenNI-Linux-x64-2.3.0.66/Redist)
target_link_libraries(${PROJECT_NAME} PRIVATE OpenNI2)
cd path/to/project
cmake -B build/Release -DCMAKE_BUILD_TYPE=Release
cmake --build build/Release
./build/Release/rgbd
Now if you're going to ship your executable, consider adding a correct installation step with install rpath handled:
cmake_policy(SET CMP0095 OLD) //CMake>=3.16 handles $ORIGIN escaping differently
set_target_properties(${PROJECT_NAME}
PROPERTIES
INSTALL_RPATH "\\\$ORIGIN/../lib"
)
include(GNUInstallDirs)
install(TARGETS ${PROJECT_NAME})
install(FILES ../OpenNI-Linux-x64-2.3.0.66/Redist/libOpenNI2.so TYPE LIB)
cmake -B build/Release -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$PWD/package
cmake --build build/Release --target install
./package/bin/rgbd
The rpath is information that's only used at runtime to find the lib. The linker will not use it to determine the location of the location of libraries to link.
With cmake you could specify an absolute path for target_link_libraries here or add a link directory and let the linker figure out which file to use.
Path to lib
# not sure if this is the exact path; you may need to do some adjustments
target_link_libraries(rgbd "${CMAKE_CURRENT_SOURCE_DIR}/../OpenNI-Linux-x64-2.3.0.66/Redist/libOpenNI2.so")
Link directory
# again not sure, if the path is correct here
target_link_directories(rgdb PRIVATE ../OpenNI-Linux-x64-2.3.0.66/Redist)
target_link_libraries(rgbd PRIVATE OpenNI2)
If you're linking the lib to multiple targets, using an imported library may be a good idea, since it allows you to add info like include directories and additional dependencies to the lib.
add_library(OpenNI2_import SHARED IMPORTED GLOBAL)
set_target_properties(OpenNI2_import PROPERTIES
IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/../OpenNI-Linux-x64-2.3.0.66/Redist/libOpenNI2.so"
)
# include directories could be added
target_include_directories(OpenNI2_import INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/../OpenNI-Linux-x64-2.3.0.66/include" # probably not the correct path; use absolute paths here
)
# could add dependencies, if necessary
target_link_libraries(OpenNI2_import INTERFACE some_other_lib)
...
target_link_libraries(rgbd PRIVATE OpenNI2_import)
You may still need to adjust the rpath, but using \$ORIGIN/... should work to get "cmake" to put $ORIGIN/... in the rpath of the resulting binary.
So I'm using CMake to build a C++ project (on Mac OS) and my project relies on a dylib (I'm using TBB https://www.threadingbuildingblocks.org/ but the specific library itself doesn't matter)
If I do a standard "cmake" and "make" it builds the executable where I want it and when I run my app, the dylib links correctly and everything works perfectly.
The problem comes in when I try to do a "make install" and try to run the resulting executable from the install directory. I get an "image not found" error:
dyld: Library not loaded: #rpath/libtbb.dylib
Referenced from:
/Users/MyName/Desktop/ProjectRoot/install/./MyApp
Reason: image not found
Interestingly, if I do a regular "make" without an install, and then manually copy over the executable to the install directory, then that will link against my dylib properly. I have no idea why that is.
My directory structure is as follows:
Root
CMakeLists.txt
Source/
Libraries/
tbb/
include/
lib/
libtbb.dylib
install/
...and my CMakeLists.txt file is below:
# Start of CMakeLists.txt
cmake_minimum_required(VERSION 3.3)
project (MyApp)
# Set C++ version and output paths
set (CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR})
set(CMAKE_MACOSX_RPATH 1)
set(CMAKE_INSTALL_PREFIX "${CMAKE_SOURCE_DIR}/install")
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
# Find TBB
find_library (
TBB_LIBRARIES
NAMES tbb libtbb # what to look for
HINTS "${CMAKE_SOURCE_DIR}/Libraries/tbb/lib" # where to look
NO_DEFAULT_PATH # do not search system default paths
)
# Set Custom Include Files + TBB header files
include_directories(Source/Headers Libraries/tbb/include)
# Set Source Files
file(GLOB_RECURSE SRC_FILES "Source/*.cpp")
add_executable(MyApp ${SRC_FILES})
# Link Libraries
target_link_libraries(MyApp ${TBB_LIBRARIES})
# Set compile flags
set_target_properties(MyApp PROPERTIES CXX_STANDARD 14) #LINK_FLAGS "-Wl")
target_compile_features(MyApp PUBLIC cxx_std_14)
# Install executable
install(TARGETS MyApp DESTINATION .)
If I try to also add the following line, and install the dylib as well:
install(TARGETS ${TBB_LIBRARIES} DESTINATION lib)
then when I do a "make install" I get the following error instead:
install TARGETS given target
"/Users/MyName/Desktop/ProjectRoot/Libraries/tbb/lib/libtbb.dylib"
which does not exist in this directory.
So I just can't seem to get this install to work. How do I fix it so that both my executable and my library get installed in the right place and that my executable will be able to link against my library when run?
I'm creating a native library under Android Studio, and I'm hurting the following problem.
In my AndroidStudio project CMakeLists.txt, I have this:
cmake_minimum_required(VERSION 3.7.0)
add_library(native-lib
SHARED
src/main/cpp/native-lib.cpp )
IF(USE_EXTERNAL)
include(ExternalProject)
ExternalProject_Add(
project_mylib
DOWNLOAD_COMMAND ""
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/mylib/"
CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX=${PROJECT_BINARY_DIR}
)
add_dependencies(native-lib project_mylib)
ELSE()
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/mylib/)
ENDIF()
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
target_link_libraries(native-lib
${log-lib}
mylib
)
A self-made library is located in src/main/cpp/mylib/, with a CMakeLists.txt:
cmake_minimum_required(VERSION 3.7.0)
project(lib)
add_library(mylib SHARED lib.cpp)
INSTALL(TARGETS mylib
RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
)
When I use the "traditional" add_subdirectory(...) everything goes well. But, if I use the ExternalProject_Add(...) version, linker is skipping the compiled libmylib.so library and so cannot link mylib to native-lib.
I have the following message: skipping incompatible /home/.../app/.externalNativeBuild/cmake/debug/arm64-v8a/lib/libmylib.so when searching for -lmylib
My guess is that all the flags set by AndroidStudio for the root CMakeLists.txt are not set when the ExternalProject is compile leading to an incompatible shared library.
So, I wonder if there is a way to compile a cmake ExternalProject like it was part of the root project (sharing the same compile flags etc) ?
Thanks for any advice