CMake: configure_file() does not touch if the file is the same? - cmake

By checking the timestamp of a sample file out.txt before and after running configure_file(in.txt out.txt), it seems like the last modified date does not change when the new content is the same when the out.txt file already exist (so when variables are the same).
Is it really true and guaranteed? I can't find about this in CMake documentation.

Is it really true and guaranteed? I can't find about this in CMake documentation.
You have partly answered your own question here: if it's not documented, it's certainly not guaranteed. Fortunately, it is documented, here: https://cmake.org/cmake/help/latest/command/configure_file.html
If the input file is modified the build system will re-run CMake to re-configure the file and generate the build system again. The generated file is modified and its timestamp updated on subsequent cmake runs only if its content is changed.
Internally, configure_file writes its output to a temp file and then calls the equivalent of cmake -E copy_if_different. That is, it does a byte-for-byte comparison and only does the copy (thereby updating timestamps) if there is a difference.
You can see the sources here: https://github.com/Kitware/CMake/blob/f0a2ec12e2743a7be39201e2bc563ab37debd791/Source/cmMakefile.cxx#L3836

Related

Is there a way in cmake to save away the install_manifest.txt for later

What is the best way to save away the install_manifest.txt file at install time so it can be used later for an uninstall.
This is a naive attempt that doesn't work because the file doesn't exist at this point.
install(FILES "${CMAKE_BINARY_DIR}/install_manifest.txt" DESTINATION /etc/${PROJECT_NAME})
-- EDIT --
I'm going with the solution from #havogt. I thought I would add the final result since I also tacked the manifest file itself onto the end. I'm also leaving my destination variable in there to make #KamilCuk a bit happier.
It's worth noting this must appear last or there will be missing files.
set(INSTALL_SHAREDIR ${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME})
install(CODE "string(REPLACE \";\" \"\\n\" MY_CMAKE_INSTALL_MANIFEST_CONTENT \"\$\{CMAKE_INSTALL_MANIFEST_FILES\}\")\n\
file(WRITE ${CMAKE_BINARY_DIR}/MANIFEST.txt \"\$\{MY_CMAKE_INSTALL_MANIFEST_CONTENT\}\\n${INSTALL_SHAREDIR}/MANIFEST.txt\")")
install(FILES "${CMAKE_BINARY_DIR}/MANIFEST.txt" DESTINATION ${INSTALL_SHAREDIR})
I don't think there is a documented way to achieve that. It seems writing of the install_manifest.txt is the very last thing that happens during installation.
However, if you really want to do it and want to rely on implementation detail of CMake, you can achieve what you want as follows.
The code that is used to write the install_manifest.txt comes from cmLocalGenerator.cxx. You can use the same pattern to write the content to your desired location. The information is contained in the CMake variable CMAKE_INSTALL_MANIFEST_FILES.
Adding the following to your CMake project will write the content of install_manifest.txt to a file called my_install_manifest.txt.
install(CODE "string(REPLACE \";\" \"\\n\" MY_CMAKE_INSTALL_MANIFEST_CONTENT \"\$\{CMAKE_INSTALL_MANIFEST_FILES\}\")\n\
file(WRITE my_install_manifest.txt \"\$\{MY_CMAKE_INSTALL_MANIFEST_CONTENT\}\")")
Check cmake_install.cmake in your build directory to see what will be executed during installation.
Obviously, there is no guarantee that this will work in future versions of CMake.

How to keep CMake generated files?

I'm using add_custom_command() to generate some files. ninja clean removes them, as it should. One of the files is intended as a default/example implementation, to be modified by the user. It is only generated if it does not already exist. I would like for ninja clean not to remove this file.
I have tried a number of things but without success:
add_custom_target(): CMake complains about the missing file unless I name it in BYPRODUCTS, but doing this also leads to removal on clean
set_file_properties(... GENERATED FALSE) doesn't work because CMake complains about the file missing.
set_directory_properties() failed in a similar way: "folder doesn't exist or not yet processed" (it does exist)
I previously generated the example implementation and just let the user copy it or model their code on it. This works, but isn't entirely satisfactory. Is my use-case so unlikely that CMake doesn't support it?
I am afraid you requirment (conceptually, have make create something which make clean does not remove) is rather unusual. I can think of two potential solutions/workarounds.
One, move the file's generation to CMake time. That is, create it using execute_process() instead of add_custom_command(). This may or may not be possible, based on whether the file-generation process (the current custom command) depends on the rest of the build or not.
Two, totally hide the example file's existence from CMake. That is, have the custom command also generate some other file (maybe just a timestamp file) and have its driving custom target depend on that one instead. Do not list the example file as ither the custom command's dependency, output, or byproduct. That way, nothing will depend on it and neither CMake nor Ninja should not care whether it exists or not, so they will not complain or try to clean it up.
If it is an example for the user, it should not be in your build folder, but in the install folder. I don't see why you would need add_custom_command or the other commands you listed.
Therefore, you have to provide install() instructions.
You can then call make install. Cleaning will not remove those and only installing again will overwrite them if necessary.
For those, who come here a long time after the original question was asked (like me), I'll write my solution:
The tool called in add_custom_command generates two files with identical content:
one that is saved in sources, never mentioned anywhere
and one that's marked as byproduct, and then is depended on
So the first one is the file we wanted in the first place.
And the second one is actually used in build process, and gets deleted on clean.
For me the issue is that I actually want to save generated files in VCS so I can track changes. And this approach gives ne what I need.

Is it possible to read the value of CMAKE_INSTALL_PREFIX variable in postinst?

Just wanted to ask if it is possible to read the value of CMAKE_INSTALL_PREFIX variable in postinst script? The goal is to have postinst script to read a file in ${CMAKE_INSTALL_PREFIX}/share/myapp folder and, based on the content of the file read, to generate a file in ${CMAKE_INSTALL_PREFIX}/etc/myapp folder. I'd like to avoid relying on absolute paths.
Thank you in advance!
Edit
Calling env from postinst gives good information:
DPKG_MAINTSCRIPT_DEBUG=0
DPKG_COLORS=never
DPKG_MAINTSCRIPT_NAME=postinst
DPKG_MAINTSCRIPT_PACKAGE=myapp
DPKG_RUNNING_VERSION=1.19.0.5
DPKG_FRONTEND_LOCKED=true
DPKG_ROOT=
DPKG_MAINTSCRIPT_ARCH=armhf
DPKG_ADMINDIR=/var/lib/dpkg
DPKG_MAINTSCRIPT_PACKAGE_REFCOUNT=1
DPKG_ROOT is probably what I need. It seems that CMAKE_INSTALL_PREFIX is not related to postinst.
Generally speaking at the moment of execution postinst there is no CMake variables (and they definitely never be in the env output). So, the way to do what you want is to generate your postinst script (from postinst.in file) using configure_file function. In the template file you can refer CMake variables, so being rendered your script would contain "hardcoded" literals instead. And that is quite normal.
But, take in account the following:
cpack uses CPACK_PACKAGING_INSTALL_PREFIX variable

Code generator generating its own CMake files and targets

Let's assume I have a script that generates a set of source files forming a target I want to link against in a CMakeLists.txt. If the file names are known to the latter then the usual add_custom_target() and add_custom_command() commands will make it possible to use the generated files as target sources.
Let's assume, though, that only the generator script knows the file names and locations. How can a target library be generated so that the parent CMakeLists.txt can link against it without its knowing the actual file names?
Note that the dependency topic isn't in this question's scope as the script knows itself when to regenerate or not. It's not the finest use of CMake, but it's sufficient in this use case.
Idea #1
The script also generates a generated.cmake file included by the parent one using include(generated.cmake). Problem: CMake doesn't find generated.cmake as it isn't existing at configuration time.
Idea #2
Similar to idea #1, but the script is called with the execute_process() so that generated.cmake is present at configuration time. Problem: The script is not called anymore at subsequent builds, thus ignoring possible changes to its input.
Idea #3
The script passes back a list of targets and files that is somehow considered by the parent CMakeLists.txt. So far I couldn't find a way to do so.
The solution I came with is eventually a mixture of all three ideas.
Solution to idea #1's problem
execute_process() actually ensures that generated_targets.cmake is present at configure time.
Solution to idea #2's and #3's problems
As stated in this answer to "Add dependency to the CMake-generated build-system itself", the CMAKE_CONFIGURE_DEPENDS directory property can be edited to add files whose touching re-triggers the configure step.
The key success factor is that this property can be set after the initial execute_process() call so that the script can identify and list its input dependencies (in an output file) that are then added to CMAKE_CONFIGURE_DEPENDS, hence also solving the input dependency problem.
Resulting pseudo code
# The script generates:
# - <output_dir>/cmake/input_files
# - <output_dir>/cmake/generated_targets.cmake
execute_process(
COMMAND myScript
--output-dir ${CMAKE_CURRENT_BINARY_DIR}/generated
)
# Mark the input files as configure step dependencies so that the execute_process
# commands are retriggered on input file change.
file(STRINGS ${CMAKE_CURRENT_BINARY_DIR}/generated/cmake/input_files _input_files)
set_property(
DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
${_input_files}
)
# Add the generated CMake targets.
include(${CMAKE_CURRENT_BINARY_DIR}/generated/cmake/generated_targets.cmake)

Can CMakeLists.txt depend on a file parsed by a function?

I am rather new to CMake, starting off for the first time with a larger project consisting of many subprojects.
For particular reasons (described below for the curious) I already have a set of include files that contain info about the source files needed for each CMake target (lib or exe) – and, for now, I prefer to (re)use these files (reason also described below)
Writing a function to parse these files and add their content as source files to the targets was a surprisingly easy task.
But – now the Problem:
Obviously I want to have each targets CMakeLists.txt depend on the particular include file, that generates the list of source files, so that changes on the include file will be detected as if it were changes to CMakeLists.txt itself, but I simply can’t find any references on how to accomplish that.
N.B.: I found AddFileDependencies but that is for adding dependencies on source files, not the CMakeLists.txt. However, CMake itself can figure out dependencies to included .cmake file somehow, so I figured, it should be possible to do somehow.
Background for the curious:
For this project (quite a number of libraries used by quite a number of executable targets, all organized as subprojects) I was using QMake (without actually using Qt itself) for setting up makefiles. Doing so I was able to use Qt Creator while still being able to generate Visual Studio Solution/Project files automagically. We’re also still in progress of evaluating different IDEs and the choice has not been made yet. But the most important reason to use a generator like QMake / CMake was not being forced to set up the VS files for all these subprojects manually.
Although I needed to trick QMake sometimes to do what I wanted to, things went quite well - even for the VS solution - except for one thing: Visual Studio messes up dependencies on Flex/Bison and other files using custom build rules. It keeps recompiling the Flex/Bison/other files saying „command line changed“ – which I gave up trying to fix.
For this reason I thougt, I’d try CMake as a generator instead, which looks very promising so far – although not having builtin precompiled header support in CMake is somewhat ridiculous these days (off topic, I know).
Since Qt Creators CMake support is by far not as good as the support for QMake projects, I firgured, using the approach of parsing the .pri files containing the source file list would enable me using QMake and CMake side by side – especially since the remaining project settings are rather less complicated than on most open source projects.
There's a nice trick which does exactly what you need. It's based on the idea I found in the git-revision module of #rpavlik see this so question
This is the overall idea:
Create a dummy timestamp file
Add a custom command which touches the timestamp whenever the input .pri file changes
include the timestamp file in your CMakeLists.txt
A possible implementation:
set(input_pri_file <path-to-the-input-pri-file>)
set(timestamp_file ${CMAKE_CURRENT_BINARY_DIR}/timestamp.cmake)
add_custom_command(
OUTPUT ${timestamp_file}
COMMAND ${CMAKE_COMMAND} -E touch ${timestamp_file}
MAIN_DEPENDENCY ${input_pri_file}
VERBATIM
COMMENT "Updating timestamp.cmake"
)
if(NOT EXISTS "${timestamp_file}")
file(WRITE ${timestamp_file} "") # create initial empty file
endif()
include(${timestamp_file})
# create the file list from input_pri_file
....
# use the file list
add_executable(main ${filelist})
Here's what happens when the .pri file changes:
the change triggers the execution of the custom command
which updates the timestamp
because the CMakeLists includes the timestamp it is dependent on it
so updating the timestamp triggers a re-configuration of the CMakeLists.txt
I use the configure_file() if I have some input that should retrigger CMake's configuration process. See e.g. How to make CMake reconfiguration depend on custom file? and configure_file()'s unit test
So in your case it would look something like:
configure_file(SomeInput.pri ${CMAKE_CURRENT_BINARY_DIR}/SomeInput.pri)
Then you use ${CMAKE_CURRENT_BINARY_DIR}/SomeInput.pri to generate the sources. Just make sure you do not add COPYONLY, because then configuration won't retrigger on changes of SomeInput.pri.
EDIT: Alternatively use - a relative new addition - the CMAKE_CONFIGURE_DEPENDS directory property.