I have the following file structure:
├── Generator/
│ ├── output/
│ └── script.py
│
└── FinalProgram/
├── build/
├── src/
│ └── main.cpp
├── include/
│ └── MyClass.h
└── CMakeLists.txt
The Generator/script.py file is a script that generates c++ files in the Generator/output folder. That script can be launched with two different arguments (SimA and SimB), each one generating a different set of files.
On the other hand, the FinalProgram needs to use that set of generated files, so every time I want to compile the FinalProgram with the SimA set of files, I have to
cd Generator
./script.py SimA
cp output/*.cpp ../FinalProgram/src
cp output/*.h ../FinalProgram/include
cd ../FinalProgram/build && cmake ..
make
What I want is to be able to type
make SimA
Or
make SimB
So everything happens automatically. In both cases the executable must be the same, so obviously I cannot have two different add_executable blocks. I guess I should create two add_custom_target blocks, one for each possible value, do all the work there and finally call the target that compiles everything. Hence, the real question is, how can I run another target from within an add_custom_target block? Of course, I guess I could use
COMMAND make
But that... that makes me cry. Isn't there a better way?
As MadScienceDreams mentioned, you can use add_custom_command to generate the c++ file, then you can use generated file in add_executable command.
add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/../Generator/output/SimA.cpp
COMMAND ${PYTHON_EXECUTABLE} script.py SimA
DEPENDS ${CMAKE_SOURCE_DIR}/../Generator/script.py
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/../Generator)
set_property(SOURCE ${CMAKE_SOURCE_DIR}/../Generator/output/SimA.cpp PROPERTY GENERATED TRUE)
add_executable(SimA ${CMAKE_SOURCE_DIR}/../Generator/output/SimA.cpp ...)
add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/../Generator/output/SimB.cpp
COMMAND ${PYTHON_EXECUTABLE} script.py SimB
DEPENDS ${CMAKE_SOURCE_DIR}/../Generator/script.py
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/../Generator)
set_property(SOURCE ${CMAKE_SOURCE_DIR}/../Generator/output/SimB.cpp PROPERTY GENERATED TRUE)
add_executable(SimB ${CMAKE_SOURCE_DIR}/../Generator/output/SimB.cpp ...)
For more information see
http://www.cmake.org/cmake/help/v3.0/command/add_custom_command.html
http://www.cmake.org/cmake/help/v3.0/prop_sf/GENERATED.html
Related
I want to create a .dll library with all its dependencies packed inside the .dll.
However, there seems to be no easy way to achieve that with Cmake. My setup:
cmake_minimum_required(VERSION 3.0.0)
project(Main VERSION 0.1.0)
add_library(Main SHARED Main.cpp)
find_package(libzippp REQUIRED)
target_link_libraries(Main PRIVATE libzippp::libzippp)
This will produce both Main.dll but also libzippp.dll.
I would like to have libzippp.dll packed (statically linked) into Main.dll.
Of course you can't pack one DLL into another. You have to make libzippp a static library in the first place. To do this, build libzippp with BUILD_SHARED_LIBS set to NO at the CMake command line. Then libzippp::libzippp will be a static library when you go to find_package it.
This is easy enough to show steps for:
$ git clone git#github.com:ctabin/libzippp.git
$ cmake -S libzippp -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=NO -DCMAKE_INSTALL_PREFIX=$PWD/local -DLIBZIPPP_BUILD_TESTS=NO
$ cmake --build build --target install
$ tree local
local/
├── include
│ └── libzippp
│ └── libzippp.h
├── lib
│ └── libzippp_static.a
└── share
└── libzippp
├── FindLIBZIP.cmake
├── libzipppConfig.cmake
├── libzipppConfigVersion.cmake
├── libzipppTargets.cmake
└── libzipppTargets-release.cmake
I was learning CMake for a project and feel confused when linking libraries.
I find it easier to ask with an example, as using terms I am not familiar could be misleading.
The questions are (also commented in the example codes)
how can I know what are the library name I can link to my target? Can I tell that from the build file structure, or I need to look into CMakeLists.txt of the external libraries. (e.g. the gtest-main and libhermes in the example)
How can I use a "grandchild" library that is part of the external library? (e.g. the jsi library in the example)
Example
Let's say I am writing some tests with dependency on the Hermes Library. I then write a CMakeList.txt file like
cmake_minimum_required(VERSION 3.13)
project(mytests_cxx)
set(CMAKE_CXX_STANDARD 14)
include(FetchContent)
FetchContent_Declare(
hermes
URL https://github.com/facebook/hermes/archive/d63feeb46d26fe0ca7e789fc793f409e5158b27f.zip
)
FetchContent_MakeAvailable(hermes)
enable_testing()
add_executable(
hello_test
./test.cpp
)
find_library (
LIB_HERMES
hermes # <- how can I know what is the name?
)
find_library (
LIB_JSI
jsi # <- How could I use a "grandchild" dependency, it is defined here (https://github.com/facebook/hermes/tree/main/API/jsi/jsi)
)
target_link_libraries(
hello_test
gtest_main <- Hermes seems to use gtest, how could make sure gtest_main is available?
LIB_HERMES
LIB_JSI
)
include(GoogleTest)
gtest_discover_tests(hello_test)
Running cmake to build
cmake -S . -B build -DHERMES_BUILD_SHARED_JSI=ON // I see an option defined in heremes'CMakeLists.txt but not sure if I am using it right.
the directory in build
tree ./build -L 2
./build
├── CMakeCache.txt
├── CMakeFiles
│ ├── 3.23.1
│ ├── CMakeDirectoryInformation.cmake
│ ├── CMakeError.log
│ ├── CMakeOutput.log
│ ├── CMakeRuleHashes.txt
│ ├── CMakeTmp
│ ├── Makefile.cmake
│ ├── Makefile2
│ ├── Progress
│ ├── TargetDirectories.txt
│ ├── cmake.check_cache
│ ├── hello_test.dir
│ └── progress.marks
├── CTestTestfile.cmake
├── ImportHermesc.cmake
├── Makefile
├── _deps
│ ├── hermes-build
│ ├── hermes-src
│ └── hermes-subbuild
├── bin
│ └── hermes-lit
├── cmake_install.cmake
├── compile_commands.json
└── hello_test[1]_include.cmake
then I run cmake --build build and see this error
/.../test.cpp:8:10: fatal error: 'jsi/jsi.h' file not found
#include <jsi/jsi.h>
Your question is too broad. Either ask a question about the list of libraries in cmake, or ask a question about how to compile and link with hermes, or ask a question about a specific problem with your build about including a third party. These should be 3 separate questions on this site.
how can I know what are the library name I can link to my target?
There are two things that you can target_link_libraries - you can link with libraries available on your system and link with targets defined in cmake.
You can list libraries available on your system by traversing all paths outputted by ld --verbose and listing all lib*.so files in those directories. Or https://unix.stackexchange.com/questions/43187/how-do-i-get-a-list-of-shared-library-filenames-under-linux or similar. See man ld.so.
You can list all targets defined in cmake. How do I list the defined make targets from the command line? .
These lists combined consist of all possible libraries names that you can link with.
Can I tell that from the build file structure
I do not understand. No.
I need to look into CMakeLists.txt of the external libraries
Yes, usually you want to look into any external libraries CMakeLists.txt at least to check for malicious code.
You usually look into documentation of external libraries. And, because source code is a form of documentation in open-source projects, you would look into the source code, yes.
How can I use a "grandchild" library that is part of the external library?
There are multiple ways for including external projects. Cases were outlined in Correct way to use third-party libraries in cmake project .
That said, not all CMake projects work properly with add_subdirectory() (which is called by FetchContent_MakeAvailable) - there are many cases where the third-party projects CMakeLists.txt may not work with parent project. In such cases, I would go with compiling and installing the project into a subdirectory for example before running CMake or with ExternalProject_Add, and then only including the result of compilation with hand-crafted find_library and target_include_directories from the installation directory.
Overall, including a third party is not always an easy task, and yes it requires you to become intimate with the specifics of a third party project and how it works and how it was written and decide how you want it to include in your project.
I want to create my first big project in c++.
And I need to use some library. So I have made this structure
├── CMakeLists.txt
├── README.md
├── src
│ ├── CMakeLists.txt
│ ├── HelloWorld.cpp
│ ├── HelloWorld.h
│ └── main.cpp
├── tst
| ├── CMakeLists.txt
| ├── HelloWorld-test.cpp
| └── main.cpp
└── lib
and I want to automatically download and place the library in the lib folder. In my case, I want to clone googletest int lib folder. So I have tried that in my main project CMakeLists.txt file:
if(UNIX)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib/googletest)
execute_process(
COMMAND git clone "https://github.com/google/googletest.git" googletest
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib)
add_subdirectory(lib/googletest)
endif(UNIX)
But when I build I have this error :
CMake Error at CMakeLists.txt:17 (add_subdirectory):
add_subdirectory given source "lib/googletest" which is not an existing
directory.
The issue here is that the repository is cloned to the binary directory, but add_subdirectory looks for the directory relative to the source directory.
You need to use add_subdirectory with an absolute path here and pass a build directory. Furthermore add quotes to avoid issues with spaces in the file path.
set(REPO_PARENT "${CMAKE_CURRENT_BINARY_DIR}/lib")
set(REPO_DIR "${REPO_PARENT}/googletest")
set(REPO_BIN_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest_build")
file(MAKE_DIRECTORY ${REPO_DIR})
file(MAKE_DIRECTORY ${REPO_BIN_DIR})
execute_process(
COMMAND git clone "https://github.com/google/googletest.git" googletest
WORKING_DIRECTORY ${REPO_PARENT})
add_subdirectory(${REPO_DIR} ${REPO_BIN_DIR})
I have for some time had some problems incorporating modern cmake into a project I've been working fr some while and seem to be stuck on how i should define the the individual CMakeLists, and the top-level CMakeList.
My project structure is pretty simple.
├── build
└── src
├── include
│ ├── database
│ │ ├── database.cpp
│ │ └── database.h
│ ├── match
│ │ ├── match.h
│ │ └── mathc.cpp
│ ├── record
│ │ ├── lib
│ │ ├── record.cpp
│ │ └── record.h
│ └── spectogram
│ ├── spectogram.cpp
│ └── spectrogram.h
└── main.cpp
main.cpp are linked to all the includes, and some of the includes should know the presence of other includes, meaning, I should be able to include match.h in
database.h. Some third-party libs are also going to be used, in this case I am using portaudio, download and installed using externalproject_add, which should only be visible for the include which holds the library, in this case record, should only see this.
But how I should define the individual CMakeList, is currently unknown.
I've scouted the net for a proper way of setting this up, but cannot seem to find one that I understand.
How do i define the CMakeLists for this project, and how do i make sure that the includes, are visible for the Main.cpp and the includes files that need them, and how do I make third-party-libraries visible only for the includes that it is being used for.
CMakeLists example structure tried:
CMakeLists.txt
cmake_minimum_required(VERSION 3.2)
project(soundcloud)
#add_subdirectory(src/database)
#add_subdirectory(src/match)
#add_subdirectory(src/record)
add_subdirectory(src/include/spectogram)
add_executable(cmakeDemo src/main.cpp)
SET_TARGET_PROPERTIES(cmakeDemo PROPERTIES LINKER_LANGUAGE Cxx)
target_link_libraries(cmakeDemo spectogram)
#target_link_libraries(cmakeDemo database match record spectogram)
src/include/database/CMakeLists.txt
add_library(spectogram STATIC .)
target_include_directories(spectogram PUBLIC .)
getting error message:
CMake Error: Error required internal CMake variable not set, cmake may not be built correctly.
Missing variable is:
CMAKE_Cxx_LINK_EXECUTABLE
CMake Error: Cannot determine link language for target "spectogram".
CMake Error: CMake can not determine linker language for target: spectogram
You can use the PRIVATE property of target_include_directories. When PRIVATE, it means that the include directories will be available to the sources of the target. When PUBLIC, it will also be available to whomever links to the target.
If I were writing this in a single CMakeLists.txt, I'd do this:
cmake_minimum_required(VERSION 3.0)
add_library(database STATIC src/include/database.cpp)
target_include_directories(database PUBLIC src/include/database)
################
add_library(match STATIC src/include/mathc.cpp)
target_include_directories(match PUBLIC src/include/match)
################
include(ExternalProject)
ExternalProject_Add(portAudio ${SomeCommands})
add_library(record STATIC src/include/record.cpp)
target_include_directories(record PUBLIC src/include/record) # When cmakeDemo links to this, it'll get these includes
target_include_directories(record PRIVATE src/include/record/lib) # When cmakeDemo links to this, it won't get these includes
target_link_libraries(record portAudio)
#################
add_library(spectogram STATIC src/include/spectogram.cpp)
target_include_directories(spectogram PUBLIC src/include/spectogram)
##################
add_executable(cmakeDemo src/main.cpp)
target_link_libraries(cmakeDemo database match record spectogram)
If I were to do this with distributed CMakeLists.txt, I'd split the files where the ###### lines are, fix the paths, and use add_subdirectory() to include the sub-directories from higher-level cmake files.
First, you don't link to includes, you just "include" them during compilation.
Some people put one CMakeList.txt into every directory that contains a fairly independent compilation unit.
Some people just use one big one at the the top.
Having just one CMakeList.txt file makes it easier to start, but if the project gets huge, things get messy.
For every compilation unit, you can specify the include directories with target_include_directories
Would anyone be able to post a simple example of how to compile code which uses libfreenect2? After installing the library, the following structure is created in my home directory:
→ tree freenect2
freenect2
├── include
│ └── libfreenect2
│ ├── config.h
│ ├── export.h
│ ├── frame_listener.hpp
│ ├── frame_listener_impl.h
│ ├── libfreenect2.hpp
│ ├── logger.h
│ ├── packet_pipeline.h
│ └── registration.h
└── lib
├── cmake
│ └── freenect2
│ └── freenect2Config.cmake
├── libfreenect2.so -> libfreenect2.so.0.2
├── libfreenect2.so.0.2 -> libfreenect2.so.0.2.0
├── libfreenect2.so.0.2.0
└── pkgconfig
└── freenect2.pc
I attempted to compile with the .pc file using a line similar to this found on the pkg-config wikipedia page:
gcc -o test test.c $(pkg-config --libs --cflags libpng)
But came with up with this error:
./test: error while loading shared libraries: libfreenect2.so.0.2: cannot open shared object file: No such file or directory
Obviously, I messed up the compilation process somewhere, but I'm not sure where to look since this is error occurs on runtime and not at compile time. There's also a .cmake file created with the library install, which I'm sure would lead to a more robust and proper solution, but I'm not entirely sure how to use that and haven't been able to find a simple guide showing how to do so. Any links to beginner-friendly documentation are also appreciated. In the documentation for libfreenect2, it says to use this line when compiling cmake -Dfreenect2_DIR=$HOME/freenect2/lib/cmake/freenect2 -- is this something that I'd have to use when making the library or when making my application?
Another tangentially related question, would it be better to move the /include and /lib directories to /usr/local/include and /usr/local/lib respectively? I believe that would "install" the library system-wide, but I imagine there's some reason that libfreenect2 doesn't do it automatically and I'm not sure what that is.
Well, I just use cmake with a CMakeLists.txt file that I create. Do like this:
Create a CMakeLists.txt file:
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
project("My Project")
set(CMAKE_CXX_FLAGS "-std=c++11")
find_package(freenect2 REQUIRED)
include_directories("/usr/include/libusb-1.0/")
INCLUDE_DIRECTORIES(
${freenect2_INCLUDE_DIR}
)
add_executable(main ./main.cpp)
target_link_libraries(main ${freenect2_LIBRARIES})
In this file, I assume we want to compile the main.cpp file that uses libfreenect2. So, in your local directory create a build folder, using the terminal:
mkdir build && cd build
Then, run the command in the terminal:
cmake -Dfreenect2_DIR=$HOME/freenect2/lib/cmake/freenect2 .. && make
this should create main executable in the build folder. Please, note that this cmake command specifies the freenect2 directory. In this case I assume it was placed in the /home directory.
However, I understand that having to type that long cmake command or search for it on the terminal history may be boring for some people. So, it is possible to embed the command like this:
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
project("My Project")
set(CMAKE_CXX_FLAGS "-std=c++11")
# Set cmake prefix path to enable cmake to find freenect2
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} $ENV{HOME}/freenect2/lib/cmake/freenect2)
find_package(freenect2 REQUIRED)
include_directories("/usr/include/libusb-1.0/")
INCLUDE_DIRECTORIES(
${freenect2_INCLUDE_DIR}
)
add_executable(main ./main.cpp)
target_link_libraries(main ${freenect2_LIBRARIES})
After, just run this in the terminal:
mkdir build && cd build && cmake .. & make
This answer was my source for this second way of compiling the code.
Hope this helps!