how do I skip symlinks in cmake file GLOB_RECURSE? - cmake

I would like to find all files match glob pattern but skip the ones are symlinks. What kind of parameters allow me to do this in file(GLOB_RECURSE) ?

What kind of parameters allow me to do this in file(GLOB_RECURSE) ?
None, there is no such option. You have to write such code yourself.
how do I skip symlinks in cmake file GLOB_RECURSE?
Run file(GLOB_RECURSE and then iterate over the returned list of paths and remove paths that are symlinks.

Related

Should I modify CMAKE_MODULE_PATH in PackageConfig.cmake

I am writing a MyPackageConfig file for my project with exported targets so that other projects can easily find MyPackage and it's dependencies. It looks like this:
include(CMakeFindDependencyMacro)
find_dependency(LIB1_WITHOUT_CMAKE_CONFIG)
find_dependency(LIB2_WITH_CMAKE_CONFIG)
include (Some/Install/Dir/MyPackageTargets.cmake)
I am wondering if it is smart to add the following lines to the MyPackageConfig.cmake before the find_dependency calls
# Find target dependencies
# Allows packages linking with MyPackage to use the find modules that
# MyPackage used to find it's dependencies. Since this path is appended to
# the existing module path, the calling package's module path will take
# precedence
list(APPEND CMAKE_MODULE_PATH #CMAKE_INSTALL_PREFIX#/lib/cmake/MyPackage/modules)
# Allows packages linking with MyPackage to find MyPacakge's dependencies if
# they don't already have them. Since this path (or these paths) are
# appended to the existing prefix path, the calling package's prefix
# path will take precedence
list(APPEND CMAKE_PREFIX_PATH #CMAKE_PREFIX_PATH#)
find_dependency(LIB1_WITHOUT_CMAKE_CONFIG)
find_dependency(LIB2_WITH_CMAKE_CONFIG)
Good idea? No?
Longer explanation of my rationale:
How does YourPackage that uses MyPackage find LIB1?
(i). You could write your own FindLIB1.cmake but that's duplication of effort
(ii). I could install my FindLIB1.cmake alongside my MyPackageConfig.cmake in a Modules dir. But you will have to include this path in your module path.
My suggestion: Add a line before find_dependency(LIB1_WITHOUT_CMAKE_CONFIG) modifying the module path like so:
list(APPEND CMAKE_MODULE_PATH #CMAKE_INSTALL_PREFIX#/lib/cmake/mstk/modules)
This will ensure that if you have a FindLIB1.cmake, it will be used but if you don't mine will be found and used.
How do you know where the LIB1 and LIB2 reside (including LIB2's Config file)?
By adding the line
list(APPEND CMAKE_PREFIX_PATH #CMAKE_PREFIX_PATH#)
I tell your package where I searched and found my dependencies (but only if you didn't already have them in the CMAKE_PREFIX_PATH you specified)
Yes, you may change variables like CMAKE_MODULE_PATH or CMAKE_PREFIX_PATH for the purpose of your config script.
Any "good" project should be prepared to prepending/appending CMAKE_PREFIX_PATH, because this variable could normally be set by a user (when call cmake). As for CMAKE_MODULE_PATH, module's names in different directories are rarely conflicted.
Some hints:
You may restore the variables at the end of your script. Such way you won't affect the calling code when changing the variables:
# Store old value of the variable
set(old_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH})
# Change variable, use it, ...
# ...
# Restore the variable at the end
set(CMAKE_MODULE_PATH ${old_CMAKE_MODULE_PATH})
Note, however, that find_dependency returns from the script if an (inner) package not found, so restoring the variable won't trigger in that case. But usually find_package() is called with REQUIRED keyword, so failing to find the inner package would be fatal.
Instead of changing CMAKE_PREFIX_PATH you may set other variables which affect only specific find script and doesn't affect others. E.g many find scripts use XXX_ROOT variables for hint about location.
For find config script itself, you may use PATHS or HINTS options of find_package():
# Was:
#- list(APPEND CMAKE_PREFIX_PATH #CMAKE_PREFIX_PATH#)
#- find_dependency(LIB2_WITH_CMAKE_CONFIG)
# Replace with:
find_dependency(LIB2_WITH_CMAKE_CONFIG
PATHS #CMAKE_PREFIX_PATH# # Where to find config script
NO_DEFAULT_PATH # Do not search other places
)

why Cmake's " file(REMOVE_RECURSE [file1 ...]) "does not remove file having *.xxx.yy extension file?

I want to remove all the files from my binary directory which has ".asm.js" extension
below is my source code
file (REMOVE
${CMAKE_BINARY_DIR}/dist/*.asm.js
)
Unfortunately, It's not able to delete the file which has .asm.js extension.
is there anyone who can help me with this
Thanks in advance
As CMake docs says :
Remove the given files. The REMOVE_RECURSE mode will remove the given
files and directories, also non-empty directories. No error is emitted
if a given file does not exist.
So you need to do a list of files to send it to file(REMOVE)
To do it you can use :
file(GLOB MY_FILES ${CMAKE_BINARY_DIR}/dist/*.asm.js)
Or if you want to match them in subdirectories :
file(GLOB_RECURSE MY_FILES ${CMAKE_BINARY_DIR}/dist/*.asm.js)
Then you can use your command :
file (REMOVE
${MY_FILES}
)
if you want to delete all the "asm.js" type of files in the subdirectory as well then you can use the below command
file(GLOB_RECURSE MY_FILES ${CMAKE_BINARY_DIR}/dist/**/*.asm.js)

Where is CMake's `FILE (GLOB` relative to?

If I do FILE (GLOB "*.cpp") where does it look? What is the working directory of the search? Is it the current source directory? This doesn't seem to be documented anywhere (although it is for FILE (COPY).
The FILE(GLOB globbing expression) accepts paths in the globbing expression to. Thus you can include any path into the expression. The following example finds all files with extension dat in subfolder testdata:
file(GLOB files "${CMAKE_CURRENT_SOURCE_DIR}/testdata/*.dat")
Note: The usage of predefined path variables like CMAKE_CURRENT_SOURCE_DIR avoids any thinking about relative paths and made CMakeLists.txt more reusable and platform independent.
Bad:
file(GLOB generatedSources "../build-arm/autocode/*.c")
Good:
file(GLOB generatedSources "${PROJECT_BINARY_DIR}/autocode/*.c")
You can also specify a RELATIVE path. I'm not sure when the RELATIVE option was added, but it appears to be available from at least v3.0.2.
The search occurs in the current source directory, i.e. the directory of your CMakeLists.txt.
As a side remark regarding what you want to achieve with this, I would emphasize the following:
Note: We do not recommend using GLOB to collect a list of source files
from your source tree. If no CMakeLists.txt file changes when a source
is added or removed then the generated build system cannot know when
to ask CMake to regenerate.

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

Ignoring a single file when using include_directories with CMake

is there anyway to tell CMake to ignore one specific header file when using include_directories ?
Since the directories are not expanded into single files I can't just list(REMOVE_ITEM ..) the file out.
cheers Daniel
An include_directories call in CMakeLists does not result in a list of all available header-files. It simply passes those directories directly to the compiler as a search-dir for your "#include <...>" E.g. look at the documentation for the "-I" option of GCC 4.6.
In addition: I am not sure why you would want to exclude a single file. Very often you can work around a problematic header file, by creating your own header-file in your own project which includes everything except that single-file...
If you really, really want something like this and you insist on solving it with CMake, you will probably end up with file-globing all header-files, copying (without that one file of course) them manually to a new include-dir and then include that new dir.