mirror of
https://github.com/Kitware/CMake.git
synced 2025-10-15 20:46:37 +08:00
file(MAKE_DIRECTORY): Add optional RESULT keyword to capture failure.
Fixes: #26041
This commit is contained in:
@@ -398,10 +398,19 @@ Filesystem
|
|||||||
============== ======================================================
|
============== ======================================================
|
||||||
|
|
||||||
.. signature::
|
.. signature::
|
||||||
file(MAKE_DIRECTORY <directories>...)
|
file(MAKE_DIRECTORY <directories>... [RESULT <result>])
|
||||||
|
|
||||||
Create the given directories and their parents as needed.
|
Create the given directories and their parents as needed.
|
||||||
|
|
||||||
|
The options are:
|
||||||
|
|
||||||
|
``RESULT <result>``
|
||||||
|
.. versionadded:: 3.31
|
||||||
|
|
||||||
|
Set ``<result>`` 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
|
.. versionchanged:: 3.30
|
||||||
``<directories>`` can be an empty list. CMake 3.29 and earlier required
|
``<directories>`` can be an empty list. CMake 3.29 and earlier required
|
||||||
at least one directory to be given.
|
at least one directory to be given.
|
||||||
|
5
Help/dev/make_directory-optional-result.rst
Normal file
5
Help/dev/make_directory-optional-result.rst
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
make_directory-optional-result
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
* The :command:`file(MAKE_DIRECTORY)` learned to
|
||||||
|
optionally capture failure in a result variable.
|
@@ -8,6 +8,7 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <iterator>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -881,6 +882,42 @@ bool HandleMakeDirectoryCommand(std::vector<std::string> const& args,
|
|||||||
// Projects might pass a dynamically generated list of directories, and it
|
// 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.
|
// could be an empty list. We should not assume there is at least one.
|
||||||
|
|
||||||
|
cmRange<std::vector<std::string>::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<Arguments>{}.Bind("RESULT"_s, &Arguments::Result);
|
||||||
|
std::vector<std::string> 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;
|
std::string expr;
|
||||||
for (std::string const& arg :
|
for (std::string const& arg :
|
||||||
cmMakeRange(args).advance(1)) // Get rid of subcommand
|
cmMakeRange(args).advance(1)) // Get rid of subcommand
|
||||||
@@ -892,19 +929,33 @@ bool HandleMakeDirectoryCommand(std::vector<std::string> const& args,
|
|||||||
cdir = &expr;
|
cdir = &expr;
|
||||||
}
|
}
|
||||||
if (!status.GetMakefile().CanIWriteThisFile(*cdir)) {
|
if (!status.GetMakefile().CanIWriteThisFile(*cdir)) {
|
||||||
std::string e = "attempted to create a directory: " + *cdir +
|
std::string e = cmStrCat("attempted to create a directory: ", *cdir,
|
||||||
" into a source directory.";
|
" into a source directory.");
|
||||||
|
if (arguments.Result.empty()) {
|
||||||
status.SetError(e);
|
status.SetError(e);
|
||||||
cmSystemTools::SetFatalErrorOccurred();
|
cmSystemTools::SetFatalErrorOccurred();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
status.GetMakefile().AddDefinition(arguments.Result, e);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
cmsys::Status mkdirStatus = cmSystemTools::MakeDirectory(*cdir);
|
cmsys::Status mkdirStatus = cmSystemTools::MakeDirectory(*cdir);
|
||||||
if (!mkdirStatus) {
|
if (!mkdirStatus) {
|
||||||
std::string error = cmStrCat("failed to create directory:\n ", *cdir,
|
if (arguments.Result.empty()) {
|
||||||
|
std::string errorOutput =
|
||||||
|
cmStrCat("failed to create directory:\n ", *cdir,
|
||||||
"\nbecause: ", mkdirStatus.GetString());
|
"\nbecause: ", mkdirStatus.GetString());
|
||||||
status.SetError(error);
|
status.SetError(errorOutput);
|
||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -588,6 +588,7 @@ foreach(var
|
|||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
add_RunCMake_test(file-DOWNLOAD)
|
add_RunCMake_test(file-DOWNLOAD)
|
||||||
|
add_RunCMake_test(file-MAKE_DIRECTORY)
|
||||||
add_RunCMake_test(file-RPATH
|
add_RunCMake_test(file-RPATH
|
||||||
-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
|
-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
|
||||||
-DCMake_TEST_ELF_LARGE=${CMake_TEST_ELF_LARGE}
|
-DCMake_TEST_ELF_LARGE=${CMake_TEST_ELF_LARGE}
|
||||||
|
3
Tests/RunCMake/file-MAKE_DIRECTORY/CMakeLists.txt
Normal file
3
Tests/RunCMake/file-MAKE_DIRECTORY/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
project(${RunCMake_TEST} NONE)
|
||||||
|
include(${RunCMake_TEST}.cmake)
|
@@ -0,0 +1,3 @@
|
|||||||
|
^-- Result=Failed to create directory: [^
|
||||||
|
]*/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-FAIL-build/file/directory0 Error: [^
|
||||||
|
]*$
|
@@ -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}")
|
@@ -0,0 +1 @@
|
|||||||
|
^-- Result=0
|
@@ -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}")
|
@@ -0,0 +1,3 @@
|
|||||||
|
^-- Result=Failed to create directory: [^
|
||||||
|
]*/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-FAIL-build/file/directory Error: [^
|
||||||
|
]*$
|
@@ -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}")
|
@@ -0,0 +1 @@
|
|||||||
|
^-- Result=0
|
@@ -0,0 +1,2 @@
|
|||||||
|
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/file/directory" RESULT resultVal)
|
||||||
|
message(STATUS "Result=${resultVal}")
|
@@ -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: [^
|
||||||
|
]+$
|
7
Tests/RunCMake/file-MAKE_DIRECTORY/RunCMakeTest.cmake
Normal file
7
Tests/RunCMake/file-MAKE_DIRECTORY/RunCMakeTest.cmake
Normal file
@@ -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)
|
@@ -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: [^
|
|
||||||
]+$
|
|
@@ -62,8 +62,6 @@ run_cmake_script(COPY_FILE-arg-unknown)
|
|||||||
run_cmake_script(COPY_FILE-input-missing)
|
run_cmake_script(COPY_FILE-input-missing)
|
||||||
run_cmake_script(COPY_FILE-output-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-replace)
|
||||||
run_cmake_script(RENAME-file-to-file)
|
run_cmake_script(RENAME-file-to-file)
|
||||||
run_cmake_script(RENAME-file-to-dir-capture)
|
run_cmake_script(RENAME-file-to-dir-capture)
|
||||||
|
Reference in New Issue
Block a user