From a2c068a7ce47ab5934735b9f9168dda9760646ec Mon Sep 17 00:00:00 2001 From: Domen Vrankar Date: Tue, 17 Mar 2015 23:34:03 +0100 Subject: [PATCH 1/2] file: Teach GLOB to list directories optionally GLOB lists directories by default and GLOB_RECURSE does not. LIST_DIRECTORIES enables user to control the behavior explicitly for consistently for both GLOB and GLOB_RECURSE. --- Help/command/file.rst | 17 ++++- Source/cmFileCommand.cxx | 62 ++++++++++++++++++- ...B-error-LIST_DIRECTORIES-no-arg-result.txt | 1 + ...B-error-LIST_DIRECTORIES-no-arg-stderr.txt | 1 + .../GLOB-error-LIST_DIRECTORIES-no-arg.cmake | 1 + ...or-LIST_DIRECTORIES-not-boolean-result.txt | 1 + ...or-LIST_DIRECTORIES-not-boolean-stderr.txt | 1 + ...B-error-LIST_DIRECTORIES-not-boolean.cmake | 1 + Tests/RunCMake/file/GLOB-stderr.txt | 6 ++ Tests/RunCMake/file/GLOB.cmake | 28 +++++++++ .../GLOB_RECURSE-cyclic-recursion-stderr.txt | 15 +++++ .../file/GLOB_RECURSE-cyclic-recursion.cmake | 23 +++++++ Tests/RunCMake/file/GLOB_RECURSE-stderr.txt | 6 ++ Tests/RunCMake/file/GLOB_RECURSE.cmake | 28 +++++++++ Tests/RunCMake/file/RunCMakeTest.cmake | 10 +++ 15 files changed, 195 insertions(+), 6 deletions(-) create mode 100644 Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-no-arg-result.txt create mode 100644 Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-no-arg-stderr.txt create mode 100644 Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-no-arg.cmake create mode 100644 Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-not-boolean-result.txt create mode 100644 Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-not-boolean-stderr.txt create mode 100644 Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-not-boolean.cmake create mode 100644 Tests/RunCMake/file/GLOB-stderr.txt create mode 100644 Tests/RunCMake/file/GLOB.cmake create mode 100644 Tests/RunCMake/file/GLOB_RECURSE-cyclic-recursion-stderr.txt create mode 100644 Tests/RunCMake/file/GLOB_RECURSE-cyclic-recursion.cmake create mode 100644 Tests/RunCMake/file/GLOB_RECURSE-stderr.txt create mode 100644 Tests/RunCMake/file/GLOB_RECURSE.cmake diff --git a/Help/command/file.rst b/Help/command/file.rst index 73d4cfa79..2fe741490 100644 --- a/Help/command/file.rst +++ b/Help/command/file.rst @@ -92,9 +92,12 @@ store it in a ````. :: - file(GLOB [RELATIVE ] [...]) - file(GLOB_RECURSE [RELATIVE ] - [FOLLOW_SYMLINKS] [...]) + file(GLOB + [LIST_DIRECTORIES true|false] [RELATIVE ] + [...]) + file(GLOB_RECURSE [FOLLOW_SYMLINKS] + [LIST_DIRECTORIES true|false] [RELATIVE ] + [...]) Generate a list of files that match the ```` and store it into the ````. Globbing expressions are similar to @@ -102,6 +105,9 @@ regular expressions, but much simpler. If ``RELATIVE`` flag is specified, the results will be returned as relative paths to the given path. +By default ``GLOB`` lists directories - directories are omited in result if +``LIST_DIRECTORIES`` is set to false. + .. 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 @@ -119,6 +125,11 @@ matched directory and match the files. Subdirectories that are symlinks are only traversed if ``FOLLOW_SYMLINKS`` is given or policy :policy:`CMP0009` is not set to ``NEW``. +By default ``GLOB_RECURSE`` omits directories from result list - setting +``LIST_DIRECTORIES`` to true adds directories to result list. +If ``FOLLOW_SYMLINKS`` is given or policy :policy:`CMP0009` is not set to +``OLD`` then ``LIST_DIRECTORIES`` treats symlinks as directories. + Examples of recursive globbing include:: /dir/*.py - match all python files in /dir and subdirectories diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index ec22ea0a5..ae9099e93 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -920,6 +920,35 @@ bool cmFileCommand::HandleGlobCommand(std::vector const& args, bool first = true; for ( ; i != args.end(); ++i ) { + if( *i == "LIST_DIRECTORIES" ) + { + ++i; + if(i != args.end()) + { + if(cmSystemTools::IsOn(i->c_str())) + { + g.SetListDirs(true); + g.SetRecurseListDirs(true); + } + else if(cmSystemTools::IsOff(i->c_str())) + { + g.SetListDirs(false); + g.SetRecurseListDirs(false); + } + else + { + this->SetError("LIST_DIRECTORIES missing bool value."); + return false; + } + } + else + { + this->SetError("LIST_DIRECTORIES missing bool value."); + return false; + } + ++i; + } + if ( recurse && (*i == "FOLLOW_SYMLINKS") ) { explicitFollowSymlinks = true; @@ -950,6 +979,7 @@ bool cmFileCommand::HandleGlobCommand(std::vector const& args, } } + cmsys::Glob::GlobMessages globMessages; if ( !cmsys::SystemTools::FileIsFullPath(i->c_str()) ) { std::string expr = this->Makefile->GetCurrentDirectory(); @@ -957,16 +987,42 @@ bool cmFileCommand::HandleGlobCommand(std::vector const& args, if (!expr.empty()) { expr += "/" + *i; - g.FindFiles(expr); + g.FindFiles(expr, &globMessages); } else { - g.FindFiles(*i); + g.FindFiles(*i, &globMessages); } } else { - g.FindFiles(*i); + g.FindFiles(*i, &globMessages); + } + + if(!globMessages.empty()) + { + bool shouldExit = false; + for(cmsys::Glob::GlobMessagesIterator it=globMessages.begin(); + it != globMessages.end(); ++it) + { + if(it->type == cmsys::Glob::cyclicRecursion) + { + this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, + "Cyclic recursion detected while globbing for '" + + *i + "':\n" + it->content); + } + else + { + this->Makefile->IssueMessage(cmake::FATAL_ERROR, + "Error has occured while globbing for '" + + *i + "' - " + it->content); + shouldExit = true; + } + } + if(shouldExit) + { + return false; + } } std::vector::size_type cc; diff --git a/Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-no-arg-result.txt b/Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-no-arg-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-no-arg-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-no-arg-stderr.txt b/Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-no-arg-stderr.txt new file mode 100644 index 000000000..9629cfd68 --- /dev/null +++ b/Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-no-arg-stderr.txt @@ -0,0 +1 @@ +.*file LIST_DIRECTORIES missing bool value\. diff --git a/Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-no-arg.cmake b/Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-no-arg.cmake new file mode 100644 index 000000000..a8e15f23e --- /dev/null +++ b/Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-no-arg.cmake @@ -0,0 +1 @@ +file(GLOB CONTENT_LIST LIST_DIRECTORIES) diff --git a/Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-not-boolean-result.txt b/Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-not-boolean-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-not-boolean-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-not-boolean-stderr.txt b/Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-not-boolean-stderr.txt new file mode 100644 index 000000000..9629cfd68 --- /dev/null +++ b/Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-not-boolean-stderr.txt @@ -0,0 +1 @@ +.*file LIST_DIRECTORIES missing bool value\. diff --git a/Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-not-boolean.cmake b/Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-not-boolean.cmake new file mode 100644 index 000000000..f73543398 --- /dev/null +++ b/Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-not-boolean.cmake @@ -0,0 +1 @@ +file(GLOB CONTENT_LIST LIST_DIRECTORIES 13) diff --git a/Tests/RunCMake/file/GLOB-stderr.txt b/Tests/RunCMake/file/GLOB-stderr.txt new file mode 100644 index 000000000..c47dc4097 --- /dev/null +++ b/Tests/RunCMake/file/GLOB-stderr.txt @@ -0,0 +1,6 @@ +content: 6[ ] +.*/test/dir 1/dir 1 file;.*/test/dir 1/empty_dir;.*/test/dir 1/non_empty_dir;.*/test/dir 2/dir 2 file;.*/test/dir 2/empty_dir;.*/test/dir 2/non_empty_dir +content: 6[ ] +.*/test/dir 1/dir 1 file;.*/test/dir 1/empty_dir;.*/test/dir 1/non_empty_dir;.*/test/dir 2/dir 2 file;.*/test/dir 2/empty_dir;.*/test/dir 2/non_empty_dir +content: 2[ ] +.*/test/dir 1/dir 1 file;.*/test/dir 2/dir 2 file diff --git a/Tests/RunCMake/file/GLOB.cmake b/Tests/RunCMake/file/GLOB.cmake new file mode 100644 index 000000000..3d577e3a0 --- /dev/null +++ b/Tests/RunCMake/file/GLOB.cmake @@ -0,0 +1,28 @@ +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test/dir 1/empty_dir") +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test/dir 1/non_empty_dir") +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test/dir 2/empty_dir") +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test/dir 2/non_empty_dir") + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/dir 1/dir 1 file" "test file") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/dir 1/non_empty_dir/dir 1 subdir file" "test file") + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/dir 2/dir 2 file" "test file") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/dir 2/non_empty_dir/dir 2 subdir file" "test file") + +file(GLOB CONTENT_LIST "${CMAKE_CURRENT_BINARY_DIR}/test/*/*") +list(LENGTH CONTENT_LIST CONTENT_COUNT) +message("content: ${CONTENT_COUNT} ") +list(SORT CONTENT_LIST) +message("${CONTENT_LIST}") + +file(GLOB CONTENT_LIST LIST_DIRECTORIES true "${CMAKE_CURRENT_BINARY_DIR}/test/*/*") +list(LENGTH CONTENT_LIST CONTENT_COUNT) +message("content: ${CONTENT_COUNT} ") +list(SORT CONTENT_LIST) +message("${CONTENT_LIST}") + +file(GLOB CONTENT_LIST LIST_DIRECTORIES false "${CMAKE_CURRENT_BINARY_DIR}/test/*/*") +list(LENGTH CONTENT_LIST CONTENT_COUNT) +message("content: ${CONTENT_COUNT} ") +list(SORT CONTENT_LIST) +message("${CONTENT_LIST}") diff --git a/Tests/RunCMake/file/GLOB_RECURSE-cyclic-recursion-stderr.txt b/Tests/RunCMake/file/GLOB_RECURSE-cyclic-recursion-stderr.txt new file mode 100644 index 000000000..f73aa834e --- /dev/null +++ b/Tests/RunCMake/file/GLOB_RECURSE-cyclic-recursion-stderr.txt @@ -0,0 +1,15 @@ +.*Cyclic recursion detected while globbing for.* +.*/test/depth1/depth2/depth3.* +.*/test/depth1/depth2/depth3/recursion.* +content: 4[ ] +.*/test/abc;.*/test/depth1/depth2/depth3/file_symlink;.*/test/depth1/depth2/depth3/recursion/abc;.*/test/depth1/depth2/depth3/recursion/depth1/depth2/depth3/file_symlink +.*Cyclic recursion detected while globbing for.* +.*/test/depth1/depth2/depth3.* +.*/test/depth1/depth2/depth3/recursion.* +content: 4[ ] +.*/test/abc;.*/test/depth1/depth2/depth3/file_symlink;.*/test/depth1/depth2/depth3/recursion/abc;.*/test/depth1/depth2/depth3/recursion/depth1/depth2/depth3/file_symlink +.*Cyclic recursion detected while globbing for.* +.*/test/depth1/depth2/depth3.* +.*/test/depth1/depth2/depth3/recursion.* +content: 11[ ] +.*/test/abc;.*/test/depth1;.*/test/depth1/depth2;.*/test/depth1/depth2/depth3;.*/test/depth1/depth2/depth3/file_symlink;.*/test/depth1/depth2/depth3/recursion;.*/test/depth1/depth2/depth3/recursion/abc;.*/test/depth1/depth2/depth3/recursion/depth1;.*/test/depth1/depth2/depth3/recursion/depth1/depth2;.*/test/depth1/depth2/depth3/recursion/depth1/depth2/depth3;.*/test/depth1/depth2/depth3/recursion/depth1/depth2/depth3/file_symlink diff --git a/Tests/RunCMake/file/GLOB_RECURSE-cyclic-recursion.cmake b/Tests/RunCMake/file/GLOB_RECURSE-cyclic-recursion.cmake new file mode 100644 index 000000000..a8c67849f --- /dev/null +++ b/Tests/RunCMake/file/GLOB_RECURSE-cyclic-recursion.cmake @@ -0,0 +1,23 @@ +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test/depth1/depth2/depth3") +execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "${CMAKE_CURRENT_BINARY_DIR}/test" "${CMAKE_CURRENT_BINARY_DIR}/test/depth1/depth2/depth3/recursion") + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/abc" "message to write") +execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "${CMAKE_CURRENT_BINARY_DIR}/test/abc" "${CMAKE_CURRENT_BINARY_DIR}/test/depth1/depth2/depth3/file_symlink") + +file(GLOB_RECURSE CONTENT_LIST FOLLOW_SYMLINKS "${CMAKE_CURRENT_BINARY_DIR}/test/*") +list(LENGTH CONTENT_LIST CONTENT_COUNT) +message("content: ${CONTENT_COUNT} ") +list(SORT CONTENT_LIST) +message("${CONTENT_LIST}") + +file(GLOB_RECURSE CONTENT_LIST LIST_DIRECTORIES false FOLLOW_SYMLINKS "${CMAKE_CURRENT_BINARY_DIR}/test/*") +list(LENGTH CONTENT_LIST CONTENT_COUNT) +message("content: ${CONTENT_COUNT} ") +list(SORT CONTENT_LIST) +message("${CONTENT_LIST}") + +file(GLOB_RECURSE CONTENT_LIST LIST_DIRECTORIES true FOLLOW_SYMLINKS "${CMAKE_CURRENT_BINARY_DIR}/test/*") +list(LENGTH CONTENT_LIST CONTENT_COUNT) +message("content: ${CONTENT_COUNT} ") +list(SORT CONTENT_LIST) +message("${CONTENT_LIST}") diff --git a/Tests/RunCMake/file/GLOB_RECURSE-stderr.txt b/Tests/RunCMake/file/GLOB_RECURSE-stderr.txt new file mode 100644 index 000000000..5d48e4730 --- /dev/null +++ b/Tests/RunCMake/file/GLOB_RECURSE-stderr.txt @@ -0,0 +1,6 @@ +content: 4[ ] +.*/test/dir 1/dir 1 file;.*/test/dir 1/non_empty_dir/dir 1 subdir file;.*/test/dir 2/dir 2 file;.*/test/dir 2/non_empty_dir/dir 2 subdir file +content: 4[ ] +.*/test/dir 1/dir 1 file;.*/test/dir 1/non_empty_dir/dir 1 subdir file;.*/test/dir 2/dir 2 file;.*/test/dir 2/non_empty_dir/dir 2 subdir file +content: 8[ ] +.*/test/dir 1/dir 1 file;.*/test/dir 1/empty_dir;.*/test/dir 1/non_empty_dir;.*/test/dir 1/non_empty_dir/dir 1 subdir file;.*/test/dir 2/dir 2 file;.*/test/dir 2/empty_dir;.*/test/dir 2/non_empty_dir;.*/test/dir 2/non_empty_dir/dir 2 subdir file diff --git a/Tests/RunCMake/file/GLOB_RECURSE.cmake b/Tests/RunCMake/file/GLOB_RECURSE.cmake new file mode 100644 index 000000000..6db377b41 --- /dev/null +++ b/Tests/RunCMake/file/GLOB_RECURSE.cmake @@ -0,0 +1,28 @@ +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test/dir 1/empty_dir") +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test/dir 1/non_empty_dir") +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test/dir 2/empty_dir") +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test/dir 2/non_empty_dir") + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/dir 1/dir 1 file" "test file") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/dir 1/non_empty_dir/dir 1 subdir file" "test file") + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/dir 2/dir 2 file" "test file") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/dir 2/non_empty_dir/dir 2 subdir file" "test file") + +file(GLOB_RECURSE CONTENT_LIST "${CMAKE_CURRENT_BINARY_DIR}/test/*/*") +list(LENGTH CONTENT_LIST CONTENT_COUNT) +message("content: ${CONTENT_COUNT} ") +list(SORT CONTENT_LIST) +message("${CONTENT_LIST}") + +file(GLOB_RECURSE CONTENT_LIST LIST_DIRECTORIES false "${CMAKE_CURRENT_BINARY_DIR}/test/*/*") +list(LENGTH CONTENT_LIST CONTENT_COUNT) +message("content: ${CONTENT_COUNT} ") +list(SORT CONTENT_LIST) +message("${CONTENT_LIST}") + +file(GLOB_RECURSE CONTENT_LIST LIST_DIRECTORIES true "${CMAKE_CURRENT_BINARY_DIR}/test/*/*") +list(LENGTH CONTENT_LIST CONTENT_COUNT) +message("content: ${CONTENT_COUNT} ") +list(SORT CONTENT_LIST) +message("${CONTENT_LIST}") diff --git a/Tests/RunCMake/file/RunCMakeTest.cmake b/Tests/RunCMake/file/RunCMakeTest.cmake index 14819e721..d3dfb1b7c 100644 --- a/Tests/RunCMake/file/RunCMakeTest.cmake +++ b/Tests/RunCMake/file/RunCMakeTest.cmake @@ -17,3 +17,13 @@ run_cmake(LOCK-error-no-result-variable) run_cmake(LOCK-error-no-timeout) run_cmake(LOCK-error-timeout) run_cmake(LOCK-error-unknown-option) +run_cmake(GLOB) +run_cmake(GLOB_RECURSE) +# test is valid both for GLOB and GLOB_RECURSE +run_cmake(GLOB-error-LIST_DIRECTORIES-not-boolean) +# test is valid both for GLOB and GLOB_RECURSE +run_cmake(GLOB-error-LIST_DIRECTORIES-no-arg) + +if(NOT WIN32 OR CYGWIN) + run_cmake(GLOB_RECURSE-cyclic-recursion) +endif() From 6e54b0b91032d858ae33b3cb093b5e4c9e15970e Mon Sep 17 00:00:00 2001 From: Brad King Date: Fri, 20 Mar 2015 09:43:37 -0400 Subject: [PATCH 2/2] Help: Add notes for topic 'file-globbing-directory-listing' --- Help/release/dev/file-globbing-directory-listing.rst | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Help/release/dev/file-globbing-directory-listing.rst diff --git a/Help/release/dev/file-globbing-directory-listing.rst b/Help/release/dev/file-globbing-directory-listing.rst new file mode 100644 index 000000000..c4d7ba500 --- /dev/null +++ b/Help/release/dev/file-globbing-directory-listing.rst @@ -0,0 +1,6 @@ +file-globbing-directory-listing +------------------------------- + +* The :command:`file(GLOB)` and :command:`file(GLOB_RECURSE)` commands + learned a new ``LIST_DIRECTORIES `` option to specify whether + the glob result should include directories.