As this is my first attempt in writing a proper cmake file I think I need some hints as to where I misunderstood things.
Reading this: https://cmake.org/cmake/help/latest/command/add_custom_command.html
I thought I could create my icon.c file from image (bmp) files like so:
add_custom_command(
OUTPUT icons.c
COMMAND ${PBRES} -c icons.c -4 "*/*.bmp"
)
target_sources(iconLib
PRIVATE
icons.c
)
Unfortunately I get
CMake Error at CMakeLists.txt:10 (add_library):
Cannot find source file:
/project/icons/icons.c
CMake Error at CMakeLists.txt:10 (add_library):
No SOURCES given to target: iconLib
The CMakeLists.txt above is in a subdirectory "icons" and gets invoked by its parent which looks like this:
cmake_minimum_required(VERSION 3.21)
set(PBSDK /SDK_6.3.0/SDK-B288)
# set the project name
project(SGTPuzzles)
add_library(gamesLib "")
add_subdirectory(games)
add_library(iconLib "")
add_subdirectory(icons)
Update
I moved the custom command into the main CMakeLists.txt like so:
add_custom_command(
OUTPUT icons/icons.c
COMMAND ${PBRES} -c icons/icons.c -4 "icons/*/*.bmp"
)
and this seems to work.
Related
For my project, I need to build and include another library, https://github.com/t-dillon/tdoku/, as my dependency.
Toku is also built using CMake, and there's a build script I can run by doing:
$ cd lib/toku
$ ./BUILD.sh
This puts the library file in ./lib/tdoku/build/libtdoku.a.
I'm trying to build the Toku library inside CMake, and use it as a dependency. How can I do this?
I tried using add_custom_target, add_custom_command, add_dependencies, add_subdirectory to no avail.
My current CMakeLists.txt looks like:
cmake_minimum_required(VERSION 3.17)
project(project_1_cpp)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wconversion")
set(CMAKE_CXX_FLAGS_DEBUG_INIT "")
set(CMAKE_CXX_FLAGS_RELEASE_INIT "-O3")
add_custom_target(toku)
add_custom_command(
TARGET toku
COMMAND ${CMAKE_SOURCE_DIR}/lib/tdoku/BUILD.sh
WORKING_DIR ${CMAKE_SOURCE_DIR}/lib/tdoku
)
file(GLOB project_1_src "*.cpp")
add_executable(project_1_cpp ${project_1_src})
add_dependencies(tdoku project_1_cpp)
target_link_libraries(project_1_cpp ${CMAKE_SOURCE_DIR}/lib/tdoku/build/libtdoku.a)
From some help of #Tsyvarev, I first changed my CMakeLists.txt to include:
add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/lib/tdoku/build/libtdoku.a
COMMAND ${CMAKE_SOURCE_DIR}/lib/tdoku/BUILD.sh tdoku
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/lib/tdoku
)
add_custom_target(project DEPENDS ${CMAKE_SOURCE_DIR}/lib/tdoku/build/libtdoku.a)
The key is that WORKING_DIR does not work, and I need to instead have WORKING_DIRECTORY. I figured this part out by trying to modify the build.sh script, and having it print things to a file, so I can know if it's being run at all, and what's breaking inside the script. I realized that the script's working directory was wrong.
In fact, I believe I did all this before posting the question, but I got the following errors after, which lead me to think that the tdoku library wasn't compiling properly. It was, but it was linking incorrectly to my project:
/usr/bin/ld: /usr/bin/ld: DWARF error: could not find variable specification at offset 223f
/usr/bin/ld: DWARF error: could not find variable specification at offset 22ba
I googled "DWARF error: could not find variable specification", which led me to ld.exe: DWARF error: could not find variable specification at offset 101cee. Googling cmake flto led me to How do I enable link time optimization (LTO) with CMake?.
This finally allowed me to compile my project.
The entire list of changes include:
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/lib/tdoku/build/libtdoku.a
COMMAND ${CMAKE_SOURCE_DIR}/lib/tdoku/BUILD.sh tdoku
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/lib/tdoku
)
add_custom_target(project DEPENDS ${CMAKE_SOURCE_DIR}/lib/tdoku/build/libtdoku.a)
Suppose my project's CMakeLists.txt includes foo.cmake:
include(foo)
In foo.cmake, i want to know the path of foo.cmake.
How can I do that?
Note that CMAKE_CURRENT_LIST_DIR gives the directory of the including CMakeLists.txt, not that of the included foo.cmake, and is thus not what I want.
Of course, foo.cmake might be included by several projects (i.e., by several CMakeLists.txt files).
People have reported seemingly contradictory facts about how CMAKE_CURRENT_LIST_DIR behaves. Now I know the reason for the confusion:
First, in my Linux environment:
$ cd /path/to/home
$ mkdir cmake-test
$ cd cmake-test
$ mkdir source
$ mkdir source/subdirectory
$ mkdir build
I create these two files:
$ cat source/CMakeLists.txt
include(subdirectory/foo.cmake)
$ cat source/subdirectory/foo.cmake
message("CMAKE_CURRENT_LIST_DIR is ${CMAKE_CURRENT_LIST_DIR}")
CMake works as reported by Fraser and Robert Dailey:
$ cd build
$ cmake ../source
CMAKE_CURRENT_LIST_DIR is /path/to/home/cmake-test/source/subdirectory
[...]
However, I add a function to foo.cmake, which I call from CMakeLists.txt:
$ cat ../source/subdirectory/foo.cmake
message("CMAKE_CURRENT_LIST_DIR is ${CMAKE_CURRENT_LIST_DIR}")
function(bar)
message("CMAKE_CURRENT_LIST_DIR in bar() is ${CMAKE_CURRENT_LIST_DIR}")
endfunction()
$ cat ../source/CMakeLists.txt
include(subdirectory/foo.cmake)
bar()
Then:
$ cmake ../source
CMAKE_CURRENT_LIST_DIR is /path/to/home/cmake-test/source/subdirectory
CMAKE_CURRENT_LIST_DIR in bar() is /path/to/home/cmake-test/source
[...]
So, the value of CMAKE_CURRENT_LIST_DIR in foo.cmake is not the same at the time foo.cmake is included and when bar() is called. This is according to the specification of CMAKE_CURRENT_LIST_DIR.
Here is one possible solution for accessing the directory of foo.cmake from within bar():
$ cat ../source/subdirectory/foo.cmake
set(DIR_OF_FOO_CMAKE ${CMAKE_CURRENT_LIST_DIR})
function(bar)
message("DIR_OF_FOO_CMAKE in bar() is ${DIR_OF_FOO_CMAKE}")
endfunction()
after which I get the behavior I was looking for:
$ cmake ../source
DIR_OF_FOO_CMAKE in bar() is /path/to/home/cmake-test/source/subdirectory
[...]
In CMake 3.17, you have a new variable available, called CMAKE_CURRENT_FUNCTION_LIST_DIR, which can be used inside a function. It is undefined outside of a function definition.
function(foo)
configure_file(
"${CMAKE_CURRENT_FUNCTION_LIST_DIR}/some.template.in"
some.output
)
endfunction()
Prior to CMake 3.17, CMAKE_CURRENT_FUNCTION_LIST_DIR functionality has to be approximated with CMAKE_CURRENT_LIST_DIR by the following workaround, taken from CMake documentation:
set(_THIS_MODULE_BASE_DIR "${CMAKE_CURRENT_LIST_DIR}")
function(foo)
configure_file(
"${_THIS_MODULE_BASE_DIR}/some.template.in"
some.output
)
endfunction()
See CMAKE_CURRENT_LIST_DIR:
Full directory of the listfile currently being processed.
As CMake processes the listfiles in your project this variable will
always be set to the directory where the listfile which is currently
being processed (CMAKE_CURRENT_LIST_FILE) is located. The value has
dynamic scope. When CMake starts processing commands in a source file
it sets this variable to the directory where this file is located.
When CMake finishes processing commands from the file it restores the
previous value. Therefore the value of the variable inside a macro or
function is the directory of the file invoking the bottom-most entry
on the call stack, not the directory of the file containing the macro
or function definition.
Example
I have the following structure:
C:\Work\cmake-test\CMakeLists.txt
C:\Work\cmake-test\subfolder\test.cmake
In my CMakeLists.txt:
include( subfolder/test.cmake )
In my test.cmake:
message( "Current dir: ${CMAKE_CURRENT_LIST_DIR}" )
The result I get when I run CMake from C:\Work\cmake-test is:
Current dir: C:/Work/cmake-test/subfolder
The include() command searches for modules in ${CMAKE_MODULE_PATH} first and then in CMake Modules dir.
So you can just check for file presence with if(EXISTS ${CMAKE_MODULE_PATH}/foo.cmake) and if(EXISTS ${CMAKE_ROOT}/Modules/foo.cmake).
I need to invoke cmake from within cmake so that I can have binaries built before project files are generated. I have the following CmakeLists.txt:
cmake_minimum_required(VERSION 3.2)
project(StarEngine)
set(CMAKE_CXX_FLAGS "-std=c++11")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
find_package(OpenGL REQUIRED)
include_directories(${OPENGL_INCLUDE_DIR})
#copy the other cmake file into where we'd like to invoke cmake
configure_file(deps-CMakeLists.txt deps/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} . WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/deps)
#eventually binaries will be built, for now this doesn't accomplish anything
execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/deps)
And its friend, deps-CmakeLists.txt, a test script:
set(GENERATED_DIR "test")
MESSAGE( STATUS ${GENERATED_DIR} )
In the following file structure:
Project
build
Code
CmakeLists.txt
deps-CmakeLists.txt
No matter what I put in for the variable value, it is blank when displayed in MESSAGE. I imagine this is weird behavior resulting from invoking cmake from cmake. I had a bunch of other strange errors to, but I suspect if I can figure out this one that will help crack them all.
Thanks to #Florian, the problem was with variable replacement and I needed to add the COPYONLY option to configure_file
I am trying to run gperf from a cmake file.
I created a very minimal CMakeLists.txt below.
When I run it by
$ cmake .
$ make
It does not create the example.hpp file
What could be problem with the below CMakeLists.txt?
cmake_minimum_required( VERSION 2.6 )
function(gperf_generate_new source target)
add_custom_target(${target} echo "Creating ${target}")
add_custom_command(
SOURCE ${source}
TARGET ${target}
COMMAND gperf -L c++ ${source} > ${target}
OUTPUTS ${target}
DEPENDS ${source}
)
endfunction()
gperf_generate_new(command_options.new.gperf example.hpp)
Files, produced by source-files generators(like gpref) are rarely needed as standalone. Instead, these source files are usually used for creating executables or libraries inside a project.
So, standard pattern of using source-file generators in the CMake looks like:
# Call add_custom_command() with appropriate arguments for generate output file
# Note, that *gperf* will work in the build tree,
# so for file in the source tree full path should be used.
function(gperf_generate_new input output)
add_custom_command(
OUTPUT ${output}
COMMAND gperf -L c++ ${input} > ${output}
DEPENDS ${input}
COMMENT "Generate ${output}" # Just for nice message during build
)
endfunction()
# Generate *example.hpp* file ...
gperf_generate_new(${CMAKE_CURRENT_SOURCE_DIR}/command_options.new.gperf example.hpp)
# ... for use it in executable
add_executable(my_program ${CMAKE_CURRENT_BINARY_DIR}/example.hpp <other sources>)
If you want only to test whether example.hpp is generating, instead of add_executable() use
add_custom_target(my_target
ALL # Force target to be built with default build target.
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/example.hpp
)
Note, that linkage between add_custom_command and add_custom_target is expressed using same filename in their OUTPUT and DEPENDS options correspondingly. With such link order of these commands is insignificant (but both commands should be called from the same CMakeLists.txt script).
I'd like to use CMake to generate obfuscated lua files for delivery. For the life of me I cannot get add_custom_command + add_custom_target to build these files for me. There's something I'm missing.
ADD_CUSTOM_TARGET(LUABIND_COMPILED_FILES ALL)
FOREACH(F ${LUA_SCRIPT_FILES})
ADD_CUSTOM_COMMAND(
OUTPUT ${LUA_COMPILED_SCRIPTS}/${F}
COMMAND ${LUAC} -o ${LUA_COMPILED_SCRIPTS}/${F}
COMMENT "Compiling ${F} to binary"
ADD_DEPENDENCIES(LUABIND_COMPILED_FILES ${LUA_COMPILED_SCRIPTS}/${F})
ENDFOREACH()
For some reason when I run cmake + make the output tells me there's nothing to be done for target LUABIND_COMPILED_FILES. Am I missing something here? Thanks in advance.
The ADD_DEPENDENCIES command can only be used to add dependencies between top-level targets. The ADD_CUSTOM_COMMAND command however generates output files, but does not add new targets.
To make a custom target depend on generated files, use the DEPENDS options of the add_custom_target command:
set (LUA_COMPILED_FILES "")
foreach(F ${LUA_SCRIPT_FILES})
add_custom_command(
OUTPUT "${LUA_COMPILED_SCRIPTS}/${F}"
COMMAND ${LUAC} -o "${LUA_COMPILED_SCRIPTS}/${F}"
COMMENT "Compiling ${F} to binary")
list (APPEND LUA_COMPILED_FILES "${LUA_COMPILED_SCRIPTS}/${F}")
endforeach()
add_custom_target(LUABIND ALL DEPENDS ${LUA_COMPILED_FILES})