tl;dr the questions are at the bottom.
I am a developer trying something new - my last poison is c++. As I am spending half of my time on my linux laptop and the other half on Win XP PC, I tried to find a way to create basic, barebone project, using good c++ practices (well, I don't know them from experience, I just read about them). Right now my project almost works while using cmake . && make on linux (it works when header and source files are in the same folder, fails when I separate them to include / src folders). I am using nuwen's distribution of mingw on windows (and I know that the toolchain is working, it compiles projects from within Eclipse without any problems).
My project directory looks like this:
engine
|
|- main
|
|- include
|
|- App.h
|- CMakeLists.txt (2)
|- src
|
|- main.cc
|- App.cc
|- CMakeLists.txt (3)
|- CMakLists.txt (1)
The contents of the files are very simple (I will remove the include guards, etc for clarity)
App.h:
class App {
public:
App();
int onExecute();
};
App.cc:
#include <iostream>
#include "App.h"
App::App() {
}
int App::onExecute() {
std::cout << "inside app.." << '\n';
return 0;
}
main.cc:
#include <iostream>
#include "App.h"
using namespace std;
int main(int argc, char* argv[]) {
App a;
a.onExecute();
std::cout << "inside main.." << '\n';
}
CMakeLists.txt (1) - the main one:
cmake_minimum_required (VERSION 2.6)
set (CMAKE_CXX_COMPILER "g++")
project (gameengine)
add_definitions ( "-Wall -ansi -pedantic")
add_subdirectory (${CMAKE_SOURCE_DIR}/main/include)
add_subdirectory (${CMAKE_SOURCE_DIR}/main/src)
add_executable (engine ${CMAKE_SOURCE_DIR}/main/src/main.cc)
target_link_libraries (engine Application)
CMakeLists.txt (2) - inside the include directory
add_library (Application App)
set_target_properties (Application PROPERTIES LINKER_LANGUAGE CXX)
CMakeLists.txt (3) - inside the src directory
include_directories (../include)
And this is as far as I got - with some changes (i.e. moving App.cc to the include directory) the whole thing compiles and runs fine on linux - but I can't get the mingw generator to work on Win XP. I hand tuned the CMAKE_MAKE_PROGRAM in the file CMakeCache.txt to point to the proper make.exe (I know that this should be defined as a system variable but as I am working on many different PC's, I don't want to leave junk after me).
My questions are:
1) what are the guidelines for writing multiplatform CMakeLists.txt file (which will work independly of the os and the location of the project files), which preferably will allow me to easy swich my project configuration from one os the other one?
2) how can I address the error of not finding the header file (make gives: (...)\engine\main\src\main.cc:2:17: fatal error: App.h: No such file or directory) ?
Thank you for your time and help.
1) what are the guidelines for writing multiplatform CMakeLists.txt file (which will work independly of the os and the location of the project files), which preferably will allow me to easy swich my project configuration from one os the other one?
Well, I'm certainly no expert, but I can share my 10-month experience with a cross-platform cmake-based project.
Right off the bat I think you really should be using out of source builds. This means that you don't run cmake in the same directory where your code is; instead, you create a new folder, e.g. engine/build and run cmake ../main from there. This way you don't clobber your source files with cmake stuff, such as CMakeCache.txt etc. There are even some macros you can use to forbid your users from doing in-source builds.
I also find it useful to create a set of macro files to help set compiler options for different platforms. Here at work we have macros such as ADD_GCC_FLAG or ADD_MSVC_FLAG which check the current compiler and add flags accordingly.
I think it is good practice to have a single .cmake file which concentrates all your project configurations in one place. At work all our CMakeLists.txt start with include( ../cmake/configs.cmake ). This file sets all sorts of options, such as standard include directories, default compiler flags etc.
To assuage your problem with include directories, I suggest you use absolute rather than relative paths in your source files. Define a standard include directory, for instance engine/main/include and always #include files relative to that path. In your example, if you wanted to include engine/main/include/somefolder/header.h, you'd write #include <somefolder/header.h> (using <> instead of quotes tells the C++ preprocessor to skip the current directory when looking for the file).
2) how can I address the error of not finding the header file (make gives: (...)\engine\main\src\main.cc:2:17: fatal error: App.h: No such file or directory) ?
There are a number of issues with your cmake layout, but the reason you were getting that error is because you need to call include_directories in CMakeLists.txt (1) as well.
Besides that, your other CMakeLists.txt files have problems too. In CMakeLists.txt (2), the arguments to add_library are wrong; it should be ../src/App.cc, otherwise you're just adding an empty library. And you don't need that set_target_properties either, at least not if you got the add_library arguments right. You also need a include_directory call in that same CMakeLists.txt that's adding the library; putting it in (3) doesn't really do anything.
Actually, you don't need a CMakeLists.txt file in the include directory, since there's nothing to build there. It's better to put the add_library call in CMakeLists.txt (3), right after calling include_directories.
I hope this clears some of your doubts.
This is probably not the answer you expect but since you didn't stated whether you'd like alternative solutions, I will suggest it anyway:
For multi-platform projects I would recommend SConstruct which is really a great and flexible tool. I don't know CMake well so I can really provide a detailed comparison.
However, here are the reasons why I love this tool:
It's Python. So you can do almost anything you want regarding customization and/or special needs
It's really easy to learn and for simple projects, it takes only a few lines of code to get started.
It depends solely on Python so on Linux its very often already installed and on Windows it take 5 minutes to download and install.
It has a very good automatic dependency tree generation and parallel compilation support.
Related
I am trying to integrate spdlog, a header only logging library into a C++ project but I am having trouble getting CMake to recognise the include paths properly. I am using the latest CLion with CMake 3.10.2 on Ubuntu 18.04.
My project structure looks like this:
Project Dir
|- libs
| |- spdlog #this is the include directory taken straight from GitHub
|
|- src
| |-...
|- CMakeLists.txt
In the CMakeLists.txt file I define the include directory:
include_directories("libs/")
Now when I attempt to write #include <spdlog/spdlog.h> in a header file located in src/ CLion complains that it cannot find spdlog.h even though I have checked and the file is definitely in the spdlog folder. Using quotation marks instead of angled brackets in the include statement does not fix the problem however using the path relative to the file (e.g. ../libs/spdlog/spdlog.h) works as it should. What is more confusing to me is that in the source file corresponding to the header I can include the file no problem.
I haven't been able to find anything like this issue anywhere and I'm struggling to understand what is causing CMake or CLion to behave like this.
EDIT: As per Matthieu Brucher's suggestion I have tried using fully qualified paths to the include folder but it still does not work. The problem seems to be that folders seem to not be recognised in headers, as they work in source files.
EDIT2: Here is the entire CMakeLists.txt I am using. It is nested into a different directory than the top level CMakeLists.txt for the entire project as this was the only solution I have found online to get something akin to VS's multiple projects in a solution in CLion. However since all paths are relative I don't think this would be a problem. I also know that spdlog is a C++11 library but I will need some C++14 features elsewhere in the near future.
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
project(TokenEngine VERSION 0.0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 14)
set(SOURCE_FILES src/Application.cpp src/Application.hpp src/EntryPoint.hpp src/Logger.cpp src/Logger.hpp)
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/libs/")
add_library(TokenEngine SHARED ${SOURCE_FILES})
#Expose the public API of the engine to any project that might use it
target_include_directories(TokenEngine PUBLIC include)
EDIT3: When I attempted to recreate the error to show the full message given somehow the it was gone, being replaced by a different one totally unrelated to CMake...
You may want to use fully qualified paths:
include_directories(${CMAKE_SOURCE_DIR}/libs/)
You also have to add the include folder to the include paths to be able to include via #include <spdlog/spdlog.h>. This should look like
include_directories(${CMAKE_SOURCE_DIR}/libs)
But I prefer using
target_include_directories(yourTarget PUBLIC ${CMAKE_SOURCE_DIR}/libs)
where yourTarget is the target where you want to use spdlog. Like that you will have the include directories also available if you are going to link your yourTarget to something else. If you need spdlog just in yourTargets cpp files you can change the PUBLIC to PRIVATE. See cmake doc.
I added the xgboost library as a git submodule of my project and I'm trying to add it to cmake as a subdirectory. Unfortunately it's not working. A simple hello world project with the following CMakeLists.txt replicates the error that I'm getting.
cmake_minimum_required(VERSION 3.2)
project(foo)
add_subdirectory(xgboost)
add_executable(${PROJECT_NAME} foo.cpp)
target_link_libraries(${PROJECT_NAME} xgboost)
After building the library there is nothing in the xgboost/lib directory so I get the following error.
clang: error: no such file or directory:
'/Users/.../myproject/xgboost/lib/libxgboost.dylib'
I think that the problem is generated in their CMakeLists file since they have two different targets. Maybe cmake is choosing the wrong target but I'm not familiar enough with cmake to figure it out. The following code is from xgboost's CMakeLists.
# Executable
add_executable(runxgboost $<TARGET_OBJECTS:objxgboost> src/cli_main.cc)
set_target_properties(runxgboost PROPERTIES
OUTPUT_NAME xgboost
)
set_output_directory(runxgboost ${PROJECT_SOURCE_DIR})
target_link_libraries(runxgboost ${LINK_LIBRARIES})
# Shared library
add_library(xgboost SHARED $<TARGET_OBJECTS:objxgboost>)
target_link_libraries(xgboost ${LINK_LIBRARIES})
set_output_directory(xgboost ${PROJECT_SOURCE_DIR}/lib)
#Ensure these two targets do not build simultaneously, as they produce outputs with conflicting names
add_dependencies(xgboost runxgboost)
My questions in order of importance are:
Is there any way to fix it without modifying xgboost's CMakeLists.txt file?
Is it reasonable to try to add xgboost to my project as a git submodule?
Is there any reason cmake is not instructing to build the library?
Note: There were several edits to this question since I tried to narrow down the problem and to provide more information.
(I would love to ask for few things beforehand in the comment section, but I have too low reputation to do so, so I will just give it a shot ;))
I have few suspects, and one of them is ${CMAKE_SOURCE_DIR} of the submodule's root CMakeLists.txt. Although the paths are set properly when you run that CMakeLists.txt alone, cmake gets confused the moment you add it as your subdirectory. Have you looked into another directories for your output binaries?
First I would suggest testing this hypothesis, and then I would suggest writing similar, but separate CMakeLists.txt file for xgboost library, and then substitute it in the project temporarily. Unfortunately the CMakeLists.txt filename is hardcoded and there is no possibility to have two files of that kind in one directory; so it seems that the answer to 1) is, that you rather have to change the file.
For the 2): as long as it does not require huge additional logic in your CMakeLists.txt, it makes sense. Other viable option is to create an install target, which you can use to install your xgboost library locally (using CMAKE_INSTALL_PREFIX(doc) variable), and then add the installation path to your CMAKE_LIBRARY_PATH(doc).
I just want to make sure that my understanding about CMakeLists.txt is correct. My dummy project structure:
|-+ dummy
|-+ CMakeLists.txt
|-+ src
|-- CMakeLists.txt
|-- Converter.cpp
|-- Converter.hpp
|-- main.cpp
|-+ tests
|-- CMakeLists.txt
|-- Converter_ut.cpp
|-+ thirdparty
|-+ gmock-1.7.0
My goal is to create build process with CMake. This is my first attempt so I assume that there are some mistakes. It works but I am not sure if I understand everything correctly and I would be thankful if you could share with some comments / suggestions.
dummy/CMakeLists.txt
cmake_minimum_required (VERSION 2.8.11)
project (SUB)
add_subdirectory (src)
add_subdirectory (tests)
cmake_minimum_required is pretty self-explanatory,
project (SUB) sets project variables like ${SUB_SOURCE_DIR} and ${SUB_BINARY_DIR},
add_subdirectory, tells CMake to go and process CMakeLists.txt in the following directories
src/CMakeLists.txt
add_library (Sub
main.cpp
Converter.cpp)
target_include_directories (Sub PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
# Executable
add_executable (converter
Converter.cpp)
target_link_libraries (converter Sub)
add_library, creates library called "Sub" from two source files,
target_include_directories, tells the compiler where are the header files for "Sub" library (is that "PUBLIC" really needed here?),
add_executable, creates "converter" executable from Converter.cpp (why main.cpp is not needed here?),
target_link_libraries, links "Sub" library with "converter" executable
tests/CMakeLists.txt
# GMOCK
set (GMOCK_DIR "../thirdparty/gmock-1.7.0")
add_subdirectory(${GMOCK_DIR} ${CMAKE_BINARY_DIR}/gmock)
include_directories(SYSTEM ${GMOCK_DIR}/include ${GMOCK_DIR}/gtest/include)
# Executable
add_executable (tests
Converter_ut.cpp)
target_link_libraries (tests gmock_main Sub)
set (GMOCK_DIR ...), sets local variable "GMOCK_DIR" with my gmock folder location,
add_subdirectory, tells CMake to jump into gmock location and run their CMakeLists.txt, what is the second argument? {CMAKE_BINARY_DIR}/gmock?
add_executable, creates second executable file
target_link_libraries, links gmock_main library with second executable, "Sub" library is needed here because Converter_ut.cpp
needs to include "Converter.hpp" from src directory
Thank you in advance. I have read plenty of sites / tutorials already but I am still not sure about that.
One more thing - I cannot really imagine project with plenty of source files. Isn't there a better way to add source files to add_library and add_executable functions than listing it manually? Something like "take all *.cpp files from current directory"?
Thanks.
Cmake is not properly a programming language supporting a full paradigm, so use it, but if possible never start creating "a framework with it" (it would be cumbersome without proper syntactic sugar), it is intended to make small scripts not to write thousand lines of code (and despite few good frameworks exists, I tend to not use them: "If I cannot code it in few lines, then it's not job for CMAKE").
The important parts are (not that it is slightly different, I copy-pasted the improved version i still have to commit):
cmake_minimum_required( VERSION 2.8)
project( Infectorpp2)
# find additional cmake scripts (I'm driving away from this)
set( CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
# create list of files from a glob
file( GLOB INFECTOR_SOURCE_FILES
"include/Infectorpp/priv/*.cpp")
# create list of files from a glob
file( GLOB INFECTOR_HEADER_FILES
"include/Infectorpp/priv/*.hpp"
"include/Infectorpp/*.hpp")
# or just "add_executable" the dollar "${}" just expand the list
add_library( libInfectorpp2 STATIC
${INFECTOR_SOURCE_FILES}
${INFECTOR_HEADER_FILES})
If you are not using 3rd party libraries, then you do not need to add target_include_directories because for your own application relative paths suffice.
For the testing part you are mostly ok to me, but I would do:
enable_testing()
## details omitted...
# create list of files from a glob
file( GLOB GMOCK_TESTS_SOURCE_FILES
"locationToTestFiles/*.cpp")
# Executable
add_executable (tests
${GMOCK_TESTS_SOURCE_FILES})
target_link_libraries (tests gmock_main Sub)
add_test(tests tests)
Also note that CMAKE is the only reason why I find useful having different extensions for C++ files, because of GLOBS, if you want to exclude some file you have to change its extension (to cc, c++, cxx or what your prefer).
You can do mostly anything following the same pattern, note that with GLOB you have to re-configure CMake to detect newly added files, however still better than adding them manually to build script (and anyway that will not cause a whole recompilation, CMake keep track of data and will avoid to re-compile old files)
Some people find useful adding files manually to CMake scripts because "I can keep old file there". Don't do that, move old files into an "old" folder, or just let your subversion system keep memory of them for you.
You will catch most errors earlier, and you will have a "ready to ship" project (you won't accidentally left wrong files that users will attempt to compile)
Another important point:
Do out of source builds, from your script I guess you are still not doing that.
I'm working through the CMake tutorial and I'm up to the Adding a Version Number and Configured Header File section. Unfortunately Xcode isn't recognising the generated header file:
The error:
The header file was generated by CMake ok:
TutorialConfig.h.in
// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR #Tutorial_VERSION_MAJOR#
#define Tutorial_VERSION_MINOR #Tutorial_VERSION_MINOR#
CMakeLists.txt
cmake_minimum_required (VERSION 2.6)
project (Tutorial)
# The version number.
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)
# configure a header file to pass some of the CMake settings
# to the source code
configure_file (
"${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
"${PROJECT_BINARY_DIR}/TutorialConfig.h"
)
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
include_directories("${PROJECT_BINARY_DIR}/src")
# add the executable
add_executable(Tutorial src/tutorial.cpp)
This is the tutorial I'm working from.
I don't use Xcode, but another IDE that's Linux based. But maybe I can help you a bit. I'm just wondering -- does your program compile? That's perhaps more important.
My IDE often complains about header files such as your's -- that is, configuration files that are generated by CMake. Such warnings (at least for me) can be ignored. I usually do because my build/ directory is sometimes empty. So when I've opened up the source file, there wouldn't be TutorialConfig.h yet.
In fact, the IDE can never know about such header files. That's because it doesn't know which build/ directory you're going to compile your program in.
If you are worried by the warning, then there is probably a place where you can specify to Xcode which build/ directory to search for header files. That will remove the warning, after you've run CMake once. However, removing such a warning is unreliable because you can always build in another directory that would have TutorialConfig.h missing.
Hope this helps!
Oh, haha. I changed #include "src/TutorialConfig.h" to #include "TutorialConfig.h" and all is well. I figured it out by checking out the project settings:
I have been struggling with this for quite a while, and my adventures with cmake have only resulted in hackish solutions that I am pretty sure are not correct.
I created a library that consists of several files, as follows:
-libfolder
-codepart1folder
-CMakeLists.txt
-codepart1.cpp
-codepart1.hpp
-codepart2folder
-codepart3folder
-lib.cpp
-lib.hpp
-CMakeLists.txt
I wrote a CMakeLists file to compile the library (after some experimentation), and I can generate a lib.a file. Now I would like to include this code as a library in other projects, and access it through the interface in lib.hpp. What is the best way to do this, in terms of directory structure, and what I need to put into CMakeLists.txt in my root project?
My current attempt has been to add -libfolder as a subfolder to my current project, and add the commands:
include_directories(${PROJECT_SOURCE_DIR}/libfolder)
link_directories(${PROJECT_BINARY_DIR}/libfolder)
add_subdirectory(libfolder)
target_link_libraries(project lib)
When I run make, the library compiles fine, but when project.cpp compiles, it complains that it cannot find codepart1.hpp (which is included in lib.hpp, included from project.cpp).
I suspect that this is the wrong way about doing this, but I cannot wade through the CMake documentation and find a good tutorial on setting up projects like this. Please help, CMake gurus!
The clean way to import one CMake project into another is via the find_package command. The package declaration is done by using the export command. An advantage of using find_package is that it eliminates the need to hard-code paths to the package's files.
Regarding the missing hpp file, you didn't include codepart1folder, so it's not on the include path.
Ok, so after consulting a coworker of mine who is a CMake guru, it seems CMake does not have support for what I am trying to do, leaving one with 3 options:
Add all of the dependencies to the parent projects CMakeLists.txt - not very clean, but it will get the thing to work. You'll have to do this for every project you add the code to, and go back and fix things if your library changes.
clean up your library headers. This is done through some compiler hackery. The idea is to forward-declare every class, and use only pointers or boost::shared_ptr, and then include the dependencies only in the cpp file. That way you can build the cpp file using all the findpackage stuff, and you get the bonus of being able to use the lib by only including the header and linking to the library.
Look into build systems. Having portable code and fast code compilation with complex dependencies is not a solved problem! From my investigations it turned out to be quite complicated. I ended up adopting my coworkers build system which he created himself in cmake, using things he picked up from Google.
Looking at your post you don't seem to add 'codepart1folder' to the includes anywhere. How are you including codepart1.hpp as:
#include <codepart1.hpp>
#include "codepart1folder/codepart1.hpp"
I don't think there is a standard accepted way to structure cmake projects. I've looked at a bunch of cmake repos and they tend to have differences. Personally I do the following:
-project
CMakeLists.txt
-build
-cmake
OptionalCmakeModule.cmake
-src
-Main
Main.cpp
Main.hpp
-DataStructs
SomeTree.hpp
SomeObject.hpp
-Debug
Debug.hpp
-UI
Window.hpp
Window.cpp
Basically that dumps all the source code into 1 directory, then you perform an out of source build with: 'mkdir build && cd build && cmake .. && make' in the projects root folder.
If you have separate libs as part of your project, then you might want a separate libs directory with another subfolder for your specific lib.
I have some of my repos on: https://github.com/dcbishop/ if you want to look at the CMakeLists.txt files.
The main problems with my project structure are that I use the FILE_GLOB which is apparently the 'wrong' way to do things (if you add files after running 'cmake ..' then they won't be picked up hen you do a 'make'). I haven't figured out what the 'right' way to do it is (from what I can see it involves keeping a separate list of files) I also only use 1 CMakeLists.txt file.
Some projects also choose to separate their cpp and hpp files into separate directories. So you would have an include and src folders (at least for the hpp files that are intended to be used externally). I think that would mainly be for projects that are mainly large libraries. Would also make installing header files much easier.
You are probably missing
include_directories(${PROJECT_SOURCE_DIR}/libfolder/codepart1folder)
In such a case you might want to set( CMAKE_INCLUDE_CURRENT_DIR on) to add all folders to the include directory path variable.
Check cmake's output on the command line whether the correct include folders are set or not. Additionally you can always use message() as "print debugging" for cmake variables.
In case of include directories however you need to read the directory property to see what is actually in the include directories.
get_property(inc_dirs DIRECTORY PROPERTY INCLUDE_DIRECTORIES)
message("inc_dirs = ${inc_dirs}")
I hope this helps you figuring out what is missing.
Edit
I just saw your comment about added codepart1folder in the libfolder. It is only available in the libfolder's include_directory path and not propagated to the root folder.
Since the include codepart1.hpp is present in the lib.hpp however you need to have it also available in the project path otherwise you will get missing declaration errors when you build your project.