Browse Source

Merge topic 'file-globbing-directory-listing'

6e54b0b9 Help: Add notes for topic 'file-globbing-directory-listing'
a2c068a7 file: Teach GLOB to list directories optionally
Brad King 10 years ago
parent
commit
c95e523db8

+ 14 - 3
Help/command/file.rst

@@ -92,9 +92,12 @@ store it in a ``<variable>``.
 
 ::
 
-  file(GLOB <variable> [RELATIVE <path>] [<globbing-expressions>...])
-  file(GLOB_RECURSE <variable> [RELATIVE <path>]
-       [FOLLOW_SYMLINKS] [<globbing-expressions>...])
+  file(GLOB <variable>
+       [LIST_DIRECTORIES true|false] [RELATIVE <path>]
+       [<globbing-expressions>...])
+  file(GLOB_RECURSE <variable> [FOLLOW_SYMLINKS]
+       [LIST_DIRECTORIES true|false] [RELATIVE <path>]
+       [<globbing-expressions>...])
 
 Generate a list of files that match the ``<globbing-expressions>`` and
 store it into the ``<variable>``.  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

+ 6 - 0
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 <bool>`` option to specify whether
+  the glob result should include directories.

+ 59 - 3
Source/cmFileCommand.cxx

@@ -920,6 +920,35 @@ bool cmFileCommand::HandleGlobCommand(std::vector<std::string> 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<std::string> 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<std::string> 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<std::string>::size_type cc;

+ 1 - 0
Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-no-arg-result.txt

@@ -0,0 +1 @@
+1

+ 1 - 0
Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-no-arg-stderr.txt

@@ -0,0 +1 @@
+.*file LIST_DIRECTORIES missing bool value\.

+ 1 - 0
Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-no-arg.cmake

@@ -0,0 +1 @@
+file(GLOB CONTENT_LIST LIST_DIRECTORIES)

+ 1 - 0
Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-not-boolean-result.txt

@@ -0,0 +1 @@
+1

+ 1 - 0
Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-not-boolean-stderr.txt

@@ -0,0 +1 @@
+.*file LIST_DIRECTORIES missing bool value\.

+ 1 - 0
Tests/RunCMake/file/GLOB-error-LIST_DIRECTORIES-not-boolean.cmake

@@ -0,0 +1 @@
+file(GLOB CONTENT_LIST LIST_DIRECTORIES 13)

+ 6 - 0
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

+ 28 - 0
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}")

+ 15 - 0
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

+ 23 - 0
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}")

+ 6 - 0
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

+ 28 - 0
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}")

+ 10 - 0
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()