I have a custom command that is executed if a directory exists. I need to know if the directory is not empty before executing another command.
Question: How to read, detect or get the number of files of a directory?
You can run regular CMake code as script using cmake -P as part of build process.
The script itself would contain somethng like
file(GLOB RESULT DIR)
list(LENGTH RESULT RES_LEN)
if(RES_LEN EQUAL 0)
# DIR is empty, do something
endif()
Related
How to get the working directory from which cmake was called?
I know that I can run execute_process() at the beginning of my CMake code but maybe there is some better way like a built-in variable to the working directory of the CMake process.
What I want to achieve is to convert a relative path given in a CMake variable (given with -D...) to an absolute path in CMake.
What I want to achieve is to convert a relative path given in a CMake variable (given with -D...) to an absolute path in CMake.
Then simply create a cache variable of type PATH (for directories) or FILEPATH in your CMakeLists.txt. CMake will convert untyped command line arguments to absolute paths automatically.
$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
project(test NONE)
set(input_file "" CACHE FILEPATH "some input file")
message(STATUS "${input_file}")
$ cmake -S . -B build -Dinput_file=foo.txt
-- /path/to/foo.txt
-- Configuring done
-- Generating done
-- Build files have been written to: /path/to/build
See the documentation:
It is possible for the cache entry to exist prior to the call but have no type set if it was created on the cmake(1) command line by a user through the -D<var>=<value> option without specifying a type. In this case the set command will add the type. Furthermore, if the <type> is PATH or FILEPATH and the <value> provided on the command line is a relative path, then the set command will treat the path as relative to the current working directory and convert it to an absolute path.
Here: https://cmake.org/cmake/help/latest/command/set.html?highlight=current%20working%20directory
Relatedly, when running in script mode (rather than project mode), several of the usual variables are set to the current working directory:
When run in -P script mode, CMake sets the variables CMAKE_BINARY_DIR, CMAKE_SOURCE_DIR, CMAKE_CURRENT_BINARY_DIR and CMAKE_CURRENT_SOURCE_DIR to the current working directory.
https://cmake.org/cmake/help/latest/variable/CMAKE_SOURCE_DIR.html
I want to be able to do something like:
C:\> build.cmd MyFile.in
And have build.cmd invoke a CMake build system, using MyFile.in as input. This would ideally not have to regenerate the CMake cache every time, but just run the existing cache on the specified file.
I might then also want to invoke this build on other files, to produce other outputs:
C:\> build.cmd OtherFile.in
Does that make sense? I.e. I want CMake to be runnable almost like a command line tool.
Just introduce a cache variable...
set(MY_FILE "" CACHE FILEPATH "Input file")
if (NOT MY_FILE)
message(FATAL_ERROR "Must supply a file as input.")
endif ()
Then you can just pass a file path to an existing CMake build tree like so:
$ cmake -B /path/to/build -DMY_FILE:FILEPATH=MyFile.in
Is it possible to specify an include directory when running cmake. For example
cmake . -INCLUDE=/foo/bar
The header files are in a separate directory from the sources that I would like to compile, and I would like to remedy this without tinkering with the Makefile generated by cmake.
Update
The project does have a CMakeLists.txt. Excerpt:
INCLUDE_DIRECTORIES(${EO_SOURCE_DIR}/src)
INCLUDE_DIRECTORIES(${EO_SOURCE_DIR}/src/ga)
INCLUDE_DIRECTORIES(${EO_SOURCE_DIR}/src/utils)
Can ${EO_SOURCE_DIR} be set from the command line?
If the path to your headers is fixed in relation to your sources, then you should be able to avoid having to pass this info via the command line.
Say your project's directory structure is:
/CMakeLists.txt
/my_sources/main.cpp
/my_sources/foo.cpp
/my_includes/foo.hpp
and in your CMakeLists.txt, you currently have something like:
add_executable(MyExe my_sources/main.cpp my_sources/foo.cpp)
then to add the /my_includes folder to the the list of include search paths, you only need to add the following:
include_directories(my_includes)
For further info about include_directories, run
cmake --help-command include_directories
Answer to update in question:
Yes, using the -D command line option just do
cmake . -DEO_SOURCE_DIR:PATH=<Path to required dir>
The variable ${EO_SOURCE_DIR} gets cached as a result of this, so you only need this -D argument once (unless the required path changes or you delete your CMakeCache file, etc.)
Proper way to do this is to define a variable in CMakeLists.txt and ask user to set it:
set(YOURLIB_INCLUDE_DIR "" CACHE FILEPATH "Path to yourlib includes")
if(NOT EXISTS ${YOURLIB_INCLUDE_DIR}/header.h)
message(SEND_ERROR "Can't find header.h in ${YOURLIB_INCLUDE_DIR})
endif()
include_directories(${YOURLIB_INCLUDE_DIR})
Now you can set it from the command line: cmake -D YOURLIB_INCLUDE_DIR=/path/to/yourlib/include .
In my CMakeLists.txt, I define a custom target and command:
add_custom_command(
OUTPUT
${CMAKE_CURRENT_SOURCE_DIR}/input.csv
${CMAKE_CURRENT_SOURCE_DIR}/output1.csv
${CMAKE_CURRENT_SOURCE_DIR}/output2.csv
COMMAND python3
${CMAKE_CURRENT_SOURCE_DIR}/tests/genVectors.py)
add_custom_target(TEST_VECTORS
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/input.csv
${CMAKE_CURRENT_SOURCE_DIR}/output1.csv
${CMAKE_CURRENT_SOURCE_DIR}/output2.csv)
add_executable(VectorTest tests/VectorTest.cpp)
add_dependencies(VectorTest TEST_VECTORS)
It always generates new CSV files even though the files exist. I only need to generate the vectors (with genVectors.py python file) if they do not exist. Is that something wrong with my configuration?
The OUTPUT option of add_custom_command does not guarantee that the generated files are placed here; it just tells CMake that the generated files are expected to be placed there. It is likely that your python script is generating files at a relative path, so they are just being placed somewhere in your CMake binary directory (your build folder). So while your files may be generated correctly, your custom target doesn't see them because it is looking in CMAKE_CURRENT_SOURCE_DIR. Thus, the custom target will always trigger the custom command to re-run.
CMake runs add_custom_command from the CMAKE_CURRENT_BINARY_DIR by default, but you can change it to run from CMAKE_CURRENT_SOURCE_DIR by adding the WORKING_DIRECTORY option. This way, the generated files will be placed at the expected location, and achieve your desired behavior. Try something like this:
add_custom_command(
OUTPUT
${CMAKE_CURRENT_SOURCE_DIR}/input.csv
${CMAKE_CURRENT_SOURCE_DIR}/output1.csv
${CMAKE_CURRENT_SOURCE_DIR}/output2.csv
COMMAND python3
${CMAKE_CURRENT_SOURCE_DIR}/tests/genVectors.py
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
add_custom_target(TEST_VECTORS
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/input.csv
${CMAKE_CURRENT_SOURCE_DIR}/output1.csv
${CMAKE_CURRENT_SOURCE_DIR}/output2.csv)
add_executable(VectorTest tests/VectorTest.cpp)
add_dependencies(VectorTest TEST_VECTORS)
You can try to generate your file at time of configuration(i.e. while calling cmake). By this way it will created only once.
You can remove add_custom_command and use execute_process to create your files.
I have a cmake project with a bunch of different targets in it. First it builds an executable that is used to process some data files (lets call this DataProcessor). Then it processes those data files with the executable. Then, a second executable (we'll call this MyApp) is built and it runs with the processed data files.
There is also a target that takes all the processed data files and MyApp and bundles them up in a tar file so we can distribute them.
In my CMakeLists.txt file there I have the following lines:
get_target_property(DATA_PROCESSOR_EXE DataProcessor LOCATION)
get_target_property(MY_APP_EXE MyApp LOCATION)
I need these to run various other commands in my CMakeLists file. For example, the DATA_PROCESSOR_EXE is used in the custom command to process the data files, like so:
add_custom_command(
OUTPUT ${DATA_OUT}
COMMAND ${DATA_PROCESSOR_EXE} -o ${DATA_OUT} ${DATA_IN}
DEPENDS DataProcessor)
And when I bundle everything up I use MyApp's location as well:
# Convert executable paths to relative paths.
string(REPLACE "${CMAKE_SOURCE_DIR}/" "" MY_APP_EXE_RELATIVE
"${MY_APP_EXE}")
string(REPLACE "${CMAKE_SOURCE_DIR}/" "" DATA_PROCESSOR_EXE_RELATIVE
"${DATA_PROCESSOR_EXE}")
# The set of files and folders to export
set(EXPORT_FILES
assets
src/rawdata
${MY_APP_EXE_RELATIVE}
${DATA_PROCESSOR_EXE_RELATIVE})
# Create a zipped tar of all the necessary files to run the game.
add_custom_target(export
COMMAND cd ${CMAKE_SOURCE_DIR} && tar -czvf myapp.tar.gz ${EXPORT_FILES}
DEPENDS splat
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/..)
The problem is that trying to get the target's location property now causes a warning saying:
CMake Warning (dev) at CMakeLists.txt:86 (get_target_property):
Policy CMP0026 is not set: Disallow use of the LOCATION target property.
Run "cmake --help-policy CMP0026" for policy details. Use the cmake_policy
command to set the policy and suppress this warning.
The LOCATION property should not be read from target "DataProcessor".
Use the target name directly with add_custom_command, or use the generator
expression $<TARGET_FILE>, as appropriate.
I don't understand how it wants me to use add_custome_command or what it means by use the generator expression $<TARGET_FILE>
add_custom_command knows about target names. You don't need to fetch the location yourself.
Replace code like
add_custom_command(
OUTPUT ${DATA_OUT}
COMMAND ${DATA_PROCESSOR_EXE}
with code like
add_custom_command(
OUTPUT ${DATA_OUT}
COMMAND DataProcessor
ie, use the name of the target.
The final location of the output executables is not known while CMake is running over your imperative code at 'configure time'. However, add_custom_target can be told to create build rules with content which is only known at generate-time (after configure-time). The way to do that is to use a generator expression.
http://www.cmake.org/cmake/help/v3.0/manual/cmake-generator-expressions.7.html#informational-expressions
Use $<TARGET_FILE_DIR:MyApp> instead of ${MY_APP_EXE_RELATIVE} in your add_custom_target call, for example.