CMake: Building a object file from a source in a object library in a sub directory out of source - cmake

Consider the following mock project with these files:
# CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(myproject)
add_subdirectory(sub)
# sub/CMakeLists.txt
add_library(myLib OBJECT bar.c)
add_executable(foo foo.c)
target_link_libraries(foo PRIVATE myLib)
# sub/foo.c
int main() {return 0;}
# sub/bar.c
void bar(){}
When building in source after cmake -G Unix\ Makefiles -B . -S . I can go to the the directory sub and execute make bar.o. How can I achieve the same results when building out of source? I've tried these commands from the project root:
cmake -G Unix\ Makefiles -B test-build -S .
cmake --build test-build --target sub/bar.o
cmake --build test-build --target bar.o
cmake --build test-build --target bar
I get these kinds of error messages gmake: *** No rule to make target 'sub/bar.o'. Stop.
I know it's possible to build the library, but in reality our library is quite large and I would like to focus on one file at the time.

Related

How to copy linked and mingw libs to output dir of executable?

I'm new in CMake.
Could someone please describe to me what I should write in my CMakeLists.txt to copy dependencies to the output directory of the executable file?
So, I have a CMake project:
# top-level CMakeLists.txt
cmake_minimum_required(VERSION 3.23)
project(CoolLib VERSION 0.0.1)
set(CMAKE_CXX_STANDARD 23)
add_subdirectory(mylib)
add_subdirectory(mytestconsole)
install(TARGETS mnconsole mnlib
RUNTIME DESTINATION bin COMPONENT Runtime
LIBRARY DESTINATION lib COMPONENT Runtime
ARCHIVE DESTINATION lib/myproject COMPONENT Development)
# mylib/CMakeLists.txt
cmake_minimum_required(VERSION 3.23)
project(mylib VERSION 0.0.1)
add_library(mylib SHARED mylib.cpp mylib.h)
target_include_directories(mylib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
# mytestconsole/CMakeLists.txt
cmake_minimum_required(VERSION 3.23)
project(mytestconsole VERSION 0.0.1)
add_executable(mytestconsole main.cpp)
target_link_libraries(mytestconsole LINK_PUBLIC mylib)
target_include_directories(mytestconsole PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/mylib)
As you can see, I've added an install() command, but this isn't exactly what I want.
As far as I understand, the install() command will copy my program to the Program Files directory in Windows.
GOAL:
I want to copy the mingw libraries (e.g. libgcc_s_seh-1, libstdc++-6, libwinpthread-1, etc.) and mylib to the output directory of the mytestconsole.exe file.
PS: I saw this answer, but GetPrerequesites is deprecated. And I didn't understand how to use it.
I tried to do this:
add_custom_command(
TARGET mnconsole POST_BUILD
COMMAND LIST_PREREQUISITES($<TARGET_FILE:mnconsole>)
VERBATIM)
But got an error:
D:\Programs\JetBrains\CLion\bin\cmake\win\bin\cmake.exe --build D:\Projects\MethaneNumber\_builds\debug --target mnconsole -j 9
[1/1] Linking CXX executable mnconsole\mnconsole.exe
FAILED: mnconsole/mnconsole.exe
cmd.exe /C "cd . && D:\Programs\mingw64\12.2.0\x64\posix-seh\bin\c++.exe -g mnconsole/CMakeFiles/mnconsole.dir/main.cpp.obj -o mnconsole\mnconsole.exe -Wl,--out-implib,mnconsole\libmnconsole.dll.a -Wl,--major-image-version,0,--minor-image-version,0 mnlib/libmnlib.a -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 && cmd.exe /C "cd /D D:\Projects\MethaneNumber\_builds\debug\mnconsole && LIST_PREREQUISITES ( D:/Projects/MethaneNumber/_builds/debug/mnconsole/mnconsole.exe )""
"LIST_PREREQUISITES" is ­not recognized as an internal command.
ninja: build stopped: subcommand failed.
PS2: It should work like windeployqt.exe with Qt, but I'm not using Qt in this project right now.

Using GLSLC's depfile to make included files automatically trigger recompile of SPIRV in CMake

I'm trying to create a cmake function that automatically recompiles glsl to spirv upon changes to the shader files. Right now direct dependencies work, ie the shaders I use as compile arguments. However I make heavy use of #include feature that glslc provides, and by default I can't get changes in that stuff to trigger recompile. I made sure that I'm using the Ninja
Right now I have the following CMake function and arguments:
cmake -DCMAKE_BUILD_TYPE=Debug "-DCMAKE_MAKE_PROGRAM=JETBRAINSPATH/bin/ninja/win/ninja.exe" -G Ninja "PATH_TO_CURRENT_DIRECTORY"
function
set(GLSLC "$ENV{VULKAN_SDK}/Bin/glslc")
function(target_shader_function SHADER_TARGET)
foreach (SHADER_SOURCE_FILEPATH ${ARGN})
get_filename_component(SHADER_SOURCE_FILENAME ${SHADER_SOURCE_FILEPATH} NAME)
get_filename_component(SHADER_SOURCE_DIRECTORY ${SHADER_SOURCE_FILEPATH} DIRECTORY)
set(SHADER_TARGET_NAME "${SHADER_TARGET}_${SHADER_SOURCE_FILENAME}")
set(SHADER_BINARY_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/spirv")
set(SHADER_FINAL_BINARY_FILEPATH "${SHADER_BINARY_DIRECTORY}/${SHADER_SOURCE_FILENAME}.spv")
#we can use depfiles instead
#https://stackoverflow.com/questions/60420700/cmake-invocation-of-glslc-with-respect-to-includes-dependencies
add_custom_command(
OUTPUT ${SHADER_FINAL_BINARY_FILEPATH}
DEPENDS ${SHADER_SOURCE_FILEPATH}
DEPFILE ${SHADER_SOURCE_FILEPATH}.d
COMMAND ${CMAKE_COMMAND} -E make_directory ${SHADER_BINARY_DIRECTORY}
COMMAND ${GLSLC} -MD -MF ${SHADER_SOURCE_FILEPATH}.d -O ${SHADER_SOURCE_FILEPATH} -o ${SHADER_FINAL_BINARY_FILEPATH} --target-env=vulkan1.2 -I ${CMAKE_SOURCE_DIR}/shaderutils
DEPENDS ${SHADER_SOURCE_FILEPATH}
# BYPRODUCTS ${SHADER_FINAL_BINARY_FILEPATH} ${SHADER_SOURCE_FILEPATH}.d causes ninja to no longer work
COMMENT "Compiling SPIRV for \nsource: \n\t${SHADER_SOURCE_FILEPATH} \nbinary: \n\t${SHADER_FINAL_BINARY_FILEPATH} \n"
)
add_custom_target(${SHADER_TARGET_NAME} DEPENDS ${SHADER_FINAL_BINARY_FILEPATH} ${SHADER_SOURCE_FILEPATH}.d)
add_dependencies(${SHADER_TARGET} ${SHADER_TARGET_NAME})
endforeach (SHADER_SOURCE_FILEPATH)
endfunction()
and I use it like this:
cmake_minimum_required(VERSION 3.21)
cmake_policy(SET CMP0116 NEW)
project(my_workspace)
add_executable(my_target main.cpp)
...
target_shader_function(my_target
${CMAKE_CURRENT_SOURCE_DIR}/shaders/example.comp
)
main.cpp
#include <iostream>
int main(){
std::cout << "hello world!" << std::endl;
return 0;
}
Again, everything works fine if I change, for example, example.comp.
However, lets say I have the following shader (lets say that this is example.comp):
#version 460
#include "fooutils.glsl"
#define WORKGROUP_SIZE 1024
layout (local_size_x = WORKGROUP_SIZE, local_size_y = 1, local_size_z = 1) in;
layout(set = 0, binding = 0) buffer MyBufferBlock{
float data[];
}
void main(){
uint tidx = gl_GlobalInvocationID.x;
data[tidx] += foo(tidx);
}
and I include the following:
#ifndef FOOUTILS_GLSL
#define FOOUTILS_GLSL
float foo(uint tidx){
return mod(tidx, 4.51);
}
#endif //FOOUTILS_GLSL
and I change fooutils.glsl after everything is compiled once (for example in a way that stops it from compiling),
#ifndef FOOUTILS_GLSL
#define FOOUTILS_GLSL
float foo(uint tidx){
return x;
return mod(tidx, 4.51);
}
#endif //FOOUTILS_GLSL
I don't get a recompile triggered. I had assumed that ninja would use this info to accomplish this, but I haven't seen it happen.
How do I use this depfile to force a recompile when an include dependency changes?
Here's my working implementation. But first, here's my terminal output so you can see it's working:
$ tree
.
├── CMakeLists.txt
├── main.cpp
├── shaders
│   └── example.comp
└── shaderutils
└── fooutils.glsl
$ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
...
$ cmake --build build/
[1/3] Compiling SPIRV: shaders/example.comp -> spirv/example.spv
[2/3] Building CXX object CMakeFiles/my_target.dir/main.cpp.o
[3/3] Linking CXX executable my_target
$ cmake --build build/
ninja: no work to do.
$ touch shaderutils/fooutils.glsl
$ cmake --build build/
[1/1] Compiling SPIRV: shaders/example.comp -> spirv/example.spv
$ cat build/spirv/example.d
spirv/example.spv: /path/to/shaders/example.comp /path/to/shaderutils/fooutils.glsl
$ cat build/CMakeFiles/d/*.d
spirv/example.spv: \
../shaders/example.comp \
../shaderutils/fooutils.glsl
Now on to the implementation
cmake_minimum_required(VERSION 3.22)
project(test)
function(target_shader_function TARGET)
find_package(Vulkan REQUIRED)
if (NOT TARGET Vulkan::glslc)
message(FATAL_ERROR "Could not find glslc")
endif ()
foreach (source IN LISTS ARGN)
cmake_path(ABSOLUTE_PATH source OUTPUT_VARIABLE source_abs)
cmake_path(GET source STEM basename)
set(depfile "spirv/${basename}.d")
set(output "spirv/${basename}.spv")
set(dirs "$<TARGET_PROPERTY:${TARGET},INCLUDE_DIRECTORIES>")
set(include_flags "$<$<BOOL:${dirs}>:-I$<JOIN:${dirs},;-I>>")
add_custom_command(
OUTPUT "${output}"
COMMAND "${CMAKE_COMMAND}" -E make_directory spirv
COMMAND Vulkan::glslc -MD -MF "${depfile}" -O "${source_abs}"
-o "${output}" --target-env=vulkan1.2 "${include_flags}"
DEPENDS "${source_abs}"
BYPRODUCTS "${depfile}"
COMMENT "Compiling SPIRV: ${source} -> ${output}"
DEPFILE "${depfile}"
VERBATIM
COMMAND_EXPAND_LISTS
)
set(shader_target "${TARGET}_${basename}")
add_custom_target("${shader_target}"
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${output}")
add_dependencies("${TARGET}" "${shader_target}")
endforeach ()
endfunction()
add_executable(my_target main.cpp)
target_shader_function(my_target shaders/example.comp)
target_include_directories(
my_target PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/shaderutils")
With a CMake minimum version of 3.20 or greater, CMP0116 will be set, which adjusts depfiles that were generated with relative paths to be relative to the top-level binary directory. You can see this in action in the last two command outputs.
For compatibility with this policy, the command to invoke glslc is careful to use only absolute paths or paths relative to ${CMAKE_CURRENT_BINARY_DIR}.
To increase the reusability of this function, I had it reuse the include paths from the TARGET rather than hard-coding shaderutils.
Also remember to always pass absolute paths to the DEPENDS arguments of add_custom_{command,target} to avoid surprising path resolution behaviors.
Finally, since CMake actually comes with a FindVulkan module that can locate glslc, we use that to get the Vulkan::glslc target. Per the documentation, it can be overridden by setting Vulkan_GLSLC_EXECUTABLE.
Terminal logs for VS2022 on Windows with MSVC:
> cmake -S . -B build
...
> cmake --build build --config Release
Checking Build System
Compiling SPIRV: shaders/example.comp -> spirv/example.spv
Building Custom Rule D:/test/CMakeLists.txt
Building Custom Rule D:/test/CMakeLists.txt
main.cpp
my_target.vcxproj -> D:\test\build\Release\my_target.exe
Building Custom Rule D:/test/CMakeLists.txt
> cmake --build build --config Release -- -noLogo
my_target.vcxproj -> D:\test\build\Release\my_target.exe
> notepad shaderutils\fooutils.glsl
> cmake --build build --config Release -- -noLogo
Compiling SPIRV: shaders/example.comp -> spirv/example.spv
my_target.vcxproj -> D:\test\build\Release\my_target.exe
> cmake --build build --config Release -- -noLogo
my_target.vcxproj -> D:\test\build\Release\my_target.exe
and again with Ninja instead of msbuild:
> cmake -G Ninja -S . -B build -DCMAKE_BUILD_TYPE=Release ^
-DVulkan_ROOT=C:/VulkanSDK/1.2.198.1
...
> powershell "cmake --build build | tee output.txt"
[1/3] Compiling SPIRV: shaders/example.comp -> spirv/example.spv
[2/3] Building CXX object CMakeFiles\my_target.dir\main.cpp.obj
[3/3] Linking CXX executable my_target.exe
> powershell "cmake --build build | tee output.txt"
ninja: no work to do.
> notepad shaderutils\fooutils.glsl
> powershell "cmake --build build | tee output.txt"
[1/1] Compiling SPIRV: shaders/example.comp -> spirv/example.spv
The little powershell + tee trick is just to keep the Ninja command log from overwriting itself. I could use --verbose, but then the full command lines would be printed, rather than the tidy summaries.

How to use CMake to build sources with ghdl as custom target?

I would like to build VHDL sources with CMake. For this I have a directory structure:
<root>
- CMakeLists.txt
- src/CMakeLists.txt
- src/mymodule.vhdl
- build
Inside the root CMakeLists.txt I have:
cmake_minimum_required(VERSION 3.12)
project(vhdlsdb LANGUAGES NONE)
add_subdirectory(src)
In the src/CMakeLists.txt I have:
set(filelist mymodule.vhdl )
add_custom_target(vhdlize COMMAND ghdl -a ${filelist})
However, when I run cd build && cmake .. -GNinja && cmake --build . --target vhdlize I get:
error: cannot open mymodule
C:\Users\pm\git\GHDL\0.36-mingw64-llvm\bin\ghdl.exe: compilation error
How can I tell CMake to copy or look for the sources in the src folder?

CMAKE build gui application with MinGW

I want to build gui application with SDL2. I link SDL2 libraries(libSDL2.dll.a and libSDL2main.a) but I don't know how a can apply -mwindows flag to my application. Without him .exe file of my application doesn't show window (executing have not any effect). I use MinGW-w64 my OS is Windows 10. In command line I can do this like here (see section B). How I can apply this flag with usage cmake? Console application works fine.
I try next variant but it doesn't work.
cmake -G "MinGW Makefiles" -D CMAKE_C_COMPILER=gcc -D CMAKE_CXX_COMPILER=g++ -D CMAKE_EXE_LINKER_FLAGS="-mwindows"
cmake -G "MinGW Makefiles" -D CMAKE_C_COMPILER=gcc -D CMAKE_CXX_COMPILER=g++ -D CMAKE_CXX_FLAGS="-mwindows"
Also in CMakeLists.txt I try do like this
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mwindows")
You can pass WIN32 argument to add_executable and CMake will do this for you:
add_executable(target_name WIN32 ${sources})
I basically copy-pasted the stuff, which CLion executes in the command line, and it worked:
$ cmake DCMAKE_BUILD_TYPE=Debug -G "CodeBlocks - MinGW Makefiles" ../
$ cmake --build ./ --target target_name -j 6
[ 50%] Building CXX object CMakeFiles/target_name.dir/main.cpp.obj
[100%] Linking CXX executable target_name.exe
[100%] Built target tree_traverse
After this the executable target_name.exe appeared in the directory.

How to instruct CMake to use the build architecture compiler

When using CMake for cross compiling, one generally specifies a toolchain file via the CMAKE_TOOLCHAIN_FILE option. In GNU terminology, one can specify the host architecture toolset using this file. However, one can generally not expect to be able to execute anything built with this toolchain. So often enough, some build tools need to be compiled for the build architecture.
Consider the following setup. I have two source files genfoo.c and bar.c. During build, genfoo.c needs to be compiled and run. Its output needs to be written to foo.h. Then I can compile bar.c, which #include "foo.h". Since CMake defaults to using the host architecture toolchain, the instructions for bar.c are easy. But how do I tell it to use the build architecture toolchain for compiling genfoo.c? Simply saying add_executable(genfoo genfoo.c) will result in using the wrong compiler.
CMake can only handle one compiler at a time. So - if you don't go the long way to set up the other compiler as a new language - you will end up with two configuration cycles.
I see the following approaches to automate this process:
Taking the example "CMake Cross Compiling - Using executables in the build created during the build?" from the CMake pages as a starting point I'll get:
CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(FooBarTest)
# When crosscompiling import the executable targets
if (CMAKE_CROSSCOMPILING)
set(IMPORT_PATH "IMPORTFILE-NOTFOUND" CACHE FILEPATH "Point it to the export file path from a native build")
file(TO_CMAKE_PATH "${IMPORT_PATH}" IMPORT_PATH_CMAKE)
include(${IMPORT_PATH_CMAKE}/genfooTargets.cmake)
# Then use the target name as COMMAND, CMake >= 2.6 knows how to handle this
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/foo.h
COMMAND genfoo
)
add_executable(bar bar.cpp ${CMAKE_CURRENT_BINARY_DIR}/foo.h)
target_include_directories(bar PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
endif()
# Only build the generator if not crosscompiling
if (NOT CMAKE_CROSSCOMPILING)
add_executable(genfoo genfoo.cpp)
export(TARGETS genfoo FILE "${CMAKE_CURRENT_BINARY_DIR}/genfooTargets.cmake")
endif()
Then using a script like:
build.sh
#!/bin/bash
if [ ! -d hostBuild ]; then
cmake -E make_directory hostBuild
cmake -E chdir hostBuild cmake ..
fi
cmake --build hostBuild
if [ ! -d crossBuild ]; then
cmake -E make_directory crossBuild
cmake -E chdir crossBuild cmake .. -DIMPORT_PATH=${PWD}/hostBuild -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake
fi
cmake --build crossBuild
I'll get the desired results by calling ./build.sh.
Splitting the CMakeLists.txt and maybe even replace the export()/include() with something where I know the output path of my build tools e.g. by using CMAKE_RUNTIME_OUTPUT_DIRECTORY would simplify things:
CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(FooBarTest)
# Then use the target name as COMMAND. CMake >= 2.6 knows how to handle this
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/foo.h
COMMAND genfoo
)
add_executable(bar bar.cpp ${CMAKE_CURRENT_BINARY_DIR}/foo.h)
target_include_directories(bar PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
buildTools/CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(BuildTools)
add_executable(genfoo genfoo.cpp)
build.sh
#!/bin/bash
if [ ! -d crossBuild ]; then
cmake -E make_directory crossBuild
cmake -E chdir crossBuild cmake .. -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake
fi
if [ ! -d hostBuild ]; then
cmake -E make_directory hostBuild
cmake -E chdir hostBuild cmake ../buildTools -DCMAKE_RUNTIME_OUTPUT_DIRECTORY:PATH=${PWD}/crossBuild
fi
cmake --build hostBuild
cmake --build crossBuild
References
Making a CMake library accessible by other CMake packages automatically
CMake build multiple targets in different build directories
How do I make CMake output into a 'bin' dir?
It is possible to do that completely within CMake.
The trick is to run a separate CMake configuring stage within its own space, silently dismissing every crosscompiling setting and using the host's default toolchain, then import the generated outputs into it's parent, crosscompiling build.
First part:
set(host_tools_list wxrc generate_foo)
if(CMAKE_CROSSCOMPILING)
# Pawn off the creation of the host utilities into its own dedicated space
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/host_tools)
file(TO_NATIVE_PATH ${CMAKE_COMMAND} native_cmake_command)
file(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR} native_cmake_current_source_dir)
execute_process(
COMMAND "${native_cmake_command}" "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" "${native_cmake_current_source_dir}"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/host_tools
)
add_custom_target(host_tools
COMMAND ${CMAKE_COMMAND} --build . --target host_tools --config $<CONFIG>
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/host_tools
)
include(${CMAKE_CURRENT_BINARY_DIR}/host_tools/host_tools.cmake)
foreach(tgt IN ITEMS ${host_tools_list})
add_dependencies(host${tgt} host_tools)
endforeach()
else()
# Add an empty target, host tools are built inplace
add_custom_target(host_tools
DEPENDS ${host_tools_list}
)
endif()
... then you add the usual add_executable and whatever ...
At the end:
if(NOT CMAKE_CROSSCOMPILING)
foreach(tgt IN ITEMS ${host_tools_list})
add_executable(host${tgt} ALIAS ${tgt})
endforeach()
export(TARGETS ${host_tools_list} NAMESPACE host FILE host_tools.cmake)
endif()
When it crosscompiles, it pawns off the creation of the host-run tools into its own dedicated space, and imports the targets as "hostwxrc" and "hostgenerate_foo", with a dependency on generating the host_tools themselves .
When it doesn't crosscompile, it builds wxrc and generate_foo as-is, and aliases them to hostwxrc and hostgenerate_foo.
After this, when you use $<TARGET_FILE:wxrc>, you refer to the wxrc built for the target platform, and $<TARGET_FILE:hostwxrc> refers to the wxrc built for the host platform, regardless whether they are the same or not.