CMakeLists - how to recursively delete directories - cmake

How can I have cmake recursively delete directories, equivalent to rm -rf?
I have tried using file(REMOVE_RECURSE which sounds like the right thing but I found that file(REMOVE_RECURSE test/) will delete a directory called test if test is empty or contains files or empty directories. However, if test contains another directory which contains a file, nothing is deleted - it is silently ignored.
cmake version 3.18.4

I found the same with cmake 3.17.5.
My fix was to specify the full path, as in:
file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/test/)

Related

Cmake - how include paths with only read access? [duplicate]

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 .

Why doesn't CMake-generated make clean delete files created with configure_file

I'm using CMake for a project, and generating some configuration .h files with a configure_file() command. This works well enough, but - if I make clean, the generated file is not deleted - nor is it overwritten when I invoke cmake again with different parameters (or ccmake and so on).
Why is this the case, and how can I force re-generation of configure_file output files - when necessary / always?
It seems this may happen if the files are generated under the source folder rather than under the build folder. I think CMake treated the (generated) files it found as source files - or at least, not build files (see #Tsyvarev's comment), so they must not be deleted/altered.

do post processing after make install in cmake

I am trying to make a copy of my executable at the end of my"make install"
I need to do something like:
cp bin/prog bin/prog1
I have added the following as the last line in my CMakelists.txt
install (CODE "execute_process(COMMAND /src/copyExe.sh ${BIN_DIR})")
copyExe.sh is a bash script that does the copying. In order to have the desired affect, I need to run "make install" twice. The first time it complains that prog doesn't exist, and then copies the file to bin. The second time it finds prog and is able to make the copy.
Is there a way to ensure that my copyExe script runs AFTER the files get copied to bin?
Directory structure
site
bin
src
CMakeLists.txt ( contains add_dir(foo) and install(CODE....))
foo
CMakeLists.txt ( contains install( TARGET..... ))
While CMake documentation for install command says (about installation logic):
The order across directories is not defined.
it looks like it tends to process installation logic in different subdirectories in the same order as add_subdirectory() calls.
However, it processes installation logic of install() call in the current directory before ones in subdirectories.
You may move install(CODE) into some subdirectory (say, fix_binaries), and add this subdirectory at the end of CMakeLists.txt in src:
src/fix_binaries/CMakeLists.txt:
install(CODE ...)
src/CMakeLists.txt:
...
add_subdirectory(foo)
...
# After all add_subdirectory() calls
add_subdirectory(fix_binaries)
Such approach works for one of the project I have involved (related code).

CPack: Ignoring files using regex

(apologies: cross-posted from CMake mailing list)
I'm trying to get my head round CMake's regex implementation; I have a folder containing 4 folders and 2 text files as follows:
build/
projectA/
CMakeLists.txt
extrafiles/
README
temp/
One line of CMakeLists.txt is:
set(CPACK_SOURCE_IGNORE_FILES "[^projectA]$")
In my source package that is then subsequently generated, build/, projectA/ and extrafiles are present, but temp/ and the 2 text files are not. I'm trying to get to a stage where the regex will ignore everything in the folder except for projectA/, README and CMakeLists.txt, but can't work out at the moment how the regex I've
supplied is giving those results.
I guess what this boils down to is how to match a whole string using regex. I realise that the docs say Matches any character(s) not inside the brackets which is where I guess I'm going wrong...
Further exploration
In trying to understand CMake's regex implementation, I thought I'd start from 1st principles and do some easy stuff.
If I do
set(CPACK_SOURCE_IGNORE_FILES projectA)
then the folder projectA doesn't appear in my source package (as expected); however, if I do
set(CPACK_SOURCE_IGNORE_FILES ^projectA$)
or
set(CPACK_SOURCE_IGNORE_FILES ^/projectA/$)
then projectA does appear. What is it about the ^ (beginning of line) and $ (end of line) that I'm not understanding?
Even more
As probably obvious, projectA is not actually the name of my project, but everything above holds true when I physically rename my project folder to projectA. But, when I replace
set(CPACK_SOURCE_IGNORE_FILES projectA)
with
set(CPACK_SOURCE_IGNORE_FILES <name of my project>)
and rename my actual project folder from projectA to its actual name, I end up with an empty tarball! Argh! I have absolutely no idea what strange tricks CMake is playing on me, but I just want to cry.
Any insight will be greatly appreciated!
SELF CONTAINED EXAMPLE
As requested by Fraser, a self contained example showing 2 of the 'features' I've described. However, I do know that I'm running CMake in a slightly non-standard way, in order to keep everything to do with individual builds together, so if there's any proof running CMake in a more standard way eliminates these problems I'd be interested to see them.
Step 1: creating files
Create tree:
cd ~
mkdir
cd projectA
mkdir projectA
Create C file, and save it as ~/projectA/projectA/helloworld.c:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
printf("!!!Hello World!!!\n"); /* prints !!!Hello World!!! */
printf("!!!Hello CMake!!!\n"); /* prints !!!Hello CMake!!! */
return 0;
}
create a file that won't need compiling, and save it as ~/projectA/test.sh:
#A non compiled program
echo "Hello world!"
create ~/projectA/CMakeLists.txt:
cmake_minimum_required (VERSION 2.6)
project (HelloWorld)
set(CMAKE_INSTALL_PREFIX "$ENV{HOME}/projectAinstall")
add_executable(helloworld projectA/helloworld.c)
install(TARGETS helloworld DESTINATION .)
include(InstallRequiredSystemLibraries)
set(CPACK_GENERATOR "TGZ")
set(CPACK_SOURCE_GENERATOR "TGZ")
include(CPack)
Step 2: compiling
In ~/projectA, run:
chris#chris:~/projectA$ cmake -H. -Bbuild
then:
make -C build && make -C build package && make -C build package_source
this results in 2 tarballs in the build folder. Moving these somewhere else and untarring them shows helloworld in the binary tarball (as expected), and everything from the ~/projectA/projectA in the source tarball, including test.sh which won't get compiled (which Fraser seemed surprised about)
Step 3: random tests
Modifying CMakeLists.txt to include
set(CPACK_SOURCE_IGNORE_FILES "projectA")
and rerunning the CMake / Make commands above results in an empty source tarball, but with the same binary tarball as above. I have now realised that changing the directory tree so that the top level directory is testproject (and so different to its child folder) doesn't result in an empty source tarball, and does only remove the files listed in CPACK_SOURCE_IGNORE_FILES
I don't think you can achieve what you're after using CPACK_SOURCE_IGNORE_FILES (although I'm not certain). As you rightly noted, CMake's regex handling allows for excluding groups of characters, but I don't think it allows for negating whole patterns. [See updated answer at the end of the edits.]
That being said, I guess you can list all the folders you wish to exclude in your install command. Not as robust as excluding everything except "projectA", but still here's the syntax:
install(DIRECTORY .
DESTINATION the_install_subdir
REGEX "build|extrafiles|temp+" EXCLUDE)
Regarding the empty tarball, I imagine that you maybe have <name of my project> both as your project's root dir and as a subdir? So in your example, if you called your project "projectA", then you'd have "projectA/build", "projectA/projectA", etc.
If so, the regex will work on the full path, and hence all files within your project will contain projectA/ within their paths.
As for the crying... well, I can only advise you to get a grip and pull yourself together! :-)
Edit: In response to the comments, here's a quick example of using the install command to achieve the goal:
install(DIRECTORY projectA
DESTINATION the_install_subdir)
install(FILES CMakeLists.txt README DESTINATION the_install_subdir)
Further Edit:
OK, your example helps a lot - I had indeed misunderstood what you were doing. I hadn't picked up that you were actually making 2 different targets ("package" and "package_source"). I had thought you were creating the binary package by doing something like
cpack -G DEB
and that you were creating the other package by doing
cpack -G TGZ
These both build the binary package. My mistake - I should have paid more attention. Sorry!
As for your specific questions:
Question 1
It seems to me that installing files / directories that aren't compiled but are at the same level as the folder containing all the compiled files (i.e. bin), and then ignoring the bin folder using CPACK_SOURCE_IGNORE_FILES results in an empty tarball - is this correct?
I take this to mean: "Should doing set(CPACK_SOURCE_IGNORE_FILES "${CMAKE_BINARY_DIR}") result in an empty tarball?" The answer is probably not.
Because CPACK_SOURCE_IGNORE_FILES represents a regex, I'm sure there are cases where the resultant regex could match every file in the project, and this would cause an empty tarball. However I imagine it's fairly unlikely.
If, rather than using the full path to your bin dir via the variable ${CMAKE_BINARY_DIR} you were to just give the folder name, there would be a much greater chance of an empty tarball. Say you call your bin dir "build" and have set(CPACK_SOURCE_IGNORE_FILES "build"). If your project lived in say ~/test_builds/projectA, then the regex "build" would match every file in the project since each contains "test_builds"; resulting in an empty tarball.
I think this is the crux of issue each time you've generated an empty tarball. Whatever the regex is trying to achieve, it actually ends up matching and excluding all files.
Question 2
It also seems that files in the CMAKE_SOURCE_DIR which aren't 'installed' don't end up in the binary tarball but do end up in the source tarball
Yes, the "package_source" is indeed a different target to the binary package. It by default contains all files in the ${CMAKE_SOURCE_DIR}, whereas the "package" target contains only items added via install commands. Here, the term "source files" is probably a slight misnomer since it means all files in the source tree - not just .c, .cc, .cxx, etc.
Original Question
I think there's a reasonably safe way to achieve your original aim after all! If you use file(GLOB ...) to generate a non-recursive list of all files/folders in your root, then remove those you wish to keep in the source package, you should be able to use the remaining list as the regex value of CPACK_SOURCE_IGNORE_FILES:
file(GLOB SourceIgnoreFiles "${CMAKE_SOURCE_DIR}/*")
set(SourceKeepFiles "${CMAKE_SOURCE_DIR}/projectA"
"${CMAKE_SOURCE_DIR}/CMakeLists.txt"
"${CMAKE_SOURCE_DIR}/README")
list(REMOVE_ITEM SourceIgnoreFiles ${SourceKeepFiles})
# Escape any '.' characters
string(REPLACE "." "\\\\." SourceIgnoreFiles "${SourceIgnoreFiles}")
set(CPACK_SOURCE_IGNORE_FILES "${SourceIgnoreFiles}")
Hopefully this should now work for you. Sorry again for the misdirections.
CMake tends to use absolute paths except in contexts where there's a strong argument for using relative paths. So I'm pretty sure it's running each regex in CPACK_SOURCE_IGNORE_FILES against absolute paths of files (which should answer your question "What is it about the ^ (beginning of line) and $ (end of line) that I'm not understanding?"). Anything that isn't matched by any regex in CPACK_SOURCE_IGNORE_FILES is not ignored.
What you want is probably something like:
set(CPACK_SOURCE_IGNORE_FILES
/build/
/extrafiles/
/temp/
)

How to copy a directory using CMake in combination with a clean target?

How do I define a rule in CMake that will recursively copy a complete directory structure to a target directory, as well as remove the copied directory when running make clean afterwards?
Use
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/target_dir
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/dir ${CMAKE_BINARY_DIR}/target_dir)
add_custom_target(copy_dir_target ALL
DEPENDS ${CMAKE_BINARY_DIR}/target_dir)
CMake should automatically add any files/dirs which are produced with OUTPUT of add_custom_command().
If that wouldn't work you can try to gather files you wish to copy with file(GLOB ...), then add_custom_command() which copies only single file with foreach(FILE ${FILES}), and finally wrap everthing into the single add_custom_command() call. This way every file will be cleaned, except of target dir itself.
Finally, you can play with setting ADDITIONAL_MAKE_CLEAN_FILES property on source dir, but be sure to copy files into respective binary dir.