-1

I am working on a C++ project using CMake, and I manage third-party dependencies via FetchContent. I ran into an issue when trying to deduplicate the sub-builds generated by FetchContent for different build configurations.

Below is a minimal working example that replicates the issue:

cmake_minimum_required(VERSION 3.21)

# Add application
add_executable(FetchContentTest main.cpp)

include(FetchContent)

# Configure FetchContent
set(FETCHCONTENT_QUIET off)
set(BUILD_SHARED_LIBS ON)

# Cache original FC base dir
set(ORIGINAL_FC_DIR ${FETCHCONTENT_BASE_DIR})

# Redirect FC base dir to a shared location (instead of per-preset)
get_filename_component(FC_NEW_BASE_DIR "../FetchContent" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
set(FETCHCONTENT_BASE_DIR ${FC_NEW_BASE_DIR})

FetchContent_Declare(
  sdl2
  URL https://github.com/libsdl-org/SDL/releases/download/release-2.24.0/SDL2-2.24.0.zip
)

FetchContent_GetProperties(sdl2)
if(NOT sdl2_POPULATED)
    FetchContent_Populate(sdl2)

    # Source stays in shared folder, while the build goes to the binary folder
    set(SDL_BINARY_DIR "${CMAKE_BINARY_DIR}/_deps/sdl2-build")
    add_subdirectory(${sdl2_SOURCE_DIR} ${SDL_BINARY_DIR})
    
    set(SDL_LIBS SDL2main SDL2)

    target_link_libraries(FetchContentTest PRIVATE ${SDL_LIBS})
endif()

# Reset FC base dir
set(FETCHCONTENT_BASE_DIR ${ORIGINAL_FC_DIR})

This is combined with CMake presets in the following way:

{
  "version": 3,
  "cmakeMinimumRequired": {
    "major": 3,
    "minor": 21,
    "patch": 0
  },
  "configurePresets": [
    {
      "name": "base",
      "hidden": true,
      "condition": {
        "type": "equals",
        "lhs": "${hostSystemName}",
        "rhs": "Windows"
      },
      "generator": "Visual Studio 17 2022",
      "binaryDir": "${sourceDir}/out/build/${presetName}",
      "cacheVariables": {
        "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}",   
        "CMAKE_CXX_COMPILER": "cl",
        "CMAKE_CONFIGURATION_TYPES": "Debug;Release"
      },
      "warnings": { "dev": false },
      "vendor": {
        "microsoft.com/VisualStudioSettings/CMake/1.0": {
          "hostOS": [ "Windows" ]
        }
      }
    },
    {
      "name": "windows-x86",
      "inherits": "base",
      "architecture": {
        "value": "Win32",
        "strategy": "set"
      }
    },
    {
      "name": "windows-x64",
      "inherits": "base",
      "architecture": {
        "value": "x64",
        "strategy": "set"
      }
    }
  ],
  "buildPresets": [
    {
      "name": "windows-x64-debug",
      "configurePreset": "windows-x64",
      "configuration": "Debug",
      "cleanFirst": false
    },
    {
      "name": "windows-x64-release",
      "configurePreset": "windows-x64",
      "configuration": "Release",
      "cleanFirst": false
    }
  ]
}

The intended result is that FetchContent downloads and unpacks the sources to a shared location, since this is meant to be agnostic to the project build configuration, and the build output is then directed to a subfolder corresponding to the CMake preset (thus we would separate 32 and 64 bit builds, debug from release, etc.)

When I open this in Visual Studio with a clean project (i.e nothing was generated yet), the initial configuration (e.g 32 bit) works correctly, no errors. Once I switch to a different configuration, I get the following error:

1> [CMake] CMake Error: Error: generator platform: x64
1> [CMake] Does not match the platform used previously: Win32
1> [CMake] Either remove the CMakeCache.txt file and CMakeFiles directory or choose a different binary directory.

This appears to be due to a conflict with the architecture strategy setting in CMakePresets. If I change it from "set" to "external", I don't get the error when switching presets, instead CMake just cancels generation.

How can I make the FetchContent source and sub-build location agnostic to the build configuration, assuming it is possible?

yah_nosh
  • 155
  • 1
  • 8

1 Answers1

0

Asked around in the CMake forum. The bookkeeping used by FetchContent does not expect such configuration changes to happen for any given sub-build, and thus it does not currently support the behaviour I outlined in my original post.

This may change in the future, as developers do seem to want to avoid duplicated downloads/sources/etc., but for now each dependency has to be fetched for each configuration.

yah_nosh
  • 155
  • 1
  • 8