0

Setup: modern(ish) CMake 3.31.6, main CMakeLists.txt below.

As you can see, I'm generating a version.h at cmake time.

This all works fine when I run

cmake -S . -B /tmp/build

from the project directory but, and that's problematic because it's such a common usage pattern even (and especially) in Linux distro packaging scripts,

mkdir build
cd build
cmake ..

fails, with output further down.

The problem seems to be that I can't use target_sources's FILE_SET feature if parts of the files are in CMAKE_CURRENT_BINARY_DIR if that is under the project's source root; if it's outside, everything seems fine.

Question now is:

How can I enable my users to build in a build/ directory made within the source directory in this situation?

Exhibit A: CMakeLists.txt

cmake_minimum_required(VERSION 3.25)
project(
  libsigf
  VERSION 0.1.0
  LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)

add_library(sigf SHARED src/libsigf.cc)
add_library(SigF::sigf ALIAS sigf)
configure_file(include/sigf/version.h.in include/sigf/version.h)

target_sources(
  sigf
  PUBLIC FILE_SET HEADERS BASE_DIRS
         $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> FILES
         include/sigf/sigf.h)
target_sources(
  sigf
  PUBLIC FILE_SET HEADERS BASE_DIRS
         $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}> FILES
         $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include/sigf/version.h>)

target_include_directories(
  sigf
  PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
         $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>
         $<INSTALL_INTERFACE:include>)

# Installation targets
install(
  TARGETS sigf
  EXPORT SigFTargets
  LIBRARY DESTINATION lib
  ARCHIVE DESTINATION lib
  RUNTIME DESTINATION bin
  INCLUDES
  DESTINATION include
  FILE_SET HEADERS)

Exhibit 2: cmake .. output

-- The CXX compiler identification is GNU 15.2.1
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.1s)
CMake Error at CMakeLists.txt:17 (target_sources):
  Base directories in file set cannot be subdirectories of each other:

    /home/marcus/src/libsigf
    /home/marcus/src/libsigf/build


CMake Error at CMakeLists.txt:12 (target_sources):
  File:

    /home/marcus/src/libsigf/include/sigf/sigf.h

  must be in one of the file set's base directories:


CMake Error at CMakeLists.txt:17 (target_sources):
  File:

    /home/marcus/src/libsigf/build/include/sigf/version.h

  must be in one of the file set's base directories:


CMake Error at CMakeLists.txt:17 (target_sources):
  Base directories in file set cannot be subdirectories of each other:

    /home/marcus/src/libsigf
    /home/marcus/src/libsigf/build


CMake Error at CMakeLists.txt:12 (target_sources):
  File:

    /home/marcus/src/libsigf/include/sigf/sigf.h

  must be in one of the file set's base directories:


CMake Error at CMakeLists.txt:17 (target_sources):
  File:

    /home/marcus/src/libsigf/build/include/sigf/version.h

  must be in one of the file set's base directories:


CMake Error at CMakeLists.txt:17 (target_sources):
  Base directories in file set cannot be subdirectories of each other:

    /home/marcus/src/libsigf
    /home/marcus/src/libsigf/build


CMake Error at CMakeLists.txt:12 (target_sources):
  File:

    /home/marcus/src/libsigf/include/sigf/sigf.h

  must be in one of the file set's base directories:


CMake Error at CMakeLists.txt:17 (target_sources):
  File:

    /home/marcus/src/libsigf/build/include/sigf/version.h

  must be in one of the file set's base directories:


CMake Error at CMakeLists.txt:17 (target_sources):
  Base directories in file set cannot be subdirectories of each other:

    /home/marcus/src/libsigf
    /home/marcus/src/libsigf/build


CMake Error at CMakeLists.txt:12 (target_sources):
  File:

    /home/marcus/src/libsigf/include/sigf/sigf.h

  must be in one of the file set's base directories:


CMake Error at CMakeLists.txt:17 (target_sources):
  File:

    /home/marcus/src/libsigf/build/include/sigf/version.h

  must be in one of the file set's base directories:


CMake Error at CMakeLists.txt:17 (target_sources):
  Base directories in file set cannot be subdirectories of each other:

    /home/marcus/src/libsigf
    /home/marcus/src/libsigf/build


CMake Error at CMakeLists.txt:12 (target_sources):
  File:

    /home/marcus/src/libsigf/include/sigf/sigf.h

  must be in one of the file set's base directories:


CMake Error at CMakeLists.txt:17 (target_sources):
  File:

    /home/marcus/src/libsigf/build/include/sigf/version.h

  must be in one of the file set's base directories:


CMake Error at CMakeLists.txt:17 (target_sources):
  Base directories in file set cannot be subdirectories of each other:

    /home/marcus/src/libsigf
    /home/marcus/src/libsigf/build


CMake Error at CMakeLists.txt:12 (target_sources):
  File:

    /home/marcus/src/libsigf/include/sigf/sigf.h

  must be in one of the file set's base directories:


CMake Error at CMakeLists.txt:17 (target_sources):
  File:

    /home/marcus/src/libsigf/build/include/sigf/version.h

  must be in one of the file set's base directories:


CMake Error at CMakeLists.txt:17 (target_sources):
  Base directories in file set cannot be subdirectories of each other:

    /home/marcus/src/libsigf
    /home/marcus/src/libsigf/build


CMake Error at CMakeLists.txt:12 (target_sources):
  File:

    /home/marcus/src/libsigf/include/sigf/sigf.h

  must be in one of the file set's base directories:


CMake Error at CMakeLists.txt:17 (target_sources):
  File:

    /home/marcus/src/libsigf/build/include/sigf/version.h

  must be in one of the file set's base directories:


-- Generating done (0.0s)
CMake Generate step failed.  Build files cannot be regenerated correctly.

1 Answer 1

1

How can I enable my users to build in a build/ directory made within the source directory in this situation?

Just do not use top-level source directory as a base directory for HEADERS file set. That way you will overcome the error message

Base directories in file set cannot be subdirectories of each other

Actually, a base directory for HEADERS file set is an include directory. So, if you want to include your header include/sigf/sigf.h via

#include <sigf/sigf.h>

then you should use include/ subdirectory as its base directory. Similar is for include/sigf/version.h header in the build prefix. Correct:

target_sources(
  sigf
  PUBLIC FILE_SET HEADERS
         BASE_DIRS include
         FILES include/sigf/sigf.h)
target_sources(
  sigf
  PUBLIC FILE_SET HEADERS
         BASE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/include
         FILES ${CMAKE_CURRENT_BINARY_DIR}/include/sigf/version.h>)

That way you don't need to specify target_include_directories at all: CMake will automatically deduce include directories from the base directories. Moreover, that deduction will be correct both for the build tree and for the install tree (when you install(EXPORT) your library target).

Sign up to request clarification or add additional context in comments.

1 Comment

Imagine the sound of the palm of my head hitting my forehead! Thank you, that is both logical and cleaner! And most importantly: it works!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.