How should I setup script-based source generation with CMake? - c++

I'm managing the build of a certain C++ repository using CMake. In this repository, and among other things, I have a bunch of .hpp files in a directory, and for each of these, I need to compile a generated bit of source code, which depends on its contents.
Naively, I would do this by generating a correspond .cpp file, and include that file in the source files of the CMake target for the library or executable I'm building. Now, since I don't actually need the source files themselves, I could theoretically just arrange for the compiler to get its source from the command-line instead.
My question: How would I set up this source generation and compilation, using CMake as idiomatically as possible?
Notes:
Assume I have, say, a bash script which can read the .hpp and generate the .cpp on the standard output.
CMake version 3.24 or whichever you like.
Should work on Unix-like operating systems, and hopefully on Windows-like OSes other than the fact that the bash script will fail.
Please comment if additional information is necessary to answer my question.

Let's assume for the sake of portability that you have a Python script, rather than a bash script that manages your code generation. Let's say that it takes two arguments: the source .hpp file and the destination .cpp file. We'll assume it is in your source tree under ./tools/codegen.py.
Now let's assume that your .hpp files are in ./src/genmod for "generated module" because the sources for these headers are generated by codegen.py.
Finally, we'll assume there's a final executable target, app, with a single source file, ./src/main.cpp.
Here's a minimal build that will work for this, with some step-by-step discussion.
We start with some boring boilerplate.
cmake_minimum_required(VERSION 3.24)
project(example)
This probably works on earlier versions, I just haven't tested it, so YMMV. Now we'll create the executable target and link it to our generated sources preemptively. Note that the dependent target does not need to exist before calling target_link_libraries.
add_executable(app src/main.cpp)
target_link_libraries(app PRIVATE genmod)
Now we'll find a Python interpreter and write down an absolute path to the codegen tool.
find_package(Python3 REQUIRED)
set(codegen_py "${CMAKE_CURRENT_SOURCE_DIR}/tools/codegen.py")
Next we'll construct the list of input headers. I'm imagining there are three: A.hpp, B.hpp, and C.hpp.
set(input_headers A.hpp B.hpp C.hpp)
list(TRANSFORM input_headers PREPEND src/genmod/)
I used list(TRANSFORM) here to save some typing. Now we'll just create an object library called genmod, which will "hold" the objects for the generated C++ files.
add_library(genmod OBJECT)
And now comes the real meat. For each of the headers, ...
foreach (header IN LISTS input_headers)
we'll construct absolute paths to the header and generated source files ...
string(REGEX REPLACE "\\.hpp$" ".cpp" gensrc "${header}")
set(header "${CMAKE_CURRENT_SOURCE_DIR}/${header}")
set(gensrc "${CMAKE_CURRENT_BINARY_DIR}/${gensrc}")
and then write a custom command that knows how to call codegen.py. We specify the outputs, command arguments, and dependencies. Don't forget to include the generator script as a dependency, and never forget to pass VERBATIM to ensure consistent, cross-platform, argument quoting.
add_custom_command(
OUTPUT "${gensrc}"
COMMAND Python3::Interpreter "${codegen_py}" "${header}" "${gensrc}"
DEPENDS "${header}" "${codegen_py}"
VERBATIM
)
Finally, we attach this source to genmod.
target_sources(genmod PRIVATE "${gensrc}")
endforeach ()
We can test this build using Ninja's dry-run feature to make sure the commands look correct.
$ cmake -G Ninja -S . -B build -DCMAKE_BUILD_TYPE=Release
-- The C compiler identification is GNU 10.2.1
-- The CXX compiler identification is GNU 10.2.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- 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
-- Found Python3: /home/reinking/.venv/default/bin/python3.9 (found version "3.9.2") found components: Interpreter
-- Configuring done
-- Generating done
-- Build files have been written to: /home/reinking/test/build
$ cmake --build build -- -nv
[1/8] cd /home/reinking/test/build && /home/reinking/.venv/default/bin/python3.9 /home/reinking/test/tools/codegen.py /home/reinking/test/src/genmod/A.hpp /home/reinking/test/build/src/genmod/A.cpp
[2/8] cd /home/reinking/test/build && /home/reinking/.venv/default/bin/python3.9 /home/reinking/test/tools/codegen.py /home/reinking/test/src/genmod/B.hpp /home/reinking/test/build/src/genmod/B.cpp
[3/8] cd /home/reinking/test/build && /home/reinking/.venv/default/bin/python3.9 /home/reinking/test/tools/codegen.py /home/reinking/test/src/genmod/C.hpp /home/reinking/test/build/src/genmod/C.cpp
[4/8] /usr/bin/c++ -O3 -DNDEBUG -MD -MT CMakeFiles/genmod.dir/src/genmod/A.cpp.o -MF CMakeFiles/genmod.dir/src/genmod/A.cpp.o.d -o CMakeFiles/genmod.dir/src/genmod/A.cpp.o -c /home/reinking/test/build/src/genmod/A.cpp
[5/8] /usr/bin/c++ -O3 -DNDEBUG -MD -MT CMakeFiles/genmod.dir/src/genmod/B.cpp.o -MF CMakeFiles/genmod.dir/src/genmod/B.cpp.o.d -o CMakeFiles/genmod.dir/src/genmod/B.cpp.o -c /home/reinking/test/build/src/genmod/B.cpp
[6/8] /usr/bin/c++ -O3 -DNDEBUG -MD -MT CMakeFiles/genmod.dir/src/genmod/C.cpp.o -MF CMakeFiles/genmod.dir/src/genmod/C.cpp.o.d -o CMakeFiles/genmod.dir/src/genmod/C.cpp.o -c /home/reinking/test/build/src/genmod/C.cpp
[7/8] /usr/bin/c++ -O3 -DNDEBUG -MD -MT CMakeFiles/app.dir/src/main.cpp.o -MF CMakeFiles/app.dir/src/main.cpp.o.d -o CMakeFiles/app.dir/src/main.cpp.o -c /home/reinking/test/src/main.cpp
[8/8] : && /usr/bin/c++ -O3 -DNDEBUG CMakeFiles/genmod.dir/src/genmod/A.cpp.o CMakeFiles/genmod.dir/src/genmod/B.cpp.o CMakeFiles/genmod.dir/src/genmod/C.cpp.o CMakeFiles/app.dir/src/main.cpp.o -o app && :
And indeed we can see that the commands are what we'd naturally expect them to be.

Related

CMake toolchain file - setting CMAKE_CXX_FLAGS

I have seen the following way of setting CMAKE_CXX_FLAGS in the toolchain file:
SET(CMAKE_CXX_FLAGS "-m32" CACHE STRING "C++ compiler flags" FORCE)
Should I use it in the toolchain file instead of
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32")
?
What are differences between them ?
tl;dr: there are two acceptable ways of doing this.
First, and most of the time (90%+), you can use the _INIT variables as suggested by the documentation:
set(CMAKE_CXX_FLAGS_INIT "-m32")
Second, if CMake is adding incorrect/conflicting flags for your compiler/platform combination, you can override it completely by setting the cache variable without FORCE.
set(CMAKE_CXX_FLAGS "-m32" CACHE STRING "C++ compiler flags")
Read on for further details.
Let's run a few experiments. We'll use the following CMakeLists.txt:
cmake_minimum_required(VERSION 3.23)
project(test LANGUAGES CXX)
message(STATUS "CMAKE_CXX_FLAGS_DEBUG = ${CMAKE_CXX_FLAGS_DEBUG}")
On most systems, CMake leaves CMAKE_CXX_FLAGS blank by default. The main exception is Windows with MSVC, where it adds /EHsc and (on older versions) /GR to ensure that standard C++ exception handling and RTTI are enabled.
Since I don't have ready access to a Windows system, I use CMAKE_CXX_FLAGS_DEBUG, which does have default-initialized flags on most compilers. The same principles apply, though, since it is the responsibility of the platform module to set these in both cases.
Experiment 1: No toolchain file
$ cmake -S . -B build
-- The CXX compiler identification is GNU 11.2.0
-- 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
-- CMAKE_CXX_FLAGS_DEBUG = -g
-- Configuring done
-- Generating done
-- Build files have been written to: /path/to/build
So on this compiler, CMAKE_CXX_FLAGS_DEBUG is set to -g. This is our baseline.
Experiment 2: Set-cache with force
Now we'll create a toolchain file called set-cache-force.cmake:
# set-cache-force.cmake
set(CMAKE_CXX_FLAGS_DEBUG "-DMY_DEBUG" CACHE STRING "C++ compiler flags" FORCE)
We'll configure the project with this toolchain:
$ rm -rf build
$ cmake -S . -B build --toolchain set-cache-force.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
As we can see, the original -g flag was suppressed and the -DMY_DEBUG cache value "won". Of course, this isn't really a debug mode anymore, which should illustrate why overriding all the flags isn't always what we want.
Even worse, using FORCE here disables a user's ability to override CMAKE_CXX_FLAGS_DEBUG themselves:
$ rm -rf build
$ cmake -S . -B build --toolchain set-cache-force.cmake -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
This is highly undesirable behavior. A user would need to edit your toolchain file to work around a bug or add further customizations.
Experiment 3: Set-cache without force
If we run the same experiment as before without FORCE setting it, then we still get the same flags, but we retain the ability to incrementally override the toolchain file.
# set-cache.cmake
set(CMAKE_CXX_FLAGS_DEBUG "-DMY_DEBUG" CACHE STRING "C++ compiler flags")
Now we can see that it works:
$ rm -rf build
$ cmake -S . -B build --toolchain set-cache.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
And that it can still be overridden:
$ rm -rf build
$ cmake -S . -B build --toolchain set-cache.cmake -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DOVERRIDE
...
And it can even be overridden again:
$ cmake -S . -B build -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE2"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DOVERRIDE2
...
Experiment 4: Set normal variable
Now we'll try to set this as a normal variable. Again, we'll create a toolchain file called set-normal.cmake:
# set-normal.cmake
set(CMAKE_CXX_FLAGS_DEBUG "-DMY_DEBUG")
Again, running this shows that -DMY_DEBUG "wins", overriding CMake's default flags:
$ cmake -S . -B build --toolchain set-normal.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
Like experiment 2, this prevents users from overriding it... bad!
$ cmake -S . -B build --toolchain set-normal.cmake -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
Experiment 5: Append normal variable
Now we'll try with the code in your post. Again, we'll use a toolchain called append-normal.cmake:
# append-normal.cmake
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DMY_DEBUG")
Now we get a very different result:
$ rm -rf build
$ cmake -S . -B build --toolchain append-normal.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG -DMY_DEBUG
...
This is just completely wrong! What happened here? Well, the toolchain file gets read multiple times during project initialization, and here this causes the -DMY_DEBUG flag to be appended twice. At least that's what happens on the first run:
$ cmake -S . -B build
...
-- CMAKE_CXX_FLAGS_DEBUG = -g -DMY_DEBUG
...
After the first run, the CMake default gets cached and so we append to that on subsequent runs. Furthermore, CMake only reads your toolchain file once now.
You must always make your toolchain files idempotent. That means that running it twice does the same thing as running it once.
Experiment 6: Using _INIT variables
This is the developer-intended way of doing things per the documentation. See the documentation here: https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_FLAGS_INIT.html
Value used to initialize the CMAKE_<LANG>_FLAGS cache entry the first time a build tree is configured for language <LANG>. This variable is meant to be set by a toolchain file. CMake may prepend or append content to the value based on the environment and target platform.
Now we use a toolchain file called init-var.cmake:
# init-var.cmake
set(CMAKE_CXX_FLAGS_DEBUG_INIT "-DMY_DEBUG")
And we re-run the build:
$ rm -rf build
$ cmake -S . -B build --toolchain init-var.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG -g
...
Now we can see that CMake appended its default flags to the initial ones we provided. And indeed this still allows users to override things:
$ cmake -S . -B build --toolchain init-var.cmake -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DOVERRIDE
...
In my experience, 90%+ of the time it's correct to let CMake add in its extra flags using Experiment 6 (the _INIT variables). But every so often you'll want to completely override CMake using Experiment 3 (set(CACHE) without FORCE).
What you do not want to do is anything that behaves differently on subsequent runs (like experiment 5) or that disables key CMake functionality (ie. respecting the cache variable, like experiments 2 and 4).
When you use set(variable "value" CACHE STRING "..." FORCE), the variable is set for all the projects built in the current session (including those that are in the sub-directories).
But simply using set(variable "value") without the cache part only adds the flags for the immediate project scope (current CMakeLists.txt) and not the upper directories that have their own CMakeLists.txt.

Run a script upon linking to a CMake target

I have a situation where I need to run a script when a CMake target is linked-to so that it can automatically generate files in the current project directory that are used to interface with the library.
I know when you link to a CMake target it automatically pulls in the headers for the library so they become visible to the compiler, but I need it to also generate some files within the directory of the linkee that will also be visible to the compiler upon building.
How can I tell CMake that I want to run a script to generate the files every time my_cmake_target is linked to?
Example of linking in CMakeLists.txt:
target_link_libraries(my_executable PRIVATE my_cmake_target)
I want the command to run at the same time that CMake transitively updates the include directories based on the target passed to "target_link_libraries". (Before any building/linking actually takes place)
See here for more info on how that works:
https://schneide.blog/2016/04/08/modern-cmake-with-target_link_libraries/
Using target_link_libraries to link A to an internal target B will not only add the linker flags required to link to B, but also the definitions, include paths and other settings – even transitively – if they are configured that way.
Unfortunately, there's nothing built-in to help you do this. Propagating custom commands through interface properties is not something CMake has implemented (or has plans to, afaik).
However, and this is kind of cursed, here is a way.
You create a function that scans the directory for targets that link to your special library. For each one of those targets, it attaches a special source file in the binary directory and a command for generating that file. It uses a custom property (here, MAGIC) for determining whether to actually generate the source file and include it in your target's sources.
Then, use cmake_language(DEFER CALL ...) to run that function at the end of the current directory's build script. This part ensures the function does not have to be called manually, even in find_package scenarios.
TODOS:
Running this code twice will likely cause errors. However, you can avoid problems by marking whether a target has already been processed with another bespoke property.
# ./CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
project(example LANGUAGES CXX)
add_subdirectory(subdir)
add_executable(my_executable main.cpp)
target_link_libraries(my_executable PRIVATE my_cmake_target)
add_executable(excluded main.cpp default-name.cpp)
# ./subdir/CMakeLists.txt
function (MyProj_post_build)
set(dirs ".")
while (dirs)
list(POP_FRONT dirs dir)
get_property(subdirs DIRECTORY "${dir}" PROPERTY SUBDIRECTORIES)
list(APPEND dirs ${subdirs})
get_property(targets DIRECTORY "${dir}" PROPERTY BUILDSYSTEM_TARGETS)
foreach (target IN LISTS targets)
# Do whatever you want here, really. The key is checking
# that $<BOOL:$<TARGET_PROPERTY:MAGIC>> is set on the
# target at generation time. I use a custom command here,
# but you could use file(GENERATE).
add_custom_command(
OUTPUT "MyProj_${target}.cpp"
COMMAND "${CMAKE_COMMAND}" -E echo "const char* Name = \"$<TARGET_PROPERTY:${target},NAME>\";" > "MyProj_${target}.cpp"
VERBATIM
)
target_sources(
"${target}"
PRIVATE
"$<$<BOOL:$<TARGET_PROPERTY:MAGIC>>:$<TARGET_OUT/MyProj_${target}.cpp>"
)
endforeach ()
endwhile ()
endfunction ()
cmake_language(DEFER DIRECTORY "${CMAKE_SOURCE_DIR}" CALL MyProj_post_build)
add_library(my_cmake_target INTERFACE)
set_target_properties(my_cmake_target PROPERTIES INTERFACE_MAGIC ON)
set_property(TARGET my_cmake_target APPEND PROPERTY COMPATIBLE_INTERFACE_STRING MAGIC)
// main.cpp
#include <iostream>
extern const char* Name;
int main () { std::cout << Name << "\n"; }
// default-name.cpp
const char* Name = "default";
Here's proof it works...
$ cmake -G Ninja -S . -B build
[1/7] cd /home/alex/test/build && /usr/bin/cmake -E echo "const char* Name = \"my_executable\";" > MyProj_my_executable.cpp
[2/7] /usr/bin/c++ -MD -MT CMakeFiles/excluded.dir/default-name.cpp.o -MF CMakeFiles/excluded.dir/default-name.cpp.o.d -o CMakeFiles/excluded.dir/default-name.cpp.o -c /home/alex/test/default-name.cpp
[3/7] /usr/bin/c++ -MD -MT CMakeFiles/my_executable.dir/MyProj_my_executable.cpp.o -MF CMakeFiles/my_executable.dir/MyProj_my_executable.cpp.o.d -o CMakeFiles/my_executable.dir/MyProj_my_executable.cpp.o -c /home/alex/test/build/MyProj_my_executable.cpp
[4/7] /usr/bin/c++ -MD -MT CMakeFiles/excluded.dir/main.cpp.o -MF CMakeFiles/excluded.dir/main.cpp.o.d -o CMakeFiles/excluded.dir/main.cpp.o -c /home/alex/test/main.cpp
[5/7] /usr/bin/c++ -MD -MT CMakeFiles/my_executable.dir/main.cpp.o -MF CMakeFiles/my_executable.dir/main.cpp.o.d -o CMakeFiles/my_executable.dir/main.cpp.o -c /home/alex/test/main.cpp
[6/7] : && /usr/bin/c++ CMakeFiles/my_executable.dir/main.cpp.o CMakeFiles/my_executable.dir/MyProj_my_executable.cpp.o -o my_executable && :
[7/7] : && /usr/bin/c++ CMakeFiles/excluded.dir/main.cpp.o CMakeFiles/excluded.dir/default-name.cpp.o -o excluded && :
$ ./build/my_executable
my_executable
$ ./build/excluded
default

How to pre-build a library in CMake

I have been learning CMake and am having persistent build issues with the library Catch2.
I am using Qt as my IDE and have downlaoded Qt6 and MinGW 8 via the Qt maintainance tool.
My issue seems to be that during compilation my test/main.cpp, which includes Catch2, is bloating to the point where the compiler errors.
14:57:22: Running steps for project CMakeTestbed...
14:57:22: Starting: "<dir>\CMake\bin\cmake.exe" --build . --target all
[0/1 ?/sec] Re-running CMake...
-- Could NOT find Vulkan (missing: Vulkan_INCLUDE_DIR)
-- Using the single-header code from <dir>/build-CMakeTestbed-Desktop_Qt_6_0_0_MinGW_64_bit-Debug/_deps/json-src/single_include/
-- Version: 7.1.3
-- Build type: Debug
-- CXX_STANDARD: 17
-- Required features: cxx_variadic_templates
-- Configuring done
-- Generating done
-- Build files have been written to: <dir>/build-CMakeTestbed-Desktop_Qt_6_0_0_MinGW_64_bit-Debug
[1/17 3.7/sec] Automatic MOC and UIC for target fmt
[2/15 5.6/sec] Automatic MOC and UIC for target Catch2WithMain
[3/14 6.8/sec] Automatic MOC and UIC for target CMakeTestbedLib
[4/12 3.6/sec] Automatic MOC and UIC for target CMakeTestbed
[5/10 4.5/sec] Automatic MOC and UIC for target Tests
[6/9 0.4/sec] Building CXX object _deps/catch2-build/CMakeFiles/Catch2WithMain.dir/src/catch_with_main.cpp.obj
FAILED: _deps/catch2-build/CMakeFiles/Catch2WithMain.dir/src/catch_with_main.cpp.obj
C:\Qt\Tools\mingw810_64\bin\g++.exe -I_deps/catch2-build -I_deps/catch2-src -I_deps/catch2-build/Catch2WithMain_autogen/include -I_deps/json-src/include -I_deps/fmt-src/include -I_deps/catch2-src/single_include -g -std=gnu++17 -MD -MT _deps/catch2-build/CMakeFiles/Catch2WithMain.dir/src/catch_with_main.cpp.obj -MF _deps\catch2-build\CMakeFiles\Catch2WithMain.dir\src\catch_with_main.cpp.obj.d -o _deps/catch2-build/CMakeFiles/Catch2WithMain.dir/src/catch_with_main.cpp.obj -c _deps/catch2-src/src/catch_with_main.cpp
C:/Qt/Tools/mingw810_64/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/bin/as.exe: _deps/catch2-build/CMakeFiles/Catch2WithMain.dir/src/catch_with_main.cpp.obj: too many sections (32805)
<dir>\AppData\Local\Temp\ccE30B5r.s: Assembler messages:
<dir>\AppData\Local\Temp\ccE30B5r.s: Fatal error: can't write 233 bytes to section .text of _deps/catch2-build/CMakeFiles/Catch2WithMain.dir/src/catch_with_main.cpp.obj: 'File too big'
C:/Qt/Tools/mingw810_64/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/bin/as.exe: _deps/catch2-build/CMakeFiles/Catch2WithMain.dir/src/catch_with_main.cpp.obj: too many sections (32805)
<dir>\AppData\Local\Temp\ccE30B5r.s: Fatal error: can't close _deps/catch2-build/CMakeFiles/Catch2WithMain.dir/src/catch_with_main.cpp.obj: File too big
[7/9 0.4/sec] Building CXX object test/CMakeFiles/Tests.dir/main.cpp.obj
FAILED: test/CMakeFiles/Tests.dir/main.cpp.obj
C:\Qt\Tools\mingw810_64\bin\g++.exe -DFMT_LOCALE -DJSON_USE_IMPLICIT_CONVERSIONS=1 -Itest -I"<dir>/CMakeTestbed/test" -Itest/Tests_autogen/include -I_deps/json-src/include -I_deps/fmt-src/include -I_deps/catch2-src/single_include -I"<dir>/CMakeTestbed/test/../libs" -I"<dir>/CMakeTestbed/test/../src" -I_deps/json-src/single_include -g -std=gnu++17 -MD -MT test/CMakeFiles/Tests.dir/main.cpp.obj -MF test\CMakeFiles\Tests.dir\main.cpp.obj.d -o test/CMakeFiles/Tests.dir/main.cpp.obj -c "<dir>/CMakeTestbed/test/main.cpp"
C:/Qt/Tools/mingw810_64/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/bin/as.exe: test/CMakeFiles/Tests.dir/main.cpp.obj: too many sections (32805)
<dir>\AppData\Local\Temp\ccARyb79.s: Assembler messages:
<dir>\AppData\Local\Temp\ccARyb79.s: Fatal error: can't write 233 bytes to section .text of test/CMakeFiles/Tests.dir/main.cpp.obj: 'File too big'
C:/Qt/Tools/mingw810_64/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/bin/as.exe: test/CMakeFiles/Tests.dir/main.cpp.obj: too many sections (32805)
<dir>\AppData\Local\Temp\ccARyb79.s: Fatal error: can't close test/CMakeFiles/Tests.dir/main.cpp.obj: File too big
ninja: build stopped: subcommand failed.
14:57:49: The process "C:\Program Files\CMake\bin\cmake.exe" exited with code 1.
Error while building/deploying project CMakeTestbed (kit: Desktop Qt 6.0.0 MinGW 64-bit)
When executing step "Build"
These issues only occur in debug builds, so I believe the combination of heavy templating, along with the debug symbols is hitting some MinGW compiler threshold...
I am trying something like
target_compile_options(Tests
-Wa
-mbig-obj
)
(Doesn't seem to actually be valid CMake, tried without the '-' too, regardless, this doesn't fix the problem, just hides it...)
But it seems to me the best option would be to make the library precompile in release (I don't need to debug the Catch2 internals, if I am debugging, it is because I have a broken test that I am investigating, and I shouldn't need the dirty details of the lib internals to do that right?)
I have a minimal working example here https://github.com/SebastianTroy/CMakeTestbed.
The main files of interest here would be:
catch2.cmake (from https://github.com/catchorg/Catch2/blob/v2.x/docs/cmake-integration.md#cmake-target)
test/CMakeLists.txt (includes Catch2)
test/main.cpp (the unit that is failing to compile)
I would really appreciate some help with either forcing CMake to precompile the library with higher optimisation flags than the target (to prevent the issue altogether) I would settle for help telling the compiler to ignore the overly large file (to just sidestep the issue)

Set GCC path in makefile

Whenever I am building my package it uses /usr/bin/g++ (system compiler).
I want to build my package with C++11 constructs. I have tried -std=c++11 option but with system compiler it says unrecognized option.
I want to build my package from a different gcc compiler which will get downloaded as part of my package dependency.
So, how can I specify the location of gcc compiler in Makefile?
There are multiple ways to achieve what you are looking for:
Setting the environment variable CXX just for the process that will run make:
$ CXX=/path-to-your-compiler/g++ make
Exporting the environment variable CXX in your shell:
$ CXX=/path-to-your-compiler/g++
$ export CXX
$ make
Setting CXX at make's command-line:
$ make CXX=/path-to-your-compiler/g++
Inside your makefile:
CXX := /path-to-your-compiler/g++
Note that setting the variable at make's command line overrides the other values, and variables set inside the makefile override the ones obtained from the environment (unless the command-line option -e or --environment-overrides is provided).
Inside your makefile, you can still override any value set by other means by using the override directive:
override CXX := /path-to-your-compiler/g++

Cmake doesn't honour -D CMAKE_CXX_COMPILER=g++

I'm trying to force cmake to build my cpp code with g++, as by default it uses clang instead. So I use: cmake -D CMAKE_CXX_COMPILER=g++ ../src/CMakeLists.txt after which cmake checks for gcc and g++ (with success), but nonetheless make VERBOSE=1 yields
/usr/bin/c++ -o CMakeFiles/trial_cpp.dir/trial.cpp.o -c "/Users/Kuba/Code/Sketchpad/Trial project/src/trial.cpp"
Linking CXX executable trial_cpp
/opt/etlocal/bin/cmake -E cmake_link_script CMakeFiles/trial_cpp.dir/link.txt --verbose=1
/usr/bin/c++ -Wl,-search_paths_first -Wl,-headerpad_max_install_names CMakeFiles/trial_cpp.dir/trial.cpp.o -o trial_cpp
As it calls /usr/bin/c++ not /usr/bin/g++ I concur it still uses clang. Any idea what's the problem? I know I have g++ and it's in /usr/bin/.
I'm running Mac OS X 10.8.2
CMAKE_CXX_COMPILER can only be set the first time cmake is run in a given build directory. On subsequent runs it is ignored. In order to change CMAKE_CXX_COMPILER you first need to delete the contents of the build directory and then run cmake again with that option.
Source: http://www.cmake.org/Wiki/CMake_Useful_Variables
I believe the reasoning for only using that variable on the first run is because changing it later would potentially invalidate everything already built including the configuration checks so cmake would have to start from scratch anyway.
I do it like this instead:
CXX=/usr/bin/g++ cmake ../src/CMakeLists.txt