cmake 3.18: generator expressions with add_subdirectory ?

How could I define a binary_dir dependent on generator expression.

The goal is to have a different build directory (binary_dir) dependent on a configuration, when I call add_subdirectory.

Currently with cmake v3.18 I have the following:

set(BINARY_PATH $<$<CONFIG:Debug>:${PATH_BASE}Debug>$<$<CONFIG:Release>:${PATH_BASE}Release>)

add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../module/ ${BINARY_PATH})

and the add_subdirectory fails while generating with

Generator expressions in custom command outputs are not implemented

What you’re asking for doesn’t really make sense. In a single-configuration generator, you can just use CMAKE_BUILD_TYPE to make decisions at configure time. In a multi-configuration generator, multiple configurations are managed by the same buildsystem (e.g. by one .vcxproj file in the binary directory), so having different binary directories for different configurations is impossible.

Are you perhaps looking for something like CMAKE_CFG_INTDIR, which allows you to have per-configuration paths at build time with multi-config generators?

Wise words “doesnt really make sense” :slight_smile: but thanks, I needed really advises. As far as I understand from the link you gave, CMAKE_CFG_INTDIR expands to the ${OutDir} but I want to set it up manually. (Besides the help tells about problems with Ninja-Multiconfiguration and that is actually depreciated and add_custom_command should be used instead). So, it seems, this won’t help.

I want to be able with a multi-configuration generator to set up the build folder like
build/Release_x64, Release_x86 etc.
or
build/Debug_x64, Debug _x64 etc.

I tried with add_subdirectory and generator expressions (see the initial post) but cmake complains about this combination. (Which imho is a bug, and not a feature).

When you want certain setup for a multi-configuration project, you should always cross-check it by asking “how would this work with a multi-configuration generator like VS?” That will show you that having a per-configuration binary directory is impossible, because the binary directory is where the project file (.vcxproj) is stored. You cannot have one file in multiple directories.

Can you explain why you want such per-configuration directories? Perhaps it’s a bit of an X-Y problem and your goal could be better achieved by modifying CMAKE_RUNTIME_OUTPUT_DIRECTORY and similar?

I would really like to get an answer to the question, why add_subdirectory doesn’t work with generator expressions ?

In VS it is also possible to specify different OutputDirectory per Configuration, e.g.
$(ProjectDir)\windows_$(Platform)_$(Configuration)_$(PlatformToolset)\

Why is it not possible to set it up with cmake like this? How could I achieve this with cmake?

You can specify a different output directory for each configuration, even with generator expressions (see e.g. RUNTIME_OUTPUT_DIRECTORY property):

add_executable(foo ${sources})
set_property(
  TARGET foo 
  PROPERTY RUNTIME_OUTPUT_DIRECTORY
  windows_$<PLATFORM_ID>_$<CONFIG>
)

What you cannot do is specify a different binary directory for each configuration, because the binary directory is the place where the buildsystem files (e.g. project files, makefiles) go, and these are not necessarily per-configuration. The binary directory is also the default output directory, but that can be changed.

Many of the items that go into the binary directory specified in add_subdirectory, like .vcxproj or Makefiles and other build system files, are created when the project is generated. CMake isn’t going to make duplicate vcxproj for each of the proposed binary directories. That would like be having two top level binary directories and two solution files.

There are plenty of ways to control where the final build outputs goes per configuration. The concept doesn’t readily apply to where the build system files or intermediate objects go. You don’t get to decide total control of the entire build folder.

This would suffice, however after generating and opening the VS, the OutputDirectory is unaffected. It shows the predifined OutputDirectory, and I cannot find any place where the set property is considered when generating. So what is missing?

Strange, I’ve just tested that it works for me with this minimal example:

cmake_minimum_required(VERSION 3.14)
project(Test)

add_executable(foo main.cpp)
set_property(TARGET foo PROPERTY RUNTIME_OUTPUT_DIRECTORY win_$<PLATFORM_ID>_$<CONFIG>)

Can you post the relevant part of your CMakeList to see if everything’s ok there?

I tested it with your example in my project. It wont change the Output Directory either

Here my modified cmake extracts, I always see in visual studio the unwanted/predefined Output Directory

cmake_minimum_required (VERSION 3.14)

project(myTarget C CXX)
add_library(myTarget ${SRCS} ${HDRS})

include(cmakeSetUpSomeVars.cmake)

# variant 1
set_property(TARGET myTarget PROPERTY RUNTIME_OUTPUT_DIRECTORY win_$<PLATFORM_ID>_$<CONFIG>)
# 1.2
#set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/win_$<PLATFORM_ID>_$<CONFIG> )

# variant 2
set_property(TARGET myTarget PROPERTY LIBRARY_OUTPUT_DIRECTORY win_$<PLATFORM_ID>_$<CONFIG>)
#2.2 
# set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/win_$<PLATFORM_ID>_$<CONFIG> )

The correct property for specifying output location depends on the type of target. For executables, it’s RUNTIME_OUTPUT_DIRECTORY. For static libraries, it’s ARCHIVE_OUTPUT_DIRECTORY. For shared libraries on DLL platforms (Windows), it’s RUNTIME_OUTPUT_DIRECTORY for the .dll and ARCHIVE_OUTPUT_DIRECTORY for the import .lib. For shared libraries on non-DLL platforms (Linux), it’s LIBRARY_OUTPUT_DIRECTORY. See the discussion of Output artifacts for details: https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#output-artifacts . (Note that this is also linked from the documentation of RUNTIME_OUTPUT_DIRECTORY to which I pointed you earlier).

Regarding the variables CMAKE_[XYZ]_OUTPUT_DIRECTORY, these follow the standard CMake approach of initialising the corresponding target property at the moment the target is created. So if you want to use these (i.e. CMAKE_ARCHIVE_OUTPUT_DIRECTORY in your case), it must be set before the add_library() command.

1 Like

I see. I hadnt dived into details of the output variants… Thanks about it.

What is the way to change the VS Intermediate Directory in the same way? Otherwise it would be not a complete solution.

AFAIK, there is none. Location of object files is an implementation detail of CMake, something you should never need to be concerned about. CMake will make sure everything works as it should, and we don’t really need to know or care how. CMake reserves this to be free to evolve object file layout and handling across versions.

1 Like

The problem with VS Intermediate Directory is, that when you clean one project which has the same intermediate directory with another project, you force rebuild of the other project.

In VS there is a possibility to change the Intermediate Directory (see above screenshot), why is not possible with cmake (it is just one json entry in the vs project configuration)?

My workflow is: cmake config & generate and VS build. Do you know of a better/another one?

How did you get CMake to generate two projects with the same Intermediate directory? Normally CMake generates .vcxproj files such that each project has its own (effectively TargetName.dir).

1 Like

This would be the case when you choose a different architecture type (x64/x86) for the same project differently configured.

Btw, why (with deleted cache) when set with the cmake-gui (the 2nd line of the gui - Optional platform for generator), the variable CMAKE_GENERATOR_PLATFORM is not affected, or at least cmake doesnt know it at configuration start? Or do I check for a wrong variable again?

  1. Do you know, when setting of the CMAKE_ARCHIVE_OUTPUT_DIRECTORY has no effect, is it an IDE (in this case Qt Creator) issue, or this property works only for VS ?

  2. If I want to build the above static lib, triggered by a module which uses it, how is this done in a clean way? My consideration is:
    In the static lib, I have set_property(TARGET myLib PROPERTY ARCHIVE_OUTPUT_DIRECTORY ${MYLIB_OUTPUT_PATH})
    and in the module, depending on this static, I have to include
    add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../mylib binary_dir)
    But I was hoping, that specifying the ARCHIVE_OUTPUT_DIRECTORY, would release the requirement of specifying the binary_dir.
    For me two contradictory points.

It’s supposed to work for all generators. If it doesn’t work for you, it’s either a Qt Creator issue, or you’re using it incorrectly (such as only setting it after the static library target has already been created).

Please try to understand this: the binary_dir is where the buildsystem files (Makefiles, .vcxproj files etc.) go; it is also used by CMake to store the intermediate build artifacts (.o, .obj). Finally, it’s also the default location where final build artifacts (.lib, .dll, .a, .exe, .so etc.) go, but that location can be changed.

Therefore, setting the *_OUTPUT_DIRECTORY properties and/or variables cannot possibly remove the need for specifying the location of buildsystem files (= the binary directory), since it doesn’t affect them at all.

Note that you only need to specify the binary directory explicitly if you’re using an absolute path as the source directory argument. Quoting add_subdirectory() docs:

If binary_dir is not specified, the value of source_dir, before expanding any relative path, will be used (the typical usage).

So in your case, you could just do this:

add_subdirectory(../mylib)

and the binary directory for mylib would evaluate to ${CMAKE_CURRENT_BINARY_DIR}/../mylib, i.e. the hierarchy of binary directories will mirror your source directory hierarchy.


Perhaps you should think of these as two separate issues: one is the organisation of the buildsystem files, and one is the organisation of final build artifacts (executables, libraries).

If you really care about the former, you can control it by specifying binary directory arguments to add_subdirectory() calls, but you may find you don’t actually need control of that at all.

For the latter (build artifact locations), you can use the *_OUTPUT_DIRECTORY variables/properties, and if you want total manual control, you can root these in CMAKE_BINARY_DIRECTORY (the binary dir of the top-level project) instead of in CMAKE_CURRENT_BINARY_DIRECTORY. That way, you can lay out the binary artifacts however you like, regardless of the binary directory hierarchy.

1 Like

10x a lot. Just some final remarks:

This alone didnt work. Setting the binary explicitly was required.

OK; I was writing from memory and docs, didn’t test it. Perhaps it doesn’t like the fact that the path goes outside the current directory subtree, so it doesn’t want to mess up directories outside the normally corresponding binary tree without explicit instruction.