0

My Situation

I`m trying to set up a project structure and the build scripts using cmake but I get an error that my application is unable to find the header file that I try to include.

I hope that someone can guide me to a solution for this.


Current project

The project ist just an example reduced to one application and one library project. The goal is to have multiple clients and more than one library (some depending on each other).

clients/Desktop e.g. is an application making use of the core/ library.

The image shows the project structure (screenshot from Visual Studio Code explorer)

d

Having a folder named like the project in the include folder is to change the includes from Core.h to Core/Core.h. Or at least this was the plan. Maybe this is the problem?

Okay, so now the code:

clients\Desktop\src\main.cpp:

#include "Core/Core.h"

#include <iostream>

int main(int argc, char* argv[]) {
    ProjectStructure::Core::Example ex;
    ex.sayHello();
}

Core\include\Core\Core.h:

#ifndef PROJECTSTRUCTURE_CORE_CORE_H
#define PROJECTSTRUCTURE_CORE_CORE_H

#include <iostream>

namespace ProjectStructure {
namespace Core {

class Example {
public:
    void sayHello();
};

} // namespace Core
} // namespace ProjectStructure 

#endif

Core\src\Core.cpp:

#include "Core/Core.h"

namespace ProjectStructure {
namespace Core {

void Example::sayHello() {
    std::cout << "Hello world.";
}

} // namespace Core
} // namespace ProjectStructure

clients\Desktop\include\CMakeLists.txt:

ADD_SUBDIRECTORY("Desktop")

clients\Desktop\src\CMakeLists.txt:

SET(APP_HEADERS
)

SET(APP_SOURCES
"main.cpp"
)

# Make executable
ADD_EXECUTABLE("${PROJECT_NAME}" "${APP_SOURCES}")
TARGET_LINK_LIBRARIES("${PROJECT_NAME}" "Core")

clients\Desktop\CMakeLists.txt:

# Version check
cmake_minimum_required(VERSION 3.5)

# Project
PROJECT("Desktop" CXX)

INCLUDE_DIRECTORIES("${PROJECT_SOURCE_DIR}/include")

ADD_SUBDIRECTORY("include")
ADD_SUBDIRECTORY("src")
ADD_SUBDIRECTORY("test")

clients\CMakeLists.txt:

# Executables
ADD_SUBDIRECTORY("${PROJECTSTRUCTURE_CLIENTS_DIR}/Desktop")

Core\include\CMakeLists.txt:

ADD_SUBDIRECTORY("Core")

Core\src\CMakeLists.txt:

SET(LIB_HEADERS
"../include/Core/Core.h"
)

SET(LIB_SOURCES
"Core.cpp"
)

# Make library
ADD_LIBRARY("${PROJECT_NAME}" STATIC "${LIB_SOURCES}")

SET_TARGET_PROPERTIES("${PROJECT_NAME}" PROPERTIES
FRAMEWORK FALSE
PUBLIC_HEADER "${LIB_HEADERS}"
)

INSTALL(TARGETS "${PROJECT_NAME}"
EXPORT "${PROJECT_NAME}Targets"
ARCHIVE DESTINATION lib COMPONENT libs
RUNTIME DESTINATION bin COMPONENT libs
LIBRARY DESTINATION lib COMPONENT libs
PUBLIC_HEADER DESTINATION include/${PROJECT_NAME} COMPONENT devel
INCLUDES DESTINATION include
)

Core\CMakeLists.txt:

# Version check
CMAKE_MINIMUM_REQUIRED(VERSION 3.5)

# Project
PROJECT("Core" CXX)

INCLUDE_DIRECTORIES("${PROJECT_SOURCE_DIR}/include")

ADD_SUBDIRECTORY("include")
ADD_SUBDIRECTORY("src")
ADD_SUBDIRECTORY("test")

And last but not least the top level CMakeLists.txt:

# Version check
CMAKE_MINIMUM_REQUIRED(VERSION 3.5)

# Project
PROJECT("ProjectStructure" CXX)

# Status
MESSAGE(STATUS "Cmake running...")
MESSAGE(STATUS "System: ${CMAKE_SYSTEM}")
MESSAGE(STATUS "System: ${CMAKE_SYSTEM_PROCESSOR}") 
MESSAGE(STATUS "Cmake-Version: ${CMAKE_VERSION}")
MESSAGE(STATUS "C++ compiler: ${CMAKE_CXX_COMPILER}")

# Project structure
SET(CMAKE_INCLUDE_PATH "${CMAKE_SOURCE_DIR}/include")

SET(CMAKE_BINARY_DIR "${CMAKE_SOURCE_DIR}/build")
SET(EXECUTABLE_OUTPUT_PATH "${CMAKE_BINARY_DIR}")
SET(LIBRARY_OUTPUT_PATH "${CMAKE_BINARY_DIR}")

SET(PROJECTSTRUCTURE_CLIENTS_DIR "${CMAKE_SOURCE_DIR}/clients")

# Prohibit in source builds
IF("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
MESSAGE(SEND_ERROR "In-source builds are not allowed. Remove generated files.")
ENDIF()

# Output settings
SET(CMAKE_VERBOSE_MAKEFILE ON)
SET(CMAKE_COLOR_MAKEFILE   ON)

# Build mode
SET(CMAKE_BUILD_TYPE Debug)                      # Dev
#SET(CMAKE_BUILD_TYPE RelWithDebInfo)            # Test
#SET(CMAKE_BUILD_TYPE Release)                   # Release

# Used features
SET(CMAKE_CXX_STANDARD 14) # -std=c++14
SET(CMAKE_CXX_STANDARD_REQUIRED ON)

# Compiler flags
SET(CMAKE_CXX_FLAGS "-Wall -Wextra")
SET(CMAKE_CXX_FLAGS_DEBUG "-g3 -O0")             # Dev
SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3")        # Test
SET(CMAKE_CXX_FLAGS_RELEASE "-O3")               # Release

# Libraries
ADD_SUBDIRECTORY("${CMAKE_SOURCE_DIR}/core")

# Executables
ADD_SUBDIRECTORY("${PROJECTSTRUCTURE_CLIENTS_DIR}")

I'm using out of source builds using the following commands:

cd build
cmake .. -G "MSYS Makefiles"

And then run using:

make

Note: The real project is also containing directories for documentation and some other things. Also files like .gitignore and a .travis.yml (but that is the next thing I have to get working).


The problem:

The cmakecommand is working but the makecommand tells me that the included file (Core.h) could not be found.
I think the problem is in the part where the library gets installed. But I was unable to find the right way to do it. The solution I posted here is only one from many that I tried.


Additional questions:

1. The CMakeLists marked with an A are empty. They where just added so that every directory has a CMakeLists file. Should i remove them?

2. I would like to use google-test/mock in my projects with tests for each project in the project/test folder. I it enough to include google test in the top level CMakeLists to make this work or do I have to include it in each sub project?

3. Is it good practice to list the source files or to automatically load them? I think listing them is better even if it is more work. I think this has also advantages for cmake when reloading the project. Is this also true for tests? I am thinking about making a test file for each source file.

FrTerstappen
  • 220
  • 1
  • 4
  • 10

1 Answers1

1

This does not seem to be the problem with the installation part. I think there is a problem with the way you add include directories. :)

When you use INCLUDE_DIRECTORIES you say that cmake should add proper flags for compiler for files in this CMakeLists. But NOT in files added in parent CMakeLists. I think the stack is a great analogy for this situation. (https://cmake.org/cmake/help/v3.5/command/include_directories.html)

Note that you added ./Core/CMakeLists.txt in ./CMakeLists.txt:ADD_SUBDIRECTORY("${CMAKE_SOURCE_DIR}/core"), ./Core/include is no more in use when you add the clients subdirectory later.

IMHO the most elegant solution is to use TARGET_INCLUDE_DIRECTORIES(Core PUBLIC pathToCoresHeaders) instead. If you decide to use it, the line TARGET_LINK_LIBRARIES("${PROJECT_NAME}" "Core") will add proper include paths to desktop executable.

AD1. Yes, I think that you should remove them. You can leave the CMakeLists.txt in test's directories as it usually takes one more targets for UT.

AD2. I would recommend adding it in top level directory before you start to define own targets, and the link your testing execs with gtest and gtest_main, this should register gtest's include paths.

AD3. I list all my files in cmake. I'd rather see error about missing file than unresolved external symbol at the end of build. But it is not black and white (Specify source files globally with GLOB?). You should make your own decision and live with consequences.

PS1. I would suggest to additionally test your build on some unix like system, because Windows is way too liberal with the case insensitivity.

There are the changes I would have done to improve your build system.

./Core/CMakeLists.txt

CMAKE_MINIMUM_REQUIRED(VERSION 3.5)

PROJECT(Core CXX)

SET(INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include)
SET(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)

SET(LIB_HEADERS
    ${INCLUDE_DIRS}/Core/Core.h
)

SET(LIB_SOURCES
    ${SOURCE_DIR}/Core.cpp
)

# Make library
ADD_LIBRARY("${PROJECT_NAME}" STATIC ${LIB_SOURCES} ${LIB_HEADERS})

TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME}
    PUBLIC ${INCLUDE_DIRS}
)

SET_TARGET_PROPERTIES("${PROJECT_NAME}" PROPERTIES
FRAMEWORK FALSE
PUBLIC_HEADER "${LIB_HEADERS}"
)

INSTALL(TARGETS "${PROJECT_NAME}"
EXPORT "${PROJECT_NAME}Targets"
ARCHIVE DESTINATION lib COMPONENT libs
RUNTIME DESTINATION bin COMPONENT libs
LIBRARY DESTINATION lib COMPONENT libs
PUBLIC_HEADER DESTINATION include/${PROJECT_NAME} COMPONENT devel
INCLUDES DESTINATION include
)

ADD_SUBDIRECTORY(test)

./clients/Desktop/CMakeLists.txt

cmake_minimum_required(VERSION 3.5)

project("Desktop" CXX)

SET(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
SET(INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include)

SET(APP_HEADERS
)

SET(APP_SOURCES
    ${SOURCE_DIR}/main.cpp
)

# Make executable
ADD_EXECUTABLE(${PROJECT_NAME} ${APP_SOURCES} ${APP_HEADERS})
TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME} PRIVATE ${INCLUDE_DIR})
TARGET_LINK_LIBRARIES(${PROJECT_NAME} Core)

add_subdirectory(test)
Community
  • 1
  • 1
luantkow
  • 2,809
  • 20
  • 14
  • Thank you very much. Building works now. I'm now working on setting up the google test support. About the part with the testing on linux. I plan to do this using travis (I i get it to work). Only one question. The bin dir in my project seems to be unused. My plan was to have the compiled application in there at the end. Is this possible or should i remove it? – FrTerstappen Apr 24 '16 at 09:34
  • @FrTerstappen You may consider using https://cmake.org/cmake/help/v3.5/variable/CMAKE_RUNTIME_OUTPUT_DIRECTORY.html to put your binaries in one directory. I would rather use `ctest` to execute tests https://cmake.org/Wiki/CMake/Testing_With_CTest and install https://cmake.org/cmake/help/v3.0/command/install.html to deploy your application. – luantkow Apr 30 '16 at 07:16
  • Thank you for your suggestions. I'm currently trying to get google-test to work in my project. I think i will go with the option you recommend even if the first look at the links made me feel a bit overwhelmed. I think that this is more complicated than it should be considering that testing a important aspect. – FrTerstappen May 01 '16 at 10:03