ShadowDragon
ShadowDragon

Reputation: 2398

CMake Multi-Project setup for Visual Studio 2017

To begin with, I'm new to CMake.

What I want to do is to switch from compiling my solution directly in VS 2017 to CMake in order to build the project easily on other platforms like Linux.

Folder structure:

project
  |─── CMakeLists.txt
  |─── bin
  |     | ─── intermediates
  |     |       |─── Win32
  |     |       |      |─── Debug
  |     |       |      └─── Release
  |     |       └─── Linux
  |     |              |─── Debug
  |     |              └─── Release
  |     |─── Win32
  |     |      |─── Debug
  |     |      └─── Release
  |     └─── Linux
  |            |─── Debug
  |            └─── Release
  |─── include
  |─── Project 1
  |      |─── CMakeLists.txt
  |      |─── src
  |      |─── res
  |      └─── header
  └─── Project 2
         |─── CMakeLists.txt
         |─── src
         |─── res
         └─── header

CMake files

And here are my CMakeLists, the first one for the root project, the second one is for the first project and the second project:

# Specify the minimum version for CMake
cmake_minimum_required(VERSION 3.8.2)

# Project's name
project(Project X)

# Set the C++ Version
message("!REQUIRED! -- Supported features = ${cxx_std_14}")
message("Supported features = ${cxx_std_17}")

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# Set the output folder where the program will be created

set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/bin)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR})

# Add the modules
add_subdirectory(Project 1)
add_subdirectory(Project 2) # depends on Project 1

The CMakeLists.txt for the first and second project, as the content of the file is the same:

# Specify the minimum version for CMake
cmake_minimum_required(VERSION 3.8.2)

project(Project 1)

# In order to find all cpp files automatically for compilation
file(GLOB CPP_FILES src/*.cpp)
file(GLOB CPP_FILES header/*.h)

# Set the version number of the project here
set(VERSION_MAJOR "0")
set(VERSION_MINOR "1")
set(VERSION_PATCH "0")
set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})

And this are my VS settings:

Output Directory:       $(SolutionDir)bin\$(Platform)\$(Configuration)\
Intermediate Directory: $(SolutionDir)bin\intermediates\$(Platform)\$(Configuration)\

Project B is in this case the executable while project A is just a lib which has to be linked to Project B in order for Project B to compile.

Question:

Upvotes: 3

Views: 3837

Answers (1)

There are multiple points to address:

  1. CMAKE_BINARY_DIR is, for all intents and purposes, a read-only variable, and should definitely be treated as such. The way to specify the binary directory is to run CMake in that directory (when running from the command line), or set that directory as the binary/output directory (when using a CMake GUI). So in your case, to achieve the setup you claim you want, you'd do this:

    > cd project/bin
    > cmake .. -G "Visual Studio whatever" ...
    

    ("whatever" is, of course, a placeholder)

    However, I strongly suggest you do not use a layout like this. A much better layout would a purely out-of-source-build, like this:

    project
    project/source/CMakeLists.txt
    project/source/include
    project/source/...
    project/build/Win32
    project/build/Linux
    

    With this setup, you'd then run like this:

    > cd project/build/Win32
    > cmake ../../source -G "Visual Studio whatever" ...
    
  2. There is a conflict between how you're setting your binary output paths in CMake, and what you've presented as your "VS settings." I do not know which is your inteded state, but in case you want to set up CMake so that your binaries end up in the output directory you've listed in the VS settings, you'd do it like this:

    set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR})
    

    Ditto for LIBRARY_OUTPUT_PATH.

  3. As far as setting the intermediate directory goes, this is something CMake manages itself and you cannot really affect this. The question is why you'd want to, though.

  4. Regarding project dependencies: you haven't shown the CMake commands which actually create the binaries (add_library()/add_executable()) or commands which set up their properties. However, if you specify that the executable created by Project 2 links against the library created by Project 1, CMake will track the dependency correctly. You'd specify it like this:

    target_link_libraries(targetNameOfProject2executable targetNameOfProject1library)
    
  5. Please note that using file(GLOB) to construct a list of source files is a bad idea and is discouraged by CMake. The reason is simple: adding a source file will not cause the buildsystem to be regenerated, meaning the source file will not be picked up. If you instead list source files explicitly, adding the file will mean you must add it to the list in the CMakeList (or in a file included by it), which will trigger a re-generation of the buildsystem.

  6. A note for Linux: unlike Visual Studio, the Makefile generator (and in fact most of CMake's generators) are single-configuration, which means you must generate a buildsystem for each configuration. In the out-of-souce setup, you'd do it like this:

    > cd project/build/Linux/Debug
    > cmake ../../../../source -DCMAKE_BUILD_TYPE=Debug ...
    > cd project/build/Linux/Release
    > cmake ../../../../source -DCMAKE_BUILD_TYPE=Release ...
    

Upvotes: 2

Related Questions