diff --git a/Help/command/file.rst b/Help/command/file.rst index 5b9dfac5a0..ede95a131e 100644 --- a/Help/command/file.rst +++ b/Help/command/file.rst @@ -398,10 +398,19 @@ Filesystem ============== ====================================================== .. signature:: - file(MAKE_DIRECTORY ...) + file(MAKE_DIRECTORY ... [RESULT ]) Create the given directories and their parents as needed. + The options are: + + ``RESULT `` + .. versionadded:: 3.31 + + Set ```` variable to ``0`` on success or an error message + otherwise. If ``RESULT`` is not specified and the operation fails, + an error is emitted. + .. versionchanged:: 3.30 ```` can be an empty list. CMake 3.29 and earlier required at least one directory to be given. diff --git a/Help/dev/make_directory-optional-result.rst b/Help/dev/make_directory-optional-result.rst new file mode 100644 index 0000000000..be842fe449 --- /dev/null +++ b/Help/dev/make_directory-optional-result.rst @@ -0,0 +1,5 @@ +make_directory-optional-result +------------------------------ + +* The :command:`file(MAKE_DIRECTORY)` learned to +optionally capture failure in a result variable. diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index ce8cc2a39f..88555b6d97 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -881,6 +882,42 @@ bool HandleMakeDirectoryCommand(std::vector const& args, // Projects might pass a dynamically generated list of directories, and it // could be an empty list. We should not assume there is at least one. + cmRange::const_iterator> argsRange = + cmMakeRange(args).advance(1); // Get rid of subcommand + + struct Arguments : public ArgumentParser::ParseResult + { + std::string Result; + }; + Arguments arguments; + + auto resultPosItr = + std::find(cm::begin(argsRange), cm::end(argsRange), "RESULT"); + if (resultPosItr != cm::end(argsRange)) { + static auto const parser = + cmArgumentParser{}.Bind("RESULT"_s, &Arguments::Result); + std::vector unparsedArguments; + auto resultDistanceFromBegin = + std::distance(cm::begin(argsRange), resultPosItr); + arguments = + parser.Parse(cmMakeRange(argsRange).advance(resultDistanceFromBegin), + &unparsedArguments); + + if (!unparsedArguments.empty()) { + std::string unexpectedArgsStr = cmJoin( + cmMakeRange(cm::begin(unparsedArguments), cm::end(unparsedArguments)), + "\n"); + status.SetError("MAKE_DIRECTORY called with unexpected\n" + "arguments:\n" + + unexpectedArgsStr); + return false; + } + + auto resultDistanceFromEnd = + std::distance(cm::end(argsRange), resultPosItr); + argsRange = argsRange.retreat(-resultDistanceFromEnd); + } + std::string expr; for (std::string const& arg : cmMakeRange(args).advance(1)) // Get rid of subcommand @@ -892,20 +929,34 @@ bool HandleMakeDirectoryCommand(std::vector const& args, cdir = &expr; } if (!status.GetMakefile().CanIWriteThisFile(*cdir)) { - std::string e = "attempted to create a directory: " + *cdir + - " into a source directory."; - status.SetError(e); - cmSystemTools::SetFatalErrorOccurred(); - return false; + std::string e = cmStrCat("attempted to create a directory: ", *cdir, + " into a source directory."); + if (arguments.Result.empty()) { + status.SetError(e); + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + status.GetMakefile().AddDefinition(arguments.Result, e); + return true; } cmsys::Status mkdirStatus = cmSystemTools::MakeDirectory(*cdir); if (!mkdirStatus) { - std::string error = cmStrCat("failed to create directory:\n ", *cdir, - "\nbecause: ", mkdirStatus.GetString()); - status.SetError(error); - return false; + if (arguments.Result.empty()) { + std::string errorOutput = + cmStrCat("failed to create directory:\n ", *cdir, + "\nbecause: ", mkdirStatus.GetString()); + status.SetError(errorOutput); + return false; + } + std::string errorResult = cmStrCat("Failed to create directory: ", *cdir, + " Error: ", mkdirStatus.GetString()); + status.GetMakefile().AddDefinition(arguments.Result, errorResult); + return true; } } + if (!arguments.Result.empty()) { + status.GetMakefile().AddDefinition(arguments.Result, "0"); + } return true; } diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 213c18d044..49773592a4 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -588,6 +588,7 @@ foreach(var endif() endforeach() add_RunCMake_test(file-DOWNLOAD) +add_RunCMake_test(file-MAKE_DIRECTORY) add_RunCMake_test(file-RPATH -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} -DCMake_TEST_ELF_LARGE=${CMake_TEST_ELF_LARGE} diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/CMakeLists.txt b/Tests/RunCMake/file-MAKE_DIRECTORY/CMakeLists.txt new file mode 100644 index 0000000000..93ee9dfd5f --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.5) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-FAIL-stdout.txt b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-FAIL-stdout.txt new file mode 100644 index 0000000000..63d46fd0a1 --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-FAIL-stdout.txt @@ -0,0 +1,3 @@ +^-- Result=Failed to create directory: [^ +]*/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-FAIL-build/file/directory0 Error: [^ +]*$ diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-FAIL.cmake b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-FAIL.cmake new file mode 100644 index 0000000000..0cfccbfae7 --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-FAIL.cmake @@ -0,0 +1,9 @@ +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/file" "") + +file(MAKE_DIRECTORY + "${CMAKE_CURRENT_BINARY_DIR}/file/directory0" + "${CMAKE_CURRENT_BINARY_DIR}/file/directory1" + "${CMAKE_CURRENT_BINARY_DIR}/file/directory2" + RESULT resultVal +) +message(STATUS "Result=${resultVal}") diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-SUCCESS-stdout.txt b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-SUCCESS-stdout.txt new file mode 100644 index 0000000000..09df4f9e55 --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-SUCCESS-stdout.txt @@ -0,0 +1 @@ +^-- Result=0 diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-SUCCESS.cmake b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-SUCCESS.cmake new file mode 100644 index 0000000000..e0781ce175 --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-SUCCESS.cmake @@ -0,0 +1,7 @@ +file(MAKE_DIRECTORY + "${CMAKE_CURRENT_BINARY_DIR}/file/directory0" + "${CMAKE_CURRENT_BINARY_DIR}/file/directory1" + "${CMAKE_CURRENT_BINARY_DIR}/file/directory2" + RESULT resultVal +) +message(STATUS "Result=${resultVal}") diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-FAIL-stdout.txt b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-FAIL-stdout.txt new file mode 100644 index 0000000000..5d16178ac8 --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-FAIL-stdout.txt @@ -0,0 +1,3 @@ +^-- Result=Failed to create directory: [^ +]*/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-FAIL-build/file/directory Error: [^ +]*$ diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-FAIL.cmake b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-FAIL.cmake new file mode 100644 index 0000000000..0287d67910 --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-FAIL.cmake @@ -0,0 +1,3 @@ +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/file" "") +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/file/directory" RESULT resultVal) +message(STATUS "Result=${resultVal}") diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-SUCCESS-stdout.txt b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-SUCCESS-stdout.txt new file mode 100644 index 0000000000..09df4f9e55 --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-SUCCESS-stdout.txt @@ -0,0 +1 @@ +^-- Result=0 diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-SUCCESS.cmake b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-SUCCESS.cmake new file mode 100644 index 0000000000..3005b830e9 --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-SUCCESS.cmake @@ -0,0 +1,2 @@ +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/file/directory" RESULT resultVal) +message(STATUS "Result=${resultVal}") diff --git a/Tests/RunCMake/file/MAKE_DIRECTORY-fail-result.txt b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL-result.txt similarity index 100% rename from Tests/RunCMake/file/MAKE_DIRECTORY-fail-result.txt rename to Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL-result.txt diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL-stderr.txt b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL-stderr.txt new file mode 100644 index 0000000000..2bc275c548 --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL-stderr.txt @@ -0,0 +1,9 @@ +^CMake Error at [^ +]*/MAKE_DIRECTORY-one-dir-FAIL.cmake:[0-9]+ \(file\): + file failed to create directory: + + [^ +]*/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL-build/file/directory + + because: [^ +]+$ diff --git a/Tests/RunCMake/file/MAKE_DIRECTORY-fail.cmake b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL.cmake similarity index 100% rename from Tests/RunCMake/file/MAKE_DIRECTORY-fail.cmake rename to Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL.cmake diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/RunCMakeTest.cmake b/Tests/RunCMake/file-MAKE_DIRECTORY/RunCMakeTest.cmake new file mode 100644 index 0000000000..1eacd9065e --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/RunCMakeTest.cmake @@ -0,0 +1,7 @@ +include(RunCMake) + +run_cmake_script(MAKE_DIRECTORY-one-dir-FAIL) +run_cmake_script(MAKE_DIRECTORY-Result-one-dir-FAIL) +run_cmake_script(MAKE_DIRECTORY-Result-one-dir-SUCCESS) +run_cmake_script(MAKE_DIRECTORY-Result-many-dirs-FAIL) +run_cmake_script(MAKE_DIRECTORY-Result-many-dirs-SUCCESS) diff --git a/Tests/RunCMake/file/MAKE_DIRECTORY-fail-stderr.txt b/Tests/RunCMake/file/MAKE_DIRECTORY-fail-stderr.txt deleted file mode 100644 index 95fccdf9cb..0000000000 --- a/Tests/RunCMake/file/MAKE_DIRECTORY-fail-stderr.txt +++ /dev/null @@ -1,9 +0,0 @@ -^CMake Error at [^ -]*/MAKE_DIRECTORY-fail.cmake:[0-9]+ \(file\): - file failed to create directory: - - [^ -]*/Tests/RunCMake/file/MAKE_DIRECTORY-fail-build/file/directory - - because: [^ -]+$ diff --git a/Tests/RunCMake/file/RunCMakeTest.cmake b/Tests/RunCMake/file/RunCMakeTest.cmake index be8ee7c67a..524636d32a 100644 --- a/Tests/RunCMake/file/RunCMakeTest.cmake +++ b/Tests/RunCMake/file/RunCMakeTest.cmake @@ -62,8 +62,6 @@ run_cmake_script(COPY_FILE-arg-unknown) run_cmake_script(COPY_FILE-input-missing) run_cmake_script(COPY_FILE-output-missing) -run_cmake_script(MAKE_DIRECTORY-fail) - run_cmake_script(RENAME-file-replace) run_cmake_script(RENAME-file-to-file) run_cmake_script(RENAME-file-to-dir-capture)