CMake custom command to generate a non-config file - cmake

I've read:
cmake custom command to copy and rename
and I want to do something similar, but rather than copying a file, I want to generate a file. If that wasn't in a custom command, I would write:
file(WRITE "generated.c" "int main() { return 0; }")
but it doesn't seem like cmake -E supports this directly. What should I do (other than run something platform-dependent)?

Use CMake's script mode (-P). In CMakeLists.txt:
cmake_minimum_required(VERSION 3.21)
project(test)
file(
GENERATE
OUTPUT "gen.cmake"
CONTENT [[
cmake_minimum_required(VERSION 3.21)
file(WRITE "${OUT}" "int main() { return 0; }")
]])
add_custom_command(
OUTPUT generated.c
COMMAND ${CMAKE_COMMAND} -DOUT=generated.c
-P gen.cmake
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/gen.cmake"
)
add_executable(main "${CMAKE_CURRENT_BINARY_DIR}/generated.c")
Note that if the file you generate has configuration-dependent contents, then you must include $<CONFIG> somewhere in the file name. See the docs here for more detail: https://cmake.org/cmake/help/latest/command/file.html#generate

Related

CMake output from add_custom_command not found

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.

Random executable output with CMake

Can I have a random name for the executable file of each build?
Or, in another words, a different name for the executable of each build action?
I wonder if a random-variable could be inserted into the build-tool-chain.
The reason of such a name is that my company's virus-checking is quite slow -- it took a long long time checking each executable, even longer then the build.
I'm using CLion 2016.2 on Win7, tool-chain is MinGW_w64_5.0, bundled CMake 3.5.2
You could always define POST_BUILD steps that call another CMake script. The only downside in the following approach would be that you can't - since it's random - reuse the executable's output name in CMake itself:
CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(RandomExeName)
file(WRITE main.cpp "int main() { return 0; }")
add_executable(${PROJECT_NAME} main.cpp)
add_custom_command(
TARGET ${PROJECT_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -D _file:PATH="$<TARGET_FILE:${PROJECT_NAME}>"
-P ${CMAKE_SOURCE_DIR}/CopyToRandom.cmake
)
set_property(TARGET ${PROJECT_NAME} PROPERTY SUFFIX ".temp")
CopyToRandom.cmake
string(RANDOM _random)
file(GLOB _old_files RELATIVE "${CMAKE_BINARY_DIR}" "*.exe")
execute_process(
COMMAND "${CMAKE_COMMAND}" -E remove ${_old_files}
COMMAND "${CMAKE_COMMAND}" -E copy "${_file}" "${_random}.exe"
)
# generate shortcut
get_filename_component(_name "${_file}" NAME_WE)
file(
WRITE "${_name}.sh"
"#!/bin/bash\n"
"${_random}.exe"
)
No you can't. Or you have to reconfigure for every build.
Regarding your actual problem: Advice the virus checker to exclude your build directories.

cmake add_custom_command not working

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).

How to integrate such kind of source generator into CMake build chain?

I'm writing a C++ project which use a code generator (perl xsubpp). It generates C/C++ source code from XS file. As xsubpp sometimes produce incomplete output file, I want to have it run before the actual binary target is built, regardless there exists generated source file.
I can find out two ways to achieve it:
# the target is always out-of-date, so the command is always run
add_custom_target(...)
add_library(lib_name ...)
add_dependencies(lib_name ...)
and
add_library(lib_name ...)
# the command is always run before lib_name is build
add_custom_command(TARGET lib_name PRE_BUILD ...)
However, none of them works, because add_library() checks source file at configure time. The source file must either exist, or as an output target of add_custom_command().
For the first way, the add_custom_target() don't have the concept of output target; and for the second way, the add_custom_command() is used as an auxiliary of lib_name, which also don't have the concept of output target.
The following works for me. I hope that this is what you want.
The source (foo.cpp) is re-generated every time I run make.
src/C_generated/CMakeLists.txt:
add_custom_target(generate_foo
touch ${CMAKE_CURRENT_SOURCE_DIR}/script.sh
COMMENT "add_custom_target, touch script.sh"
)
ADD_CUSTOM_COMMAND(
TARGET generate_foo
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/script.sh
ARGS "some args"
COMMENT "custom commands, executing script.sh"
)
set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/foo.cpp PROPERTIES GENERATED 1)
add_library(LIBC ${CMAKE_CURRENT_BINARY_DIR}/foo.cpp)
ADD_DEPENDENCIES(LIBC generate_foo)
src/C_generated/script.sh:
#!/bin/bash
echo "Running script.sh"
echo "#include <stdio.h>" > foo.cpp
echo "/*" >> foo.cpp
date >> foo.cpp
echo "*/" >> foo.cpp
echo >> foo.cpp
echo "void testC()" >> foo.cpp
echo "{" >> foo.cpp
echo " printf(\"Generated source.\");" >> foo.cpp
echo "}" >> foo.cpp
Main CMakeLists.txt which combines generated source with non-generated source:
project(test)
cmake_minimum_required(VERSION 2.8)
INCLUDE_DIRECTORIES(src)
ADD_SUBDIRECTORY(src/A)
ADD_SUBDIRECTORY(src/B)
# Generated files only.
ADD_SUBDIRECTORY(src/C_generated)
# Combine the different libraries into one.
add_library(TESTLIB STATIC src/dummy.c)
ADD_DEPENDENCIES(TESTLIB LIBA)
ADD_DEPENDENCIES(TESTLIB LIBB)
ADD_DEPENDENCIES(TESTLIB LIBC)
GET_TARGET_PROPERTY(LIBA_LOC LIBA LOCATION)
GET_TARGET_PROPERTY(LIBB_LOC LIBB LOCATION)
GET_TARGET_PROPERTY(LIBC_LOC LIBC LOCATION)
SET_TARGET_PROPERTIES(TESTLIB PROPERTIES STATIC_LIBRARY_FLAGS "${LIBA_LOC} ${LIBB_LOC} ${LIBC_LOC}")
Download this example from:
https://dl.dropboxusercontent.com/u/68798379/cmake-code-generator.tar.bz2

How to make temp directory in CMake?

I need the CMake analog of mktemp command in linux. What macro provides this?
Was looking for this too to evaluate expressions as suggested in the CMake Wiki. Wrote some macros and an example for generating temp file names and executing them:
#!/usr/bin/cmake -P
macro(temp_name fname)
if(${ARGC} GREATER 1) # Have to escape ARGC to correctly compare
set(_base ${ARGV1})
else(${ARGC} GREATER 1)
set(_base ".cmake-tmp")
endif(${ARGC} GREATER 1)
set(_counter 0)
while(EXISTS "${_base}${_counter}")
math(EXPR _counter "${_counter} + 1")
endwhile(EXISTS "${_base}${_counter}")
set(${fname} "${_base}${_counter}")
endmacro(temp_name)
# Evaluate expression
# Suggestion from the Wiki: http://cmake.org/Wiki/CMake/Language_Syntax
# Unfortunately, no built-in stuff for this: http://public.kitware.com/Bug/view.php?id=4034
macro(eval expr)
temp_name(_fname)
file(WRITE ${_fname} "${expr}")
include(${_fname})
file(REMOVE ${_fname})
endmacro(eval)
# Examples
eval("message(\"Hai\")")
set(funcs a;b)
macro(test_a arg)
message("A: ${arg}")
endmacro(test_a)
macro(test_b arg)
message("B: ${arg}")
endmacro(test_b)
foreach(func ${funcs})
set(func_name test_${func})
eval("${func_name}(\"Test\")")
endforeach(func)
Output:
Hai
A: Test
B: Test
Note that in Linux you can set this script to executable and run it using cmake -P. Useful for testing stuff out.
There is no direct CMake analog of "mktemp".
From inside a CMake script or CMakeLists.txt file, your best bet is to use the
file(MAKE_DIRECTORY "/path/to/dir/name")
command, and give it a name of a directory that you know you have write access to. Help for the file command is found here: https://cmake.org/cmake/help/latest/command/file.html
You could also possibly simply use
$ENV{TMP}
if there is an environment variable that points you to a system-provided temp directory.
If you are invoking CMake directly, you could also use
cmake -E make_directory /path/to/dir/name
Finally, see also the execute_process command, which allows you to call arbitrary command line tools from within a cmake script or CMakeLists file and capture the output. That may prove useful if you have another tool that you can call that gives you mktemp functionality. https://cmake.org/cmake/help/latest/command/execute_process.html
I implemented the following macro:
#!/usr/bin/cmake -P
include(CMakeParseArguments)
function(MKTEMP)
set(options CREATE_FOLDER CREATE_FILE)
set(oneValueArgs PREFIX PARENT OUTPUT_VARIABLE)
cmake_parse_arguments(MKTEMP "${options}" "${oneValueArgs}" "" ${ARGN})
if(NOT DEFINED MKTEMP_CREATE_FOLDER)
set(MKTEMP_CREATE_FOLDER FALSE)
endif()
if(NOT DEFINED MKTEMP_CREATE_FILE)
set(MKTEMP_CREATE_FILE FALSE)
endif()
if(MKTEMP_CREATE_FOLDER AND MKTEMP_CREATE_FILE)
# Can not create folder and file with the same name
message(FATAL_ERROR "Both flags CREATE_FOLDER and CREATE_FILE are set")
endif()
if(NOT DEFINED MKTEMP_PREFIX)
set(MKTEMP_PREFIX "tmp")
endif()
if(NOT DEFINED MKTEMP_PARENT)
set(MKTEMP_PARENT "$ENV{TMP}")
endif()
set(_COUNTER 0)
while(EXISTS "${MKTEMP_PARENT}/${MKTEMP_PREFIX}${_COUNTER}")
math(EXPR _COUNTER "${_COUNTER} + 1")
endwhile()
set(_NAME "${MKTEMP_PARENT}/${MKTEMP_PREFIX}${_COUNTER}")
set(${MKTEMP_OUTPUT_VARIABLE} "${_NAME}" PARENT_SCOPE)
if(MKTEMP_CREATE_FOLDER)
file(MAKE_DIRECTORY "${_NAME}")
elseif(MKTEMP_CREATE_FILE)
file(WRITE "${_NAME}" "")
endif()
endfunction()
Usage:
# only generate name - with default prefix ("tmp")
MKTEMP(OUTPUT_VARIABLE TMPONLYNAME)
message("TMPONLYNAME is ${TMPONLYNAME}")
# only generate name - with custom prefix ("myapp")
MKTEMP(PREFIX "myapp" OUTPUT_VARIABLE TMPONLYNAME)
message("TMPONLYNAME is ${TMPONLYNAME}")
# only generate name - use current folder as temp
MKTEMP(PARENT "." OUTPUT_VARIABLE TMPONLYNAME)
message("TMPONLYNAME is ${TMPONLYNAME}")
# create file
MKTEMP(PREFIX "myapp" OUTPUT_VARIABLE TMPFILE CREATE_FILE)
message("TMPFILE is ${TMPFILE}")
# ... work with file ...
file(REMOVE "${TMPFILE}")
# create folder
MKTEMP(PREFIX "myapp" OUTPUT_VARIABLE TMPFOLDER CREATE_FOLDER)
message("TMPFOLDER is ${TMPFOLDER}")
# ... work with folder ...
file(REMOVE_RECURSE "${TMPFOLDER}")
Example of output on my Windows environment ("myapp7" the same because of deletion):
TMPONLYNAME is C:\Users\msuslov\AppData\Local\Temp\tmp1
TMPONLYNAME is C:\Users\msuslov\AppData\Local\Temp\myapp7
TMPONLYNAME is .\tmp0
TMPFILE is C:\Users\msuslov\AppData\Local\Temp\myapp7
TMPFOLDER is C:\Users\msuslov\AppData\Local\Temp\myapp7