From 95323c90a1a7599272d4e2a074d3ab4c856095cd Mon Sep 17 00:00:00 2001 From: Asit Dhal Date: Sun, 16 Jun 2024 19:44:03 +0200 Subject: file(MAKE_DIRECTORY): Add optional RESULT keyword to capture failure. Fixes: #26041 --- Help/command/file.rst | 11 +++- Help/dev/make_directory-optional-result.rst | 5 ++ Source/cmFileCommand.cxx | 69 +++++++++++++++++++--- Tests/RunCMake/CMakeLists.txt | 1 + Tests/RunCMake/file-MAKE_DIRECTORY/CMakeLists.txt | 3 + ...MAKE_DIRECTORY-Result-many-dirs-FAIL-stdout.txt | 3 + .../MAKE_DIRECTORY-Result-many-dirs-FAIL.cmake | 9 +++ ...E_DIRECTORY-Result-many-dirs-SUCCESS-stdout.txt | 1 + .../MAKE_DIRECTORY-Result-many-dirs-SUCCESS.cmake | 7 +++ .../MAKE_DIRECTORY-Result-one-dir-FAIL-stdout.txt | 3 + .../MAKE_DIRECTORY-Result-one-dir-FAIL.cmake | 3 + ...AKE_DIRECTORY-Result-one-dir-SUCCESS-stdout.txt | 1 + .../MAKE_DIRECTORY-Result-one-dir-SUCCESS.cmake | 2 + .../MAKE_DIRECTORY-one-dir-FAIL-result.txt | 1 + .../MAKE_DIRECTORY-one-dir-FAIL-stderr.txt | 9 +++ .../MAKE_DIRECTORY-one-dir-FAIL.cmake | 2 + .../file-MAKE_DIRECTORY/RunCMakeTest.cmake | 7 +++ Tests/RunCMake/file/MAKE_DIRECTORY-fail-result.txt | 1 - Tests/RunCMake/file/MAKE_DIRECTORY-fail-stderr.txt | 9 --- Tests/RunCMake/file/MAKE_DIRECTORY-fail.cmake | 2 - Tests/RunCMake/file/RunCMakeTest.cmake | 2 - 21 files changed, 127 insertions(+), 24 deletions(-) create mode 100644 Help/dev/make_directory-optional-result.rst create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/CMakeLists.txt create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-FAIL-stdout.txt create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-FAIL.cmake create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-SUCCESS-stdout.txt create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-SUCCESS.cmake create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-FAIL-stdout.txt create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-FAIL.cmake create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-SUCCESS-stdout.txt create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-SUCCESS.cmake create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL-result.txt create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL-stderr.txt create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL.cmake create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/RunCMakeTest.cmake delete mode 100644 Tests/RunCMake/file/MAKE_DIRECTORY-fail-result.txt delete mode 100644 Tests/RunCMake/file/MAKE_DIRECTORY-fail-stderr.txt delete mode 100644 Tests/RunCMake/file/MAKE_DIRECTORY-fail.cmake diff --git a/Help/command/file.rst b/Help/command/file.rst index 5b9dfac..ede95a1 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 0000000..be842fe --- /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 ce8cc2a..88555b6 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 213c18d..4977359 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 0000000..93ee9df --- /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 0000000..63d46fd --- /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 0000000..0cfccbf --- /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 0000000..09df4f9 --- /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 0000000..e0781ce --- /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 0000000..5d16178 --- /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 0000000..0287d67 --- /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 0000000..09df4f9 --- /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 0000000..3005b83 --- /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/MAKE_DIRECTORY-one-dir-FAIL-result.txt b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL-result.txt @@ -0,0 +1 @@ +1 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 0000000..2bc275c --- /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/MAKE_DIRECTORY-one-dir-FAIL.cmake b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL.cmake new file mode 100644 index 0000000..57a68e5 --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL.cmake @@ -0,0 +1,2 @@ +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/file" "") +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/file/directory") diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/RunCMakeTest.cmake b/Tests/RunCMake/file-MAKE_DIRECTORY/RunCMakeTest.cmake new file mode 100644 index 0000000..1eacd90 --- /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-result.txt b/Tests/RunCMake/file/MAKE_DIRECTORY-fail-result.txt deleted file mode 100644 index d00491f..0000000 --- a/Tests/RunCMake/file/MAKE_DIRECTORY-fail-result.txt +++ /dev/null @@ -1 +0,0 @@ -1 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 95fccdf..0000000 --- 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/MAKE_DIRECTORY-fail.cmake b/Tests/RunCMake/file/MAKE_DIRECTORY-fail.cmake deleted file mode 100644 index 57a68e5..0000000 --- a/Tests/RunCMake/file/MAKE_DIRECTORY-fail.cmake +++ /dev/null @@ -1,2 +0,0 @@ -file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/file" "") -file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/file/directory") diff --git a/Tests/RunCMake/file/RunCMakeTest.cmake b/Tests/RunCMake/file/RunCMakeTest.cmake index be8ee7c..524636d 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) -- cgit v0.12