diff options
Diffstat (limited to 'Source')
92 files changed, 1669 insertions, 4667 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index ba378fb..cb954e5 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -441,6 +441,8 @@ set(SRCS cmTest.h cmTestGenerator.cxx cmTestGenerator.h + cmTransformDepfile.cxx + cmTransformDepfile.h cmUuid.cxx cmUVHandlePtr.cxx cmUVHandlePtr.h @@ -505,6 +507,8 @@ set(SRCS cmCMakeLanguageCommand.h cmCMakeMinimumRequired.cxx cmCMakeMinimumRequired.h + cmCMakePathCommand.h + cmCMakePathCommand.cxx cmCMakePolicyCommand.cxx cmCMakePolicyCommand.h cmConditionEvaluator.cxx @@ -1154,20 +1158,6 @@ add_executable(cmake cmakemain.cxx cmcmd.cxx cmcmd.h ${MANIFEST_FILE}) list(APPEND _tools cmake) target_link_libraries(cmake CMakeLib) -add_library(CMakeServerLib - cmConnection.h cmConnection.cxx - cmFileMonitor.cxx cmFileMonitor.h - cmJsonObjectDictionary.h - cmJsonObjects.h - cmJsonObjects.cxx - cmPipeConnection.cxx cmPipeConnection.h - cmServer.cxx cmServer.h - cmServerConnection.cxx cmServerConnection.h - cmServerProtocol.cxx cmServerProtocol.h - ) -target_link_libraries(CMakeServerLib CMakeLib) -target_link_libraries(cmake CMakeServerLib) - # Build CTest executable add_executable(ctest ctest.cxx ${MANIFEST_FILE}) list(APPEND _tools ctest) diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index c3b4521..6e0a944 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,8 +1,8 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 19) -set(CMake_VERSION_PATCH 0) -set(CMake_VERSION_RC 3) +set(CMake_VERSION_PATCH 20201118) +#set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) # Start with the full version number used in tags. It has no dev info. diff --git a/Source/CPack/cmCPackDebGenerator.cxx b/Source/CPack/cmCPackDebGenerator.cxx index 560e5c1..6f21d87 100644 --- a/Source/CPack/cmCPackDebGenerator.cxx +++ b/Source/CPack/cmCPackDebGenerator.cxx @@ -507,7 +507,8 @@ int cmCPackDebGenerator::PackageOnePack(std::string const& initialTopLevel, this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME")); packageFileNames.push_back(std::move(packageFileName)); - if (this->IsOn("GEN_CPACK_DEBIAN_DEBUGINFO_PACKAGE")) { + if (this->IsOn("GEN_CPACK_DEBIAN_DEBUGINFO_PACKAGE") && + this->GetOption("GEN_DBGSYMDIR")) { cmsys::Glob gl; std::string findExpr(this->GetOption("GEN_DBGSYMDIR")); findExpr += "/*"; diff --git a/Source/CPack/cmCPackGenerator.cxx b/Source/CPack/cmCPackGenerator.cxx index a9fe91c..8b544b4 100644 --- a/Source/CPack/cmCPackGenerator.cxx +++ b/Source/CPack/cmCPackGenerator.cxx @@ -1329,7 +1329,7 @@ bool cmCPackGenerator::ConfigureFile(const std::string& inName, bool copyOnly /* = false */) { return this->MakefileMap->ConfigureFile(inName, outName, copyOnly, true, - false, true) == 1; + false) == 1; } int cmCPackGenerator::CleanTemporaryDirectory() diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx index 4d1a589..84bb791 100644 --- a/Source/CTest/cmCTestTestHandler.cxx +++ b/Source/CTest/cmCTestTestHandler.cxx @@ -122,7 +122,7 @@ bool cmCTestSubdirCommand(std::vector<std::string> const& args, readit = status.GetMakefile().ReadDependentFile(fname); } if (!readit) { - status.SetError(cmStrCat("Could not find include file: ", fname)); + status.SetError(cmStrCat("Could not load include file: ", fname)); return false; } } diff --git a/Source/CTest/cmCTestUpdateCommand.cxx b/Source/CTest/cmCTestUpdateCommand.cxx index 6fef90a..0ba2c41 100644 --- a/Source/CTest/cmCTestUpdateCommand.cxx +++ b/Source/CTest/cmCTestUpdateCommand.cxx @@ -5,7 +5,6 @@ #include "cmCTest.h" #include "cmCTestUpdateHandler.h" #include "cmMakefile.h" -#include "cmProperty.h" #include "cmSystemTools.h" cmCTestGenericHandler* cmCTestUpdateCommand::InitializeHandler() @@ -18,7 +17,7 @@ cmCTestGenericHandler* cmCTestUpdateCommand::InitializeHandler() this->CTest->SetCTestConfiguration( "SourceDirectory", cmSystemTools::CollapseFullPath( - cmToCStrSafe(this->Makefile->GetDefinition("CTEST_SOURCE_DIRECTORY"))), + this->Makefile->GetSafeDefinition("CTEST_SOURCE_DIRECTORY")), this->Quiet); } std::string source_dir = diff --git a/Source/LexerParser/cmGccDepfileLexer.cxx b/Source/LexerParser/cmGccDepfileLexer.cxx index a98969d..3630f4e 100644 --- a/Source/LexerParser/cmGccDepfileLexer.cxx +++ b/Source/LexerParser/cmGccDepfileLexer.cxx @@ -994,7 +994,7 @@ case 5: YY_RULE_SETUP { // A line continuation ends the current file name. - yyextra->newDependency(); + yyextra->newRuleOrDependency(); } YY_BREAK case 6: diff --git a/Source/LexerParser/cmGccDepfileLexer.in.l b/Source/LexerParser/cmGccDepfileLexer.in.l index 08f8577..c83cb75 100644 --- a/Source/LexerParser/cmGccDepfileLexer.in.l +++ b/Source/LexerParser/cmGccDepfileLexer.in.l @@ -42,7 +42,7 @@ NEWLINE \r?\n } {WSPACE}*\\{NEWLINE} { // A line continuation ends the current file name. - yyextra->newDependency(); + yyextra->newRuleOrDependency(); } {NEWLINE} { // A newline ends the current file name and the current rule. diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx index 231a2d6..c1f98fa 100644 --- a/Source/cmAddCustomCommandCommand.cxx +++ b/Source/cmAddCustomCommandCommand.cxx @@ -190,15 +190,7 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args, case doing_byproducts: if (!cmSystemTools::FileIsFullPath(copy)) { // This is an output to be generated, so it should be - // under the build tree. CMake 2.4 placed this under the - // source tree. However the only case that this change - // will break is when someone writes - // - // add_custom_command(OUTPUT out.txt ...) - // - // and later references "${CMAKE_CURRENT_SOURCE_DIR}/out.txt". - // This is fairly obscure so we can wait for someone to - // complain. + // under the build tree. filename = cmStrCat(mf.GetCurrentBinaryDirectory(), '/'); } filename += copy; @@ -215,8 +207,7 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args, } if (cmSystemTools::FileIsFullPath(filename)) { - filename = cmSystemTools::CollapseFullPath( - filename, status.GetMakefile().GetHomeOutputDirectory()); + filename = cmSystemTools::CollapseFullPath(filename); } switch (doing) { case doing_depfile: @@ -314,16 +305,9 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args, // Check for an append request. if (append) { - if (mf.AppendCustomCommandToOutput(output[0], depends, implicit_depends, - commandLines)) { - return true; - } - - // No command for this output exists. - status.SetError( - cmStrCat("given APPEND option with output\n ", output[0], - "\nwhich is not already a custom command output.")); - return false; + mf.AppendCustomCommandToOutput(output[0], depends, implicit_depends, + commandLines); + return true; } if (uses_terminal && !job_pool.empty()) { diff --git a/Source/cmCMakePathCommand.cxx b/Source/cmCMakePathCommand.cxx index 720f582..85e7d9e 100644 --- a/Source/cmCMakePathCommand.cxx +++ b/Source/cmCMakePathCommand.cxx @@ -250,9 +250,48 @@ bool HandleGetCommand(std::vector<std::string> const& args, return true; } +bool HandleSetCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + if (args.size() < 3 || args.size() > 4) { + status.SetError("SET must be called with two or three arguments."); + return false; + } + + if (args[1].empty()) { + status.SetError("Invalid name for path variable."); + return false; + } + + static NormalizeParser const parser; + + const auto arguments = parser.Parse(args); + + if (parser.GetInputs().size() != 1) { + status.SetError("SET called with unexpected arguments."); + return false; + } + + auto path = + cmCMakePath(parser.GetInputs().front(), cmCMakePath::native_format); + + if (arguments.Normalize) { + path = path.Normal(); + } + + status.GetMakefile().AddDefinition(args[1], path.GenericString()); + + return true; +} + bool HandleAppendCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { + if (args[1].empty()) { + status.SetError("Invalid name for path variable."); + return false; + } + static OutputVariableParser const parser{}; const auto arguments = parser.Parse(args); @@ -272,8 +311,8 @@ bool HandleAppendCommand(std::vector<std::string> const& args, return true; } -bool HandleConcatCommand(std::vector<std::string> const& args, - cmExecutionStatus& status) +bool HandleAppendStringCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) { static OutputVariableParser const parser{}; @@ -546,16 +585,6 @@ bool HandleRelativePathCommand(std::vector<std::string> const& args, }); } -bool HandleProximatePathCommand(std::vector<std::string> const& args, - cmExecutionStatus& status) -{ - return HandleTransformPathCommand( - args, status, - [](const cmCMakePath& path, const std::string& base) -> cmCMakePath { - return path.Proximate(base); - }); -} - bool HandleAbsolutePathCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { @@ -567,40 +596,6 @@ bool HandleAbsolutePathCommand(std::vector<std::string> const& args, true); } -bool HandleCMakePathCommand(std::vector<std::string> const& args, - cmExecutionStatus& status) -{ - if (args.size() < 3 || args.size() > 4) { - status.SetError("CMAKE_PATH must be called with two or three arguments."); - return false; - } - - static NormalizeParser const parser; - - const auto arguments = parser.Parse(args); - - if (parser.GetInputs().size() != 1) { - status.SetError("CMAKE_PATH called with unexpected arguments."); - return false; - } - - if (args[1].empty()) { - status.SetError("Invalid name for output variable."); - return false; - } - - auto path = - cmCMakePath(parser.GetInputs().front(), cmCMakePath::native_format); - - if (arguments.Normalize) { - path = path.Normal(); - } - - status.GetMakefile().AddDefinition(args[1], path.GenericString()); - - return true; -} - bool HandleNativePathCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { @@ -737,12 +732,7 @@ bool HandleCompareCommand(std::vector<std::string> const& args, return false; } - std::string inputPath; - if (!getInputPath(args[1], status, inputPath)) { - return false; - } - - cmCMakePath path1(inputPath); + cmCMakePath path1(args[1]); cmCMakePath path2(args[3]); auto result = op->second(path1, path2); @@ -987,17 +977,16 @@ bool cmCMakePathCommand(std::vector<std::string> const& args, static cmSubcommandTable const subcommand{ { "GET"_s, HandleGetCommand }, + { "SET"_s, HandleSetCommand }, { "APPEND"_s, HandleAppendCommand }, - { "CONCAT"_s, HandleConcatCommand }, + { "APPEND_STRING"_s, HandleAppendStringCommand }, { "REMOVE_FILENAME"_s, HandleRemoveFilenameCommand }, { "REPLACE_FILENAME"_s, HandleReplaceFilenameCommand }, { "REMOVE_EXTENSION"_s, HandleRemoveExtensionCommand }, { "REPLACE_EXTENSION"_s, HandleReplaceExtensionCommand }, { "NORMAL_PATH"_s, HandleNormalPathCommand }, { "RELATIVE_PATH"_s, HandleRelativePathCommand }, - { "PROXIMATE_PATH"_s, HandleProximatePathCommand }, { "ABSOLUTE_PATH"_s, HandleAbsolutePathCommand }, - { "CMAKE_PATH"_s, HandleCMakePathCommand }, { "NATIVE_PATH"_s, HandleNativePathCommand }, { "CONVERT"_s, HandleConvertCommand }, { "COMPARE"_s, HandleCompareCommand }, diff --git a/Source/cmCPluginAPI.cxx b/Source/cmCPluginAPI.cxx index 8ebf6d2..968fa54 100644 --- a/Source/cmCPluginAPI.cxx +++ b/Source/cmCPluginAPI.cxx @@ -583,14 +583,12 @@ const char* CCONV cmSourceFileGetProperty(void* arg, const char* prop) { cmCPluginAPISourceFile* sf = static_cast<cmCPluginAPISourceFile*>(arg); if (cmSourceFile* rsf = sf->RealSourceFile) { - cmProp p = rsf->GetProperty(prop); - return cmToCStr(p); + return cmToCStr(rsf->GetProperty(prop)); } if (!strcmp(prop, "LOCATION")) { return sf->FullPath.c_str(); } - cmProp retVal = sf->Properties.GetPropertyValue(prop); - return cmToCStr(retVal); + return cmToCStr(sf->Properties.GetPropertyValue(prop)); } int CCONV cmSourceFileGetPropertyAsBool(void* arg, const char* prop) diff --git a/Source/cmCommands.cxx b/Source/cmCommands.cxx index c94f128..9e5b783 100644 --- a/Source/cmCommands.cxx +++ b/Source/cmCommands.cxx @@ -17,6 +17,7 @@ #include "cmBreakCommand.h" #include "cmBuildCommand.h" #include "cmCMakeMinimumRequired.h" +#include "cmCMakePathCommand.h" #include "cmCMakePolicyCommand.h" #include "cmCommand.h" #include "cmConfigureFileCommand.h" @@ -118,11 +119,19 @@ void GetScriptingCommands(cmState* state) { - state->AddBuiltinCommand("break", cmBreakCommand); + state->AddFlowControlCommand("break", cmBreakCommand); + state->AddFlowControlCommand("continue", cmContinueCommand); + state->AddFlowControlCommand("foreach", cmForEachCommand); + state->AddFlowControlCommand("function", cmFunctionCommand); + state->AddFlowControlCommand("if", cmIfCommand); + state->AddFlowControlCommand("macro", cmMacroCommand); + state->AddFlowControlCommand("return", cmReturnCommand); + state->AddFlowControlCommand("while", cmWhileCommand); + state->AddBuiltinCommand("cmake_minimum_required", cmCMakeMinimumRequired); + state->AddBuiltinCommand("cmake_path", cmCMakePathCommand); state->AddBuiltinCommand("cmake_policy", cmCMakePolicyCommand); state->AddBuiltinCommand("configure_file", cmConfigureFileCommand); - state->AddBuiltinCommand("continue", cmContinueCommand); state->AddBuiltinCommand("exec_program", cmExecProgramCommand); state->AddBuiltinCommand("execute_process", cmExecuteProcessCommand); state->AddBuiltinCommand("file", cmFileCommand); @@ -131,26 +140,21 @@ void GetScriptingCommands(cmState* state) state->AddBuiltinCommand("find_package", cmFindPackage); state->AddBuiltinCommand("find_path", cmFindPath); state->AddBuiltinCommand("find_program", cmFindProgram); - state->AddBuiltinCommand("foreach", cmForEachCommand); - state->AddBuiltinCommand("function", cmFunctionCommand); state->AddBuiltinCommand("get_cmake_property", cmGetCMakePropertyCommand); state->AddBuiltinCommand("get_directory_property", cmGetDirectoryPropertyCommand); state->AddBuiltinCommand("get_filename_component", cmGetFilenameComponentCommand); state->AddBuiltinCommand("get_property", cmGetPropertyCommand); - state->AddBuiltinCommand("if", cmIfCommand); state->AddBuiltinCommand("include", cmIncludeCommand); state->AddBuiltinCommand("include_guard", cmIncludeGuardCommand); state->AddBuiltinCommand("list", cmListCommand); - state->AddBuiltinCommand("macro", cmMacroCommand); state->AddBuiltinCommand("make_directory", cmMakeDirectoryCommand); state->AddBuiltinCommand("mark_as_advanced", cmMarkAsAdvancedCommand); state->AddBuiltinCommand("math", cmMathCommand); state->AddBuiltinCommand("message", cmMessageCommand); state->AddBuiltinCommand("option", cmOptionCommand); state->AddBuiltinCommand("cmake_parse_arguments", cmParseArgumentsCommand); - state->AddBuiltinCommand("return", cmReturnCommand); state->AddBuiltinCommand("separate_arguments", cmSeparateArgumentsCommand); state->AddBuiltinCommand("set", cmSetCommand); state->AddBuiltinCommand("set_directory_properties", @@ -159,7 +163,6 @@ void GetScriptingCommands(cmState* state) state->AddBuiltinCommand("site_name", cmSiteNameCommand); state->AddBuiltinCommand("string", cmStringCommand); state->AddBuiltinCommand("unset", cmUnsetCommand); - state->AddBuiltinCommand("while", cmWhileCommand); state->AddUnexpectedCommand( "else", diff --git a/Source/cmConfigureFileCommand.cxx b/Source/cmConfigureFileCommand.cxx index 68322cc..edd261d 100644 --- a/Source/cmConfigureFileCommand.cxx +++ b/Source/cmConfigureFileCommand.cxx @@ -3,11 +3,15 @@ #include "cmConfigureFileCommand.h" #include <set> +#include <sstream> #include <cm/string_view> #include <cmext/string_view> +#include <sys/types.h> + #include "cmExecutionStatus.h" +#include "cmFSPermissions.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmNewLineStyle.h" @@ -60,7 +64,19 @@ bool cmConfigureFileCommand(std::vector<std::string> const& args, } bool copyOnly = false; bool escapeQuotes = false; - bool use_source_permissions = true; + bool useSourcePermissions = false; + bool noSourcePermissions = false; + bool filePermissions = false; + std::vector<std::string> filePermissionOptions; + + enum class Doing + { + DoingNone, + DoingFilePermissions, + DoneFilePermissions + }; + + Doing doing = Doing::DoingNone; static std::set<cm::string_view> noopOptions = { /* Legacy. */ @@ -78,6 +94,9 @@ bool cmConfigureFileCommand(std::vector<std::string> const& args, bool atOnly = false; for (unsigned int i = 2; i < args.size(); ++i) { if (args[i] == "COPYONLY") { + if (doing == Doing::DoingFilePermissions) { + doing = Doing::DoneFilePermissions; + } copyOnly = true; if (newLineStyle.IsValid()) { status.SetError("COPYONLY could not be used in combination " @@ -85,13 +104,49 @@ bool cmConfigureFileCommand(std::vector<std::string> const& args, return false; } } else if (args[i] == "ESCAPE_QUOTES") { + if (doing == Doing::DoingFilePermissions) { + doing = Doing::DoneFilePermissions; + } escapeQuotes = true; } else if (args[i] == "@ONLY") { + if (doing == Doing::DoingFilePermissions) { + doing = Doing::DoneFilePermissions; + } atOnly = true; } else if (args[i] == "NO_SOURCE_PERMISSIONS") { - use_source_permissions = false; + if (doing == Doing::DoingFilePermissions) { + status.SetError(" given both FILE_PERMISSIONS and " + "NO_SOURCE_PERMISSIONS. Only one option allowed."); + return false; + } + noSourcePermissions = true; + } else if (args[i] == "USE_SOURCE_PERMISSIONS") { + if (doing == Doing::DoingFilePermissions) { + status.SetError(" given both FILE_PERMISSIONS and " + "USE_SOURCE_PERMISSIONS. Only one option allowed."); + return false; + } + useSourcePermissions = true; + } else if (args[i] == "FILE_PERMISSIONS") { + if (useSourcePermissions) { + status.SetError(" given both FILE_PERMISSIONS and " + "USE_SOURCE_PERMISSIONS. Only one option allowed."); + return false; + } + if (noSourcePermissions) { + status.SetError(" given both FILE_PERMISSIONS and " + "NO_SOURCE_PERMISSIONS. Only one option allowed."); + return false; + } + + if (doing == Doing::DoingNone) { + doing = Doing::DoingFilePermissions; + filePermissions = true; + } } else if (noopOptions.find(args[i]) != noopOptions.end()) { /* Ignore no-op options. */ + } else if (doing == Doing::DoingFilePermissions) { + filePermissionOptions.push_back(args[i]); } else { unknown_args += " "; unknown_args += args[i]; @@ -104,9 +159,53 @@ bool cmConfigureFileCommand(std::vector<std::string> const& args, status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, msg); } - if (!status.GetMakefile().ConfigureFile( - inputFile, outputFile, copyOnly, atOnly, escapeQuotes, - use_source_permissions, newLineStyle)) { + if (useSourcePermissions && noSourcePermissions) { + status.SetError(" given both USE_SOURCE_PERMISSIONS and " + "NO_SOURCE_PERMISSIONS. Only one option allowed."); + return false; + } + + mode_t permisiions = 0; + + if (filePermissions) { + if (filePermissionOptions.empty()) { + status.SetError(" given FILE_PERMISSIONS without any options."); + return false; + } + + std::vector<std::string> invalidOptions; + for (auto const& e : filePermissionOptions) { + if (!cmFSPermissions::stringToModeT(e, permisiions)) { + invalidOptions.push_back(e); + } + } + + if (!invalidOptions.empty()) { + std::ostringstream oss; + oss << " given invalid permission "; + for (auto i = 0u; i < invalidOptions.size(); i++) { + if (i == 0u) { + oss << "\"" << invalidOptions[i] << "\""; + } else { + oss << ",\"" << invalidOptions[i] << "\""; + } + } + oss << "."; + status.SetError(oss.str()); + return false; + } + } + + if (noSourcePermissions) { + permisiions |= cmFSPermissions::mode_owner_read; + permisiions |= cmFSPermissions::mode_owner_write; + permisiions |= cmFSPermissions::mode_group_read; + permisiions |= cmFSPermissions::mode_world_read; + } + + if (!status.GetMakefile().ConfigureFile(inputFile, outputFile, copyOnly, + atOnly, escapeQuotes, permisiions, + newLineStyle)) { status.SetError("Problem configuring file"); return false; } diff --git a/Source/cmConnection.cxx b/Source/cmConnection.cxx deleted file mode 100644 index e4d0cf1..0000000 --- a/Source/cmConnection.cxx +++ /dev/null @@ -1,173 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmConnection.h" - -#include <cassert> -#include <cstring> - -#include <cm3p/uv.h> - -#include "cmServer.h" - -struct write_req_t -{ - uv_write_t req; - uv_buf_t buf; -}; - -void cmEventBasedConnection::on_alloc_buffer(uv_handle_t* handle, - size_t suggested_size, - uv_buf_t* buf) -{ - (void)(handle); -#ifndef __clang_analyzer__ - char* rawBuffer = new char[suggested_size]; - *buf = uv_buf_init(rawBuffer, static_cast<unsigned int>(suggested_size)); -#else - (void)(suggested_size); - (void)(buf); -#endif /* __clang_analyzer__ */ -} - -void cmEventBasedConnection::on_read(uv_stream_t* stream, ssize_t nread, - const uv_buf_t* buf) -{ - auto conn = static_cast<cmEventBasedConnection*>(stream->data); - if (conn) { - if (nread >= 0) { - conn->ReadData(std::string(buf->base, buf->base + nread)); - } else { - conn->OnDisconnect(static_cast<int>(nread)); - } - } - - delete[](buf->base); -} - -void cmEventBasedConnection::on_close(uv_handle_t* /*handle*/) -{ -} - -void cmEventBasedConnection::on_write(uv_write_t* req, int status) -{ - (void)(status); - - // Free req and buffer - write_req_t* wr = reinterpret_cast<write_req_t*>(req); - delete[](wr->buf.base); - delete wr; -} - -void cmEventBasedConnection::on_new_connection(uv_stream_t* stream, int status) -{ - (void)(status); - auto conn = static_cast<cmEventBasedConnection*>(stream->data); - - if (conn) { - conn->Connect(stream); - } -} - -bool cmEventBasedConnection::IsOpen() const -{ - return this->WriteStream != nullptr; -} - -void cmEventBasedConnection::WriteData(const std::string& _data) -{ -#ifndef NDEBUG - auto curr_thread_id = uv_thread_self(); - assert(this->Server); - assert(uv_thread_equal(&curr_thread_id, &this->Server->ServeThreadId)); -#endif - -#ifndef __clang_analyzer__ - auto data = _data; - assert(this->WriteStream.get()); - if (BufferStrategy) { - data = BufferStrategy->BufferOutMessage(data); - } - - auto ds = data.size(); - - write_req_t* req = new write_req_t; - req->req.data = this; - req->buf = uv_buf_init(new char[ds], static_cast<unsigned int>(ds)); - memcpy(req->buf.base, data.c_str(), ds); - uv_write(reinterpret_cast<uv_write_t*>(req), this->WriteStream, &req->buf, 1, - on_write); -#else - (void)(_data); -#endif /* __clang_analyzer__ */ -} - -void cmEventBasedConnection::ReadData(const std::string& data) -{ - this->RawReadBuffer += data; - if (BufferStrategy) { - std::string packet = BufferStrategy->BufferMessage(this->RawReadBuffer); - while (!packet.empty()) { - ProcessRequest(packet); - packet = BufferStrategy->BufferMessage(this->RawReadBuffer); - } - } else { - ProcessRequest(this->RawReadBuffer); - this->RawReadBuffer.clear(); - } -} - -cmEventBasedConnection::cmEventBasedConnection( - cmConnectionBufferStrategy* bufferStrategy) - : BufferStrategy(bufferStrategy) -{ -} - -void cmEventBasedConnection::Connect(uv_stream_t* server) -{ - (void)server; - Server->OnConnected(nullptr); -} - -void cmEventBasedConnection::OnDisconnect(int onerror) -{ - (void)onerror; - this->OnConnectionShuttingDown(); - if (this->Server) { - this->Server->OnDisconnect(this); - } -} - -cmConnection::~cmConnection() = default; - -bool cmConnection::OnConnectionShuttingDown() -{ - this->Server = nullptr; - return true; -} - -void cmConnection::SetServer(cmServerBase* s) -{ - Server = s; -} - -void cmConnection::ProcessRequest(const std::string& request) -{ - Server->ProcessRequest(this, request); -} - -bool cmConnection::OnServeStart(std::string* errString) -{ - (void)errString; - return true; -} - -bool cmEventBasedConnection::OnConnectionShuttingDown() -{ - if (this->WriteStream.get()) { - this->WriteStream->data = nullptr; - } - - WriteStream.reset(); - - return true; -} diff --git a/Source/cmConnection.h b/Source/cmConnection.h deleted file mode 100644 index 5335a7f..0000000 --- a/Source/cmConnection.h +++ /dev/null @@ -1,137 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ - -#pragma once - -#include "cmConfigure.h" // IWYU pragma: keep - -#include <cstddef> -#include <memory> -#include <string> - -#include <cm3p/uv.h> - -#include "cmUVHandlePtr.h" - -class cmServerBase; - -/*** - * Given a sequence of bytes with any kind of buffering, instances of this - * class arrange logical chunks according to whatever the use case is for - * the connection. - */ -class cmConnectionBufferStrategy -{ -public: - virtual ~cmConnectionBufferStrategy(); - - /*** - * Called whenever with an active raw buffer. If a logical chunk - * becomes available, that chunk is returned and that portion is - * removed from the rawBuffer - * - * @param rawBuffer in/out parameter. Receive buffer; the buffer strategy is - * free to manipulate this buffer anyway it needs to. - * - * @return Next chunk from the stream. Returns the empty string if a chunk - * isn't ready yet. Users of this interface should repeatedly call this - * function until an empty string is returned since its entirely possible - * multiple chunks come in a single raw buffer. - */ - virtual std::string BufferMessage(std::string& rawBuffer) = 0; - - /*** - * Called to properly buffer an outgoing message. - * - * @param rawBuffer Message to format in the correct way - * - * @return Formatted message - */ - virtual std::string BufferOutMessage(const std::string& rawBuffer) const - { - return rawBuffer; - }; - /*** - * Resets the internal state of the buffering - */ - virtual void clear(); - - // TODO: There should be a callback / flag set for errors -}; - -class cmConnection -{ -public: - cmConnection() = default; - - cmConnection(cmConnection const&) = delete; - cmConnection& operator=(cmConnection const&) = delete; - - virtual void WriteData(const std::string& data) = 0; - - virtual ~cmConnection(); - - virtual bool OnConnectionShuttingDown(); - - virtual bool IsOpen() const = 0; - - virtual void SetServer(cmServerBase* s); - - virtual void ProcessRequest(const std::string& request); - - virtual bool OnServeStart(std::string* pString); - -protected: - cmServerBase* Server = nullptr; -}; - -/*** - * Abstraction of a connection; ties in event callbacks from libuv and notifies - * the server when appropriate - */ -class cmEventBasedConnection : public cmConnection -{ - -public: - /*** - * @param bufferStrategy If no strategy is given, it will process the raw - * chunks as they come in. The connection - * owns the pointer given. - */ - cmEventBasedConnection(cmConnectionBufferStrategy* bufferStrategy = nullptr); - - virtual void Connect(uv_stream_t* server); - - virtual void ReadData(const std::string& data); - - bool IsOpen() const override; - - void WriteData(const std::string& data) override; - bool OnConnectionShuttingDown() override; - - virtual void OnDisconnect(int errorCode); - - static void on_close(uv_handle_t* handle); - - template <typename T> - static void on_close_delete(uv_handle_t* handle) - { - delete reinterpret_cast<T*>(handle); - } - -protected: - cm::uv_stream_ptr WriteStream; - - std::string RawReadBuffer; - - std::unique_ptr<cmConnectionBufferStrategy> BufferStrategy; - - static void on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf); - - static void on_write(uv_write_t* req, int status); - - static void on_new_connection(uv_stream_t* stream, int status); - - static void on_alloc_buffer(uv_handle_t* handle, size_t suggested_size, - uv_buf_t* buf); -}; diff --git a/Source/cmCreateTestSourceList.cxx b/Source/cmCreateTestSourceList.cxx index 9c4deea..9d492ba 100644 --- a/Source/cmCreateTestSourceList.cxx +++ b/Source/cmCreateTestSourceList.cxx @@ -127,7 +127,7 @@ bool cmCreateTestSourceList(std::vector<std::string> const& args, mf.AddDefinition("CMAKE_FORWARD_DECLARE_TESTS", forwardDeclareCode); mf.AddDefinition("CMAKE_FUNCTION_TABLE_ENTIRES", functionMapCode); bool res = true; - if (!mf.ConfigureFile(configFile, driver, false, true, false, true)) { + if (!mf.ConfigureFile(configFile, driver, false, true, false)) { res = false; } diff --git a/Source/cmCustomCommandGenerator.cxx b/Source/cmCustomCommandGenerator.cxx index 60504ba..08a0574 100644 --- a/Source/cmCustomCommandGenerator.cxx +++ b/Source/cmCustomCommandGenerator.cxx @@ -6,18 +6,22 @@ #include <memory> #include <utility> +#include <cm/optional> #include <cmext/algorithm> +#include "cmCryptoHash.h" #include "cmCustomCommand.h" #include "cmCustomCommandLines.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" +#include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmProperty.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmTransformDepfile.h" namespace { void AppendPaths(const std::vector<std::string>& inputs, @@ -31,8 +35,7 @@ void AppendPaths(const std::vector<std::string>& inputs, for (std::string& it : result) { cmSystemTools::ConvertToUnixSlashes(it); if (cmSystemTools::FileIsFullPath(it)) { - it = cmSystemTools::CollapseFullPath( - it, lg->GetMakefile()->GetHomeOutputDirectory()); + it = cmSystemTools::CollapseFullPath(it); } } cm::append(output, result); @@ -42,8 +45,9 @@ void AppendPaths(const std::vector<std::string>& inputs, cmCustomCommandGenerator::cmCustomCommandGenerator(cmCustomCommand const& cc, std::string config, - cmLocalGenerator* lg) - : CC(cc) + cmLocalGenerator* lg, + bool transformDepfile) + : CC(&cc) , Config(std::move(config)) , LG(lg) , OldStyle(cc.GetEscapeOldStyle()) @@ -52,34 +56,76 @@ cmCustomCommandGenerator::cmCustomCommandGenerator(cmCustomCommand const& cc, { cmGeneratorExpression ge(cc.GetBacktrace()); - const cmCustomCommandLines& cmdlines = this->CC.GetCommandLines(); + const cmCustomCommandLines& cmdlines = this->CC->GetCommandLines(); for (cmCustomCommandLine const& cmdline : cmdlines) { cmCustomCommandLine argv; for (std::string const& clarg : cmdline) { std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(clarg); std::string parsed_arg = cge->Evaluate(this->LG, this->Config); - if (this->CC.GetCommandExpandLists()) { + for (cmGeneratorTarget* gt : cge->GetTargets()) { + this->Utilities.emplace(BT<std::pair<std::string, bool>>( + { gt->GetName(), true }, cge->GetBacktrace())); + } + if (this->CC->GetCommandExpandLists()) { cm::append(argv, cmExpandedList(parsed_arg)); } else { argv.push_back(std::move(parsed_arg)); } } - // Later code assumes at least one entry exists, but expanding - // lists on an empty command may have left this empty. - // FIXME: Should we define behavior for removing empty commands? - if (argv.empty()) { + if (!argv.empty()) { + // If the command references an executable target by name, + // collect the target to add a target-level dependency on it. + cmGeneratorTarget* gt = this->LG->FindGeneratorTargetToUse(argv.front()); + if (gt && gt->GetType() == cmStateEnums::EXECUTABLE) { + this->Utilities.emplace(BT<std::pair<std::string, bool>>( + { gt->GetName(), true }, cc.GetBacktrace())); + } + } else { + // Later code assumes at least one entry exists, but expanding + // lists on an empty command may have left this empty. + // FIXME: Should we define behavior for removing empty commands? argv.emplace_back(); } this->CommandLines.push_back(std::move(argv)); } + if (transformDepfile && !this->CommandLines.empty() && + !cc.GetDepfile().empty() && + this->LG->GetGlobalGenerator()->DepfileFormat()) { + cmCustomCommandLine argv; + argv.push_back(cmSystemTools::GetCMakeCommand()); + argv.emplace_back("-E"); + argv.emplace_back("cmake_transform_depfile"); + switch (*this->LG->GetGlobalGenerator()->DepfileFormat()) { + case cmDepfileFormat::GccDepfile: + argv.emplace_back("gccdepfile"); + break; + case cmDepfileFormat::VsTlog: + argv.emplace_back("vstlog"); + break; + } + if (this->LG->GetCurrentBinaryDirectory() == + this->LG->GetBinaryDirectory()) { + argv.emplace_back("./"); + } else { + argv.push_back(cmStrCat(this->LG->MaybeConvertToRelativePath( + this->LG->GetBinaryDirectory(), + this->LG->GetCurrentBinaryDirectory()), + '/')); + } + argv.push_back(this->GetFullDepfile()); + argv.push_back(this->GetInternalDepfile()); + + this->CommandLines.push_back(std::move(argv)); + } + AppendPaths(cc.GetByproducts(), ge, this->LG, this->Config, this->Byproducts); AppendPaths(cc.GetDepends(), ge, this->LG, this->Config, this->Depends); - const std::string& workingdirectory = this->CC.GetWorkingDirectory(); + const std::string& workingdirectory = this->CC->GetWorkingDirectory(); if (!workingdirectory.empty()) { std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(workingdirectory); @@ -97,7 +143,7 @@ cmCustomCommandGenerator::cmCustomCommandGenerator(cmCustomCommand const& cc, unsigned int cmCustomCommandGenerator::GetNumberOfCommands() const { - return static_cast<unsigned int>(this->CC.GetCommandLines().size()); + return static_cast<unsigned int>(this->CommandLines.size()); } void cmCustomCommandGenerator::FillEmulatorsWithArguments() @@ -234,9 +280,43 @@ void cmCustomCommandGenerator::AppendArguments(unsigned int c, } } +std::string cmCustomCommandGenerator::GetFullDepfile() const +{ + std::string depfile = this->CC->GetDepfile(); + if (depfile.empty()) { + return ""; + } + + if (!cmSystemTools::FileIsFullPath(depfile)) { + depfile = cmStrCat(this->LG->GetCurrentBinaryDirectory(), '/', depfile); + } + return cmSystemTools::CollapseFullPath(depfile); +} + +std::string cmCustomCommandGenerator::GetInternalDepfile() const +{ + std::string depfile = this->GetFullDepfile(); + if (depfile.empty()) { + return ""; + } + + cmCryptoHash hash(cmCryptoHash::AlgoSHA256); + std::string extension; + switch (*this->LG->GetGlobalGenerator()->DepfileFormat()) { + case cmDepfileFormat::GccDepfile: + extension = ".d"; + break; + case cmDepfileFormat::VsTlog: + extension = ".tlog"; + break; + } + return cmStrCat(this->LG->GetBinaryDirectory(), "/CMakeFiles/d/", + hash.HashString(depfile), extension); +} + const char* cmCustomCommandGenerator::GetComment() const { - return this->CC.GetComment(); + return this->CC->GetComment(); } std::string cmCustomCommandGenerator::GetWorkingDirectory() const @@ -246,7 +326,7 @@ std::string cmCustomCommandGenerator::GetWorkingDirectory() const std::vector<std::string> const& cmCustomCommandGenerator::GetOutputs() const { - return this->CC.GetOutputs(); + return this->CC->GetOutputs(); } std::vector<std::string> const& cmCustomCommandGenerator::GetByproducts() const @@ -258,3 +338,9 @@ std::vector<std::string> const& cmCustomCommandGenerator::GetDepends() const { return this->Depends; } + +std::set<BT<std::pair<std::string, bool>>> const& +cmCustomCommandGenerator::GetUtilities() const +{ + return this->Utilities; +} diff --git a/Source/cmCustomCommandGenerator.h b/Source/cmCustomCommandGenerator.h index 412eba8..cb0d7df 100644 --- a/Source/cmCustomCommandGenerator.h +++ b/Source/cmCustomCommandGenerator.h @@ -4,17 +4,20 @@ #include "cmConfigure.h" // IWYU pragma: keep +#include <set> #include <string> +#include <utility> #include <vector> #include "cmCustomCommandLines.h" +#include "cmListFileCache.h" class cmCustomCommand; class cmLocalGenerator; class cmCustomCommandGenerator { - cmCustomCommand const& CC; + cmCustomCommand const* CC; std::string Config; cmLocalGenerator* LG; bool OldStyle; @@ -24,6 +27,7 @@ class cmCustomCommandGenerator std::vector<std::string> Byproducts; std::vector<std::string> Depends; std::string WorkingDirectory; + std::set<BT<std::pair<std::string, bool>>> Utilities; void FillEmulatorsWithArguments(); std::vector<std::string> GetCrossCompilingEmulator(unsigned int c) const; @@ -31,11 +35,13 @@ class cmCustomCommandGenerator public: cmCustomCommandGenerator(cmCustomCommand const& cc, std::string config, - cmLocalGenerator* lg); + cmLocalGenerator* lg, bool transformDepfile = true); cmCustomCommandGenerator(const cmCustomCommandGenerator&) = delete; + cmCustomCommandGenerator(cmCustomCommandGenerator&&) = default; cmCustomCommandGenerator& operator=(const cmCustomCommandGenerator&) = delete; - cmCustomCommand const& GetCC() const { return this->CC; } + cmCustomCommandGenerator& operator=(cmCustomCommandGenerator&&) = default; + cmCustomCommand const& GetCC() const { return *(this->CC); } unsigned int GetNumberOfCommands() const; std::string GetCommand(unsigned int c) const; void AppendArguments(unsigned int c, std::string& cmd) const; @@ -44,5 +50,8 @@ public: std::vector<std::string> const& GetOutputs() const; std::vector<std::string> const& GetByproducts() const; std::vector<std::string> const& GetDepends() const; + std::set<BT<std::pair<std::string, bool>>> const& GetUtilities() const; bool HasOnlyEmptyCommandLines() const; + std::string GetFullDepfile() const; + std::string GetInternalDepfile() const; }; diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index cbae4e5..a853bb1 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -924,13 +924,13 @@ void cmExportFileGenerator::GeneratePolicyHeaderCode(std::ostream& os) // Isolate the file policy level. // Support CMake versions as far back as 2.6 but also support using NEW - // policy settings for up to CMake 3.17 (this upper limit may be reviewed + // policy settings for up to CMake 3.18 (this upper limit may be reviewed // and increased from time to time). This reduces the opportunity for CMake // warnings when an older export file is later used with newer CMake // versions. /* clang-format off */ os << "cmake_policy(PUSH)\n" - << "cmake_policy(VERSION 2.6...3.17)\n"; + << "cmake_policy(VERSION 2.6...3.18)\n"; /* clang-format on */ } diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index 8a3aad2..cd440ad 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -15,6 +15,7 @@ #include <vector> #include <cm/memory> +#include <cm/string_view> #include <cmext/algorithm> #include <cmext/string_view> @@ -2314,49 +2315,92 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, status.SetError("Incorrect arguments to GENERATE subcommand."); return false; } - if (args[1] != "OUTPUT") { + + struct Arguments + { + std::string Output; + std::string Input; + std::string Content; + std::string Condition; + std::string Target; + }; + + static auto const parser = cmArgumentParser<Arguments>{} + .Bind("OUTPUT"_s, &Arguments::Output) + .Bind("INPUT"_s, &Arguments::Input) + .Bind("CONTENT"_s, &Arguments::Content) + .Bind("CONDITION"_s, &Arguments::Condition) + .Bind("TARGET"_s, &Arguments::Target); + + std::vector<std::string> unparsedArguments; + std::vector<std::string> keywordsMissingValues; + std::vector<std::string> parsedKeywords; + Arguments const arguments = + parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments, + &keywordsMissingValues, &parsedKeywords); + + if (!keywordsMissingValues.empty()) { status.SetError("Incorrect arguments to GENERATE subcommand."); return false; } - std::string condition; - std::string target; - - for (std::size_t i = 5; i < args.size();) { - const std::string& arg = args[i++]; + if (!unparsedArguments.empty()) { + status.SetError("Unknown argument to GENERATE subcommand."); + return false; + } - if (args.size() - i == 0) { - status.SetError("Incorrect arguments to GENERATE subcommand."); - return false; + bool mandatoryOptionsSpecified = false; + if (parsedKeywords.size() > 1) { + const bool outputOprionSpecified = parsedKeywords[0] == "OUTPUT"_s; + const bool inputOrContentSpecified = + parsedKeywords[1] == "INPUT"_s || parsedKeywords[1] == "CONTENT"_s; + if (outputOprionSpecified && inputOrContentSpecified) { + mandatoryOptionsSpecified = true; } + } + if (!mandatoryOptionsSpecified) { + status.SetError("Incorrect arguments to GENERATE subcommand."); + return false; + } - const std::string& value = args[i++]; - - if (value.empty()) { - status.SetError( - arg + " of sub-command GENERATE must not be empty if specified."); - return false; - } + const bool conditionOptionSpecified = + std::find(parsedKeywords.begin(), parsedKeywords.end(), "CONDITION"_s) != + parsedKeywords.end(); + if (conditionOptionSpecified && arguments.Condition.empty()) { + status.SetError("CONDITION of sub-command GENERATE must not be empty " + "if specified."); + return false; + } - if (arg == "CONDITION") { - condition = value; - } else if (arg == "TARGET") { - target = value; - } else { - status.SetError("Unknown argument to GENERATE subcommand."); - return false; - } + const bool targetOptionSpecified = + std::find(parsedKeywords.begin(), parsedKeywords.end(), "TARGET"_s) != + parsedKeywords.end(); + if (targetOptionSpecified && arguments.Target.empty()) { + status.SetError("TARGET of sub-command GENERATE must not be empty " + "if specified."); + return false; } - std::string output = args[2]; - const bool inputIsContent = args[3] != "INPUT"; - if (inputIsContent && args[3] != "CONTENT") { + const bool outputOptionSpecified = + std::find(parsedKeywords.begin(), parsedKeywords.end(), "OUTPUT"_s) != + parsedKeywords.end(); + if (outputOptionSpecified && parsedKeywords[0] != "OUTPUT"_s) { status.SetError("Incorrect arguments to GENERATE subcommand."); return false; } - std::string input = args[4]; - AddEvaluationFile(input, target, output, condition, inputIsContent, status); + const bool inputIsContent = parsedKeywords[1] != "INPUT"_s; + if (inputIsContent && parsedKeywords[1] != "CONTENT") { + status.SetError("Unknown argument to GENERATE subcommand."); + } + + std::string input = arguments.Input; + if (inputIsContent) { + input = arguments.Content; + } + + AddEvaluationFile(input, arguments.Target, arguments.Output, + arguments.Condition, inputIsContent, status); return true; } diff --git a/Source/cmFileMonitor.cxx b/Source/cmFileMonitor.cxx deleted file mode 100644 index 8cfdb2d..0000000 --- a/Source/cmFileMonitor.cxx +++ /dev/null @@ -1,383 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmFileMonitor.h" - -#include <cassert> -#include <cstddef> -#include <unordered_map> -#include <utility> - -#include <cm/memory> - -#include "cmsys/SystemTools.hxx" - -namespace { -void on_directory_change(uv_fs_event_t* handle, const char* filename, - int events, int status); -void on_fs_close(uv_handle_t* handle); -} // namespace - -class cmIBaseWatcher -{ -public: - virtual ~cmIBaseWatcher() = default; - - virtual void Trigger(const std::string& pathSegment, int events, - int status) const = 0; - virtual std::string Path() const = 0; - virtual uv_loop_t* Loop() const = 0; - - virtual void StartWatching() = 0; - virtual void StopWatching() = 0; - - virtual std::vector<std::string> WatchedFiles() const = 0; - virtual std::vector<std::string> WatchedDirectories() const = 0; -}; - -class cmVirtualDirectoryWatcher : public cmIBaseWatcher -{ -public: - ~cmVirtualDirectoryWatcher() override = default; - - cmIBaseWatcher* Find(const std::string& ps) - { - const auto i = this->Children.find(ps); - return (i == this->Children.end()) ? nullptr : i->second.get(); - } - - void Trigger(const std::string& pathSegment, int events, - int status) const final - { - if (pathSegment.empty()) { - for (auto const& child : this->Children) { - child.second->Trigger(std::string(), events, status); - } - } else { - const auto i = this->Children.find(pathSegment); - if (i != this->Children.end()) { - i->second->Trigger(std::string(), events, status); - } - } - } - - void StartWatching() override - { - for (auto const& child : this->Children) { - child.second->StartWatching(); - } - } - - void StopWatching() override - { - for (auto const& child : this->Children) { - child.second->StopWatching(); - } - } - - std::vector<std::string> WatchedFiles() const final - { - std::vector<std::string> result; - for (auto const& child : this->Children) { - for (std::string const& f : child.second->WatchedFiles()) { - result.push_back(f); - } - } - return result; - } - - std::vector<std::string> WatchedDirectories() const override - { - std::vector<std::string> result; - for (auto const& child : this->Children) { - for (std::string const& dir : child.second->WatchedDirectories()) { - result.push_back(dir); - } - } - return result; - } - - void Reset() { this->Children.clear(); } - - void AddChildWatcher(const std::string& ps, cmIBaseWatcher* watcher) - { - assert(!ps.empty()); - assert(this->Children.find(ps) == this->Children.end()); - assert(watcher); - - this->Children.emplace(ps, std::unique_ptr<cmIBaseWatcher>(watcher)); - } - -private: - std::unordered_map<std::string, std::unique_ptr<cmIBaseWatcher>> - Children; // owned! -}; - -// Root of all the different (on windows!) root directories: -class cmRootWatcher : public cmVirtualDirectoryWatcher -{ -public: - cmRootWatcher(uv_loop_t* loop) - : mLoop(loop) - { - assert(loop); - } - - std::string Path() const final - { - assert(false); - return std::string(); - } - uv_loop_t* Loop() const final { return this->mLoop; } - -private: - uv_loop_t* const mLoop; // no ownership! -}; - -// Real directories: -class cmRealDirectoryWatcher : public cmVirtualDirectoryWatcher -{ -public: - cmRealDirectoryWatcher(cmVirtualDirectoryWatcher* p, const std::string& ps) - : Parent(p) - , PathSegment(ps) - { - assert(p); - assert(!ps.empty()); - - p->AddChildWatcher(ps, this); - } - - void StartWatching() final - { - if (!this->Handle) { - this->Handle = new uv_fs_event_t; - - uv_fs_event_init(this->Loop(), this->Handle); - this->Handle->data = this; - uv_fs_event_start(this->Handle, &on_directory_change, Path().c_str(), 0); - } - cmVirtualDirectoryWatcher::StartWatching(); - } - - void StopWatching() final - { - if (this->Handle) { - uv_fs_event_stop(this->Handle); - if (!uv_is_closing(reinterpret_cast<uv_handle_t*>(this->Handle))) { - uv_close(reinterpret_cast<uv_handle_t*>(this->Handle), &on_fs_close); - } - this->Handle = nullptr; - } - cmVirtualDirectoryWatcher::StopWatching(); - } - - uv_loop_t* Loop() const final { return this->Parent->Loop(); } - - std::vector<std::string> WatchedDirectories() const override - { - std::vector<std::string> result = { Path() }; - for (std::string const& dir : - cmVirtualDirectoryWatcher::WatchedDirectories()) { - result.push_back(dir); - } - return result; - } - -protected: - cmVirtualDirectoryWatcher* const Parent; - const std::string PathSegment; - -private: - uv_fs_event_t* Handle = nullptr; // owner! -}; - -// Root directories: -class cmRootDirectoryWatcher : public cmRealDirectoryWatcher -{ -public: - cmRootDirectoryWatcher(cmRootWatcher* p, const std::string& ps) - : cmRealDirectoryWatcher(p, ps) - { - } - - std::string Path() const final { return this->PathSegment; } -}; - -// Normal directories below root: -class cmDirectoryWatcher : public cmRealDirectoryWatcher -{ -public: - cmDirectoryWatcher(cmRealDirectoryWatcher* p, const std::string& ps) - : cmRealDirectoryWatcher(p, ps) - { - } - - std::string Path() const final - { - return this->Parent->Path() + this->PathSegment + "/"; - } -}; - -class cmFileWatcher : public cmIBaseWatcher -{ -public: - cmFileWatcher(cmRealDirectoryWatcher* p, const std::string& ps, - cmFileMonitor::Callback cb) - : Parent(p) - , PathSegment(ps) - , CbList({ std::move(cb) }) - { - assert(p); - assert(!ps.empty()); - p->AddChildWatcher(ps, this); - } - - void StartWatching() final {} - - void StopWatching() final {} - - void AppendCallback(cmFileMonitor::Callback const& cb) - { - this->CbList.push_back(cb); - } - - std::string Path() const final - { - return this->Parent->Path() + this->PathSegment; - } - - std::vector<std::string> WatchedDirectories() const final { return {}; } - - std::vector<std::string> WatchedFiles() const final - { - return { this->Path() }; - } - - void Trigger(const std::string& ps, int events, int status) const final - { - assert(ps.empty()); - assert(status == 0); - static_cast<void>(ps); - - const std::string path = this->Path(); - for (cmFileMonitor::Callback const& cb : this->CbList) { - cb(path, events, status); - } - } - - uv_loop_t* Loop() const final { return this->Parent->Loop(); } - -private: - cmRealDirectoryWatcher* Parent; - const std::string PathSegment; - std::vector<cmFileMonitor::Callback> CbList; -}; - -namespace { - -void on_directory_change(uv_fs_event_t* handle, const char* filename, - int events, int status) -{ - const cmIBaseWatcher* const watcher = - static_cast<const cmIBaseWatcher*>(handle->data); - const std::string pathSegment(filename ? filename : ""); - watcher->Trigger(pathSegment, events, status); -} - -void on_fs_close(uv_handle_t* handle) -{ - delete reinterpret_cast<uv_fs_event_t*>(handle); -} - -} // namespace - -cmFileMonitor::cmFileMonitor(uv_loop_t* l) - : Root(cm::make_unique<cmRootWatcher>(l)) -{ -} - -cmFileMonitor::~cmFileMonitor() = default; - -void cmFileMonitor::MonitorPaths(const std::vector<std::string>& paths, - Callback const& cb) -{ - for (std::string const& p : paths) { - std::vector<std::string> pathSegments; - cmsys::SystemTools::SplitPath(p, pathSegments, true); - const bool pathIsFile = !cmsys::SystemTools::FileIsDirectory(p); - - const size_t segmentCount = pathSegments.size(); - if (segmentCount < 2) { // Expect at least rootdir and filename - continue; - } - cmVirtualDirectoryWatcher* currentWatcher = this->Root.get(); - for (size_t i = 0; i < segmentCount; ++i) { - assert(currentWatcher); - - const bool fileSegment = (i == segmentCount - 1 && pathIsFile); - const bool rootSegment = (i == 0); - assert( - !(fileSegment && - rootSegment)); // Can not be both filename and root part of the path! - - const std::string& currentSegment = pathSegments[i]; - if (currentSegment.empty()) { - continue; - } - - cmIBaseWatcher* nextWatcher = currentWatcher->Find(currentSegment); - if (!nextWatcher) { - if (rootSegment) { // Root part - assert(currentWatcher == this->Root.get()); - nextWatcher = - new cmRootDirectoryWatcher(this->Root.get(), currentSegment); - assert(currentWatcher->Find(currentSegment) == nextWatcher); - } else if (fileSegment) { // File part - assert(currentWatcher != this->Root.get()); - nextWatcher = new cmFileWatcher( - dynamic_cast<cmRealDirectoryWatcher*>(currentWatcher), - currentSegment, cb); - assert(currentWatcher->Find(currentSegment) == nextWatcher); - } else { // Any normal directory in between - nextWatcher = new cmDirectoryWatcher( - dynamic_cast<cmRealDirectoryWatcher*>(currentWatcher), - currentSegment); - assert(currentWatcher->Find(currentSegment) == nextWatcher); - } - } else { - if (fileSegment) { - auto filePtr = dynamic_cast<cmFileWatcher*>(nextWatcher); - assert(filePtr); - filePtr->AppendCallback(cb); - continue; - } - } - currentWatcher = dynamic_cast<cmVirtualDirectoryWatcher*>(nextWatcher); - } - } - this->Root->StartWatching(); -} - -void cmFileMonitor::StopMonitoring() -{ - this->Root->StopWatching(); - this->Root->Reset(); -} - -std::vector<std::string> cmFileMonitor::WatchedFiles() const -{ - std::vector<std::string> result; - if (this->Root) { - result = this->Root->WatchedFiles(); - } - return result; -} - -std::vector<std::string> cmFileMonitor::WatchedDirectories() const -{ - std::vector<std::string> result; - if (this->Root) { - result = this->Root->WatchedDirectories(); - } - return result; -} diff --git a/Source/cmFileMonitor.h b/Source/cmFileMonitor.h deleted file mode 100644 index fc75b0c..0000000 --- a/Source/cmFileMonitor.h +++ /dev/null @@ -1,35 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#pragma once - -#include "cmConfigure.h" // IWYU pragma: keep - -#include <functional> -#include <memory> -#include <string> -#include <vector> - -#include <cm3p/uv.h> - -class cmRootWatcher; - -class cmFileMonitor -{ - -public: - cmFileMonitor(uv_loop_t* l); - ~cmFileMonitor(); - - cmFileMonitor(cmFileMonitor const&) = delete; - cmFileMonitor& operator=(cmFileMonitor const&) = delete; - - using Callback = std::function<void(const std::string&, int, int)>; - void MonitorPaths(const std::vector<std::string>& paths, Callback const& cb); - void StopMonitoring(); - - std::vector<std::string> WatchedFiles() const; - std::vector<std::string> WatchedDirectories() const; - -private: - std::unique_ptr<cmRootWatcher> Root; -}; diff --git a/Source/cmFindCommon.cxx b/Source/cmFindCommon.cxx index dee91d7..7952336 100644 --- a/Source/cmFindCommon.cxx +++ b/Source/cmFindCommon.cxx @@ -182,7 +182,7 @@ void cmFindCommon::SelectDefaultSearchModes() { this->NoCMakeSystemPath, "CMAKE_FIND_USE_CMAKE_SYSTEM_PATH" } } }; - for (auto& path : search_paths) { + for (auto const& path : search_paths) { cmProp def = this->Makefile->GetDefinition(path.second); if (def) { path.first = !cmIsOn(*def); diff --git a/Source/cmFunctionCommand.cxx b/Source/cmFunctionCommand.cxx index 71c82d6..1359009 100644 --- a/Source/cmFunctionCommand.cxx +++ b/Source/cmFunctionCommand.cxx @@ -163,8 +163,11 @@ bool cmFunctionFunctionBlocker::Replay( f.FilePath = this->GetStartingContext().FilePath; f.Line = this->GetStartingContext().Line; mf.RecordPolicies(f.Policies); - mf.GetState()->AddScriptedCommand(this->Args.front(), std::move(f)); - return true; + return mf.GetState()->AddScriptedCommand( + this->Args.front(), + BT<cmState::Command>(std::move(f), + mf.GetBacktrace().Push(this->GetStartingContext())), + mf); } } // anonymous namespace diff --git a/Source/cmGccDepfileLexerHelper.cxx b/Source/cmGccDepfileLexerHelper.cxx index 957896f..c782bcd 100644 --- a/Source/cmGccDepfileLexerHelper.cxx +++ b/Source/cmGccDepfileLexerHelper.cxx @@ -27,23 +27,30 @@ bool cmGccDepfileLexerHelper::readFile(const char* filePath) if (!file) { return false; } - newEntry(); + this->newEntry(); yyscan_t scanner; cmGccDepfile_yylex_init(&scanner); cmGccDepfile_yyset_extra(this, scanner); cmGccDepfile_yyrestart(file, scanner); cmGccDepfile_yylex(scanner); cmGccDepfile_yylex_destroy(scanner); - sanitizeContent(); + this->sanitizeContent(); fclose(file); - return true; + return this->HelperState != State::Failed; } void cmGccDepfileLexerHelper::newEntry() { + if (this->HelperState == State::Rule && !this->Content.empty()) { + if (!this->Content.back().rules.empty() && + !this->Content.back().rules.back().empty()) { + this->HelperState = State::Failed; + } + return; + } this->HelperState = State::Rule; this->Content.emplace_back(); - newRule(); + this->newRule(); } void cmGccDepfileLexerHelper::newRule() @@ -56,20 +63,22 @@ void cmGccDepfileLexerHelper::newRule() void cmGccDepfileLexerHelper::newDependency() { - // printf("NEW DEP\n"); + if (this->HelperState == State::Failed) { + return; + } this->HelperState = State::Dependency; - if (this->Content.back().paths.empty() || - !this->Content.back().paths.back().empty()) { - this->Content.back().paths.emplace_back(); + auto& entry = this->Content.back(); + if (entry.paths.empty() || !entry.paths.back().empty()) { + entry.paths.emplace_back(); } } void cmGccDepfileLexerHelper::newRuleOrDependency() { if (this->HelperState == State::Rule) { - newRule(); - } else { - newDependency(); + this->newRule(); + } else if (this->HelperState == State::Dependency) { + this->newDependency(); } } @@ -93,6 +102,8 @@ void cmGccDepfileLexerHelper::addToCurrentPath(const char* s) } dst = &dep->paths.back(); } break; + case State::Failed: + return; } dst->append(s); } diff --git a/Source/cmGccDepfileLexerHelper.h b/Source/cmGccDepfileLexerHelper.h index 07ca61d..91132f5 100644 --- a/Source/cmGccDepfileLexerHelper.h +++ b/Source/cmGccDepfileLexerHelper.h @@ -29,7 +29,8 @@ private: enum class State { Rule, - Dependency + Dependency, + Failed, }; State HelperState = State::Rule; }; diff --git a/Source/cmGccDepfileReader.cxx b/Source/cmGccDepfileReader.cxx index 9d70ede..eb3511a 100644 --- a/Source/cmGccDepfileReader.cxx +++ b/Source/cmGccDepfileReader.cxx @@ -5,14 +5,15 @@ #include <type_traits> #include <utility> +#include <cm/optional> + #include "cmGccDepfileLexerHelper.h" -cmGccDepfileContent cmReadGccDepfile(const char* filePath) +cm::optional<cmGccDepfileContent> cmReadGccDepfile(const char* filePath) { - cmGccDepfileContent result; cmGccDepfileLexerHelper helper; if (helper.readFile(filePath)) { - result = std::move(helper).extractContent(); + return cm::make_optional(std::move(helper).extractContent()); } - return result; + return cm::nullopt; } diff --git a/Source/cmGccDepfileReader.h b/Source/cmGccDepfileReader.h index 395dd77..59ed7fd 100644 --- a/Source/cmGccDepfileReader.h +++ b/Source/cmGccDepfileReader.h @@ -2,6 +2,8 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #pragma once +#include <cm/optional> + #include "cmGccDepfileReaderTypes.h" -cmGccDepfileContent cmReadGccDepfile(const char* filePath); +cm::optional<cmGccDepfileContent> cmReadGccDepfile(const char* filePath); diff --git a/Source/cmGeneratorExpressionEvaluator.h b/Source/cmGeneratorExpressionEvaluator.h index 3e7737e..af2afd6 100644 --- a/Source/cmGeneratorExpressionEvaluator.h +++ b/Source/cmGeneratorExpressionEvaluator.h @@ -60,7 +60,7 @@ struct TextContent : public cmGeneratorExpressionEvaluator void Extend(size_t length) { this->Length += length; } - size_t GetLength() { return this->Length; } + size_t GetLength() const { return this->Length; } private: const char* Content; diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index 4ca7405..e40316e 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx @@ -677,7 +677,7 @@ struct CompilerIdNode : public cmGeneratorExpressionNode } static cmsys::RegularExpression compilerIdValidator("^[A-Za-z0-9_]*$"); - for (auto& param : parameters) { + for (auto const& param : parameters) { if (!compilerIdValidator.find(param)) { reportError(context, content->GetOriginalExpression(), @@ -805,7 +805,7 @@ struct PlatformIdNode : public cmGeneratorExpressionNode return parameters.front().empty() ? "1" : "0"; } - for (auto& param : parameters) { + for (auto const& param : parameters) { if (param == platformId) { return "1"; } @@ -901,7 +901,7 @@ static const struct ConfigurationTestNode : public cmGeneratorExpressionNode return std::string(); } context->HadContextSensitiveCondition = true; - for (auto& param : parameters) { + for (auto const& param : parameters) { if (context->Config.empty()) { if (param.empty()) { return "1"; @@ -927,7 +927,7 @@ static const struct ConfigurationTestNode : public cmGeneratorExpressionNode if (cmProp mapValue = context->CurrentTarget->GetProperty(mapProp)) { cmExpandList(cmSystemTools::UpperCase(*mapValue), mappedConfigs); - for (auto& param : parameters) { + for (auto const& param : parameters) { if (cm::contains(mappedConfigs, cmSystemTools::UpperCase(param))) { return "1"; } @@ -995,7 +995,7 @@ static const struct CompileLanguageNode : public cmGeneratorExpressionNode return context->Language; } - for (auto& param : parameters) { + for (auto const& param : parameters) { if (context->Language == param) { return "1"; } @@ -1101,7 +1101,7 @@ static const struct LinkLanguageNode : public cmGeneratorExpressionNode return context->Language; } - for (auto& param : parameters) { + for (auto const& param : parameters) { if (context->Language == param) { return "1"; } @@ -1129,7 +1129,7 @@ struct LinkerId } static cmsys::RegularExpression linkerIdValidator("^[A-Za-z0-9_]*$"); - for (auto& param : parameters) { + for (auto const& param : parameters) { if (!linkerIdValidator.find(param)) { reportError(context, content->GetOriginalExpression(), "Expression syntax not recognized."); diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index e735897..4a79a3d 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -24,9 +24,7 @@ #include "cmAlgorithms.h" #include "cmComputeLinkInformation.h" -#include "cmCustomCommand.h" #include "cmCustomCommandGenerator.h" -#include "cmCustomCommandLines.h" #include "cmFileTimes.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" @@ -254,7 +252,7 @@ EvaluatedTargetPropertyEntries EvaluateTargetPropertyEntries( { EvaluatedTargetPropertyEntries out; out.Entries.reserve(in.size()); - for (auto& entry : in) { + for (auto const& entry : in) { out.Entries.emplace_back(EvaluateTargetPropertyEntry( thisTarget, config, lang, dagChecker, *entry)); } @@ -332,7 +330,7 @@ cmGeneratorTarget::~cmGeneratorTarget() = default; const std::string& cmGeneratorTarget::GetSourcesProperty() const { std::vector<std::string> values; - for (auto& se : this->SourceEntries) { + for (auto const& se : this->SourceEntries) { values.push_back(se->GetInput()); } static std::string value; @@ -992,9 +990,8 @@ cmProp cmGeneratorTarget::GetLanguageExtensions(std::string const& lang) const bool cmGeneratorTarget::GetLanguageStandardRequired( std::string const& lang) const { - cmProp p = - this->GetPropertyWithPairedLanguageSupport(lang, "_STANDARD_REQUIRED"); - return cmIsOn(p); + return cmIsOn( + this->GetPropertyWithPairedLanguageSupport(lang, "_STANDARD_REQUIRED")); } void cmGeneratorTarget::GetModuleDefinitionSources( @@ -1539,10 +1536,14 @@ bool processSources(cmGeneratorTarget const* tgt, for (std::string& src : entry.Values) { cmSourceFile* sf = mf->GetOrCreateSource(src); std::string e; - std::string fullPath = sf->ResolveFullPath(&e); + std::string w; + std::string fullPath = sf->ResolveFullPath(&e, &w); + cmake* cm = tgt->GetLocalGenerator()->GetCMakeInstance(); + if (!w.empty()) { + cm->IssueMessage(MessageType::AUTHOR_WARNING, w, tgt->GetBacktrace()); + } if (fullPath.empty()) { if (!e.empty()) { - cmake* cm = tgt->GetLocalGenerator()->GetCMakeInstance(); cm->IssueMessage(MessageType::FATAL_ERROR, e, tgt->GetBacktrace()); } return contextDependent; @@ -2500,7 +2501,7 @@ public: } } - bool GetHadLinkLanguageSensitiveCondition() + bool GetHadLinkLanguageSensitiveCondition() const { return HadLinkLanguageSensitiveCondition; } @@ -2888,9 +2889,6 @@ private: bool IsUtility(std::string const& dep); void CheckCustomCommand(cmCustomCommand const& cc); void CheckCustomCommands(const std::vector<cmCustomCommand>& commands); - void FollowCommandDepends(cmCustomCommand const& cc, - const std::string& config, - std::set<std::string>& emitted); }; cmTargetTraceDependencies::cmTargetTraceDependencies(cmGeneratorTarget* target) @@ -2986,7 +2984,8 @@ void cmTargetTraceDependencies::FollowName(std::string const& name) auto i = this->NameMap.lower_bound(name); if (i == this->NameMap.end() || i->first != name) { // Check if we know how to generate this file. - cmSourcesWithOutput sources = this->Makefile->GetSourcesWithOutput(name); + cmSourcesWithOutput sources = + this->LocalGenerator->GetSourcesWithOutput(name); // If we failed to find a target or source and we have a relative path, it // might be a valid source if made relative to the current binary // directory. @@ -2996,7 +2995,7 @@ void cmTargetTraceDependencies::FollowName(std::string const& name) cmStrCat(this->Makefile->GetCurrentBinaryDirectory(), '/', name); fullname = cmSystemTools::CollapseFullPath( fullname, this->Makefile->GetHomeOutputDirectory()); - sources = this->Makefile->GetSourcesWithOutput(fullname); + sources = this->LocalGenerator->GetSourcesWithOutput(fullname); } i = this->NameMap.emplace_hint(i, name, sources); } @@ -3076,71 +3075,27 @@ bool cmTargetTraceDependencies::IsUtility(std::string const& dep) void cmTargetTraceDependencies::CheckCustomCommand(cmCustomCommand const& cc) { - // Transform command names that reference targets built in this - // project to corresponding target-level dependencies. - cmGeneratorExpression ge(cc.GetBacktrace()); - - // Add target-level dependencies referenced by generator expressions. - std::set<cmGeneratorTarget*> targets; - - for (cmCustomCommandLine const& cCmdLine : cc.GetCommandLines()) { - std::string const& command = cCmdLine.front(); - // Check for a target with this name. - if (cmGeneratorTarget* t = - this->LocalGenerator->FindGeneratorTargetToUse(command)) { - if (t->GetType() == cmStateEnums::EXECUTABLE) { - // The command refers to an executable target built in - // this project. Add the target-level dependency to make - // sure the executable is up to date before this custom - // command possibly runs. - this->GeneratorTarget->Target->AddUtility(command, true); - } - } + // Collect dependencies referenced by all configurations. + std::set<std::string> depends; + for (std::string const& config : + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig)) { + cmCustomCommandGenerator ccg(cc, config, this->LocalGenerator); - // Check for target references in generator expressions. - std::vector<std::string> const& configs = - this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); - for (std::string const& c : configs) { - for (std::string const& cl : cCmdLine) { - const std::unique_ptr<cmCompiledGeneratorExpression> cge = - ge.Parse(cl); - cge->SetQuiet(true); - cge->Evaluate(this->GeneratorTarget->GetLocalGenerator(), c); - std::set<cmGeneratorTarget*> geTargets = cge->GetTargets(); - targets.insert(geTargets.begin(), geTargets.end()); - } + // Collect target-level dependencies referenced in command lines. + for (auto const& util : ccg.GetUtilities()) { + this->GeneratorTarget->Target->AddUtility(util); } - } - - for (cmGeneratorTarget* target : targets) { - this->GeneratorTarget->Target->AddUtility(target->GetName(), true); - } - // Queue the custom command dependencies. - std::set<std::string> emitted; - std::vector<std::string> const& configs = - this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); - for (std::string const& conf : configs) { - this->FollowCommandDepends(cc, conf, emitted); + // Collect file-level dependencies referenced in DEPENDS. + depends.insert(ccg.GetDepends().begin(), ccg.GetDepends().end()); } -} - -void cmTargetTraceDependencies::FollowCommandDepends( - cmCustomCommand const& cc, const std::string& config, - std::set<std::string>& emitted) -{ - cmCustomCommandGenerator ccg(cc, config, - this->GeneratorTarget->LocalGenerator); - - const std::vector<std::string>& depends = ccg.GetDepends(); + // Queue file-level dependencies. for (std::string const& dep : depends) { - if (emitted.insert(dep).second) { - if (!this->IsUtility(dep)) { - // The dependency does not name a target and may be a file we - // know how to generate. Queue it. - this->FollowName(dep); - } + if (!this->IsUtility(dep)) { + // The dependency does not name a target and may be a file we + // know how to generate. Queue it. + this->FollowName(dep); } } } diff --git a/Source/cmGetPropertyCommand.cxx b/Source/cmGetPropertyCommand.cxx index 3a5b39d..e755399 100644 --- a/Source/cmGetPropertyCommand.cxx +++ b/Source/cmGetPropertyCommand.cxx @@ -280,8 +280,9 @@ bool HandleGlobalMode(cmExecutionStatus& status, const std::string& name, // Get the property. cmake* cm = status.GetMakefile().GetCMakeInstance(); - cmProp p = cm->GetState()->GetGlobalProperty(propertyName); - return StoreResult(infoType, status.GetMakefile(), variable, cmToCStr(p)); + return StoreResult( + infoType, status.GetMakefile(), variable, + cmToCStr(cm->GetState()->GetGlobalProperty(propertyName))); } bool HandleDirectoryMode(cmExecutionStatus& status, const std::string& name, @@ -327,8 +328,8 @@ bool HandleDirectoryMode(cmExecutionStatus& status, const std::string& name, } // Get the property. - cmProp p = mf->GetProperty(propertyName); - return StoreResult(infoType, status.GetMakefile(), variable, cmToCStr(p)); + return StoreResult(infoType, status.GetMakefile(), variable, + cmToCStr(mf->GetProperty(propertyName))); } bool HandleTargetMode(cmExecutionStatus& status, const std::string& name, @@ -358,15 +359,14 @@ bool HandleTargetMode(cmExecutionStatus& status, const std::string& name, } return StoreResult(infoType, status.GetMakefile(), variable, nullptr); } - cmProp prop_cstr = nullptr; cmListFileBacktrace bt = status.GetMakefile().GetBacktrace(); cmMessenger* messenger = status.GetMakefile().GetMessenger(); - prop_cstr = target->GetComputedProperty(propertyName, messenger, bt); - if (!prop_cstr) { - prop_cstr = target->GetProperty(propertyName); + cmProp prop = target->GetComputedProperty(propertyName, messenger, bt); + if (!prop) { + prop = target->GetProperty(propertyName); } return StoreResult(infoType, status.GetMakefile(), variable, - prop_cstr ? prop_cstr->c_str() : nullptr); + cmToCStr(prop)); } status.SetError(cmStrCat("could not find TARGET ", name, ". Perhaps it has not yet been created.")); @@ -391,7 +391,7 @@ bool HandleSourceMode(cmExecutionStatus& status, const std::string& name, if (cmSourceFile* sf = directory_makefile.GetOrCreateSource(source_file_absolute_path)) { return StoreResult(infoType, status.GetMakefile(), variable, - sf->GetPropertyForUser(propertyName)); + cmToCStr(sf->GetPropertyForUser(propertyName))); } status.SetError( cmStrCat("given SOURCE name that could not be found or created: ", diff --git a/Source/cmGetSourceFilePropertyCommand.cxx b/Source/cmGetSourceFilePropertyCommand.cxx index 5395bc8..212a968 100644 --- a/Source/cmGetSourceFilePropertyCommand.cxx +++ b/Source/cmGetSourceFilePropertyCommand.cxx @@ -4,6 +4,7 @@ #include "cmExecutionStatus.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmSetPropertyCommand.h" #include "cmSourceFile.h" @@ -57,14 +58,14 @@ bool cmGetSourceFilePropertyCommand(std::vector<std::string> const& args, } if (sf) { - const char* prop = nullptr; + cmProp prop = nullptr; if (!args[property_arg_index].empty()) { prop = sf->GetPropertyForUser(args[property_arg_index]); } if (prop) { // Set the value on the original Makefile scope, not the scope of the // requested directory. - status.GetMakefile().AddDefinition(var, prop); + status.GetMakefile().AddDefinition(var, *prop); return true; } } diff --git a/Source/cmGhsMultiTargetGenerator.cxx b/Source/cmGhsMultiTargetGenerator.cxx index a8f8f57..aabe43c 100644 --- a/Source/cmGhsMultiTargetGenerator.cxx +++ b/Source/cmGhsMultiTargetGenerator.cxx @@ -721,8 +721,7 @@ void cmGhsMultiTargetGenerator::WriteObjectLangOverride( bool cmGhsMultiTargetGenerator::DetermineIfIntegrityApp() { - cmProp p = this->GeneratorTarget->GetProperty("ghs_integrity_app"); - if (p) { + if (cmProp p = this->GeneratorTarget->GetProperty("ghs_integrity_app")) { return cmIsOn(*p); } std::vector<cmSourceFile*> sources; @@ -763,9 +762,9 @@ bool cmGhsMultiTargetGenerator::VisitCustomCommand( /* set temporary mark; check if revisit*/ if (temp.insert(si).second) { for (auto& di : si->GetCustomCommand()->GetDepends()) { - cmSourceFile const* sf = this->GeneratorTarget->GetLocalGenerator() - ->GetMakefile() - ->GetSourceFileWithOutput(di); + cmSourceFile const* sf = + this->GeneratorTarget->GetLocalGenerator()->GetSourceFileWithOutput( + di); /* if sf exists then visit */ if (sf && this->VisitCustomCommand(temp, perm, order, sf)) { return true; diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 1197db6..52f1d52 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -2601,7 +2601,7 @@ void cmGlobalGenerator::AddGlobalTarget_Test( } void cmGlobalGenerator::AddGlobalTarget_EditCache( - std::vector<GlobalTargetInfo>& targets) + std::vector<GlobalTargetInfo>& targets) const { const char* editCacheTargetName = this->GetEditCacheTargetName(); if (!editCacheTargetName) { @@ -2635,7 +2635,7 @@ void cmGlobalGenerator::AddGlobalTarget_EditCache( } void cmGlobalGenerator::AddGlobalTarget_RebuildCache( - std::vector<GlobalTargetInfo>& targets) + std::vector<GlobalTargetInfo>& targets) const { const char* rebuildCacheTargetName = this->GetRebuildCacheTargetName(); if (!rebuildCacheTargetName) { @@ -2758,7 +2758,7 @@ void cmGlobalGenerator::AddGlobalTarget_Install( } } -std::string cmGlobalGenerator::GetPredefinedTargetsFolder() +std::string cmGlobalGenerator::GetPredefinedTargetsFolder() const { cmProp prop = this->GetCMakeInstance()->GetState()->GetGlobalProperty( "PREDEFINED_TARGETS_FOLDER"); diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index b532a43..afafba9 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -14,6 +14,7 @@ #include <utility> #include <vector> +#include <cm/optional> #include <cmext/algorithm> #include "cm_codecvt.hxx" @@ -26,6 +27,7 @@ #include "cmSystemTools.h" #include "cmTarget.h" #include "cmTargetDepend.h" +#include "cmTransformDepfile.h" #if !defined(CMAKE_BOOTSTRAP) # include <cm3p/json/value.h> @@ -452,6 +454,10 @@ public: virtual bool ShouldStripResourcePath(cmMakefile*) const; virtual bool SupportsCustomCommandDepfile() const { return false; } + virtual cm::optional<cmDepfileFormat> DepfileFormat() const + { + return cm::nullopt; + } std::string GetSharedLibFlagsForLanguage(std::string const& lang) const; @@ -568,8 +574,9 @@ protected: void AddGlobalTarget_Package(std::vector<GlobalTargetInfo>& targets); void AddGlobalTarget_PackageSource(std::vector<GlobalTargetInfo>& targets); void AddGlobalTarget_Test(std::vector<GlobalTargetInfo>& targets); - void AddGlobalTarget_EditCache(std::vector<GlobalTargetInfo>& targets); - void AddGlobalTarget_RebuildCache(std::vector<GlobalTargetInfo>& targets); + void AddGlobalTarget_EditCache(std::vector<GlobalTargetInfo>& targets) const; + void AddGlobalTarget_RebuildCache( + std::vector<GlobalTargetInfo>& targets) const; void AddGlobalTarget_Install(std::vector<GlobalTargetInfo>& targets); cmTarget CreateGlobalTarget(GlobalTargetInfo const& gti, cmMakefile* mf); @@ -595,7 +602,7 @@ protected: cmGeneratorTarget* FindGeneratorTargetImpl(std::string const& name) const; - std::string GetPredefinedTargetsFolder(); + std::string GetPredefinedTargetsFolder() const; private: using TargetMap = std::unordered_map<std::string, cmTarget*>; diff --git a/Source/cmGlobalGhsMultiGenerator.cxx b/Source/cmGlobalGhsMultiGenerator.cxx index c08c9cf..33bf830 100644 --- a/Source/cmGlobalGhsMultiGenerator.cxx +++ b/Source/cmGlobalGhsMultiGenerator.cxx @@ -704,7 +704,7 @@ bool cmGlobalGhsMultiGenerator::ComputeTargetBuildOrder( std::set<cmGeneratorTarget const*> temp; std::set<cmGeneratorTarget const*> perm; - for (auto ti : tgt) { + for (auto const ti : tgt) { bool r = VisitTarget(temp, perm, build, ti); if (r) { return r; @@ -726,7 +726,7 @@ bool cmGlobalGhsMultiGenerator::VisitTarget( * in the same order */ OrderedTargetDependSet sortedTargets(this->GetTargetDirectDepends(ti), ""); - for (auto& di : sortedTargets) { + for (auto const& di : sortedTargets) { if (this->VisitTarget(temp, perm, order, di)) { return true; } diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index cf45da9..83d15ab 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -1064,7 +1064,7 @@ void cmGlobalNinjaGenerator::CloseCompileCommandsStream() } } -void cmGlobalNinjaGenerator::WriteDisclaimer(std::ostream& os) +void cmGlobalNinjaGenerator::WriteDisclaimer(std::ostream& os) const { os << "# CMAKE generated file: DO NOT EDIT!\n" << "# Generated by \"" << this->GetName() << "\"" @@ -1095,7 +1095,7 @@ std::string cmGlobalNinjaGenerator::OrderDependsTargetForTarget( void cmGlobalNinjaGenerator::AppendTargetOutputs( cmGeneratorTarget const* target, cmNinjaDeps& outputs, - const std::string& config, cmNinjaTargetDepends depends) + const std::string& config, cmNinjaTargetDepends depends) const { // for frameworks, we want the real name, not smple name // frameworks always appear versioned, and the build.ninja @@ -2659,30 +2659,17 @@ void cmGlobalNinjaMultiGenerator::GetQtAutoGenConfigs( bool cmGlobalNinjaMultiGenerator::InspectConfigTypeVariables() { - this->GetCMakeInstance()->MarkCliAsUsed("CMAKE_DEFAULT_BUILD_TYPE"); - this->GetCMakeInstance()->MarkCliAsUsed("CMAKE_CROSS_CONFIGS"); - this->GetCMakeInstance()->MarkCliAsUsed("CMAKE_DEFAULT_CONFIGS"); - return this->ReadCacheEntriesForBuild(*this->Makefiles.front()->GetState()); -} - -std::string cmGlobalNinjaMultiGenerator::GetDefaultBuildConfig() const -{ - return ""; -} - -bool cmGlobalNinjaMultiGenerator::ReadCacheEntriesForBuild( - const cmState& state) -{ std::vector<std::string> configsVec; - cmExpandList(state.GetSafeCacheEntryValue("CMAKE_CONFIGURATION_TYPES"), - configsVec); + cmExpandList( + this->Makefiles.front()->GetSafeDefinition("CMAKE_CONFIGURATION_TYPES"), + configsVec); if (configsVec.empty()) { configsVec.emplace_back(); } std::set<std::string> configs(configsVec.cbegin(), configsVec.cend()); this->DefaultFileConfig = - state.GetSafeCacheEntryValue("CMAKE_DEFAULT_BUILD_TYPE"); + this->Makefiles.front()->GetSafeDefinition("CMAKE_DEFAULT_BUILD_TYPE"); if (this->DefaultFileConfig.empty()) { this->DefaultFileConfig = configsVec.front(); } @@ -2697,8 +2684,9 @@ bool cmGlobalNinjaMultiGenerator::ReadCacheEntriesForBuild( } std::vector<std::string> crossConfigsVec; - cmExpandList(state.GetSafeCacheEntryValue("CMAKE_CROSS_CONFIGS"), - crossConfigsVec); + cmExpandList( + this->Makefiles.front()->GetSafeDefinition("CMAKE_CROSS_CONFIGS"), + crossConfigsVec); auto crossConfigs = ListSubsetWithAll(configs, configs, crossConfigsVec); if (!crossConfigs) { std::ostringstream msg; @@ -2711,7 +2699,7 @@ bool cmGlobalNinjaMultiGenerator::ReadCacheEntriesForBuild( this->CrossConfigs = *crossConfigs; auto defaultConfigsString = - state.GetSafeCacheEntryValue("CMAKE_DEFAULT_CONFIGS"); + this->Makefiles.front()->GetSafeDefinition("CMAKE_DEFAULT_CONFIGS"); if (defaultConfigsString.empty()) { defaultConfigsString = this->DefaultFileConfig; } @@ -2745,6 +2733,11 @@ bool cmGlobalNinjaMultiGenerator::ReadCacheEntriesForBuild( return true; } +std::string cmGlobalNinjaMultiGenerator::GetDefaultBuildConfig() const +{ + return ""; +} + std::string cmGlobalNinjaMultiGenerator::OrderDependsTargetForTarget( cmGeneratorTarget const* target, const std::string& config) const { diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index a0724ac..40d3cfc 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -24,6 +24,7 @@ #include "cmNinjaTypes.h" #include "cmPolicies.h" #include "cmStringAlgorithms.h" +#include "cmTransformDepfile.h" class cmCustomCommand; class cmGeneratorTarget; @@ -31,7 +32,6 @@ class cmLinkLineComputer; class cmLocalGenerator; class cmMakefile; class cmOutputConverter; -class cmState; class cmStateDirectory; class cmake; struct cmDocumentationEntry; @@ -211,6 +211,10 @@ public: const char* GetCleanTargetName() const override { return "clean"; } bool SupportsCustomCommandDepfile() const override { return true; } + cm::optional<cmDepfileFormat> DepfileFormat() const override + { + return cmDepfileFormat::GccDepfile; + } virtual cmGeneratedFileStream* GetImplFileStream( const std::string& /*config*/) const @@ -248,7 +252,7 @@ public: : GG(gg) { } - std::string operator()(std::string const& path) + std::string operator()(std::string const& path) const { return this->GG->ConvertToNinjaPath(path); } @@ -319,7 +323,7 @@ public: void AppendTargetOutputs(cmGeneratorTarget const* target, cmNinjaDeps& outputs, const std::string& config, - cmNinjaTargetDepends depends); + cmNinjaTargetDepends depends) const; void AppendTargetDepends(cmGeneratorTarget const* target, cmNinjaDeps& outputs, const std::string& config, const std::string& fileConfig, @@ -459,7 +463,7 @@ private: void CleanMetaData(); /// Write the common disclaimer text at the top of each build file. - void WriteDisclaimer(std::ostream& os); + void WriteDisclaimer(std::ostream& os) const; void WriteAssumedSourceDependencies(); @@ -648,8 +652,6 @@ public: std::string GetDefaultBuildConfig() const override; - bool ReadCacheEntriesForBuild(const cmState& state) override; - bool SupportsDefaultBuildType() const override { return true; } bool SupportsCrossConfigs() const override { return true; } bool SupportsDefaultConfigs() const override { return true; } diff --git a/Source/cmGlobalUnixMakefileGenerator3.cxx b/Source/cmGlobalUnixMakefileGenerator3.cxx index 2c934e1..9c3a533 100644 --- a/Source/cmGlobalUnixMakefileGenerator3.cxx +++ b/Source/cmGlobalUnixMakefileGenerator3.cxx @@ -589,10 +589,16 @@ cmGlobalUnixMakefileGenerator3::GenerateBuildCommand( } makeCommand.Add(this->SelectMakeProgram(makeProgram)); + // Explicitly tell the make tool to use the Makefile written by + // cmLocalUnixMakefileGenerator3::WriteLocalMakefile + makeCommand.Add("-f"); + makeCommand.Add("Makefile"); + if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) { - makeCommand.Add("-j"); - if (jobs != cmake::DEFAULT_BUILD_PARALLEL_LEVEL) { - makeCommand.Add(std::to_string(jobs)); + if (jobs == cmake::DEFAULT_BUILD_PARALLEL_LEVEL) { + makeCommand.Add("-j"); + } else { + makeCommand.Add("-j" + std::to_string(jobs)); } } diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index 6267205..75cd714 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx @@ -380,9 +380,10 @@ void cmGlobalVisualStudio7Generator::WriteTargetsToSolution( std::string project = target->GetName(); std::string location = *expath; - cmProp p = target->GetProperty("VS_PROJECT_TYPE"); - this->WriteExternalProject(fout, project, location, cmToCStr(p), - target->GetUtilities()); + this->WriteExternalProject( + fout, project, location, + cmToCStr(target->GetProperty("VS_PROJECT_TYPE")), + target->GetUtilities()); written = true; } else { cmProp vcprojName = target->GetProperty("GENERATOR_FILE_NAME"); diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 766ae72..026e96c 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -503,16 +503,15 @@ cmGlobalXCodeGenerator::GenerateBuildCommand( } } - if (this->XcodeBuildSystem >= BuildSystem::Twelve) { + if ((this->XcodeBuildSystem >= BuildSystem::Twelve) || + (jobs != cmake::NO_BUILD_PARALLEL_LEVEL)) { makeCommand.Add("-parallelizeTargets"); } makeCommand.Add("-configuration", (config.empty() ? "Debug" : config)); - if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) { - makeCommand.Add("-jobs"); - if (jobs != cmake::DEFAULT_BUILD_PARALLEL_LEVEL) { - makeCommand.Add(std::to_string(jobs)); - } + if ((jobs != cmake::NO_BUILD_PARALLEL_LEVEL) && + (jobs != cmake::DEFAULT_BUILD_PARALLEL_LEVEL)) { + makeCommand.Add("-jobs", std::to_string(jobs)); } if (this->XcodeVersion >= 70) { diff --git a/Source/cmIncludeCommand.cxx b/Source/cmIncludeCommand.cxx index ae801bb..5e3aec5 100644 --- a/Source/cmIncludeCommand.cxx +++ b/Source/cmIncludeCommand.cxx @@ -146,11 +146,24 @@ bool cmIncludeCommand(std::vector<std::string> const& args, std::string listFile = cmSystemTools::CollapseFullPath( fname, status.GetMakefile().GetCurrentSourceDirectory()); - if (optional && !cmSystemTools::FileExists(listFile)) { + + const bool fileDoesnotExist = !cmSystemTools::FileExists(listFile); + const bool fileIsDirectory = cmSystemTools::FileIsDirectory(listFile); + if (fileDoesnotExist || fileIsDirectory) { if (!resultVarName.empty()) { status.GetMakefile().AddDefinition(resultVarName, "NOTFOUND"); } - return true; + if (optional) { + return true; + } + if (fileDoesnotExist) { + status.SetError(cmStrCat("could not find requested file:\n ", fname)); + return false; + } + if (fileIsDirectory) { + status.SetError(cmStrCat("requested file is a directory:\n ", fname)); + return false; + } } bool readit = @@ -163,9 +176,7 @@ bool cmIncludeCommand(std::vector<std::string> const& args, } if (!optional && !readit && !cmSystemTools::GetFatalErrorOccured()) { - std::string m = cmStrCat("could not find load file:\n" - " ", - fname); + std::string m = cmStrCat("could not load requested file:\n ", fname); status.SetError(m); return false; } diff --git a/Source/cmJsonObjectDictionary.h b/Source/cmJsonObjectDictionary.h deleted file mode 100644 index 8a2b529..0000000 --- a/Source/cmJsonObjectDictionary.h +++ /dev/null @@ -1,45 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#pragma once - -#include <string> - -// Vocabulary: - -static const std::string kARTIFACTS_KEY = "artifacts"; -static const std::string kBUILD_DIRECTORY_KEY = "buildDirectory"; -static const std::string kCOMPILE_FLAGS_KEY = "compileFlags"; -static const std::string kCONFIGURATIONS_KEY = "configurations"; -static const std::string kDEFINES_KEY = "defines"; -static const std::string kFILE_GROUPS_KEY = "fileGroups"; -static const std::string kFRAMEWORK_PATH_KEY = "frameworkPath"; -static const std::string kFULL_NAME_KEY = "fullName"; -static const std::string kINCLUDE_PATH_KEY = "includePath"; -static const std::string kIS_CMAKE_KEY = "isCMake"; -static const std::string kIS_GENERATED_KEY = "isGenerated"; -static const std::string kIS_SYSTEM_KEY = "isSystem"; -static const std::string kIS_TEMPORARY_KEY = "isTemporary"; -static const std::string kKEY_KEY = "key"; -static const std::string kLANGUAGE_KEY = "language"; -static const std::string kLINKER_LANGUAGE_KEY = "linkerLanguage"; -static const std::string kLINK_FLAGS_KEY = "linkFlags"; -static const std::string kLINK_LANGUAGE_FLAGS_KEY = "linkLanguageFlags"; -static const std::string kLINK_LIBRARIES_KEY = "linkLibraries"; -static const std::string kLINK_PATH_KEY = "linkPath"; -static const std::string kNAME_KEY = "name"; -static const std::string kPATH_KEY = "path"; -static const std::string kPROJECTS_KEY = "projects"; -static const std::string kPROPERTIES_KEY = "properties"; -static const std::string kSOURCE_DIRECTORY_KEY = "sourceDirectory"; -static const std::string kSOURCES_KEY = "sources"; -static const std::string kSYSROOT_KEY = "sysroot"; -static const std::string kTARGETS_KEY = "targets"; -static const std::string kTYPE_KEY = "type"; -static const std::string kVALUE_KEY = "value"; -static const std::string kHAS_INSTALL_RULE = "hasInstallRule"; -static const std::string kINSTALL_PATHS = "installPaths"; -static const std::string kCTEST_NAME = "ctestName"; -static const std::string kCTEST_COMMAND = "ctestCommand"; -static const std::string kCTEST_INFO = "ctestInfo"; -static const std::string kMINIMUM_CMAKE_VERSION = "minimumCMakeVersion"; -static const std::string kIS_GENERATOR_PROVIDED_KEY = "isGeneratorProvided"; diff --git a/Source/cmJsonObjects.cxx b/Source/cmJsonObjects.cxx deleted file mode 100644 index 3a7ae0c..0000000 --- a/Source/cmJsonObjects.cxx +++ /dev/null @@ -1,692 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmJsonObjects.h" // IWYU pragma: keep - -#include <algorithm> -#include <cassert> -#include <cstddef> -#include <functional> -#include <limits> -#include <map> -#include <memory> -#include <set> -#include <string> -#include <unordered_map> -#include <utility> -#include <vector> - -#include <cmext/algorithm> - -#include "cmGeneratorExpression.h" -#include "cmGeneratorTarget.h" -#include "cmGlobalGenerator.h" -#include "cmInstallGenerator.h" -#include "cmInstallSubdirectoryGenerator.h" -#include "cmInstallTargetGenerator.h" -#include "cmJsonObjectDictionary.h" -#include "cmJsonObjects.h" -#include "cmLinkLineComputer.h" -#include "cmLocalGenerator.h" -#include "cmMakefile.h" -#include "cmProperty.h" -#include "cmPropertyMap.h" -#include "cmSourceFile.h" -#include "cmState.h" -#include "cmStateDirectory.h" -#include "cmStateSnapshot.h" -#include "cmStateTypes.h" -#include "cmStringAlgorithms.h" -#include "cmSystemTools.h" -#include "cmTarget.h" -#include "cmTest.h" -#include "cmake.h" - -namespace { - -std::vector<std::string> getConfigurations(const cmake* cm) -{ - std::vector<std::string> configurations; - const auto& makefiles = cm->GetGlobalGenerator()->GetMakefiles(); - if (makefiles.empty()) { - return configurations; - } - - return makefiles[0]->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); -} - -bool hasString(const Json::Value& v, const std::string& s) -{ - return !v.isNull() && - std::any_of(v.begin(), v.end(), - [s](const Json::Value& i) { return i.asString() == s; }); -} - -template <class T> -Json::Value fromStringList(const T& in) -{ - Json::Value result = Json::arrayValue; - for (std::string const& i : in) { - result.append(i); - } - return result; -} - -} // namespace - -void cmGetCMakeInputs(const cmGlobalGenerator* gg, - const std::string& sourceDir, - const std::string& buildDir, - std::vector<std::string>* internalFiles, - std::vector<std::string>* explicitFiles, - std::vector<std::string>* tmpFiles) -{ - const std::string cmakeRootDir = cmSystemTools::GetCMakeRoot() + '/'; - auto const& makefiles = gg->GetMakefiles(); - for (const auto& mf : makefiles) { - for (std::string const& lf : mf->GetListFiles()) { - - const std::string startOfFile = lf.substr(0, cmakeRootDir.size()); - const bool isInternal = (startOfFile == cmakeRootDir); - const bool isTemporary = - !isInternal && (cmHasPrefix(lf, buildDir + '/')); - - std::string toAdd = lf; - if (!sourceDir.empty()) { - const std::string& relative = - cmSystemTools::RelativePath(sourceDir, lf); - if (toAdd.size() > relative.size()) { - toAdd = relative; - } - } - - if (isInternal) { - if (internalFiles) { - internalFiles->push_back(std::move(toAdd)); - } - } else { - if (isTemporary) { - if (tmpFiles) { - tmpFiles->push_back(std::move(toAdd)); - } - } else { - if (explicitFiles) { - explicitFiles->push_back(std::move(toAdd)); - } - } - } - } - } -} - -Json::Value cmDumpCMakeInputs(const cmake* cm) -{ - const cmGlobalGenerator* gg = cm->GetGlobalGenerator(); - const std::string& buildDir = cm->GetHomeOutputDirectory(); - const std::string& sourceDir = cm->GetHomeDirectory(); - - std::vector<std::string> internalFiles; - std::vector<std::string> explicitFiles; - std::vector<std::string> tmpFiles; - cmGetCMakeInputs(gg, sourceDir, buildDir, &internalFiles, &explicitFiles, - &tmpFiles); - - Json::Value array = Json::arrayValue; - - Json::Value tmp = Json::objectValue; - tmp[kIS_CMAKE_KEY] = true; - tmp[kIS_TEMPORARY_KEY] = false; - tmp[kSOURCES_KEY] = fromStringList(internalFiles); - array.append(tmp); - - tmp = Json::objectValue; - tmp[kIS_CMAKE_KEY] = false; - tmp[kIS_TEMPORARY_KEY] = false; - tmp[kSOURCES_KEY] = fromStringList(explicitFiles); - array.append(tmp); - - tmp = Json::objectValue; - tmp[kIS_CMAKE_KEY] = false; - tmp[kIS_TEMPORARY_KEY] = true; - tmp[kSOURCES_KEY] = fromStringList(tmpFiles); - array.append(tmp); - - return array; -} - -class LanguageData -{ -public: - bool operator==(const LanguageData& other) const; - - void SetDefines(const std::set<std::string>& defines); - - bool IsGenerated = false; - std::string Language; - std::string Flags; - std::vector<std::string> Defines; - std::vector<std::pair<std::string, bool>> IncludePathList; -}; - -bool LanguageData::operator==(const LanguageData& other) const -{ - return Language == other.Language && Defines == other.Defines && - Flags == other.Flags && IncludePathList == other.IncludePathList && - IsGenerated == other.IsGenerated; -} - -void LanguageData::SetDefines(const std::set<std::string>& defines) -{ - std::vector<std::string> result; - result.reserve(defines.size()); - for (std::string const& i : defines) { - result.push_back(i); - } - std::sort(result.begin(), result.end()); - Defines = std::move(result); -} - -namespace std { - -template <> -struct hash<LanguageData> -{ - std::size_t operator()(const LanguageData& in) const - { - using std::hash; - size_t result = - hash<std::string>()(in.Language) ^ hash<std::string>()(in.Flags); - for (auto const& i : in.IncludePathList) { - result = result ^ - (hash<std::string>()(i.first) ^ - (i.second ? std::numeric_limits<size_t>::max() : 0)); - } - for (auto const& i : in.Defines) { - result = result ^ hash<std::string>()(i); - } - result = - result ^ (in.IsGenerated ? std::numeric_limits<size_t>::max() : 0); - return result; - } -}; - -} // namespace std - -static Json::Value DumpSourceFileGroup(const LanguageData& data, - const std::vector<std::string>& files, - const std::string& baseDir) -{ - Json::Value result = Json::objectValue; - - if (!data.Language.empty()) { - result[kLANGUAGE_KEY] = data.Language; - if (!data.Flags.empty()) { - result[kCOMPILE_FLAGS_KEY] = data.Flags; - } - if (!data.IncludePathList.empty()) { - Json::Value includes = Json::arrayValue; - for (auto const& i : data.IncludePathList) { - Json::Value tmp = Json::objectValue; - tmp[kPATH_KEY] = i.first; - if (i.second) { - tmp[kIS_SYSTEM_KEY] = i.second; - } - includes.append(tmp); - } - result[kINCLUDE_PATH_KEY] = includes; - } - if (!data.Defines.empty()) { - result[kDEFINES_KEY] = fromStringList(data.Defines); - } - } - - result[kIS_GENERATED_KEY] = data.IsGenerated; - - Json::Value sourcesValue = Json::arrayValue; - for (auto const& i : files) { - const std::string relPath = cmSystemTools::RelativePath(baseDir, i); - sourcesValue.append(relPath.size() < i.size() ? relPath : i); - } - - result[kSOURCES_KEY] = sourcesValue; - return result; -} - -static Json::Value DumpSourceFilesList( - cmGeneratorTarget* target, const std::string& config, - const std::map<std::string, LanguageData>& languageDataMap) -{ - // Collect sourcefile groups: - - std::vector<cmSourceFile*> files; - target->GetSourceFiles(files, config); - - std::unordered_map<LanguageData, std::vector<std::string>> fileGroups; - for (cmSourceFile* file : files) { - LanguageData fileData; - fileData.Language = file->GetOrDetermineLanguage(); - if (!fileData.Language.empty()) { - const LanguageData& ld = languageDataMap.at(fileData.Language); - cmLocalGenerator* lg = target->GetLocalGenerator(); - cmGeneratorExpressionInterpreter genexInterpreter(lg, config, target, - fileData.Language); - - std::string compileFlags = ld.Flags; - const std::string COMPILE_FLAGS("COMPILE_FLAGS"); - if (cmProp cflags = file->GetProperty(COMPILE_FLAGS)) { - lg->AppendFlags(compileFlags, - genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS)); - } - const std::string COMPILE_OPTIONS("COMPILE_OPTIONS"); - if (cmProp coptions = file->GetProperty(COMPILE_OPTIONS)) { - lg->AppendCompileOptions( - compileFlags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS)); - } - fileData.Flags = compileFlags; - - // Add include directories from source file properties. - std::vector<std::string> includes; - - const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); - if (cmProp cincludes = file->GetProperty(INCLUDE_DIRECTORIES)) { - const std::string& evaluatedIncludes = - genexInterpreter.Evaluate(*cincludes, INCLUDE_DIRECTORIES); - lg->AppendIncludeDirectories(includes, evaluatedIncludes, *file); - - for (const auto& include : includes) { - fileData.IncludePathList.emplace_back( - include, - target->IsSystemIncludeDirectory(include, config, - fileData.Language)); - } - } - - fileData.IncludePathList.insert(fileData.IncludePathList.end(), - ld.IncludePathList.begin(), - ld.IncludePathList.end()); - - const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS"); - std::set<std::string> defines; - if (cmProp defs = file->GetProperty(COMPILE_DEFINITIONS)) { - lg->AppendDefines( - defines, genexInterpreter.Evaluate(*defs, COMPILE_DEFINITIONS)); - } - - const std::string defPropName = - "COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(config); - if (cmProp config_defs = file->GetProperty(defPropName)) { - lg->AppendDefines( - defines, - genexInterpreter.Evaluate(*config_defs, COMPILE_DEFINITIONS)); - } - - defines.insert(ld.Defines.begin(), ld.Defines.end()); - - fileData.SetDefines(defines); - } - - fileData.IsGenerated = file->GetIsGenerated(); - std::vector<std::string>& groupFileList = fileGroups[fileData]; - groupFileList.push_back(file->ResolveFullPath()); - } - - const std::string& baseDir = target->Makefile->GetCurrentSourceDirectory(); - Json::Value result = Json::arrayValue; - for (auto const& it : fileGroups) { - Json::Value group = DumpSourceFileGroup(it.first, it.second, baseDir); - if (!group.isNull()) { - result.append(group); - } - } - - return result; -} - -static Json::Value DumpCTestInfo(cmLocalGenerator* lg, cmTest* testInfo, - const std::string& config) -{ - Json::Value result = Json::objectValue; - result[kCTEST_NAME] = testInfo->GetName(); - - // Concat command entries together. After the first should be the arguments - // for the command - std::string command; - for (auto const& cmd : testInfo->GetCommand()) { - command.append(cmd); - command.append(" "); - } - - // Remove any config specific variables from the output. - result[kCTEST_COMMAND] = - cmGeneratorExpression::Evaluate(command, lg, config); - - // Build up the list of properties that may have been specified - Json::Value properties = Json::arrayValue; - for (auto& prop : testInfo->GetProperties().GetList()) { - Json::Value entry = Json::objectValue; - entry[kKEY_KEY] = prop.first; - - // Remove config variables from the value too. - entry[kVALUE_KEY] = - cmGeneratorExpression::Evaluate(prop.second, lg, config); - properties.append(entry); - } - result[kPROPERTIES_KEY] = properties; - - return result; -} - -static void DumpMakefileTests(cmLocalGenerator* lg, const std::string& config, - Json::Value* result) -{ - auto mf = lg->GetMakefile(); - std::vector<cmTest*> tests; - mf->GetTests(config, tests); - for (auto test : tests) { - Json::Value tmp = DumpCTestInfo(lg, test, config); - if (!tmp.isNull()) { - result->append(tmp); - } - } -} - -static Json::Value DumpCTestProjectList(const cmake* cm, - std::string const& config) -{ - Json::Value result = Json::arrayValue; - - auto globalGen = cm->GetGlobalGenerator(); - - for (const auto& projectIt : globalGen->GetProjectMap()) { - Json::Value pObj = Json::objectValue; - pObj[kNAME_KEY] = projectIt.first; - - Json::Value tests = Json::arrayValue; - - // Gather tests for every generator - for (const auto& lg : projectIt.second) { - // Make sure they're generated. - lg->GenerateTestFiles(); - DumpMakefileTests(lg, config, &tests); - } - - pObj[kCTEST_INFO] = tests; - - result.append(pObj); - } - - return result; -} - -static Json::Value DumpCTestConfiguration(const cmake* cm, - const std::string& config) -{ - Json::Value result = Json::objectValue; - result[kNAME_KEY] = config; - - result[kPROJECTS_KEY] = DumpCTestProjectList(cm, config); - - return result; -} - -static Json::Value DumpCTestConfigurationsList(const cmake* cm) -{ - Json::Value result = Json::arrayValue; - - for (const std::string& c : getConfigurations(cm)) { - result.append(DumpCTestConfiguration(cm, c)); - } - - return result; -} - -Json::Value cmDumpCTestInfo(const cmake* cm) -{ - Json::Value result = Json::objectValue; - result[kCONFIGURATIONS_KEY] = DumpCTestConfigurationsList(cm); - return result; -} - -static Json::Value DumpTarget(cmGeneratorTarget* target, - const std::string& config) -{ - cmLocalGenerator* lg = target->GetLocalGenerator(); - - const cmStateEnums::TargetType type = target->GetType(); - const std::string typeName = cmState::GetTargetTypeName(type); - - Json::Value ttl = Json::arrayValue; - ttl.append("EXECUTABLE"); - ttl.append("STATIC_LIBRARY"); - ttl.append("SHARED_LIBRARY"); - ttl.append("MODULE_LIBRARY"); - ttl.append("OBJECT_LIBRARY"); - ttl.append("UTILITY"); - ttl.append("INTERFACE_LIBRARY"); - - if (!hasString(ttl, typeName) || target->IsImported()) { - return Json::Value(); - } - - Json::Value result = Json::objectValue; - result[kNAME_KEY] = target->GetName(); - result[kIS_GENERATOR_PROVIDED_KEY] = - target->Target->GetIsGeneratorProvided(); - result[kTYPE_KEY] = typeName; - result[kSOURCE_DIRECTORY_KEY] = lg->GetCurrentSourceDirectory(); - result[kBUILD_DIRECTORY_KEY] = lg->GetCurrentBinaryDirectory(); - - if (type == cmStateEnums::INTERFACE_LIBRARY) { - return result; - } - - result[kFULL_NAME_KEY] = target->GetFullName(config); - - if (target->Target->GetHaveInstallRule()) { - result[kHAS_INSTALL_RULE] = true; - - Json::Value installPaths = Json::arrayValue; - for (const auto& installGenerator : - target->Makefile->GetInstallGenerators()) { - auto installTargetGenerator = - dynamic_cast<cmInstallTargetGenerator*>(installGenerator.get()); - if (installTargetGenerator != nullptr && - installTargetGenerator->GetTarget()->Target == target->Target) { - auto dest = installTargetGenerator->GetDestination(config); - - std::string installPath; - if (!dest.empty() && cmSystemTools::FileIsFullPath(dest)) { - installPath = dest; - } else { - installPath = cmStrCat( - target->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX"), '/', - dest); - } - - installPaths.append(installPath); - } - } - - result[kINSTALL_PATHS] = installPaths; - } - - if (target->HaveWellDefinedOutputFiles()) { - Json::Value artifacts = Json::arrayValue; - artifacts.append( - target->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact)); - if (target->HasImportLibrary(config)) { - artifacts.append( - target->GetFullPath(config, cmStateEnums::ImportLibraryArtifact)); - } - if (target->IsDLLPlatform()) { - const cmGeneratorTarget::OutputInfo* output = - target->GetOutputInfo(config); - if (output && !output->PdbDir.empty()) { - artifacts.append(output->PdbDir + '/' + target->GetPDBName(config)); - } - } - result[kARTIFACTS_KEY] = artifacts; - - result[kLINKER_LANGUAGE_KEY] = target->GetLinkerLanguage(config); - - std::string linkLibs; - std::string linkFlags; - std::string linkLanguageFlags; - std::string frameworkPath; - std::string linkPath; - cmLinkLineComputer linkLineComputer(lg, - lg->GetStateSnapshot().GetDirectory()); - lg->GetTargetFlags(&linkLineComputer, config, linkLibs, linkLanguageFlags, - linkFlags, frameworkPath, linkPath, target); - - linkLibs = cmTrimWhitespace(linkLibs); - linkFlags = cmTrimWhitespace(linkFlags); - linkLanguageFlags = cmTrimWhitespace(linkLanguageFlags); - frameworkPath = cmTrimWhitespace(frameworkPath); - linkPath = cmTrimWhitespace(linkPath); - - if (!cmTrimWhitespace(linkLibs).empty()) { - result[kLINK_LIBRARIES_KEY] = linkLibs; - } - if (!cmTrimWhitespace(linkFlags).empty()) { - result[kLINK_FLAGS_KEY] = linkFlags; - } - if (!cmTrimWhitespace(linkLanguageFlags).empty()) { - result[kLINK_LANGUAGE_FLAGS_KEY] = linkLanguageFlags; - } - if (!frameworkPath.empty()) { - result[kFRAMEWORK_PATH_KEY] = frameworkPath; - } - if (!linkPath.empty()) { - result[kLINK_PATH_KEY] = linkPath; - } - const std::string sysroot = - lg->GetMakefile()->GetSafeDefinition("CMAKE_SYSROOT"); - if (!sysroot.empty()) { - result[kSYSROOT_KEY] = sysroot; - } - } - - std::set<std::string> languages; - target->GetLanguages(languages, config); - std::map<std::string, LanguageData> languageDataMap; - - for (std::string const& lang : languages) { - LanguageData& ld = languageDataMap[lang]; - ld.Language = lang; - lg->GetTargetCompileFlags(target, config, lang, ld.Flags); - std::set<std::string> defines; - lg->GetTargetDefines(target, config, lang, defines); - ld.SetDefines(defines); - std::vector<std::string> includePathList; - lg->GetIncludeDirectories(includePathList, target, lang, config); - for (std::string const& i : includePathList) { - ld.IncludePathList.emplace_back( - i, target->IsSystemIncludeDirectory(i, config, lang)); - } - } - - Json::Value sourceGroupsValue = - DumpSourceFilesList(target, config, languageDataMap); - if (!sourceGroupsValue.empty()) { - result[kFILE_GROUPS_KEY] = sourceGroupsValue; - } - - return result; -} - -static Json::Value DumpTargetsList( - const std::vector<cmLocalGenerator*>& generators, const std::string& config) -{ - Json::Value result = Json::arrayValue; - - std::vector<cmGeneratorTarget*> targetList; - for (auto const& lgIt : generators) { - cm::append(targetList, lgIt->GetGeneratorTargets()); - } - std::sort(targetList.begin(), targetList.end()); - - for (cmGeneratorTarget* target : targetList) { - Json::Value tmp = DumpTarget(target, config); - if (!tmp.isNull()) { - result.append(tmp); - } - } - - return result; -} - -static Json::Value DumpProjectList(const cmake* cm, std::string const& config) -{ - Json::Value result = Json::arrayValue; - - auto globalGen = cm->GetGlobalGenerator(); - - for (auto const& projectIt : globalGen->GetProjectMap()) { - Json::Value pObj = Json::objectValue; - pObj[kNAME_KEY] = projectIt.first; - - // All Projects must have at least one local generator - assert(!projectIt.second.empty()); - const cmLocalGenerator* lg = projectIt.second.at(0); - - // Project structure information: - const cmMakefile* mf = lg->GetMakefile(); - auto minVersion = mf->GetSafeDefinition("CMAKE_MINIMUM_REQUIRED_VERSION"); - pObj[kMINIMUM_CMAKE_VERSION] = minVersion; - pObj[kSOURCE_DIRECTORY_KEY] = mf->GetCurrentSourceDirectory(); - pObj[kBUILD_DIRECTORY_KEY] = mf->GetCurrentBinaryDirectory(); - pObj[kTARGETS_KEY] = DumpTargetsList(projectIt.second, config); - - // For a project-level install rule it might be defined in any of its - // associated generators. - bool hasInstallRule = false; - for (const auto generator : projectIt.second) { - for (const auto& installGen : - generator->GetMakefile()->GetInstallGenerators()) { - if (!dynamic_cast<cmInstallSubdirectoryGenerator*>(installGen.get())) { - hasInstallRule = true; - break; - } - } - - if (hasInstallRule) { - break; - } - } - - pObj[kHAS_INSTALL_RULE] = hasInstallRule; - - result.append(pObj); - } - - return result; -} - -static Json::Value DumpConfiguration(const cmake* cm, - const std::string& config) -{ - Json::Value result = Json::objectValue; - result[kNAME_KEY] = config; - - result[kPROJECTS_KEY] = DumpProjectList(cm, config); - - return result; -} - -static Json::Value DumpConfigurationsList(const cmake* cm) -{ - Json::Value result = Json::arrayValue; - - for (std::string const& c : getConfigurations(cm)) { - result.append(DumpConfiguration(cm, c)); - } - - return result; -} - -Json::Value cmDumpCodeModel(const cmake* cm) -{ - Json::Value result = Json::objectValue; - result[kCONFIGURATIONS_KEY] = DumpConfigurationsList(cm); - return result; -} diff --git a/Source/cmJsonObjects.h b/Source/cmJsonObjects.h deleted file mode 100644 index 80a4834..0000000 --- a/Source/cmJsonObjects.h +++ /dev/null @@ -1,24 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#pragma once - -#include "cmConfigure.h" // IWYU pragma: keep - -#include <string> -#include <vector> - -#include <cm3p/json/value.h> - -class cmake; -class cmGlobalGenerator; - -extern void cmGetCMakeInputs(const cmGlobalGenerator* gg, - const std::string& sourceDir, - const std::string& buildDir, - std::vector<std::string>* internalFiles, - std::vector<std::string>* explicitFiles, - std::vector<std::string>* tmpFiles); - -extern Json::Value cmDumpCodeModel(const cmake* cm); -extern Json::Value cmDumpCTestInfo(const cmake* cm); -extern Json::Value cmDumpCMakeInputs(const cmake* cm); diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx index 70ef5af..3658d11 100644 --- a/Source/cmListFileCache.cxx +++ b/Source/cmListFileCache.cxx @@ -30,6 +30,7 @@ struct cmListFileParser bool ParseFunction(const char* name, long line); bool AddArgument(cmListFileLexer_Token* token, cmListFileArgument::Delimiter delim); + cm::optional<cmListFileContext> CheckNesting(); cmListFile* ListFile; cmListFileBacktrace Backtrace; cmMessenger* Messenger; @@ -158,6 +159,17 @@ bool cmListFileParser::Parse() return false; } } + + // Check if all functions are nested properly. + if (auto badNesting = this->CheckNesting()) { + this->Messenger->IssueMessage( + MessageType::FATAL_ERROR, + "Flow control statements are not properly nested.", + this->Backtrace.Push(*badNesting)); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + return true; } @@ -317,6 +329,112 @@ bool cmListFileParser::AddArgument(cmListFileLexer_Token* token, return true; } +namespace { +enum class NestingStateEnum +{ + If, + Else, + While, + Foreach, + Function, + Macro, +}; + +struct NestingState +{ + NestingStateEnum State; + cmListFileContext Context; +}; + +bool TopIs(std::vector<NestingState>& stack, NestingStateEnum state) +{ + return !stack.empty() && stack.back().State == state; +} +} + +cm::optional<cmListFileContext> cmListFileParser::CheckNesting() +{ + std::vector<NestingState> stack; + + for (auto const& func : this->ListFile->Functions) { + auto const& name = func.LowerCaseName(); + if (name == "if") { + stack.push_back({ + NestingStateEnum::If, + cmListFileContext::FromCommandContext(func, this->FileName), + }); + } else if (name == "elseif") { + if (!TopIs(stack, NestingStateEnum::If)) { + return cmListFileContext::FromCommandContext(func, this->FileName); + } + stack.back() = { + NestingStateEnum::If, + cmListFileContext::FromCommandContext(func, this->FileName), + }; + } else if (name == "else") { + if (!TopIs(stack, NestingStateEnum::If)) { + return cmListFileContext::FromCommandContext(func, this->FileName); + } + stack.back() = { + NestingStateEnum::Else, + cmListFileContext::FromCommandContext(func, this->FileName), + }; + } else if (name == "endif") { + if (!TopIs(stack, NestingStateEnum::If) && + !TopIs(stack, NestingStateEnum::Else)) { + return cmListFileContext::FromCommandContext(func, this->FileName); + } + stack.pop_back(); + } else if (name == "while") { + stack.push_back({ + NestingStateEnum::While, + cmListFileContext::FromCommandContext(func, this->FileName), + }); + } else if (name == "endwhile") { + if (!TopIs(stack, NestingStateEnum::While)) { + return cmListFileContext::FromCommandContext(func, this->FileName); + } + stack.pop_back(); + } else if (name == "foreach") { + stack.push_back({ + NestingStateEnum::Foreach, + cmListFileContext::FromCommandContext(func, this->FileName), + }); + } else if (name == "endforeach") { + if (!TopIs(stack, NestingStateEnum::Foreach)) { + return cmListFileContext::FromCommandContext(func, this->FileName); + } + stack.pop_back(); + } else if (name == "function") { + stack.push_back({ + NestingStateEnum::Function, + cmListFileContext::FromCommandContext(func, this->FileName), + }); + } else if (name == "endfunction") { + if (!TopIs(stack, NestingStateEnum::Function)) { + return cmListFileContext::FromCommandContext(func, this->FileName); + } + stack.pop_back(); + } else if (name == "macro") { + stack.push_back({ + NestingStateEnum::Macro, + cmListFileContext::FromCommandContext(func, this->FileName), + }); + } else if (name == "endmacro") { + if (!TopIs(stack, NestingStateEnum::Macro)) { + return cmListFileContext::FromCommandContext(func, this->FileName); + } + stack.pop_back(); + } + } + + if (!stack.empty()) { + return stack.back().Context; + } + + return cm::nullopt; +} + // We hold either the bottom scope of a directory or a call/file context. // Discriminate these cases via the parent pointer. struct cmListFileBacktrace::Entry diff --git a/Source/cmListFileCache.h b/Source/cmListFileCache.h index 727fc60..ed45c07 100644 --- a/Source/cmListFileCache.h +++ b/Source/cmListFileCache.h @@ -89,6 +89,14 @@ public: { } +#if __cplusplus < 201703L && (!defined(_MSVC_LANG) || _MSVC_LANG < 201703L) + cmListFileContext(const cmListFileContext& /*other*/) = default; + cmListFileContext(cmListFileContext&& /*other*/) = default; + + cmListFileContext& operator=(const cmListFileContext& /*other*/) = default; + cmListFileContext& operator=(cmListFileContext&& /*other*/) = delete; +#endif + static cmListFileContext FromCommandContext( cmCommandContext const& lfcc, std::string const& fileName, cm::optional<std::string> deferId = {}) diff --git a/Source/cmLoadCommandCommand.cxx b/Source/cmLoadCommandCommand.cxx index 5790e16..adebe02 100644 --- a/Source/cmLoadCommandCommand.cxx +++ b/Source/cmLoadCommandCommand.cxx @@ -24,6 +24,7 @@ #include "cmCommand.h" #include "cmDynamicLoader.h" #include "cmExecutionStatus.h" +#include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmState.h" @@ -36,8 +37,6 @@ # include <malloc.h> /* for malloc/free on QNX */ #endif -class cmListFileBacktrace; - namespace { const char* LastName = nullptr; @@ -256,10 +255,12 @@ bool cmLoadCommandCommand(std::vector<std::string> const& args, // if the symbol is found call it to set the name on the // function blocker if (initFunction) { - status.GetMakefile().GetState()->AddScriptedCommand( + return status.GetMakefile().GetState()->AddScriptedCommand( args[0], - cmLegacyCommandWrapper(cm::make_unique<cmLoadedCommand>(initFunction))); - return true; + BT<cmState::Command>( + cmLegacyCommandWrapper(cm::make_unique<cmLoadedCommand>(initFunction)), + status.GetMakefile().GetBacktrace()), + status.GetMakefile()); } status.SetError("Attempt to load command failed. " "No init function found."); diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 4e6010c..8a3f285 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -279,7 +279,7 @@ static void MoveSystemIncludesToEnd(std::vector<BT<std::string>>& includeDirs, }); } -void cmLocalGenerator::TraceDependencies() +void cmLocalGenerator::TraceDependencies() const { // Generate the rule files for each target. const auto& targets = this->GetGeneratorTargets(); @@ -823,16 +823,13 @@ cmStateSnapshot cmLocalGenerator::GetStateSnapshot() const return this->Makefile->GetStateSnapshot(); } -const char* cmLocalGenerator::GetRuleLauncher(cmGeneratorTarget* target, - const std::string& prop) +cmProp cmLocalGenerator::GetRuleLauncher(cmGeneratorTarget* target, + const std::string& prop) { - cmProp p; if (target) { - p = target->GetProperty(prop); - } else { - p = this->Makefile->GetProperty(prop); + return target->GetProperty(prop); } - return p ? p->c_str() : nullptr; + return this->Makefile->GetProperty(prop); } std::string cmLocalGenerator::ConvertToIncludeReference( @@ -2610,14 +2607,16 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target) // Add pchHeader to source files, which will // be grouped as "Precompile Header File" auto pchHeader_sf = this->Makefile->GetOrCreateSource( - pchHeader, true, cmSourceFileLocationKind::Known); + pchHeader, false, cmSourceFileLocationKind::Known); std::string err; pchHeader_sf->ResolveFullPath(&err); - - // The pch file is generated, but mark it as not generated - // so that a clean operation will not remove it from disk - pchHeader_sf->SetProperty("GENERATED", "0"); - + if (!err.empty()) { + std::ostringstream msg; + msg << "Unable to resolve full path of PCH-header '" << pchHeader + << "' assigned to target " << target->GetName() + << ", although its path is supposed to be known!"; + this->IssueMessage(MessageType::FATAL_ERROR, msg.str()); + } target->AddSource(pchHeader); } } @@ -3210,7 +3209,7 @@ std::string cmLocalGenerator::GetProjectName() const } std::string cmLocalGenerator::ConstructComment( - cmCustomCommandGenerator const& ccg, const char* default_comment) + cmCustomCommandGenerator const& ccg, const char* default_comment) const { // Check for a comment provided with the command. if (ccg.GetComment()) { @@ -3526,11 +3525,11 @@ std::string cmLocalGenerator::GetObjectFileNameWithoutTarget( // we don't end up having: // CMakeFiles/<target>.dir/CMakeFiles/<target>.dir/generated_source_file.obj cmProp unitySourceFile = source.GetProperty("UNITY_SOURCE_FILE"); - cmProp psExtension = source.GetProperty("PCH_EXTENSION"); + cmProp pchExtension = source.GetProperty("PCH_EXTENSION"); const bool isPchObject = objectName.find("cmake_pch") != std::string::npos; - if (unitySourceFile || psExtension || isPchObject) { - if (psExtension) { - customOutputExtension = psExtension->c_str(); + if (unitySourceFile || pchExtension || isPchObject) { + if (pchExtension) { + customOutputExtension = pchExtension->c_str(); } cmsys::RegularExpression var("(CMakeFiles/[^/]+.dir/)"); @@ -3768,7 +3767,7 @@ void cmLocalGenerator::GenerateAppleInfoPList(cmGeneratorTarget* target, cmLGInfoProp(mf, target, "MACOSX_BUNDLE_SHORT_VERSION_STRING"); cmLGInfoProp(mf, target, "MACOSX_BUNDLE_BUNDLE_VERSION"); cmLGInfoProp(mf, target, "MACOSX_BUNDLE_COPYRIGHT"); - mf->ConfigureFile(inFile, fname, false, false, false, true); + mf->ConfigureFile(inFile, fname, false, false, false); } void cmLocalGenerator::GenerateFrameworkInfoPList( @@ -3803,7 +3802,7 @@ void cmLocalGenerator::GenerateFrameworkInfoPList( cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_IDENTIFIER"); cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_SHORT_VERSION_STRING"); cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_BUNDLE_VERSION"); - mf->ConfigureFile(inFile, fname, false, false, false, true); + mf->ConfigureFile(inFile, fname, false, false, false); } namespace { @@ -3918,10 +3917,35 @@ cmSourceFile* AddCustomCommand( cc->SetJobPool(job_pool); file->SetCustomCommand(std::move(cc)); - mf->AddSourceOutputs(file, outputs, byproducts); + lg.AddSourceOutputs(file, outputs, byproducts); } return file; } + +bool AnyOutputMatches(const std::string& name, + const std::vector<std::string>& outputs) +{ + for (std::string const& output : outputs) { + std::string::size_type pos = output.rfind(name); + // If the output matches exactly + if (pos != std::string::npos && pos == output.size() - name.size() && + (pos == 0 || output[pos - 1] == '/')) { + return true; + } + } + return false; +} + +bool AnyTargetCommandOutputMatches( + const std::string& name, const std::vector<cmCustomCommand>& commands) +{ + for (cmCustomCommand const& command : commands) { + if (AnyOutputMatches(name, command.GetByproducts())) { + return true; + } + } + return false; +} } namespace detail { @@ -3937,8 +3961,6 @@ void AddCustomCommandToTarget(cmLocalGenerator& lg, const std::string& job_pool, bool command_expand_lists, bool stdPipesUTF8) { - cmMakefile* mf = lg.GetMakefile(); - // Always create the byproduct sources and mark them generated. CreateGeneratedSources(lg, byproducts, origin, lfbt); @@ -3964,7 +3986,7 @@ void AddCustomCommandToTarget(cmLocalGenerator& lg, break; } - mf->AddTargetByproducts(target, byproducts); + lg.AddTargetByproducts(target, byproducts); } cmSourceFile* AddCustomCommandToOutput( @@ -3996,7 +4018,7 @@ void AppendCustomCommandToOutput(cmLocalGenerator& lg, const cmCustomCommandLines& commandLines) { // Lookup an existing command. - if (cmSourceFile* sf = lg.GetMakefile()->GetSourceFileWithOutput(output)) { + if (cmSourceFile* sf = lg.GetSourceFileWithOutput(output)) { if (cmCustomCommand* cc = sf->GetCustomCommand()) { cc->AppendCommands(commandLines); cc->AppendDepends(depends); @@ -4008,7 +4030,7 @@ void AppendCustomCommandToOutput(cmLocalGenerator& lg, // No existing command found. lg.GetCMakeInstance()->IssueMessage( MessageType::FATAL_ERROR, - cmStrCat("Attempt to append to output\n ", output, + cmStrCat("Attempt to APPEND to custom command with output\n ", output, "\nwhich is not already a custom command output."), lfbt); } @@ -4040,7 +4062,7 @@ void AddUtilityCommand(cmLocalGenerator& lg, const cmListFileBacktrace& lfbt, /*replace=*/false, escapeOldStyle, uses_terminal, command_expand_lists, /*depfile=*/"", job_pool, stdPipesUTF8); if (rule) { - lg.GetMakefile()->AddTargetByproducts(target, byproducts); + lg.AddTargetByproducts(target, byproducts); } if (!force.NameCMP0049.empty()) { @@ -4088,3 +4110,166 @@ std::vector<std::string> ComputeISPCExtraObjects( return computedObjects; } } + +cmSourcesWithOutput cmLocalGenerator::GetSourcesWithOutput( + const std::string& name) const +{ + // Linear search? Also see GetSourceFileWithOutput for detail. + if (!cmSystemTools::FileIsFullPath(name)) { + cmSourcesWithOutput sources; + sources.Target = this->LinearGetTargetWithOutput(name); + sources.Source = this->LinearGetSourceFileWithOutput( + name, cmSourceOutputKind::OutputOrByproduct, sources.SourceIsByproduct); + return sources; + } + // Otherwise we use an efficient lookup map. + auto o = this->OutputToSource.find(name); + if (o != this->OutputToSource.end()) { + return o->second.Sources; + } + return {}; +} + +cmSourceFile* cmLocalGenerator::GetSourceFileWithOutput( + const std::string& name, cmSourceOutputKind kind) const +{ + // If the queried path is not absolute we use the backward compatible + // linear-time search for an output with a matching suffix. + if (!cmSystemTools::FileIsFullPath(name)) { + bool byproduct = false; + return this->LinearGetSourceFileWithOutput(name, kind, byproduct); + } + // Otherwise we use an efficient lookup map. + auto o = this->OutputToSource.find(name); + if (o != this->OutputToSource.end() && + (!o->second.Sources.SourceIsByproduct || + kind == cmSourceOutputKind::OutputOrByproduct)) { + // Source file could also be null pointer for example if we found the + // byproduct of a utility target, a PRE_BUILD, PRE_LINK, or POST_BUILD + // command of a target, or a not yet created custom command. + return o->second.Sources.Source; + } + return nullptr; +} + +void cmLocalGenerator::AddTargetByproducts( + cmTarget* target, const std::vector<std::string>& byproducts) +{ + for (std::string const& o : byproducts) { + this->UpdateOutputToSourceMap(o, target); + } +} + +void cmLocalGenerator::AddSourceOutputs( + cmSourceFile* source, const std::vector<std::string>& outputs, + const std::vector<std::string>& byproducts) +{ + for (std::string const& o : outputs) { + this->UpdateOutputToSourceMap(o, source, false); + } + for (std::string const& o : byproducts) { + this->UpdateOutputToSourceMap(o, source, true); + } +} + +void cmLocalGenerator::UpdateOutputToSourceMap(std::string const& byproduct, + cmTarget* target) +{ + SourceEntry entry; + entry.Sources.Target = target; + + auto pr = this->OutputToSource.emplace(byproduct, entry); + if (!pr.second) { + SourceEntry& current = pr.first->second; + // Has the target already been set? + if (!current.Sources.Target) { + current.Sources.Target = target; + } else { + // Multiple custom commands/targets produce the same output (source file + // or target). See also comment in other UpdateOutputToSourceMap + // overload. + // + // TODO: Warn the user about this case. + } + } +} + +void cmLocalGenerator::UpdateOutputToSourceMap(std::string const& output, + cmSourceFile* source, + bool byproduct) +{ + SourceEntry entry; + entry.Sources.Source = source; + entry.Sources.SourceIsByproduct = byproduct; + + auto pr = this->OutputToSource.emplace(output, entry); + if (!pr.second) { + SourceEntry& current = pr.first->second; + // Outputs take precedence over byproducts + if (!current.Sources.Source || + (current.Sources.SourceIsByproduct && !byproduct)) { + current.Sources.Source = source; + current.Sources.SourceIsByproduct = false; + } else { + // Multiple custom commands produce the same output but may + // be attached to a different source file (MAIN_DEPENDENCY). + // LinearGetSourceFileWithOutput would return the first one, + // so keep the mapping for the first one. + // + // TODO: Warn the user about this case. However, the VS 8 generator + // triggers it for separate generate.stamp rules in ZERO_CHECK and + // individual targets. + } + } +} + +cmTarget* cmLocalGenerator::LinearGetTargetWithOutput( + const std::string& name) const +{ + // We go through the ordered vector of targets to get reproducible results + // should multiple names match. + for (cmTarget* t : this->Makefile->GetOrderedTargets()) { + // Does the output of any command match the source file name? + if (AnyTargetCommandOutputMatches(name, t->GetPreBuildCommands())) { + return t; + } + if (AnyTargetCommandOutputMatches(name, t->GetPreLinkCommands())) { + return t; + } + if (AnyTargetCommandOutputMatches(name, t->GetPostBuildCommands())) { + return t; + } + } + return nullptr; +} + +cmSourceFile* cmLocalGenerator::LinearGetSourceFileWithOutput( + const std::string& name, cmSourceOutputKind kind, bool& byproduct) const +{ + // Outputs take precedence over byproducts. + byproduct = false; + cmSourceFile* fallback = nullptr; + + // Look through all the source files that have custom commands and see if the + // custom command has the passed source file as an output. + for (const auto& src : this->Makefile->GetSourceFiles()) { + // Does this source file have a custom command? + if (src->GetCustomCommand()) { + // Does the output of the custom command match the source file name? + if (AnyOutputMatches(name, src->GetCustomCommand()->GetOutputs())) { + // Return the first matching output. + return src.get(); + } + if (kind == cmSourceOutputKind::OutputOrByproduct) { + if (AnyOutputMatches(name, src->GetCustomCommand()->GetByproducts())) { + // Do not return the source yet as there might be a matching output. + fallback = src.get(); + } + } + } + } + + // Did we find a byproduct? + byproduct = fallback != nullptr; + return fallback; +} diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index 22d3599..30371c5 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -36,6 +36,24 @@ class cmState; class cmTarget; class cmake; +/** Flag if byproducts shall also be considered. */ +enum class cmSourceOutputKind +{ + OutputOnly, + OutputOrByproduct +}; + +/** Target and source file which have a specific output. */ +struct cmSourcesWithOutput +{ + /** Target with byproduct. */ + cmTarget* Target = nullptr; + + /** Source file with output or byproduct. */ + cmSourceFile* Source = nullptr; + bool SourceIsByproduct = false; +}; + /** \class cmLocalGenerator * \brief Create required build files for a directory. * @@ -59,7 +77,7 @@ public: /** * Calls TraceVSDependencies() on all targets of this generator. */ - void TraceDependencies(); + void TraceDependencies() const; virtual void AddHelperCommands() {} @@ -337,6 +355,34 @@ public: bool command_expand_lists = false, const std::string& job_pool = "", bool stdPipesUTF8 = false); + /** + * Add target byproducts. + */ + void AddTargetByproducts(cmTarget* target, + const std::vector<std::string>& byproducts); + + /** + * Add source file outputs. + */ + void AddSourceOutputs(cmSourceFile* source, + const std::vector<std::string>& outputs, + const std::vector<std::string>& byproducts); + + /** + * Return the target if the provided source name is a byproduct of a utility + * target or a PRE_BUILD, PRE_LINK, or POST_BUILD command. + * Return the source file which has the provided source name as output. + */ + cmSourcesWithOutput GetSourcesWithOutput(const std::string& name) const; + + /** + * Is there a source file that has the provided source name as an output? + * If so then return it. + */ + cmSourceFile* GetSourceFileWithOutput( + const std::string& name, + cmSourceOutputKind kind = cmSourceOutputKind::OutputOnly) const; + std::string GetProjectName() const; /** Compute the language used to compile the given source file. */ @@ -405,7 +451,7 @@ public: const std::string& fname); /** Construct a comment for a custom command. */ std::string ConstructComment(cmCustomCommandGenerator const& ccg, - const char* default_comment = ""); + const char* default_comment = "") const; // Compute object file names. std::string GetObjectFileNameWithoutTarget( const cmSourceFile& source, std::string const& dir_max, @@ -473,8 +519,7 @@ public: void CreateEvaluationFileOutputs(const std::string& config); void ProcessEvaluationFiles(std::vector<std::string>& generatedFiles); - const char* GetRuleLauncher(cmGeneratorTarget* target, - const std::string& prop); + cmProp GetRuleLauncher(cmGeneratorTarget* target, const std::string& prop); protected: //! put all the libraries for a target on into the given stream @@ -532,6 +577,33 @@ protected: bool BackwardsCompatibilityFinal; private: + /** + * See LinearGetSourceFileWithOutput for background information + */ + cmTarget* LinearGetTargetWithOutput(const std::string& name) const; + + /** + * Generalized old version of GetSourceFileWithOutput kept for + * backward-compatibility. It implements a linear search and supports + * relative file paths. It is used as a fall back by GetSourceFileWithOutput + * and GetSourcesWithOutput. + */ + cmSourceFile* LinearGetSourceFileWithOutput(const std::string& name, + cmSourceOutputKind kind, + bool& byproduct) const; + struct SourceEntry + { + cmSourcesWithOutput Sources; + }; + + // A map for fast output to input look up. + using OutputToSourceMap = std::unordered_map<std::string, SourceEntry>; + OutputToSourceMap OutputToSource; + + void UpdateOutputToSourceMap(std::string const& byproduct, cmTarget* target); + void UpdateOutputToSourceMap(std::string const& output, cmSourceFile* source, + bool byproduct); + void AddSharedFlags(std::string& flags, const std::string& lang, bool shared); bool GetShouldUseOldFlags(bool shared, const std::string& lang) const; diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index ad782ee..d90a37b 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -22,7 +22,9 @@ #include "cmGlobalNinjaGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" +#include "cmMessageType.h" #include "cmNinjaTargetGenerator.h" +#include "cmPolicies.h" #include "cmProperty.h" #include "cmRulePlaceholderExpander.h" #include "cmSourceFile.h" @@ -573,7 +575,20 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement( return; } - cmCustomCommandGenerator ccg(*cc, config, this); + bool transformDepfile = false; + auto cmp0116 = this->GetPolicyStatus(cmPolicies::CMP0116); + switch (cmp0116) { + case cmPolicies::OLD: + case cmPolicies::WARN: + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + transformDepfile = true; + break; + } + + cmCustomCommandGenerator ccg(*cc, config, this, transformDepfile); const std::vector<std::string>& outputs = ccg.GetOutputs(); const std::vector<std::string>& byproducts = ccg.GetByproducts(); @@ -588,11 +603,6 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement( } } -#if 0 -# error TODO: Once CC in an ExternalProject target must provide the \ - file of each imported target that has an add_dependencies pointing \ - at us. How to know which ExternalProject step actually provides it? -#endif cmNinjaDeps ninjaOutputs(outputs.size() + byproducts.size()); std::transform(outputs.begin(), outputs.end(), ninjaOutputs.begin(), gg->MapToNinjaPath()); @@ -623,10 +633,36 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement( cmCryptoHash hash(cmCryptoHash::AlgoSHA256); customStep += hash.HashString(ninjaOutputs[0]).substr(0, 7); + std::string depfile = cc->GetDepfile(); + if (!depfile.empty()) { + switch (cmp0116) { + case cmPolicies::WARN: + if (this->GetCurrentBinaryDirectory() != + this->GetBinaryDirectory() || + this->Makefile->PolicyOptionalWarningEnabled( + "CMAKE_POLICY_WARNING_CMP0116")) { + this->GetCMakeInstance()->IssueMessage( + MessageType::AUTHOR_WARNING, + cmPolicies::GetPolicyWarning(cmPolicies::CMP0116), + cc->GetBacktrace()); + } + CM_FALLTHROUGH; + case cmPolicies::OLD: + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + cmSystemTools::MakeDirectory( + cmStrCat(this->GetBinaryDirectory(), "/CMakeFiles/d")); + depfile = ccg.GetInternalDepfile(); + break; + } + } + gg->WriteCustomCommandBuild( this->BuildCommandLine(cmdLines, customStep), this->ConstructComment(ccg), "Custom command for " + ninjaOutputs[0], - cc->GetDepfile(), cc->GetJobPool(), cc->GetUsesTerminal(), + depfile, cc->GetJobPool(), cc->GetUsesTerminal(), /*restat*/ !symbolic || !byproducts.empty(), ninjaOutputs, config, ninjaDeps, orderOnlyDeps); } diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index c877cf8..dd27084 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -960,7 +960,7 @@ void cmLocalUnixMakefileGenerator3::AppendCustomCommand( std::string launcher; // Short-circuit if there is no launcher. - const char* val = this->GetRuleLauncher(target, "RULE_LAUNCH_CUSTOM"); + cmProp val = this->GetRuleLauncher(target, "RULE_LAUNCH_CUSTOM"); if (cmNonempty(val)) { // Expand rule variables referenced in the given launcher command. cmRulePlaceholderExpander::RuleVariables vars; @@ -980,7 +980,7 @@ void cmLocalUnixMakefileGenerator3::AppendCustomCommand( } vars.Output = output.c_str(); - launcher = val; + launcher = *val; rulePlaceholderExpander->ExpandRuleVariables(this, launcher, vars); if (!launcher.empty()) { launcher += " "; diff --git a/Source/cmLocalUnixMakefileGenerator3.h b/Source/cmLocalUnixMakefileGenerator3.h index 8286d67..5edca2a 100644 --- a/Source/cmLocalUnixMakefileGenerator3.h +++ b/Source/cmLocalUnixMakefileGenerator3.h @@ -178,11 +178,11 @@ public: /** Get whether to create rules to generate preprocessed and assembly sources. This could be converted to a variable lookup later. */ - bool GetCreatePreprocessedSourceRules() + bool GetCreatePreprocessedSourceRules() const { return !this->SkipPreprocessedSourceRules; } - bool GetCreateAssemblySourceRules() + bool GetCreateAssemblySourceRules() const { return !this->SkipAssemblySourceRules; } diff --git a/Source/cmMacroCommand.cxx b/Source/cmMacroCommand.cxx index 98f88c1..8c4b2a7 100644 --- a/Source/cmMacroCommand.cxx +++ b/Source/cmMacroCommand.cxx @@ -171,8 +171,11 @@ bool cmMacroFunctionBlocker::Replay(std::vector<cmListFileFunction> functions, f.Functions = std::move(functions); f.FilePath = this->GetStartingContext().FilePath; mf.RecordPolicies(f.Policies); - mf.GetState()->AddScriptedCommand(this->Args[0], std::move(f)); - return true; + return mf.GetState()->AddScriptedCommand( + this->Args[0], + BT<cmState::Command>(std::move(f), + mf.GetBacktrace().Push(this->GetStartingContext())), + mf); } } diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 0e4f888..50a7d27 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -29,15 +29,12 @@ #include "cmsys/FStream.hxx" #include "cmsys/RegularExpression.hxx" -#include "cm_sys_stat.h" - #include "cmCommandArgumentParserHelper.h" #include "cmCustomCommand.h" #include "cmCustomCommandLines.h" #include "cmExecutionStatus.h" #include "cmExpandedCommandArgument.h" // IWYU pragma: keep #include "cmExportBuildFileGenerator.h" -#include "cmFSPermissions.h" #include "cmFileLockPool.h" #include "cmFunctionBlocker.h" #include "cmGeneratedFileStream.h" @@ -72,8 +69,6 @@ class cmMessenger; -using namespace cmFSPermissions; - cmDirectoryId::cmDirectoryId(std::string s) : String(std::move(s)) { @@ -939,8 +934,6 @@ void cmMakefile::DoGenerate(cmLocalGenerator& lg) action.Value(lg, action.Backtrace); } this->GeneratorActionsInvoked = true; - this->DelayedOutputFiles.clear(); - this->DelayedOutputFilesHaveGenex = false; // go through all configured files and see which ones still exist. // we don't want cmake to re-run if a configured file is created and deleted @@ -1104,7 +1097,7 @@ cmTarget* cmMakefile::AddCustomCommandToTarget( } // Always create the byproduct sources and mark them generated. - this->CreateGeneratedByproducts(byproducts); + this->CreateGeneratedOutputs(byproducts); // Strings could be moved into the callback function with C++14. cm::optional<std::string> commentStr = MakeOptionalString(comment); @@ -1163,7 +1156,7 @@ void cmMakefile::AddCustomCommandToOutput( // Always create the output sources and mark them generated. this->CreateGeneratedOutputs(outputs); - this->CreateGeneratedByproducts(byproducts); + this->CreateGeneratedOutputs(byproducts); // Strings could be moved into the callback function with C++14. cm::optional<std::string> commentStr = MakeOptionalString(comment); @@ -1247,16 +1240,11 @@ void cmMakefile::AddCustomCommandOldStyle( } } -bool cmMakefile::AppendCustomCommandToOutput( +void cmMakefile::AppendCustomCommandToOutput( const std::string& output, const std::vector<std::string>& depends, const cmImplicitDependsList& implicit_depends, const cmCustomCommandLines& commandLines) { - // Check as good as we can if there will be a command for this output. - if (!this->MightHaveCustomCommand(output)) { - return false; - } - // Validate custom commands. if (this->ValidateCustomCommand(commandLines)) { // Dispatch command creation to allow generator expressions in outputs. @@ -1267,8 +1255,6 @@ bool cmMakefile::AppendCustomCommandToOutput( implicit_depends, commandLines); }); } - - return true; } cmUtilityOutput cmMakefile::GetUtilityOutput(cmTarget* target) @@ -1313,7 +1299,7 @@ cmTarget* cmMakefile::AddUtilityCommand( this->GetOrCreateGeneratedSource(force.Name); // Always create the byproduct sources and mark them generated. - this->CreateGeneratedByproducts(byproducts); + this->CreateGeneratedOutputs(byproducts); // Strings could be moved into the callback function with C++14. cm::optional<std::string> commentStr = MakeOptionalString(comment); @@ -1496,15 +1482,14 @@ void cmMakefile::InitializeFromParent(cmMakefile* parent) // Include transform property. There is no per-config version. { const char* prop = "IMPLICIT_DEPENDS_INCLUDE_TRANSFORM"; - cmProp p = parent->GetProperty(prop); - this->SetProperty(prop, cmToCStr(p)); + this->SetProperty(prop, cmToCStr(parent->GetProperty(prop))); } // compile definitions property and per-config versions cmPolicies::PolicyStatus polSt = this->GetPolicyStatus(cmPolicies::CMP0043); if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) { - cmProp p = parent->GetProperty("COMPILE_DEFINITIONS"); - this->SetProperty("COMPILE_DEFINITIONS", cmToCStr(p)); + this->SetProperty("COMPILE_DEFINITIONS", + cmToCStr(parent->GetProperty("COMPILE_DEFINITIONS"))); std::vector<std::string> configs = this->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); for (std::string const& config : configs) { @@ -1516,12 +1501,11 @@ void cmMakefile::InitializeFromParent(cmMakefile* parent) } // labels - cmProp p = parent->GetProperty("LABELS"); - this->SetProperty("LABELS", cmToCStr(p)); + this->SetProperty("LABELS", cmToCStr(parent->GetProperty("LABELS"))); // link libraries - p = parent->GetProperty("LINK_LIBRARIES"); - this->SetProperty("LINK_LIBRARIES", cmToCStr(p)); + this->SetProperty("LINK_LIBRARIES", + cmToCStr(parent->GetProperty("LINK_LIBRARIES"))); // the initial project name this->StateSnapshot.SetProjectName(parent->StateSnapshot.GetProjectName()); @@ -2038,7 +2022,7 @@ void cmMakefile::RemoveDefinition(const std::string& name) #endif } -void cmMakefile::RemoveCacheDefinition(const std::string& name) +void cmMakefile::RemoveCacheDefinition(const std::string& name) const { this->GetState()->RemoveCacheEntry(name); } @@ -2154,213 +2138,6 @@ cmTarget* cmMakefile::AddNewUtilityTarget(const std::string& utilityName, } namespace { -bool AnyOutputMatches(const std::string& name, - const std::vector<std::string>& outputs) -{ - for (std::string const& output : outputs) { - std::string::size_type pos = output.rfind(name); - // If the output matches exactly - if (pos != std::string::npos && pos == output.size() - name.size() && - (pos == 0 || output[pos - 1] == '/')) { - return true; - } - } - return false; -} - -bool AnyTargetCommandOutputMatches( - const std::string& name, const std::vector<cmCustomCommand>& commands) -{ - for (cmCustomCommand const& command : commands) { - if (AnyOutputMatches(name, command.GetByproducts())) { - return true; - } - } - return false; -} -} - -cmTarget* cmMakefile::LinearGetTargetWithOutput(const std::string& name) const -{ - // We go through the ordered vector of targets to get reproducible results - // should multiple names match. - for (cmTarget* t : this->OrderedTargets) { - // Does the output of any command match the source file name? - if (AnyTargetCommandOutputMatches(name, t->GetPreBuildCommands())) { - return t; - } - if (AnyTargetCommandOutputMatches(name, t->GetPreLinkCommands())) { - return t; - } - if (AnyTargetCommandOutputMatches(name, t->GetPostBuildCommands())) { - return t; - } - } - return nullptr; -} - -cmSourceFile* cmMakefile::LinearGetSourceFileWithOutput( - const std::string& name, cmSourceOutputKind kind, bool& byproduct) const -{ - // Outputs take precedence over byproducts. - byproduct = false; - cmSourceFile* fallback = nullptr; - - // Look through all the source files that have custom commands and see if the - // custom command has the passed source file as an output. - for (const auto& src : this->SourceFiles) { - // Does this source file have a custom command? - if (src->GetCustomCommand()) { - // Does the output of the custom command match the source file name? - if (AnyOutputMatches(name, src->GetCustomCommand()->GetOutputs())) { - // Return the first matching output. - return src.get(); - } - if (kind == cmSourceOutputKind::OutputOrByproduct) { - if (AnyOutputMatches(name, src->GetCustomCommand()->GetByproducts())) { - // Do not return the source yet as there might be a matching output. - fallback = src.get(); - } - } - } - } - - // Did we find a byproduct? - byproduct = fallback != nullptr; - return fallback; -} - -cmSourcesWithOutput cmMakefile::GetSourcesWithOutput( - const std::string& name) const -{ - // Linear search? Also see GetSourceFileWithOutput for detail. - if (!cmSystemTools::FileIsFullPath(name)) { - cmSourcesWithOutput sources; - sources.Target = this->LinearGetTargetWithOutput(name); - sources.Source = this->LinearGetSourceFileWithOutput( - name, cmSourceOutputKind::OutputOrByproduct, sources.SourceIsByproduct); - return sources; - } - // Otherwise we use an efficient lookup map. - auto o = this->OutputToSource.find(name); - if (o != this->OutputToSource.end()) { - return o->second.Sources; - } - return {}; -} - -cmSourceFile* cmMakefile::GetSourceFileWithOutput( - const std::string& name, cmSourceOutputKind kind) const -{ - // If the queried path is not absolute we use the backward compatible - // linear-time search for an output with a matching suffix. - if (!cmSystemTools::FileIsFullPath(name)) { - bool byproduct = false; - return this->LinearGetSourceFileWithOutput(name, kind, byproduct); - } - // Otherwise we use an efficient lookup map. - auto o = this->OutputToSource.find(name); - if (o != this->OutputToSource.end() && - (!o->second.Sources.SourceIsByproduct || - kind == cmSourceOutputKind::OutputOrByproduct)) { - // Source file could also be null pointer for example if we found the - // byproduct of a utility target, a PRE_BUILD, PRE_LINK, or POST_BUILD - // command of a target, or a not yet created custom command. - return o->second.Sources.Source; - } - return nullptr; -} - -bool cmMakefile::MightHaveCustomCommand(const std::string& name) const -{ - if (this->DelayedOutputFilesHaveGenex || - cmGeneratorExpression::Find(name) != std::string::npos) { - // Could be more restrictive, but for now we assume that there could always - // be a match when generator expressions are involved. - return true; - } - // Also see LinearGetSourceFileWithOutput. - if (!cmSystemTools::FileIsFullPath(name)) { - return AnyOutputMatches(name, this->DelayedOutputFiles); - } - // Otherwise we use an efficient lookup map. - auto o = this->OutputToSource.find(name); - if (o != this->OutputToSource.end()) { - return o->second.SourceMightBeOutput; - } - return false; -} - -void cmMakefile::AddTargetByproducts( - cmTarget* target, const std::vector<std::string>& byproducts) -{ - for (std::string const& o : byproducts) { - this->UpdateOutputToSourceMap(o, target); - } -} - -void cmMakefile::AddSourceOutputs(cmSourceFile* source, - const std::vector<std::string>& outputs, - const std::vector<std::string>& byproducts) -{ - for (std::string const& o : outputs) { - this->UpdateOutputToSourceMap(o, source, false); - } - for (std::string const& o : byproducts) { - this->UpdateOutputToSourceMap(o, source, true); - } -} - -void cmMakefile::UpdateOutputToSourceMap(std::string const& byproduct, - cmTarget* target) -{ - SourceEntry entry; - entry.Sources.Target = target; - - auto pr = this->OutputToSource.emplace(byproduct, entry); - if (!pr.second) { - SourceEntry& current = pr.first->second; - // Has the target already been set? - if (!current.Sources.Target) { - current.Sources.Target = target; - } else { - // Multiple custom commands/targets produce the same output (source file - // or target). See also comment in other UpdateOutputToSourceMap - // overload. - // - // TODO: Warn the user about this case. - } - } -} - -void cmMakefile::UpdateOutputToSourceMap(std::string const& output, - cmSourceFile* source, bool byproduct) -{ - SourceEntry entry; - entry.Sources.Source = source; - entry.Sources.SourceIsByproduct = byproduct; - entry.SourceMightBeOutput = !byproduct; - - auto pr = this->OutputToSource.emplace(output, entry); - if (!pr.second) { - SourceEntry& current = pr.first->second; - // Outputs take precedence over byproducts - if (!current.Sources.Source || - (current.Sources.SourceIsByproduct && !byproduct)) { - current.Sources.Source = source; - current.Sources.SourceIsByproduct = false; - current.SourceMightBeOutput = true; - } else { - // Multiple custom commands produce the same output but may - // be attached to a different source file (MAIN_DEPENDENCY). - // LinearGetSourceFileWithOutput would return the first one, - // so keep the mapping for the first one. - // - // TODO: Warn the user about this case. However, the VS 8 generator - // triggers it for separate generate.stamp rules in ZERO_CHECK and - // individual targets. - } - } } #if !defined(CMAKE_BOOTSTRAP) @@ -3055,7 +2832,7 @@ void cmMakefile::SetRecursionDepth(int recursionDepth) this->RecursionDepth = recursionDepth; } -std::string cmMakefile::NewDeferId() +std::string cmMakefile::NewDeferId() const { return this->GetGlobalGenerator()->NewDeferId(); } @@ -3675,38 +3452,10 @@ void cmMakefile::CreateGeneratedOutputs( for (std::string const& o : outputs) { if (cmGeneratorExpression::Find(o) == std::string::npos) { this->GetOrCreateGeneratedSource(o); - this->AddDelayedOutput(o); - } else { - this->DelayedOutputFilesHaveGenex = true; - } - } -} - -void cmMakefile::CreateGeneratedByproducts( - const std::vector<std::string>& byproducts) -{ - for (std::string const& o : byproducts) { - if (cmGeneratorExpression::Find(o) == std::string::npos) { - this->GetOrCreateGeneratedSource(o); } } } -void cmMakefile::AddDelayedOutput(std::string const& output) -{ - // Note that this vector might contain the output names in a different order - // than in source file iteration order. - this->DelayedOutputFiles.push_back(output); - - SourceEntry entry; - entry.SourceMightBeOutput = true; - - auto pr = this->OutputToSource.emplace(output, entry); - if (!pr.second) { - pr.first->second.SourceMightBeOutput = true; - } -} - void cmMakefile::AddTargetObject(std::string const& tgtName, std::string const& objFile) { @@ -4094,8 +3843,7 @@ void cmMakefile::ConfigureString(const std::string& input, std::string& output, int cmMakefile::ConfigureFile(const std::string& infile, const std::string& outfile, bool copyonly, bool atOnly, bool escapeQuotes, - bool use_source_permissions, - cmNewLineStyle newLine) + mode_t permissions, cmNewLineStyle newLine) { int res = 1; if (!this->CanIWriteThisFile(outfile)) { @@ -4117,12 +3865,8 @@ int cmMakefile::ConfigureFile(const std::string& infile, // output files that now don't exist. this->AddCMakeOutputFile(soutfile); - mode_t perm = 0; - if (!use_source_permissions) { - perm = perm | mode_owner_read | mode_owner_write | mode_group_read | - mode_world_read; - } else { - cmSystemTools::GetPermissions(sinfile, perm); + if (permissions == 0) { + cmSystemTools::GetPermissions(sinfile, permissions); } std::string::size_type pos = soutfile.rfind('/'); @@ -4137,7 +3881,7 @@ int cmMakefile::ConfigureFile(const std::string& infile, cmSystemTools::GetLastSystemError()); return 0; } - if (!cmSystemTools::SetPermissions(soutfile, perm)) { + if (!cmSystemTools::SetPermissions(soutfile, permissions)) { this->IssueMessage(MessageType::FATAL_ERROR, cmSystemTools::GetLastSystemError()); return 0; @@ -4194,7 +3938,7 @@ int cmMakefile::ConfigureFile(const std::string& infile, cmSystemTools::GetLastSystemError()); res = 0; } else { - if (!cmSystemTools::SetPermissions(soutfile, perm)) { + if (!cmSystemTools::SetPermissions(soutfile, permissions)) { this->IssueMessage(MessageType::FATAL_ERROR, cmSystemTools::GetLastSystemError()); res = 0; @@ -4283,7 +4027,7 @@ cmTest* cmMakefile::GetTest(const std::string& testName) const } void cmMakefile::GetTests(const std::string& config, - std::vector<cmTest*>& tests) + std::vector<cmTest*>& tests) const { for (const auto& generator : this->GetTestGenerators()) { if (generator->TestsForConfig(config)) { @@ -4633,7 +4377,7 @@ cmPolicies::PolicyStatus cmMakefile::GetPolicyStatus(cmPolicies::PolicyID id, return this->StateSnapshot.GetPolicy(id, parent_scope); } -bool cmMakefile::PolicyOptionalWarningEnabled(std::string const& var) +bool cmMakefile::PolicyOptionalWarningEnabled(std::string const& var) const { // Check for an explicit CMAKE_POLICY_WARNING_CMP<NNNN> setting. if (cmProp val = this->GetDefinition(var)) { @@ -4670,7 +4414,7 @@ bool cmMakefile::SetPolicy(cmPolicies::PolicyID id, // Deprecate old policies, especially those that require a lot // of code to maintain the old behavior. - if (status == cmPolicies::OLD && id <= cmPolicies::CMP0072 && + if (status == cmPolicies::OLD && id <= cmPolicies::CMP0075 && !(this->GetCMakeInstance()->GetIsInTryCompile() && ( // Policies set by cmCoreTryCompile::TryCompileCode. @@ -4738,7 +4482,7 @@ bool cmMakefile::HasCMP0054AlreadyBeenReported( return !this->CMP0054ReportedIds.insert(context).second; } -void cmMakefile::RecordPolicies(cmPolicies::PolicyMap& pm) +void cmMakefile::RecordPolicies(cmPolicies::PolicyMap& pm) const { /* Record the setting of every policy. */ using PolicyID = cmPolicies::PolicyID; diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index c7940fb..a864074 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -20,6 +20,8 @@ #include "cmsys/RegularExpression.hxx" +#include "cm_sys_stat.h" + #include "cmAlgorithms.h" #include "cmCustomCommandTypes.h" #include "cmListFileCache.h" @@ -59,24 +61,6 @@ class cmTestGenerator; class cmVariableWatch; class cmake; -/** Flag if byproducts shall also be considered. */ -enum class cmSourceOutputKind -{ - OutputOnly, - OutputOrByproduct -}; - -/** Target and source file which have a specific output. */ -struct cmSourcesWithOutput -{ - /** Target with byproduct. */ - cmTarget* Target = nullptr; - - /** Source file with output or byproduct. */ - cmSourceFile* Source = nullptr; - bool SourceIsByproduct = false; -}; - /** A type-safe wrapper for a string representing a directory id. */ class cmDirectoryId { @@ -225,25 +209,12 @@ public: const std::string& source, const cmCustomCommandLines& commandLines, const char* comment); - bool AppendCustomCommandToOutput( + void AppendCustomCommandToOutput( const std::string& output, const std::vector<std::string>& depends, const cmImplicitDependsList& implicit_depends, const cmCustomCommandLines& commandLines); /** - * Add target byproducts. - */ - void AddTargetByproducts(cmTarget* target, - const std::vector<std::string>& byproducts); - - /** - * Add source file outputs. - */ - void AddSourceOutputs(cmSourceFile* source, - const std::vector<std::string>& outputs, - const std::vector<std::string>& byproducts); - - /** * Add a define flag to the build. */ void AddDefineFlag(std::string const& definition); @@ -335,7 +306,7 @@ public: */ void RemoveDefinition(const std::string& name); //! Remove a definition from the cache. - void RemoveCacheDefinition(const std::string& name); + void RemoveCacheDefinition(const std::string& name) const; /** * Specify the name of the project for this build. @@ -376,7 +347,7 @@ public: bool parent_scope = false) const; bool SetPolicyVersion(std::string const& version_min, std::string const& version_max); - void RecordPolicies(cmPolicies::PolicyMap& pm); + void RecordPolicies(cmPolicies::PolicyMap& pm) const; //@} /** Helper class to push and pop policies automatically. */ @@ -430,8 +401,7 @@ public: } const char* GetIncludeRegularExpression() const { - cmProp p = this->GetProperty("INCLUDE_REGULAR_EXPRESSION"); - return p ? p->c_str() : nullptr; + return cmToCStr(this->GetProperty("INCLUDE_REGULAR_EXPRESSION")); } /** @@ -690,8 +660,7 @@ public: */ int ConfigureFile(const std::string& infile, const std::string& outfile, bool copyonly, bool atOnly, bool escapeQuotes, - bool use_source_permissions, - cmNewLineStyle = cmNewLineStyle()); + mode_t permissions = 0, cmNewLineStyle = cmNewLineStyle()); /** * Print a command's invocation @@ -753,20 +722,10 @@ public: return this->SourceFiles; } - /** - * Return the target if the provided source name is a byproduct of a utility - * target or a PRE_BUILD, PRE_LINK, or POST_BUILD command. - * Return the source file which has the provided source name as output. - */ - cmSourcesWithOutput GetSourcesWithOutput(const std::string& name) const; - - /** - * Is there a source file that has the provided source name as an output? - * If so then return it. - */ - cmSourceFile* GetSourceFileWithOutput( - const std::string& name, - cmSourceOutputKind kind = cmSourceOutputKind::OutputOnly) const; + std::vector<cmTarget*> const& GetOrderedTargets() const + { + return this->OrderedTargets; + } //! Add a new cmTest to the list of tests for this makefile. cmTest* CreateTest(const std::string& testName); @@ -779,7 +738,7 @@ public: /** * Get all tests that run under the given configuration. */ - void GetTests(const std::string& config, std::vector<cmTest*>& tests); + void GetTests(const std::string& config, std::vector<cmTest*>& tests) const; /** * Return a location of a file in cmake or custom modules directory @@ -926,7 +885,7 @@ public: return this->SystemIncludeDirectories; } - bool PolicyOptionalWarningEnabled(std::string const& var); + bool PolicyOptionalWarningEnabled(std::string const& var) const; void PushLoopBlock(); void PopLoopBlock(); @@ -967,7 +926,7 @@ public: int GetRecursionDepth() const; void SetRecursionDepth(int recursionDepth); - std::string NewDeferId(); + std::string NewDeferId() const; bool DeferCall(std::string id, std::string fileName, cmListFileFunction lff); bool DeferCancelCall(std::string const& id); cm::optional<std::string> DeferGetCallIds() const; @@ -983,8 +942,7 @@ protected: mutable cmTargetMap Targets; std::map<std::string, std::string> AliasTargets; - using TargetsVec = std::vector<cmTarget*>; - TargetsVec OrderedTargets; + std::vector<cmTarget*> OrderedTargets; std::vector<std::unique_ptr<cmSourceFile>> SourceFiles; @@ -1129,48 +1087,9 @@ private: bool ValidateCustomCommand(const cmCustomCommandLines& commandLines) const; void CreateGeneratedOutputs(const std::vector<std::string>& outputs); - void CreateGeneratedByproducts(const std::vector<std::string>& byproducts); std::vector<BT<GeneratorAction>> GeneratorActions; bool GeneratorActionsInvoked = false; - bool DelayedOutputFilesHaveGenex = false; - std::vector<std::string> DelayedOutputFiles; - - void AddDelayedOutput(std::string const& output); - - /** - * See LinearGetSourceFileWithOutput for background information - */ - cmTarget* LinearGetTargetWithOutput(const std::string& name) const; - - /** - * Generalized old version of GetSourceFileWithOutput kept for - * backward-compatibility. It implements a linear search and supports - * relative file paths. It is used as a fall back by GetSourceFileWithOutput - * and GetSourcesWithOutput. - */ - cmSourceFile* LinearGetSourceFileWithOutput(const std::string& name, - cmSourceOutputKind kind, - bool& byproduct) const; - - struct SourceEntry - { - cmSourcesWithOutput Sources; - bool SourceMightBeOutput = false; - }; - - // A map for fast output to input look up. - using OutputToSourceMap = std::unordered_map<std::string, SourceEntry>; - OutputToSourceMap OutputToSource; - - void UpdateOutputToSourceMap(std::string const& byproduct, cmTarget* target); - void UpdateOutputToSourceMap(std::string const& output, cmSourceFile* source, - bool byproduct); - - /** - * Return if the provided source file might have a custom command. - */ - bool MightHaveCustomCommand(const std::string& name) const; bool CheckSystemVars; bool CheckCMP0000; diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx index 871878c..1750e37 100644 --- a/Source/cmMakefileExecutableTargetGenerator.cxx +++ b/Source/cmMakefileExecutableTargetGenerator.cxx @@ -21,6 +21,7 @@ #include "cmMakefile.h" #include "cmOSXBundleGenerator.h" #include "cmOutputConverter.h" +#include "cmProperty.h" #include "cmRulePlaceholderExpander.h" #include "cmState.h" #include "cmStateDirectory.h" @@ -232,10 +233,10 @@ void cmMakefileExecutableTargetGenerator::WriteNvidiaDeviceExecutableRule( std::string launcher; - const char* val = this->LocalGenerator->GetRuleLauncher( - this->GeneratorTarget, "RULE_LAUNCH_LINK"); + cmProp val = this->LocalGenerator->GetRuleLauncher(this->GeneratorTarget, + "RULE_LAUNCH_LINK"); if (cmNonempty(val)) { - launcher = cmStrCat(val, ' '); + launcher = cmStrCat(*val, ' '); } std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander( @@ -591,10 +592,10 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) std::string launcher; - const char* val = this->LocalGenerator->GetRuleLauncher( - this->GeneratorTarget, "RULE_LAUNCH_LINK"); + cmProp val = this->LocalGenerator->GetRuleLauncher(this->GeneratorTarget, + "RULE_LAUNCH_LINK"); if (cmNonempty(val)) { - launcher = cmStrCat(val, ' '); + launcher = cmStrCat(*val, ' '); } std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander( diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx index b32ea6a..ce64e2c 100644 --- a/Source/cmMakefileLibraryTargetGenerator.cxx +++ b/Source/cmMakefileLibraryTargetGenerator.cxx @@ -21,6 +21,7 @@ #include "cmMakefile.h" #include "cmOSXBundleGenerator.h" #include "cmOutputConverter.h" +#include "cmProperty.h" #include "cmRulePlaceholderExpander.h" #include "cmState.h" #include "cmStateDirectory.h" @@ -366,10 +367,10 @@ void cmMakefileLibraryTargetGenerator::WriteNvidiaDeviceLibraryRules( vars.TargetCompilePDB = targetOutPathCompilePDB.c_str(); std::string launcher; - const char* val = this->LocalGenerator->GetRuleLauncher( - this->GeneratorTarget, "RULE_LAUNCH_LINK"); + cmProp val = this->LocalGenerator->GetRuleLauncher(this->GeneratorTarget, + "RULE_LAUNCH_LINK"); if (cmNonempty(val)) { - launcher = cmStrCat(val, ' '); + launcher = cmStrCat(*val, ' '); } std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander( @@ -816,10 +817,10 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules( vars.LanguageCompileFlags = langFlags.c_str(); std::string launcher; - const char* val = this->LocalGenerator->GetRuleLauncher( - this->GeneratorTarget, "RULE_LAUNCH_LINK"); + cmProp val = this->LocalGenerator->GetRuleLauncher(this->GeneratorTarget, + "RULE_LAUNCH_LINK"); if (cmNonempty(val)) { - launcher = cmStrCat(val, ' '); + launcher = cmStrCat(*val, ' '); } std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander( diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index 5f97d86..8f3a0d8 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -795,7 +795,7 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( bool lang_has_preprocessor = ((lang == "C") || (lang == "CXX") || (lang == "OBJC") || (lang == "OBJCXX") || (lang == "Fortran") || (lang == "CUDA") || - lang == "ISPC"); + lang == "ISPC" || lang == "ASM"); bool const lang_has_assembly = lang_has_preprocessor; bool const lang_can_export_cmds = lang_has_preprocessor; @@ -873,15 +873,21 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( } // Maybe insert an include-what-you-use runner. - if (!compileCommands.empty() && (lang == "C" || lang == "CXX")) { - std::string const iwyu_prop = lang + "_INCLUDE_WHAT_YOU_USE"; - cmProp iwyu = this->GeneratorTarget->GetProperty(iwyu_prop); + if (!compileCommands.empty() && + (lang == "C" || lang == "CXX" || lang == "OBJC" || lang == "OBJCXX")) { std::string const tidy_prop = lang + "_CLANG_TIDY"; cmProp tidy = this->GeneratorTarget->GetProperty(tidy_prop); - std::string const cpplint_prop = lang + "_CPPLINT"; - cmProp cpplint = this->GeneratorTarget->GetProperty(cpplint_prop); - std::string const cppcheck_prop = lang + "_CPPCHECK"; - cmProp cppcheck = this->GeneratorTarget->GetProperty(cppcheck_prop); + cmProp iwyu = nullptr; + cmProp cpplint = nullptr; + cmProp cppcheck = nullptr; + if (lang == "C" || lang == "CXX") { + std::string const iwyu_prop = lang + "_INCLUDE_WHAT_YOU_USE"; + iwyu = this->GeneratorTarget->GetProperty(iwyu_prop); + std::string const cpplint_prop = lang + "_CPPLINT"; + cpplint = this->GeneratorTarget->GetProperty(cpplint_prop); + std::string const cppcheck_prop = lang + "_CPPCHECK"; + cppcheck = this->GeneratorTarget->GetProperty(cppcheck_prop); + } if (cmNonempty(iwyu) || cmNonempty(tidy) || cmNonempty(cpplint) || cmNonempty(cppcheck)) { std::string run_iwyu = "$(CMAKE_COMMAND) -E __run_co_compile"; @@ -943,10 +949,10 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( std::string launcher; { - const char* val = this->LocalGenerator->GetRuleLauncher( + cmProp val = this->LocalGenerator->GetRuleLauncher( this->GeneratorTarget, "RULE_LAUNCH_COMPILE"); if (cmNonempty(val)) { - launcher = cmStrCat(val, ' '); + launcher = cmStrCat(*val, ' '); } } diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index ccb959b..a5b9466 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -266,10 +266,10 @@ void cmNinjaNormalTargetGenerator::WriteNvidiaDeviceLinkRule( vars.LanguageCompileFlags = "$LANGUAGE_COMPILE_FLAGS"; std::string launcher; - const char* val = this->GetLocalGenerator()->GetRuleLauncher( + cmProp val = this->GetLocalGenerator()->GetRuleLauncher( this->GetGeneratorTarget(), "RULE_LAUNCH_LINK"); if (cmNonempty(val)) { - launcher = cmStrCat(val, ' '); + launcher = cmStrCat(*val, ' '); } std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander( @@ -452,10 +452,10 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile, } std::string launcher; - const char* val = this->GetLocalGenerator()->GetRuleLauncher( + cmProp val = this->GetLocalGenerator()->GetRuleLauncher( this->GetGeneratorTarget(), "RULE_LAUNCH_LINK"); if (cmNonempty(val)) { - launcher = cmStrCat(val, ' '); + launcher = cmStrCat(*val, ' '); } std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander( diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 04d84a0..f2bec8c 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -105,7 +105,7 @@ std::string cmNinjaTargetGenerator::LanguageCompilerRule( '_', config); } -std::string cmNinjaTargetGenerator::LanguagePreprocessRule( +std::string cmNinjaTargetGenerator::LanguagePreprocessAndScanRule( std::string const& lang, const std::string& config) const { return cmStrCat( @@ -114,7 +114,7 @@ std::string cmNinjaTargetGenerator::LanguagePreprocessRule( '_', config); } -std::string cmNinjaTargetGenerator::LanguageDependencyRule( +std::string cmNinjaTargetGenerator::LanguageScanRule( std::string const& lang, const std::string& config) const { return cmStrCat( @@ -129,14 +129,7 @@ bool cmNinjaTargetGenerator::NeedExplicitPreprocessing( return lang == "Fortran"; } -bool cmNinjaTargetGenerator::UsePreprocessedSource( - std::string const& lang) const -{ - return lang == "Fortran"; -} - -bool cmNinjaTargetGenerator::CompilePreprocessedSourceWithDefines( - std::string const& lang) const +bool cmNinjaTargetGenerator::CompileWithDefines(std::string const& lang) const { return this->Makefile->IsOn( cmStrCat("CMAKE_", lang, "_COMPILE_WITH_DEFINES")); @@ -536,82 +529,60 @@ namespace { // Create the command to run the dependency scanner std::string GetScanCommand(const std::string& cmakeCmd, const std::string& tdi, const std::string& lang, const std::string& ppFile, - bool needDyndep, const std::string& ddiFile) + const std::string& ddiFile) { - std::string ccmd = - cmStrCat(cmakeCmd, " -E cmake_ninja_depends --tdi=", tdi, " --lang=", lang, - " --pp=", ppFile, " --dep=$DEP_FILE"); - if (needDyndep) { - ccmd = cmStrCat(ccmd, " --obj=$OBJ_FILE --ddi=", ddiFile); - } - return ccmd; + return cmStrCat(cmakeCmd, " -E cmake_ninja_depends --tdi=", tdi, + " --lang=", lang, " --pp=", ppFile, + " --dep=$DEP_FILE --obj=$OBJ_FILE --ddi=", ddiFile); } -// Helper function to create dependency scanning rule, with optional -// explicit preprocessing step if preprocessCommand is non-empty -cmNinjaRule GetPreprocessScanRule( - const std::string& ruleName, cmRulePlaceholderExpander::RuleVariables& vars, +// Helper function to create dependency scanning rule that may or may +// not perform explicit preprocessing too. +cmNinjaRule GetScanRule( + const std::string& ruleName, + cmRulePlaceholderExpander::RuleVariables const& vars, const std::string& responseFlag, const std::string& flags, - const std::string& launcher, cmRulePlaceholderExpander* const rulePlaceholderExpander, - std::string scanCommand, cmLocalNinjaGenerator* generator, - const std::string& preprocessCommand = "") + cmLocalNinjaGenerator* generator, std::vector<std::string> scanCmds) { cmNinjaRule rule(ruleName); - // Explicit preprocessing always uses a depfile. + // Scanning always uses a depfile for preprocessor dependencies. rule.DepType = ""; // no deps= for multiple outputs rule.DepFile = "$DEP_FILE"; - cmRulePlaceholderExpander::RuleVariables ppVars; - ppVars.CMTargetName = vars.CMTargetName; - ppVars.CMTargetType = vars.CMTargetType; - ppVars.Language = vars.Language; - ppVars.Object = "$out"; // for RULE_LAUNCH_COMPILE - ppVars.PreprocessedSource = "$out"; - ppVars.DependencyFile = rule.DepFile.c_str(); - - // Preprocessing uses the original source, compilation uses - // preprocessed output or original source - ppVars.Source = vars.Source; - vars.Source = "$in"; - - // Copy preprocessor definitions to the preprocessor rule. - ppVars.Defines = vars.Defines; + cmRulePlaceholderExpander::RuleVariables scanVars; + scanVars.CMTargetName = vars.CMTargetName; + scanVars.CMTargetType = vars.CMTargetType; + scanVars.Language = vars.Language; + scanVars.Object = "$out"; // for RULE_LAUNCH_COMPILE + scanVars.PreprocessedSource = "$out"; + scanVars.DependencyFile = rule.DepFile.c_str(); - // Copy include directories to the preprocessor rule. The Fortran - // compilation rule still needs them for the INCLUDE directive. - ppVars.Includes = vars.Includes; + // Scanning needs the same preprocessor settings as direct compilation would. + scanVars.Source = vars.Source; + scanVars.Defines = vars.Defines; + scanVars.Includes = vars.Includes; - // Preprocessing and compilation use the same flags. - std::string ppFlags = flags; + // Scanning needs the compilation flags too. + std::string scanFlags = flags; // If using a response file, move defines, includes, and flags into it. if (!responseFlag.empty()) { rule.RspFile = "$RSP_FILE"; rule.RspContent = - cmStrCat(' ', ppVars.Defines, ' ', ppVars.Includes, ' ', ppFlags); - ppFlags = cmStrCat(responseFlag, rule.RspFile); - ppVars.Defines = ""; - ppVars.Includes = ""; + cmStrCat(' ', scanVars.Defines, ' ', scanVars.Includes, ' ', scanFlags); + scanFlags = cmStrCat(responseFlag, rule.RspFile); + scanVars.Defines = ""; + scanVars.Includes = ""; } - ppVars.Flags = ppFlags.c_str(); - - // Rule for preprocessing source file. - std::vector<std::string> ppCmds; + scanVars.Flags = scanFlags.c_str(); - if (!preprocessCommand.empty()) { - // Lookup the explicit preprocessing rule. - cmExpandList(preprocessCommand, ppCmds); - for (std::string& i : ppCmds) { - i = cmStrCat(launcher, i); - rulePlaceholderExpander->ExpandRuleVariables(generator, i, ppVars); - } + // Rule for scanning a source file. + for (std::string& scanCmd : scanCmds) { + rulePlaceholderExpander->ExpandRuleVariables(generator, scanCmd, scanVars); } - - // Run CMake dependency scanner on either preprocessed output or source file - ppCmds.emplace_back(std::move(scanCommand)); - rule.Command = generator->BuildCommandLine(ppCmds); + rule.Command = generator->BuildCommandLine(scanCmds); return rule; } @@ -637,11 +608,9 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, cmMakefile* mf = this->GetMakefile(); - // For some cases we do an explicit preprocessor invocation. - bool const explicitPP = this->NeedExplicitPreprocessing(lang); - bool const compilePPWithDefines = this->UsePreprocessedSource(lang) && - this->CompilePreprocessedSourceWithDefines(lang); + // For some cases we scan to dynamically discover dependencies. bool const needDyndep = this->NeedDyndep(lang); + bool const compilationPreprocesses = !this->NeedExplicitPreprocessing(lang); std::string flags = "$FLAGS"; @@ -664,56 +633,68 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, cmLocalGenerator::SHELL); std::string launcher; - const char* val = this->GetLocalGenerator()->GetRuleLauncher( + cmProp val = this->GetLocalGenerator()->GetRuleLauncher( this->GetGeneratorTarget(), "RULE_LAUNCH_COMPILE"); if (cmNonempty(val)) { - launcher = cmStrCat(val, ' '); + launcher = cmStrCat(*val, ' '); } std::string const cmakeCmd = this->GetLocalGenerator()->ConvertToOutputFormat( cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL); - if (explicitPP) { - // Combined preprocessing and dependency scanning - const auto ppScanCommand = GetScanCommand( - cmakeCmd, tdi, lang, "$out", needDyndep, "$DYNDEP_INTERMEDIATE_FILE"); - const auto ppVar = cmStrCat("CMAKE_", lang, "_PREPROCESS_SOURCE"); - - auto ppRule = GetPreprocessScanRule( - this->LanguagePreprocessRule(lang, config), vars, responseFlag, flags, - launcher, rulePlaceholderExpander.get(), ppScanCommand, - this->GetLocalGenerator(), mf->GetRequiredDefinition(ppVar)); + if (needDyndep) { + // Rule to scan dependencies of sources that need preprocessing. + { + std::vector<std::string> scanCommands; + std::string const& scanRuleName = + this->LanguagePreprocessAndScanRule(lang, config); + std::string const& ppCommmand = mf->GetRequiredDefinition( + cmStrCat("CMAKE_", lang, "_PREPROCESS_SOURCE")); + cmExpandList(ppCommmand, scanCommands); + for (std::string& i : scanCommands) { + i = cmStrCat(launcher, i); + } + scanCommands.emplace_back(GetScanCommand(cmakeCmd, tdi, lang, "$out", + "$DYNDEP_INTERMEDIATE_FILE")); - // Write the rule for preprocessing file of the given language. - ppRule.Comment = cmStrCat("Rule for preprocessing ", lang, " files."); - ppRule.Description = cmStrCat("Building ", lang, " preprocessed $out"); + auto scanRule = GetScanRule( + scanRuleName, vars, responseFlag, flags, rulePlaceholderExpander.get(), + this->GetLocalGenerator(), std::move(scanCommands)); - this->GetGlobalGenerator()->AddRule(ppRule); + scanRule.Comment = + cmStrCat("Rule for generating ", lang, " dependencies."); + scanRule.Description = cmStrCat("Building ", lang, " preprocessed $out"); - if (!compilePPWithDefines) { - // Remove preprocessor definitions from compilation step - vars.Defines = ""; + this->GetGlobalGenerator()->AddRule(scanRule); } - // Just dependency scanning for files that have preprocessing turned off - const auto scanCommand = - GetScanCommand(cmakeCmd, tdi, lang, "$in", needDyndep, "$out"); + { + // Compilation will not preprocess, so it does not need the defines + // unless the compiler wants them for some other purpose. + if (!this->CompileWithDefines(lang)) { + vars.Defines = ""; + } - auto scanRule = GetPreprocessScanRule( - this->LanguageDependencyRule(lang, config), vars, "", flags, launcher, - rulePlaceholderExpander.get(), scanCommand, this->GetLocalGenerator()); + // Rule to scan dependencies of sources that do not need preprocessing. + std::string const& scanRuleName = this->LanguageScanRule(lang, config); + std::vector<std::string> scanCommands; + scanCommands.emplace_back( + GetScanCommand(cmakeCmd, tdi, lang, "$in", "$out")); - // Write the rule for generating dependencies for the given language. - scanRule.Comment = cmStrCat("Rule for generating ", lang, - " dependencies on non-preprocessed files."); - scanRule.Description = - cmStrCat("Generating ", lang, " dependencies for $in"); + auto scanRule = GetScanRule( + scanRuleName, vars, "", flags, rulePlaceholderExpander.get(), + this->GetLocalGenerator(), std::move(scanCommands)); - this->GetGlobalGenerator()->AddRule(scanRule); - } + // Write the rule for generating dependencies for the given language. + scanRule.Comment = cmStrCat("Rule for generating ", lang, + " dependencies on non-preprocessed files."); + scanRule.Description = + cmStrCat("Generating ", lang, " dependencies for $in"); + + this->GetGlobalGenerator()->AddRule(scanRule); + } - if (needDyndep) { // Write the rule for ninja dyndep file generation. cmNinjaRule rule(this->LanguageDyndepRule(lang, config)); // Command line length is almost always limited -> use response file for @@ -752,8 +733,8 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, // Tell ninja dependency format so all deps can be loaded into a database std::string cldeps; - if (explicitPP) { - // The explicit preprocessing step will handle dependency scanning. + if (!compilationPreprocesses) { + // The compiler will not do preprocessing, so it has no such dependencies. } else if (this->NeedDepTypeMSVC(lang)) { rule.DepType = "msvc"; rule.DepFile.clear(); @@ -824,15 +805,21 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, } // Maybe insert an include-what-you-use runner. - if (!compileCmds.empty() && (lang == "C" || lang == "CXX")) { - std::string const iwyu_prop = cmStrCat(lang, "_INCLUDE_WHAT_YOU_USE"); - cmProp iwyu = this->GeneratorTarget->GetProperty(iwyu_prop); + if (!compileCmds.empty() && + (lang == "C" || lang == "CXX" || lang == "OBJC" || lang == "OBJCXX")) { std::string const tidy_prop = cmStrCat(lang, "_CLANG_TIDY"); cmProp tidy = this->GeneratorTarget->GetProperty(tidy_prop); - std::string const cpplint_prop = cmStrCat(lang, "_CPPLINT"); - cmProp cpplint = this->GeneratorTarget->GetProperty(cpplint_prop); - std::string const cppcheck_prop = cmStrCat(lang, "_CPPCHECK"); - cmProp cppcheck = this->GeneratorTarget->GetProperty(cppcheck_prop); + cmProp iwyu = nullptr; + cmProp cpplint = nullptr; + cmProp cppcheck = nullptr; + if (lang == "C" || lang == "CXX") { + std::string const iwyu_prop = cmStrCat(lang, "_INCLUDE_WHAT_YOU_USE"); + iwyu = this->GeneratorTarget->GetProperty(iwyu_prop); + std::string const cpplint_prop = cmStrCat(lang, "_CPPLINT"); + cpplint = this->GeneratorTarget->GetProperty(cpplint_prop); + std::string const cppcheck_prop = cmStrCat(lang, "_CPPCHECK"); + cppcheck = this->GeneratorTarget->GetProperty(cppcheck_prop); + } if (cmNonempty(iwyu) || cmNonempty(tidy) || cmNonempty(cpplint) || cmNonempty(cppcheck)) { std::string run_iwyu = cmStrCat(cmakeCmd, " -E __run_co_compile"); @@ -1071,78 +1058,81 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements( } namespace { -cmNinjaBuild GetPreprocessOrScanBuild( - const std::string& ruleName, const std::string& ppFileName, bool compilePP, - bool compilePPWithDefines, cmNinjaBuild& objBuild, cmNinjaVars& vars, - const std::string& depFileName, bool needDyndep, - const std::string& objectFileName) +cmNinjaBuild GetScanBuildStatement(const std::string& ruleName, + const std::string& ppFileName, + bool compilePP, bool compilePPWithDefines, + cmNinjaBuild& objBuild, cmNinjaVars& vars, + const std::string& objectFileName, + cmLocalGenerator* lg) { - // Explicit preprocessing and dependency - cmNinjaBuild ppBuild(ruleName); + cmNinjaBuild scanBuild(ruleName); if (!ppFileName.empty()) { - ppBuild.Outputs.push_back(ppFileName); - ppBuild.RspFile = cmStrCat(ppFileName, ".rsp"); + scanBuild.RspFile = cmStrCat(ppFileName, ".rsp"); } else { - ppBuild.RspFile = "$out.rsp"; + scanBuild.RspFile = "$out.rsp"; } if (compilePP) { - // Move compilation dependencies to the preprocessing build statement. - std::swap(ppBuild.ExplicitDeps, objBuild.ExplicitDeps); - std::swap(ppBuild.ImplicitDeps, objBuild.ImplicitDeps); - std::swap(ppBuild.OrderOnlyDeps, objBuild.OrderOnlyDeps); - std::swap(ppBuild.Variables["IN_ABS"], vars["IN_ABS"]); + // Move compilation dependencies to the scan/preprocessing build statement. + std::swap(scanBuild.ExplicitDeps, objBuild.ExplicitDeps); + std::swap(scanBuild.ImplicitDeps, objBuild.ImplicitDeps); + std::swap(scanBuild.OrderOnlyDeps, objBuild.OrderOnlyDeps); + std::swap(scanBuild.Variables["IN_ABS"], vars["IN_ABS"]); // The actual compilation will now use the preprocessed source. objBuild.ExplicitDeps.push_back(ppFileName); } else { - // Copy compilation dependencies to the preprocessing build statement. - ppBuild.ExplicitDeps = objBuild.ExplicitDeps; - ppBuild.ImplicitDeps = objBuild.ImplicitDeps; - ppBuild.OrderOnlyDeps = objBuild.OrderOnlyDeps; - ppBuild.Variables["IN_ABS"] = vars["IN_ABS"]; + // Copy compilation dependencies to the scan/preprocessing build statement. + scanBuild.ExplicitDeps = objBuild.ExplicitDeps; + scanBuild.ImplicitDeps = objBuild.ImplicitDeps; + scanBuild.OrderOnlyDeps = objBuild.OrderOnlyDeps; + scanBuild.Variables["IN_ABS"] = vars["IN_ABS"]; } - // Preprocessing and compilation generally use the same flags. - ppBuild.Variables["FLAGS"] = vars["FLAGS"]; + // Scanning and compilation generally use the same flags. + scanBuild.Variables["FLAGS"] = vars["FLAGS"]; if (compilePP && !compilePPWithDefines) { - // Move preprocessor definitions to the preprocessor build statement. - std::swap(ppBuild.Variables["DEFINES"], vars["DEFINES"]); + // Move preprocessor definitions to the scan/preprocessor build statement. + std::swap(scanBuild.Variables["DEFINES"], vars["DEFINES"]); } else { - // Copy preprocessor definitions to the preprocessor build statement. - ppBuild.Variables["DEFINES"] = vars["DEFINES"]; + // Copy preprocessor definitions to the scan/preprocessor build statement. + scanBuild.Variables["DEFINES"] = vars["DEFINES"]; } // Copy include directories to the preprocessor build statement. The // Fortran compilation build statement still needs them for the INCLUDE // directive. - ppBuild.Variables["INCLUDES"] = vars["INCLUDES"]; + scanBuild.Variables["INCLUDES"] = vars["INCLUDES"]; + + // Tell dependency scanner the object file that will result from + // compiling the source. + scanBuild.Variables["OBJ_FILE"] = objectFileName; + + // Tell dependency scanner where to store dyndep intermediate results. + std::string const& ddiFile = cmStrCat(objectFileName, ".ddi"); + scanBuild.Variables["DYNDEP_INTERMEDIATE_FILE"] = ddiFile; + + // Outputs of the scan/preprocessor build statement. + if (!ppFileName.empty()) { + scanBuild.Outputs.push_back(ppFileName); + scanBuild.ImplicitOuts.push_back(ddiFile); + } else { + scanBuild.Outputs.push_back(ddiFile); + } - // Explicit preprocessing always uses a depfile. - ppBuild.Variables["DEP_FILE"] = depFileName; + // Scanning always uses a depfile for preprocessor dependencies. + std::string const& depFileName = cmStrCat(scanBuild.Outputs.front(), ".d"); + scanBuild.Variables["DEP_FILE"] = + lg->ConvertToOutputFormat(depFileName, cmOutputConverter::SHELL); if (compilePP) { // The actual compilation does not need a depfile because it // depends on the already-preprocessed source. vars.erase("DEP_FILE"); } - if (needDyndep) { - // Tell dependency scanner the object file that will result from - // compiling the source. - ppBuild.Variables["OBJ_FILE"] = objectFileName; - - // Tell dependency scanner where to store dyndep intermediate results. - std::string const ddiFile = cmStrCat(objectFileName, ".ddi"); - if (ppFileName.empty()) { - ppBuild.Outputs.push_back(ddiFile); - } else { - ppBuild.Variables["DYNDEP_INTERMEDIATE_FILE"] = ddiFile; - ppBuild.ImplicitOuts.push_back(ddiFile); - } - } - return ppBuild; + return scanBuild; } } @@ -1279,13 +1269,12 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( sourceFileName, objBuild.OrderOnlyDeps); } - // For some cases we need to generate a ninja dyndep file. + // For some cases we scan to dynamically discover dependencies. bool const needDyndep = this->NeedDyndep(language); + bool const compilationPreprocesses = + !this->NeedExplicitPreprocessing(language); - // For some cases we do an explicit preprocessor invocation. - bool const explicitPP = this->NeedExplicitPreprocessing(language); - if (explicitPP) { - + if (needDyndep) { // If source/target has preprocessing turned off, we still need to // generate an explicit dependency step const auto srcpp = source->GetSafeProperty("Fortran_PREPROCESS"); @@ -1297,27 +1286,24 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( preprocess = cmOutputConverter::GetFortranPreprocess(tgtpp); } - bool const compilePP = this->UsePreprocessedSource(language) && + bool const compilePP = !compilationPreprocesses && (preprocess != cmOutputConverter::FortranPreprocess::NotNeeded); bool const compilePPWithDefines = - compilePP && this->CompilePreprocessedSourceWithDefines(language); - - std::string const ppFileName = compilePP - ? this->ConvertToNinjaPath(this->GetPreprocessedFilePath(source, config)) - : ""; + compilePP && this->CompileWithDefines(language); - std::string const buildName = compilePP - ? this->LanguagePreprocessRule(language, config) - : this->LanguageDependencyRule(language, config); - - const auto depExtension = compilePP ? ".pp.d" : ".d"; - const std::string depFileName = - this->GetLocalGenerator()->ConvertToOutputFormat( - cmStrCat(objectFileName, depExtension), cmOutputConverter::SHELL); + std::string scanRuleName; + std::string ppFileName; + if (compilePP) { + scanRuleName = this->LanguagePreprocessAndScanRule(language, config); + ppFileName = this->ConvertToNinjaPath( + this->GetPreprocessedFilePath(source, config)); + } else { + scanRuleName = this->LanguageScanRule(language, config); + } - cmNinjaBuild ppBuild = GetPreprocessOrScanBuild( - buildName, ppFileName, compilePP, compilePPWithDefines, objBuild, vars, - depFileName, needDyndep, objectFileName); + cmNinjaBuild ppBuild = GetScanBuildStatement( + scanRuleName, ppFileName, compilePP, compilePPWithDefines, objBuild, + vars, objectFileName, this->LocalGenerator); if (compilePP) { // In case compilation requires flags that are incompatible with @@ -1339,7 +1325,7 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( vars["INCLUDES"] = cmStrCat(sourceDirectoryFlag, ' ', vars["INCLUDES"]); } - if (firstForConfig && needDyndep) { + if (firstForConfig) { std::string const ddiFile = cmStrCat(objectFileName, ".ddi"); this->Configs[config].DDIFiles[language].push_back(ddiFile); } @@ -1349,8 +1335,7 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), ppBuild, commandLineLengthLimit); - } - if (needDyndep) { + std::string const dyndep = this->GetDyndepFilePath(language, config); objBuild.OrderOnlyDeps.push_back(dyndep); vars["dyndep"] = dyndep; diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index a27c9b4..4ba37ad 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -67,16 +67,15 @@ protected: std::string LanguageCompilerRule(const std::string& lang, const std::string& config) const; - std::string LanguagePreprocessRule(std::string const& lang, - const std::string& config) const; - std::string LanguageDependencyRule(std::string const& lang, - const std::string& config) const; - bool NeedExplicitPreprocessing(std::string const& lang) const; + std::string LanguagePreprocessAndScanRule(std::string const& lang, + const std::string& config) const; + std::string LanguageScanRule(std::string const& lang, + const std::string& config) const; std::string LanguageDyndepRule(std::string const& lang, const std::string& config) const; bool NeedDyndep(std::string const& lang) const; - bool UsePreprocessedSource(std::string const& lang) const; - bool CompilePreprocessedSourceWithDefines(std::string const& lang) const; + bool NeedExplicitPreprocessing(std::string const& lang) const; + bool CompileWithDefines(std::string const& lang) const; std::string OrderDependsTargetForTarget(const std::string& config); diff --git a/Source/cmPipeConnection.cxx b/Source/cmPipeConnection.cxx deleted file mode 100644 index 1eede13..0000000 --- a/Source/cmPipeConnection.cxx +++ /dev/null @@ -1,71 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmPipeConnection.h" - -#include <utility> - -#include "cmServer.h" - -cmPipeConnection::cmPipeConnection(std::string name, - cmConnectionBufferStrategy* bufferStrategy) - : cmEventBasedConnection(bufferStrategy) - , PipeName(std::move(name)) -{ -} - -void cmPipeConnection::Connect(uv_stream_t* server) -{ - if (this->WriteStream.get()) { - // Accept and close all pipes but the first: - cm::uv_pipe_ptr rejectPipe; - - rejectPipe.init(*this->Server->GetLoop(), 0); - uv_accept(server, rejectPipe); - - return; - } - - cm::uv_pipe_ptr ClientPipe; - ClientPipe.init(*this->Server->GetLoop(), 0, - static_cast<cmEventBasedConnection*>(this)); - - if (uv_accept(server, ClientPipe) != 0) { - return; - } - - uv_read_start(ClientPipe, on_alloc_buffer, on_read); - WriteStream = std::move(ClientPipe); - Server->OnConnected(this); -} - -bool cmPipeConnection::OnServeStart(std::string* errorMessage) -{ - this->ServerPipe.init(*this->Server->GetLoop(), 0, - static_cast<cmEventBasedConnection*>(this)); - - int r; - if ((r = uv_pipe_bind(this->ServerPipe, this->PipeName.c_str())) != 0) { - *errorMessage = std::string("Internal Error with ") + this->PipeName + - ": " + uv_err_name(r); - return false; - } - - if ((r = uv_listen(this->ServerPipe, 1, on_new_connection)) != 0) { - *errorMessage = std::string("Internal Error listening on ") + - this->PipeName + ": " + uv_err_name(r); - return false; - } - - return cmConnection::OnServeStart(errorMessage); -} - -bool cmPipeConnection::OnConnectionShuttingDown() -{ - if (this->WriteStream.get()) { - this->WriteStream->data = nullptr; - } - - this->ServerPipe.reset(); - - return cmEventBasedConnection::OnConnectionShuttingDown(); -} diff --git a/Source/cmPipeConnection.h b/Source/cmPipeConnection.h deleted file mode 100644 index 1215716..0000000 --- a/Source/cmPipeConnection.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#pragma once - -#include "cmConfigure.h" // IWYU pragma: keep - -#include <string> - -#include <cm3p/uv.h> - -#include "cmConnection.h" -#include "cmUVHandlePtr.h" - -class cmPipeConnection : public cmEventBasedConnection -{ -public: - cmPipeConnection(std::string name, - cmConnectionBufferStrategy* bufferStrategy = nullptr); - - bool OnServeStart(std::string* pString) override; - - bool OnConnectionShuttingDown() override; - - void Connect(uv_stream_t* server) override; - -private: - const std::string PipeName; - cm::uv_pipe_ptr ServerPipe; -}; diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index 18ce9c3..30cd4b7 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -340,7 +340,15 @@ class cmMakefile; 3, 19, 0, cmPolicies::WARN) \ SELECT(POLICY, CMP0114, \ "ExternalProject step targets fully adopt their steps.", 3, 19, 0, \ - cmPolicies::WARN) + cmPolicies::WARN) \ + SELECT(POLICY, CMP0115, "Source file extensions must be explicit.", 3, 20, \ + 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0116, \ + "Ninja generators transform DEPFILEs from add_custom_command().", 3, \ + 20, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0117, \ + "MSVC RTTI flag /GR is not added to CMAKE_CXX_FLAGS by default.", 3, \ + 20, 0, cmPolicies::WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) #define CM_FOR_EACH_POLICY_ID(POLICY) \ diff --git a/Source/cmProjectCommand.cxx b/Source/cmProjectCommand.cxx index 0cfba63..ed32de9 100644 --- a/Source/cmProjectCommand.cxx +++ b/Source/cmProjectCommand.cxx @@ -358,6 +358,17 @@ static bool IncludeByVariable(cmExecutionStatus& status, return true; } + std::string includeFile = + cmSystemTools::CollapseFullPath(*include, mf.GetCurrentSourceDirectory()); + if (!cmSystemTools::FileExists(includeFile)) { + status.SetError(cmStrCat("could not find requested file:\n ", *include)); + return false; + } + if (cmSystemTools::FileIsDirectory(includeFile)) { + status.SetError(cmStrCat("requested file is a directory:\n ", *include)); + return false; + } + const bool readit = mf.ReadDependentFile(*include); if (readit) { return true; @@ -367,7 +378,7 @@ static bool IncludeByVariable(cmExecutionStatus& status, return true; } - status.SetError(cmStrCat("could not find file:\n ", *include)); + status.SetError(cmStrCat("could not load requested file:\n ", *include)); return false; } diff --git a/Source/cmQTWrapCPPCommand.cxx b/Source/cmQTWrapCPPCommand.cxx index de462db..e058176 100644 --- a/Source/cmQTWrapCPPCommand.cxx +++ b/Source/cmQTWrapCPPCommand.cxx @@ -40,8 +40,7 @@ bool cmQTWrapCPPCommand(std::vector<std::string> const& args, cmStrCat(mf.GetCurrentBinaryDirectory(), "/moc_", srcName, ".cxx"); cmSourceFile* sf = mf.GetOrCreateSource(newName, true); if (curr) { - cmProp p = curr->GetProperty("ABSTRACT"); - sf->SetProperty("ABSTRACT", cmToCStr(p)); + sf->SetProperty("ABSTRACT", cmToCStr(curr->GetProperty("ABSTRACT"))); } // Compute the name of the header from which to generate the file. diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h index cf90417..2db1b84 100644 --- a/Source/cmQtAutoGen.h +++ b/Source/cmQtAutoGen.h @@ -29,13 +29,13 @@ public: { } - bool operator>(IntegerVersion const version) + bool operator>(IntegerVersion const version) const { return (this->Major > version.Major) || ((this->Major == version.Major) && (this->Minor > version.Minor)); } - bool operator>=(IntegerVersion const version) + bool operator>=(IntegerVersion const version) const { return (this->Major > version.Major) || ((this->Major == version.Major) && (this->Minor >= version.Minor)); diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index 3b62e9c..f2696a6 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -807,7 +807,7 @@ bool cmQtAutoGenInitializer::InitScanFiles() qrc.Generated = sf->GetIsGenerated(); // RCC options { - std::string const opts = sf->GetSafeProperty(kw.AUTORCC_OPTIONS); + std::string const& opts = sf->GetSafeProperty(kw.AUTORCC_OPTIONS); if (!opts.empty()) { cmExpandList(opts, qrc.Options); } diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx index 9cb172b..b27bb88 100644 --- a/Source/cmQtAutoMocUic.cxx +++ b/Source/cmQtAutoMocUic.cxx @@ -15,6 +15,7 @@ #include <vector> #include <cm/memory> +#include <cm/optional> #include <cm/string_view> #include <cmext/algorithm> @@ -26,7 +27,6 @@ #include "cmCryptoHash.h" #include "cmFileTime.h" #include "cmGccDepfileReader.h" -#include "cmGccDepfileReaderTypes.h" #include "cmGeneratedFileStream.h" #include "cmQtAutoGen.h" #include "cmQtAutoGenerator.h" @@ -2841,14 +2841,14 @@ bool cmQtAutoMocUicT::CreateDirectories() std::vector<std::string> cmQtAutoMocUicT::dependenciesFromDepFile( const char* filePath) { - cmGccDepfileContent content = cmReadGccDepfile(filePath); - if (content.empty()) { + auto const content = cmReadGccDepfile(filePath); + if (!content || content->empty()) { return {}; } // Moc outputs a depfile with exactly one rule. // Discard the rule and return the dependencies. - return content.front().paths; + return content->front().paths; } void cmQtAutoMocUicT::Abort(bool error) diff --git a/Source/cmServer.cxx b/Source/cmServer.cxx deleted file mode 100644 index 7f97406..0000000 --- a/Source/cmServer.cxx +++ /dev/null @@ -1,570 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmServer.h" - -#include <algorithm> -#include <cassert> -#include <csignal> -#include <cstdint> -#include <iostream> -#include <mutex> -#include <utility> - -#include <cm/memory> -#include <cm/shared_mutex> - -#include <cm3p/json/reader.h> -#include <cm3p/json/writer.h> - -#include "cmsys/FStream.hxx" - -#include "cmConnection.h" -#include "cmFileMonitor.h" -#include "cmJsonObjectDictionary.h" -#include "cmServerDictionary.h" -#include "cmServerProtocol.h" -#include "cmSystemTools.h" -#include "cmake.h" - -void on_signal(uv_signal_t* signal, int signum) -{ - auto conn = static_cast<cmServerBase*>(signal->data); - conn->OnSignal(signum); -} - -static void on_walk_to_shutdown(uv_handle_t* handle, void* arg) -{ - (void)arg; - assert(uv_is_closing(handle)); - if (!uv_is_closing(handle)) { - uv_close(handle, &cmEventBasedConnection::on_close); - } -} - -class cmServer::DebugInfo -{ -public: - DebugInfo() - : StartTime(uv_hrtime()) - { - } - - bool PrintStatistics = false; - - std::string OutputFile; - uint64_t StartTime; -}; - -cmServer::cmServer(cmConnection* conn, bool supportExperimental) - : cmServerBase(conn) - , SupportExperimental(supportExperimental) -{ - // Register supported protocols: - this->RegisterProtocol(cm::make_unique<cmServerProtocol1>()); -} - -cmServer::~cmServer() -{ - Close(); -} - -void cmServer::ProcessRequest(cmConnection* connection, - const std::string& input) -{ - Json::Reader reader; - Json::Value value; - if (!reader.parse(input, value)) { - this->WriteParseError(connection, "Failed to parse JSON input."); - return; - } - - std::unique_ptr<DebugInfo> debug; - Json::Value debugValue = value["debug"]; - if (!debugValue.isNull()) { - debug = cm::make_unique<DebugInfo>(); - debug->OutputFile = debugValue["dumpToFile"].asString(); - debug->PrintStatistics = debugValue["showStats"].asBool(); - } - - const cmServerRequest request(this, connection, value[kTYPE_KEY].asString(), - value[kCOOKIE_KEY].asString(), value); - - if (request.Type.empty()) { - cmServerResponse response(request); - response.SetError("No type given in request."); - this->WriteResponse(connection, response, nullptr); - return; - } - - cmSystemTools::SetMessageCallback( - [&request](const std::string& msg, const char* title) { - reportMessage(msg, title, request); - }); - - if (this->Protocol) { - this->Protocol->CMakeInstance()->SetProgressCallback( - [&request](const std::string& msg, float prog) { - reportProgress(msg, prog, request); - }); - this->WriteResponse(connection, this->Protocol->Process(request), - debug.get()); - } else { - this->WriteResponse(connection, this->SetProtocolVersion(request), - debug.get()); - } -} - -void cmServer::RegisterProtocol(std::unique_ptr<cmServerProtocol> protocol) -{ - if (protocol->IsExperimental() && !this->SupportExperimental) { - protocol.reset(); - return; - } - auto version = protocol->ProtocolVersion(); - assert(version.first >= 0); - assert(version.second >= 0); - auto it = std::find_if( - this->SupportedProtocols.begin(), this->SupportedProtocols.end(), - [version](const std::unique_ptr<cmServerProtocol>& p) { - return p->ProtocolVersion() == version; - }); - if (it == this->SupportedProtocols.end()) { - this->SupportedProtocols.push_back(std::move(protocol)); - } -} - -void cmServer::PrintHello(cmConnection* connection) const -{ - Json::Value hello = Json::objectValue; - hello[kTYPE_KEY] = "hello"; - - Json::Value& protocolVersions = hello[kSUPPORTED_PROTOCOL_VERSIONS] = - Json::arrayValue; - - for (auto const& proto : this->SupportedProtocols) { - auto version = proto->ProtocolVersion(); - Json::Value tmp = Json::objectValue; - tmp[kMAJOR_KEY] = version.first; - tmp[kMINOR_KEY] = version.second; - if (proto->IsExperimental()) { - tmp[kIS_EXPERIMENTAL_KEY] = true; - } - protocolVersions.append(tmp); - } - - this->WriteJsonObject(connection, hello, nullptr); -} - -void cmServer::reportProgress(const std::string& msg, float progress, - const cmServerRequest& request) -{ - if (progress < 0.0f || progress > 1.0f) { - request.ReportMessage(msg, ""); - } else { - request.ReportProgress(0, static_cast<int>(progress * 1000), 1000, msg); - } -} - -void cmServer::reportMessage(const std::string& msg, const char* title, - const cmServerRequest& request) -{ - std::string titleString; - if (title) { - titleString = title; - } - request.ReportMessage(msg, titleString); -} - -cmServerResponse cmServer::SetProtocolVersion(const cmServerRequest& request) -{ - if (request.Type != kHANDSHAKE_TYPE) { - return request.ReportError("Waiting for type \"" + kHANDSHAKE_TYPE + - "\"."); - } - - Json::Value requestedProtocolVersion = request.Data[kPROTOCOL_VERSION_KEY]; - if (requestedProtocolVersion.isNull()) { - return request.ReportError("\"" + kPROTOCOL_VERSION_KEY + - "\" is required for \"" + kHANDSHAKE_TYPE + - "\"."); - } - - if (!requestedProtocolVersion.isObject()) { - return request.ReportError("\"" + kPROTOCOL_VERSION_KEY + - "\" must be a JSON object."); - } - - Json::Value majorValue = requestedProtocolVersion[kMAJOR_KEY]; - if (!majorValue.isInt()) { - return request.ReportError("\"" + kMAJOR_KEY + - "\" must be set and an integer."); - } - - Json::Value minorValue = requestedProtocolVersion[kMINOR_KEY]; - if (!minorValue.isNull() && !minorValue.isInt()) { - return request.ReportError("\"" + kMINOR_KEY + - "\" must be unset or an integer."); - } - - const int major = majorValue.asInt(); - const int minor = minorValue.isNull() ? -1 : minorValue.asInt(); - if (major < 0) { - return request.ReportError("\"" + kMAJOR_KEY + "\" must be >= 0."); - } - if (!minorValue.isNull() && minor < 0) { - return request.ReportError("\"" + kMINOR_KEY + - "\" must be >= 0 when set."); - } - - this->Protocol = - cmServer::FindMatchingProtocol(this->SupportedProtocols, major, minor); - if (!this->Protocol) { - return request.ReportError("Protocol version not supported."); - } - - std::string errorMessage; - if (!this->Protocol->Activate(this, request, &errorMessage)) { - this->Protocol = nullptr; - return request.ReportError("Failed to activate protocol version: " + - errorMessage); - } - return request.Reply(Json::objectValue); -} - -bool cmServer::Serve(std::string* errorMessage) -{ - if (this->SupportedProtocols.empty()) { - *errorMessage = - "No protocol versions defined. Maybe you need --experimental?"; - return false; - } - assert(!this->Protocol); - - return cmServerBase::Serve(errorMessage); -} - -cmFileMonitor* cmServer::FileMonitor() const -{ - return fileMonitor.get(); -} - -void cmServer::WriteJsonObject(const Json::Value& jsonValue, - const DebugInfo* debug) const -{ - cm::shared_lock<cm::shared_mutex> lock(ConnectionsMutex); - for (auto& connection : this->Connections) { - WriteJsonObject(connection.get(), jsonValue, debug); - } -} - -void cmServer::WriteJsonObject(cmConnection* connection, - const Json::Value& jsonValue, - const DebugInfo* debug) const -{ - Json::FastWriter writer; - - auto beforeJson = uv_hrtime(); - std::string result = writer.write(jsonValue); - - if (debug) { - Json::Value copy = jsonValue; - if (debug->PrintStatistics) { - Json::Value stats = Json::objectValue; - auto endTime = uv_hrtime(); - - stats["jsonSerialization"] = double(endTime - beforeJson) / 1000000.0; - stats["totalTime"] = double(endTime - debug->StartTime) / 1000000.0; - stats["size"] = static_cast<int>(result.size()); - if (!debug->OutputFile.empty()) { - stats["dumpFile"] = debug->OutputFile; - } - - copy["zzzDebug"] = stats; - - result = writer.write(copy); // Update result to include debug info - } - - if (!debug->OutputFile.empty()) { - cmsys::ofstream myfile(debug->OutputFile.c_str()); - myfile << result; - } - } - - connection->WriteData(result); -} - -cmServerProtocol* cmServer::FindMatchingProtocol( - const std::vector<std::unique_ptr<cmServerProtocol>>& protocols, int major, - int minor) -{ - cmServerProtocol* bestMatch = nullptr; - for (const auto& protocol : protocols) { - auto version = protocol->ProtocolVersion(); - if (major != version.first) { - continue; - } - if (minor == version.second) { - return protocol.get(); - } - if (!bestMatch || bestMatch->ProtocolVersion().second < version.second) { - bestMatch = protocol.get(); - } - } - return minor < 0 ? bestMatch : nullptr; -} - -void cmServer::WriteProgress(const cmServerRequest& request, int min, - int current, int max, - const std::string& message) const -{ - assert(min <= current && current <= max); - assert(message.length() != 0); - - Json::Value obj = Json::objectValue; - obj[kTYPE_KEY] = kPROGRESS_TYPE; - obj[kREPLY_TO_KEY] = request.Type; - obj[kCOOKIE_KEY] = request.Cookie; - obj[kPROGRESS_MESSAGE_KEY] = message; - obj[kPROGRESS_MINIMUM_KEY] = min; - obj[kPROGRESS_MAXIMUM_KEY] = max; - obj[kPROGRESS_CURRENT_KEY] = current; - - this->WriteJsonObject(request.Connection, obj, nullptr); -} - -void cmServer::WriteMessage(const cmServerRequest& request, - const std::string& message, - const std::string& title) const -{ - if (message.empty()) { - return; - } - - Json::Value obj = Json::objectValue; - obj[kTYPE_KEY] = kMESSAGE_TYPE; - obj[kREPLY_TO_KEY] = request.Type; - obj[kCOOKIE_KEY] = request.Cookie; - obj[kMESSAGE_KEY] = message; - if (!title.empty()) { - obj[kTITLE_KEY] = title; - } - - WriteJsonObject(request.Connection, obj, nullptr); -} - -void cmServer::WriteParseError(cmConnection* connection, - const std::string& message) const -{ - Json::Value obj = Json::objectValue; - obj[kTYPE_KEY] = kERROR_TYPE; - obj[kERROR_MESSAGE_KEY] = message; - obj[kREPLY_TO_KEY] = ""; - obj[kCOOKIE_KEY] = ""; - - this->WriteJsonObject(connection, obj, nullptr); -} - -void cmServer::WriteSignal(const std::string& name, - const Json::Value& data) const -{ - assert(data.isObject()); - Json::Value obj = data; - obj[kTYPE_KEY] = kSIGNAL_TYPE; - obj[kREPLY_TO_KEY] = ""; - obj[kCOOKIE_KEY] = ""; - obj[kNAME_KEY] = name; - - WriteJsonObject(obj, nullptr); -} - -void cmServer::WriteResponse(cmConnection* connection, - const cmServerResponse& response, - const DebugInfo* debug) const -{ - assert(response.IsComplete()); - - Json::Value obj = response.Data(); - obj[kCOOKIE_KEY] = response.Cookie; - obj[kTYPE_KEY] = response.IsError() ? kERROR_TYPE : kREPLY_TYPE; - obj[kREPLY_TO_KEY] = response.Type; - if (response.IsError()) { - obj[kERROR_MESSAGE_KEY] = response.ErrorMessage(); - } - - this->WriteJsonObject(connection, obj, debug); -} - -void cmServer::OnConnected(cmConnection* connection) -{ - PrintHello(connection); -} - -void cmServer::OnServeStart() -{ - cmServerBase::OnServeStart(); - fileMonitor = std::make_shared<cmFileMonitor>(GetLoop()); -} - -void cmServer::StartShutDown() -{ - if (fileMonitor) { - fileMonitor->StopMonitoring(); - fileMonitor.reset(); - } - cmServerBase::StartShutDown(); -} - -static void __start_thread(void* arg) -{ - auto server = static_cast<cmServerBase*>(arg); - std::string error; - bool success = server->Serve(&error); - if (!success || !error.empty()) { - std::cerr << "Error during serve: " << error << std::endl; - } -} - -bool cmServerBase::StartServeThread() -{ - ServeThreadRunning = true; - uv_thread_create(&ServeThread, __start_thread, this); - return true; -} - -static void __shutdownThread(uv_async_t* arg) -{ - auto server = static_cast<cmServerBase*>(arg->data); - server->StartShutDown(); -} - -bool cmServerBase::Serve(std::string* errorMessage) -{ -#ifndef NDEBUG - uv_thread_t blank_thread_t = {}; - assert(uv_thread_equal(&blank_thread_t, &ServeThreadId)); - ServeThreadId = uv_thread_self(); -#endif - - errorMessage->clear(); - - ShutdownSignal.init(Loop, __shutdownThread, this); - - SIGINTHandler.init(Loop, this); - SIGHUPHandler.init(Loop, this); - - SIGINTHandler.start(&on_signal, SIGINT); - SIGHUPHandler.start(&on_signal, SIGHUP); - - OnServeStart(); - - { - cm::shared_lock<cm::shared_mutex> lock(ConnectionsMutex); - for (auto& connection : Connections) { - if (!connection->OnServeStart(errorMessage)) { - return false; - } - } - } - - if (uv_run(&Loop, UV_RUN_DEFAULT) != 0) { - // It is important we don't ever let the event loop exit with open handles - // at best this is a memory leak, but it can also introduce race conditions - // which can hang the program. - assert(false && "Event loop stopped in unclean state."); - - *errorMessage = "Internal Error: Event loop stopped in unclean state."; - return false; - } - - return true; -} - -void cmServerBase::OnConnected(cmConnection*) -{ -} - -void cmServerBase::OnServeStart() -{ -} - -void cmServerBase::StartShutDown() -{ - ShutdownSignal.reset(); - SIGINTHandler.reset(); - SIGHUPHandler.reset(); - - { - std::unique_lock<cm::shared_mutex> lock(ConnectionsMutex); - for (auto& connection : Connections) { - connection->OnConnectionShuttingDown(); - } - Connections.clear(); - } - - uv_walk(&Loop, on_walk_to_shutdown, nullptr); -} - -bool cmServerBase::OnSignal(int signum) -{ - (void)signum; - StartShutDown(); - return true; -} - -cmServerBase::cmServerBase(cmConnection* connection) -{ - auto err = uv_loop_init(&Loop); - (void)err; - Loop.data = this; - assert(err == 0); - - AddNewConnection(connection); -} - -void cmServerBase::Close() -{ - if (Loop.data) { - if (ServeThreadRunning) { - this->ShutdownSignal.send(); - uv_thread_join(&ServeThread); - } - - uv_loop_close(&Loop); - Loop.data = nullptr; - } -} -cmServerBase::~cmServerBase() -{ - Close(); -} - -void cmServerBase::AddNewConnection(cmConnection* ownedConnection) -{ - { - std::unique_lock<cm::shared_mutex> lock(ConnectionsMutex); - Connections.emplace_back(ownedConnection); - } - ownedConnection->SetServer(this); -} - -uv_loop_t* cmServerBase::GetLoop() -{ - return &Loop; -} - -void cmServerBase::OnDisconnect(cmConnection* pConnection) -{ - auto pred = [pConnection](const std::unique_ptr<cmConnection>& m) { - return m.get() == pConnection; - }; - { - std::unique_lock<cm::shared_mutex> lock(ConnectionsMutex); - Connections.erase( - std::remove_if(Connections.begin(), Connections.end(), pred), - Connections.end()); - } - - if (Connections.empty()) { - this->ShutdownSignal.send(); - } -} diff --git a/Source/cmServer.h b/Source/cmServer.h deleted file mode 100644 index 9543329..0000000 --- a/Source/cmServer.h +++ /dev/null @@ -1,162 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#pragma once - -#include "cmConfigure.h" // IWYU pragma: keep - -#include <memory> -#include <string> -#include <vector> - -#include <cm/shared_mutex> - -#include <cm3p/json/value.h> -#include <cm3p/uv.h> - -#include "cmUVHandlePtr.h" - -class cmConnection; -class cmFileMonitor; -class cmServerProtocol; -class cmServerRequest; -class cmServerResponse; - -/*** - * This essentially hold and manages a libuv event queue and responds to - * messages - * on any of its connections. - */ -class cmServerBase -{ -public: - cmServerBase(cmConnection* connection); - virtual ~cmServerBase(); - - virtual void AddNewConnection(cmConnection* ownedConnection); - - /*** - * The main override responsible for tailoring behavior towards - * whatever the given server is supposed to do - * - * This should almost always be called by the given connections - * directly. - * - * @param connection The connection the request was received on - * @param request The actual request - */ - virtual void ProcessRequest(cmConnection* connection, - const std::string& request) = 0; - virtual void OnConnected(cmConnection* connection); - - /*** - * Start a dedicated thread. If this is used to start the server, it will - * join on the - * servers dtor. - */ - virtual bool StartServeThread(); - virtual bool Serve(std::string* errorMessage); - - virtual void OnServeStart(); - virtual void StartShutDown(); - - virtual bool OnSignal(int signum); - uv_loop_t* GetLoop(); - void Close(); - void OnDisconnect(cmConnection* pConnection); - -protected: - mutable cm::shared_mutex ConnectionsMutex; - std::vector<std::unique_ptr<cmConnection>> Connections; - - bool ServeThreadRunning = false; - uv_thread_t ServeThread; - cm::uv_async_ptr ShutdownSignal; -#ifndef NDEBUG -public: - // When the server starts it will mark down it's current thread ID, - // which is useful in other contexts to just assert that operations - // are performed on that same thread. - uv_thread_t ServeThreadId = {}; - -protected: -#endif - - uv_loop_t Loop; - - cm::uv_signal_ptr SIGINTHandler; - cm::uv_signal_ptr SIGHUPHandler; -}; - -class cmServer : public cmServerBase -{ -public: - class DebugInfo; - - cmServer(cmConnection* conn, bool supportExperimental); - ~cmServer() override; - - cmServer(cmServer const&) = delete; - cmServer& operator=(cmServer const&) = delete; - - bool Serve(std::string* errorMessage) override; - - cmFileMonitor* FileMonitor() const; - -private: - void RegisterProtocol(std::unique_ptr<cmServerProtocol> protocol); - - // Callbacks from cmServerConnection: - - void ProcessRequest(cmConnection* connection, - const std::string& request) override; - std::shared_ptr<cmFileMonitor> fileMonitor; - -public: - void OnServeStart() override; - - void StartShutDown() override; - -public: - void OnConnected(cmConnection* connection) override; - -private: - static void reportProgress(const std::string& msg, float progress, - const cmServerRequest& request); - static void reportMessage(const std::string& msg, const char* title, - const cmServerRequest& request); - - // Handle requests: - cmServerResponse SetProtocolVersion(const cmServerRequest& request); - - void PrintHello(cmConnection* connection) const; - - // Write responses: - void WriteProgress(const cmServerRequest& request, int min, int current, - int max, const std::string& message) const; - void WriteMessage(const cmServerRequest& request, const std::string& message, - const std::string& title) const; - void WriteResponse(cmConnection* connection, - const cmServerResponse& response, - const DebugInfo* debug) const; - void WriteParseError(cmConnection* connection, - const std::string& message) const; - void WriteSignal(const std::string& name, const Json::Value& obj) const; - - void WriteJsonObject(Json::Value const& jsonValue, - const DebugInfo* debug) const; - - void WriteJsonObject(cmConnection* connection, Json::Value const& jsonValue, - const DebugInfo* debug) const; - - static cmServerProtocol* FindMatchingProtocol( - const std::vector<std::unique_ptr<cmServerProtocol>>& protocols, int major, - int minor); - - const bool SupportExperimental; - - cmServerProtocol* Protocol = nullptr; - std::vector<std::unique_ptr<cmServerProtocol>> SupportedProtocols; - - friend class cmServerProtocol; - friend class cmServerRequest; -}; diff --git a/Source/cmServerConnection.cxx b/Source/cmServerConnection.cxx deleted file mode 100644 index b4f41a0..0000000 --- a/Source/cmServerConnection.cxx +++ /dev/null @@ -1,165 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmConfigure.h" - -#include "cmServerConnection.h" - -#include <cm3p/uv.h> - -#include "cmServer.h" -#include "cmServerDictionary.h" - -#ifdef _WIN32 -# include "io.h" -#else -# include <unistd.h> -#endif -#include <cassert> -#include <utility> - -cmStdIoConnection::cmStdIoConnection( - cmConnectionBufferStrategy* bufferStrategy) - : cmEventBasedConnection(bufferStrategy) -{ -} - -cm::uv_stream_ptr cmStdIoConnection::SetupStream(int file_id) -{ - switch (uv_guess_handle(file_id)) { - case UV_TTY: { - cm::uv_tty_ptr tty; - tty.init(*this->Server->GetLoop(), file_id, file_id == 0, - static_cast<cmEventBasedConnection*>(this)); - uv_tty_set_mode(tty, UV_TTY_MODE_NORMAL); - return { std::move(tty) }; - } - case UV_FILE: - if (file_id == 0) { - return nullptr; - } - // Intentional fallthrough; stdin can _not_ be treated as a named - // pipe, however stdout can be. - CM_FALLTHROUGH; - case UV_NAMED_PIPE: { - cm::uv_pipe_ptr pipe; - pipe.init(*this->Server->GetLoop(), 0, - static_cast<cmEventBasedConnection*>(this)); - uv_pipe_open(pipe, file_id); - return { std::move(pipe) }; - } - default: - assert(false && "Unable to determine stream type"); - return nullptr; - } -} - -void cmStdIoConnection::SetServer(cmServerBase* s) -{ - cmConnection::SetServer(s); - if (!s) { - return; - } - - this->ReadStream = SetupStream(0); - this->WriteStream = SetupStream(1); -} - -void shutdown_connection(uv_prepare_t* prepare) -{ - cmStdIoConnection* connection = - static_cast<cmStdIoConnection*>(prepare->data); - - if (!uv_is_closing(reinterpret_cast<uv_handle_t*>(prepare))) { - uv_close(reinterpret_cast<uv_handle_t*>(prepare), - &cmEventBasedConnection::on_close_delete<uv_prepare_t>); - } - connection->OnDisconnect(0); -} - -bool cmStdIoConnection::OnServeStart(std::string* pString) -{ - Server->OnConnected(this); - if (this->ReadStream.get()) { - uv_read_start(this->ReadStream, on_alloc_buffer, on_read); - } else if (uv_guess_handle(0) == UV_FILE) { - char buffer[1024]; - while (auto len = read(0, buffer, sizeof(buffer))) { - ReadData(std::string(buffer, buffer + len)); - } - - // We can't start the disconnect from here, add a prepare hook to do that - // for us - auto prepare = new uv_prepare_t(); - prepare->data = this; - uv_prepare_init(Server->GetLoop(), prepare); - uv_prepare_start(prepare, shutdown_connection); - } - return cmConnection::OnServeStart(pString); -} - -bool cmStdIoConnection::OnConnectionShuttingDown() -{ - if (ReadStream.get()) { - uv_read_stop(ReadStream); - ReadStream->data = nullptr; - } - - this->ReadStream.reset(); - - cmEventBasedConnection::OnConnectionShuttingDown(); - - return true; -} - -cmServerPipeConnection::cmServerPipeConnection(const std::string& name) - : cmPipeConnection(name, new cmServerBufferStrategy) -{ -} - -cmServerStdIoConnection::cmServerStdIoConnection() - : cmStdIoConnection(new cmServerBufferStrategy) -{ -} - -cmConnectionBufferStrategy::~cmConnectionBufferStrategy() = default; - -void cmConnectionBufferStrategy::clear() -{ -} - -std::string cmServerBufferStrategy::BufferOutMessage( - const std::string& rawBuffer) const -{ - return std::string("\n") + kSTART_MAGIC + std::string("\n") + rawBuffer + - kEND_MAGIC + std::string("\n"); -} - -std::string cmServerBufferStrategy::BufferMessage(std::string& RawReadBuffer) -{ - for (;;) { - auto needle = RawReadBuffer.find('\n'); - - if (needle == std::string::npos) { - return ""; - } - std::string line = RawReadBuffer.substr(0, needle); - const auto ls = line.size(); - if (ls > 1 && line.at(ls - 1) == '\r') { - line.erase(ls - 1, 1); - } - RawReadBuffer.erase(RawReadBuffer.begin(), - RawReadBuffer.begin() + static_cast<long>(needle) + 1); - if (line == kSTART_MAGIC) { - RequestBuffer.clear(); - continue; - } - if (line == kEND_MAGIC) { - std::string rtn; - rtn.swap(this->RequestBuffer); - return rtn; - } - - this->RequestBuffer += line; - this->RequestBuffer += "\n"; - } -} diff --git a/Source/cmServerConnection.h b/Source/cmServerConnection.h deleted file mode 100644 index a70edb4..0000000 --- a/Source/cmServerConnection.h +++ /dev/null @@ -1,67 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#pragma once - -#include "cmConfigure.h" // IWYU pragma: keep - -#include <string> - -#include "cmConnection.h" -#include "cmPipeConnection.h" -#include "cmUVHandlePtr.h" - -class cmServerBase; - -/*** - * This connection buffer strategy accepts messages in the form of - * [== "CMake Server" ==[ -{ - ... some JSON message ... -} -]== "CMake Server" ==] - * and only passes on the core json; it discards the envelope. - */ -class cmServerBufferStrategy : public cmConnectionBufferStrategy -{ -public: - std::string BufferMessage(std::string& rawBuffer) override; - std::string BufferOutMessage(const std::string& rawBuffer) const override; - -private: - std::string RequestBuffer; -}; - -/*** - * Generic connection over std io interfaces -- tty - */ -class cmStdIoConnection : public cmEventBasedConnection -{ -public: - cmStdIoConnection(cmConnectionBufferStrategy* bufferStrategy); - - void SetServer(cmServerBase* s) override; - - bool OnConnectionShuttingDown() override; - - bool OnServeStart(std::string* pString) override; - -private: - cm::uv_stream_ptr SetupStream(int file_id); - cm::uv_stream_ptr ReadStream; -}; - -/*** - * These specific connections use the cmake server - * buffering strategy. - */ -class cmServerStdIoConnection : public cmStdIoConnection -{ -public: - cmServerStdIoConnection(); -}; - -class cmServerPipeConnection : public cmPipeConnection -{ -public: - cmServerPipeConnection(const std::string& name); -}; diff --git a/Source/cmServerDictionary.h b/Source/cmServerDictionary.h deleted file mode 100644 index 961e4b7..0000000 --- a/Source/cmServerDictionary.h +++ /dev/null @@ -1,67 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#pragma once - -#include <string> - -// Vocabulary: - -static const std::string kDIRTY_SIGNAL = "dirty"; -static const std::string kFILE_CHANGE_SIGNAL = "fileChange"; - -static const std::string kCACHE_TYPE = "cache"; -static const std::string kCMAKE_INPUTS_TYPE = "cmakeInputs"; -static const std::string kCODE_MODEL_TYPE = "codemodel"; -static const std::string kCOMPUTE_TYPE = "compute"; -static const std::string kCONFIGURE_TYPE = "configure"; -static const std::string kERROR_TYPE = "error"; -static const std::string kFILESYSTEM_WATCHERS_TYPE = "fileSystemWatchers"; -static const std::string kGLOBAL_SETTINGS_TYPE = "globalSettings"; -static const std::string kHANDSHAKE_TYPE = "handshake"; -static const std::string kMESSAGE_TYPE = "message"; -static const std::string kPROGRESS_TYPE = "progress"; -static const std::string kREPLY_TYPE = "reply"; -static const std::string kSET_GLOBAL_SETTINGS_TYPE = "setGlobalSettings"; -static const std::string kSIGNAL_TYPE = "signal"; -static const std::string kCTEST_INFO_TYPE = "ctestInfo"; - -static const std::string kBUILD_FILES_KEY = "buildFiles"; -static const std::string kCACHE_ARGUMENTS_KEY = "cacheArguments"; -static const std::string kCACHE_KEY = "cache"; -static const std::string kCAPABILITIES_KEY = "capabilities"; -static const std::string kCHECK_SYSTEM_VARS_KEY = "checkSystemVars"; -static const std::string kCMAKE_ROOT_DIRECTORY_KEY = "cmakeRootDirectory"; -static const std::string kCOOKIE_KEY = "cookie"; -static const std::string kDEBUG_OUTPUT_KEY = "debugOutput"; -static const std::string kERROR_MESSAGE_KEY = "errorMessage"; -static const std::string kEXTRA_GENERATOR_KEY = "extraGenerator"; -static const std::string kGENERATOR_KEY = "generator"; -static const std::string kIS_EXPERIMENTAL_KEY = "isExperimental"; -static const std::string kKEYS_KEY = "keys"; -static const std::string kMAJOR_KEY = "major"; -static const std::string kMESSAGE_KEY = "message"; -static const std::string kMINOR_KEY = "minor"; -static const std::string kPLATFORM_KEY = "platform"; -static const std::string kPROGRESS_CURRENT_KEY = "progressCurrent"; -static const std::string kPROGRESS_MAXIMUM_KEY = "progressMaximum"; -static const std::string kPROGRESS_MESSAGE_KEY = "progressMessage"; -static const std::string kPROGRESS_MINIMUM_KEY = "progressMinimum"; -static const std::string kPROTOCOL_VERSION_KEY = "protocolVersion"; -static const std::string kREPLY_TO_KEY = "inReplyTo"; -static const std::string kSUPPORTED_PROTOCOL_VERSIONS = - "supportedProtocolVersions"; -static const std::string kTITLE_KEY = "title"; -static const std::string kTOOLSET_KEY = "toolset"; -static const std::string kTRACE_EXPAND_KEY = "traceExpand"; -static const std::string kTRACE_KEY = "trace"; -static const std::string kWARN_UNINITIALIZED_KEY = "warnUninitialized"; -static const std::string kWARN_UNUSED_CLI_KEY = "warnUnusedCli"; -static const std::string kWARN_UNUSED_KEY = "warnUnused"; -static const std::string kWATCHED_DIRECTORIES_KEY = "watchedDirectories"; -static const std::string kWATCHED_FILES_KEY = "watchedFiles"; - -static const std::string kSTART_MAGIC = "[== \"CMake Server\" ==["; -static const std::string kEND_MAGIC = "]== \"CMake Server\" ==]"; - -static const std::string kRENAME_PROPERTY_VALUE = "rename"; -static const std::string kCHANGE_PROPERTY_VALUE = "change"; diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx deleted file mode 100644 index e586fd9..0000000 --- a/Source/cmServerProtocol.cxx +++ /dev/null @@ -1,760 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmServerProtocol.h" - -#include <algorithm> -#include <cassert> -#include <functional> -#include <string> -#include <utility> -#include <vector> - -#include <cm/memory> -#include <cmext/algorithm> - -#include <cm3p/uv.h> - -#include "cmExternalMakefileProjectGenerator.h" -#include "cmFileMonitor.h" -#include "cmGlobalGenerator.h" -#include "cmJsonObjectDictionary.h" -#include "cmJsonObjects.h" -#include "cmMessageType.h" -#include "cmProperty.h" -#include "cmServer.h" -#include "cmServerDictionary.h" -#include "cmState.h" -#include "cmSystemTools.h" -#include "cmake.h" - -// Get rid of some windows macros: -#undef max - -namespace { - -std::vector<std::string> toStringList(const Json::Value& in) -{ - std::vector<std::string> result; - for (auto const& it : in) { - result.push_back(it.asString()); - } - return result; -} - -} // namespace - -cmServerRequest::cmServerRequest(cmServer* server, cmConnection* connection, - std::string t, std::string c, Json::Value d) - : Type(std::move(t)) - , Cookie(std::move(c)) - , Data(std::move(d)) - , Connection(connection) - , m_Server(server) -{ -} - -void cmServerRequest::ReportProgress(int min, int current, int max, - const std::string& message) const -{ - this->m_Server->WriteProgress(*this, min, current, max, message); -} - -void cmServerRequest::ReportMessage(const std::string& message, - const std::string& title) const -{ - m_Server->WriteMessage(*this, message, title); -} - -cmServerResponse cmServerRequest::Reply(const Json::Value& data) const -{ - cmServerResponse response(*this); - response.SetData(data); - return response; -} - -cmServerResponse cmServerRequest::ReportError(const std::string& message) const -{ - cmServerResponse response(*this); - response.SetError(message); - return response; -} - -cmServerResponse::cmServerResponse(const cmServerRequest& request) - : Type(request.Type) - , Cookie(request.Cookie) -{ -} - -void cmServerResponse::SetData(const Json::Value& data) -{ - assert(this->m_Payload == PAYLOAD_UNKNOWN); - if (!data[kCOOKIE_KEY].isNull() || !data[kTYPE_KEY].isNull()) { - this->SetError("Response contains cookie or type field."); - return; - } - this->m_Payload = PAYLOAD_DATA; - this->m_Data = data; -} - -void cmServerResponse::SetError(const std::string& message) -{ - assert(this->m_Payload == PAYLOAD_UNKNOWN); - this->m_Payload = PAYLOAD_ERROR; - this->m_ErrorMessage = message; -} - -bool cmServerResponse::IsComplete() const -{ - return this->m_Payload != PAYLOAD_UNKNOWN; -} - -bool cmServerResponse::IsError() const -{ - assert(this->m_Payload != PAYLOAD_UNKNOWN); - return this->m_Payload == PAYLOAD_ERROR; -} - -std::string cmServerResponse::ErrorMessage() const -{ - if (this->m_Payload == PAYLOAD_ERROR) { - return this->m_ErrorMessage; - } - return std::string(); -} - -Json::Value cmServerResponse::Data() const -{ - assert(this->m_Payload != PAYLOAD_UNKNOWN); - return this->m_Data; -} - -bool cmServerProtocol::Activate(cmServer* server, - const cmServerRequest& request, - std::string* errorMessage) -{ - assert(server); - this->m_Server = server; - this->m_CMakeInstance = - cm::make_unique<cmake>(cmake::RoleProject, cmState::Project); - this->m_WarnUnused = false; - const bool result = this->DoActivate(request, errorMessage); - if (!result) { - this->m_CMakeInstance = nullptr; - } - return result; -} - -cmFileMonitor* cmServerProtocol::FileMonitor() const -{ - return this->m_Server ? this->m_Server->FileMonitor() : nullptr; -} - -void cmServerProtocol::SendSignal(const std::string& name, - const Json::Value& data) const -{ - if (this->m_Server) { - this->m_Server->WriteSignal(name, data); - } -} - -cmake* cmServerProtocol::CMakeInstance() const -{ - return this->m_CMakeInstance.get(); -} - -bool cmServerProtocol::DoActivate(const cmServerRequest& /*request*/, - std::string* /*errorMessage*/) -{ - return true; -} - -std::pair<int, int> cmServerProtocol1::ProtocolVersion() const -{ - return { 1, 2 }; -} - -static void setErrorMessage(std::string* errorMessage, const std::string& text) -{ - if (errorMessage) { - *errorMessage = text; - } -} - -static bool getOrTestHomeDirectory(cmState* state, std::string& value, - std::string* errorMessage) -{ - const std::string cachedValue = - *state->GetCacheEntryValue("CMAKE_HOME_DIRECTORY"); - if (value.empty()) { - value = cachedValue; - return true; - } - const std::string suffix = "/CMakeLists.txt"; - const std::string cachedValueCML = cachedValue + suffix; - const std::string valueCML = value + suffix; - if (!cmSystemTools::SameFile(valueCML, cachedValueCML)) { - setErrorMessage(errorMessage, - std::string("\"CMAKE_HOME_DIRECTORY\" is set but " - "incompatible with configured " - "source directory value.")); - return false; - } - return true; -} - -static bool getOrTestValue(cmState* state, const std::string& key, - std::string& value, - const std::string& keyDescription, - std::string* errorMessage) -{ - const std::string cachedValue = state->GetSafeCacheEntryValue(key); - if (value.empty()) { - value = cachedValue; - } - if (!cachedValue.empty() && cachedValue != value) { - setErrorMessage(errorMessage, - std::string("\"") + key + - "\" is set but incompatible with configured " + - keyDescription + " value."); - return false; - } - return true; -} - -bool cmServerProtocol1::DoActivate(const cmServerRequest& request, - std::string* errorMessage) -{ - std::string sourceDirectory = request.Data[kSOURCE_DIRECTORY_KEY].asString(); - std::string buildDirectory = request.Data[kBUILD_DIRECTORY_KEY].asString(); - std::string generator = request.Data[kGENERATOR_KEY].asString(); - std::string extraGenerator = request.Data[kEXTRA_GENERATOR_KEY].asString(); - std::string toolset = request.Data[kTOOLSET_KEY].asString(); - std::string platform = request.Data[kPLATFORM_KEY].asString(); - - // normalize source and build directory - if (!sourceDirectory.empty()) { - sourceDirectory = cmSystemTools::CollapseFullPath(sourceDirectory); - cmSystemTools::ConvertToUnixSlashes(sourceDirectory); - } - if (!buildDirectory.empty()) { - buildDirectory = cmSystemTools::CollapseFullPath(buildDirectory); - cmSystemTools::ConvertToUnixSlashes(buildDirectory); - } - - if (buildDirectory.empty()) { - setErrorMessage(errorMessage, - std::string("\"") + kBUILD_DIRECTORY_KEY + - "\" is missing."); - return false; - } - - cmake* cm = CMakeInstance(); - if (cmSystemTools::PathExists(buildDirectory)) { - if (!cmSystemTools::FileIsDirectory(buildDirectory)) { - setErrorMessage(errorMessage, - std::string("\"") + kBUILD_DIRECTORY_KEY + - "\" exists but is not a directory."); - return false; - } - - const std::string cachePath = cmake::FindCacheFile(buildDirectory); - if (cm->LoadCache(cachePath)) { - cmState* state = cm->GetState(); - - // Check generator: - if (!getOrTestValue(state, "CMAKE_GENERATOR", generator, "generator", - errorMessage)) { - return false; - } - - // check extra generator: - if (!getOrTestValue(state, "CMAKE_EXTRA_GENERATOR", extraGenerator, - "extra generator", errorMessage)) { - return false; - } - - // check sourcedir: - if (!getOrTestHomeDirectory(state, sourceDirectory, errorMessage)) { - return false; - } - - // check toolset: - if (!getOrTestValue(state, "CMAKE_GENERATOR_TOOLSET", toolset, "toolset", - errorMessage)) { - return false; - } - - // check platform: - if (!getOrTestValue(state, "CMAKE_GENERATOR_PLATFORM", platform, - "platform", errorMessage)) { - return false; - } - } - } - - if (sourceDirectory.empty()) { - setErrorMessage(errorMessage, - std::string("\"") + kSOURCE_DIRECTORY_KEY + - "\" is unset but required."); - return false; - } - if (!cmSystemTools::FileIsDirectory(sourceDirectory)) { - setErrorMessage(errorMessage, - std::string("\"") + kSOURCE_DIRECTORY_KEY + - "\" is not a directory."); - return false; - } - if (generator.empty()) { - setErrorMessage(errorMessage, - std::string("\"") + kGENERATOR_KEY + - "\" is unset but required."); - return false; - } - - std::vector<cmake::GeneratorInfo> generators; - cm->GetRegisteredGenerators(generators); - auto baseIt = std::find_if(generators.begin(), generators.end(), - [&generator](const cmake::GeneratorInfo& info) { - return info.name == generator; - }); - if (baseIt == generators.end()) { - setErrorMessage(errorMessage, - std::string("Generator \"") + generator + - "\" not supported."); - return false; - } - auto extraIt = std::find_if( - generators.begin(), generators.end(), - [&generator, &extraGenerator](const cmake::GeneratorInfo& info) { - return info.baseName == generator && info.extraName == extraGenerator; - }); - if (extraIt == generators.end()) { - setErrorMessage(errorMessage, - std::string("The combination of generator \"" + generator + - "\" and extra generator \"" + extraGenerator + - "\" is not supported.")); - return false; - } - if (!extraIt->supportsToolset && !toolset.empty()) { - setErrorMessage(errorMessage, - std::string("Toolset was provided but is not supported by " - "the requested generator.")); - return false; - } - if (!extraIt->supportsPlatform && !platform.empty()) { - setErrorMessage(errorMessage, - std::string("Platform was provided but is not supported " - "by the requested generator.")); - return false; - } - - this->GeneratorInfo = - GeneratorInformation(generator, extraGenerator, toolset, platform, - sourceDirectory, buildDirectory); - - this->m_State = STATE_ACTIVE; - return true; -} - -void cmServerProtocol1::HandleCMakeFileChanges(const std::string& path, - int event, int status) -{ - assert(status == 0); - static_cast<void>(status); - - if (!m_isDirty) { - m_isDirty = true; - SendSignal(kDIRTY_SIGNAL, Json::objectValue); - } - Json::Value obj = Json::objectValue; - obj[kPATH_KEY] = path; - Json::Value properties = Json::arrayValue; - if (event & UV_RENAME) { - properties.append(kRENAME_PROPERTY_VALUE); - } - if (event & UV_CHANGE) { - properties.append(kCHANGE_PROPERTY_VALUE); - } - - obj[kPROPERTIES_KEY] = properties; - SendSignal(kFILE_CHANGE_SIGNAL, obj); -} - -cmServerResponse cmServerProtocol1::Process(const cmServerRequest& request) -{ - assert(this->m_State >= STATE_ACTIVE); - - if (request.Type == kCACHE_TYPE) { - return this->ProcessCache(request); - } - if (request.Type == kCMAKE_INPUTS_TYPE) { - return this->ProcessCMakeInputs(request); - } - if (request.Type == kCODE_MODEL_TYPE) { - return this->ProcessCodeModel(request); - } - if (request.Type == kCOMPUTE_TYPE) { - return this->ProcessCompute(request); - } - if (request.Type == kCONFIGURE_TYPE) { - return this->ProcessConfigure(request); - } - if (request.Type == kFILESYSTEM_WATCHERS_TYPE) { - return this->ProcessFileSystemWatchers(request); - } - if (request.Type == kGLOBAL_SETTINGS_TYPE) { - return this->ProcessGlobalSettings(request); - } - if (request.Type == kSET_GLOBAL_SETTINGS_TYPE) { - return this->ProcessSetGlobalSettings(request); - } - if (request.Type == kCTEST_INFO_TYPE) { - return this->ProcessCTests(request); - } - - return request.ReportError("Unknown command!"); -} - -bool cmServerProtocol1::IsExperimental() const -{ - return true; -} - -cmServerResponse cmServerProtocol1::ProcessCache( - const cmServerRequest& request) -{ - cmState* state = this->CMakeInstance()->GetState(); - - Json::Value result = Json::objectValue; - - std::vector<std::string> allKeys = state->GetCacheEntryKeys(); - - Json::Value list = Json::arrayValue; - std::vector<std::string> keys = toStringList(request.Data[kKEYS_KEY]); - if (keys.empty()) { - keys = allKeys; - } else { - for (auto const& i : keys) { - if (!cm::contains(allKeys, i)) { - return request.ReportError("Key \"" + i + "\" not found in cache."); - } - } - } - std::sort(keys.begin(), keys.end()); - for (auto const& key : keys) { - Json::Value entry = Json::objectValue; - entry[kKEY_KEY] = key; - entry[kTYPE_KEY] = - cmState::CacheEntryTypeToString(state->GetCacheEntryType(key)); - entry[kVALUE_KEY] = *state->GetCacheEntryValue(key); - - Json::Value props = Json::objectValue; - bool haveProperties = false; - for (auto const& prop : state->GetCacheEntryPropertyList(key)) { - haveProperties = true; - props[prop] = *state->GetCacheEntryProperty(key, prop); - } - if (haveProperties) { - entry[kPROPERTIES_KEY] = props; - } - - list.append(entry); - } - - result[kCACHE_KEY] = list; - return request.Reply(result); -} - -cmServerResponse cmServerProtocol1::ProcessCMakeInputs( - const cmServerRequest& request) -{ - if (this->m_State < STATE_CONFIGURED) { - return request.ReportError("This instance was not yet configured."); - } - - const cmake* cm = this->CMakeInstance(); - const std::string cmakeRootDir = cmSystemTools::GetCMakeRoot(); - const std::string& sourceDir = cm->GetHomeDirectory(); - - Json::Value result = Json::objectValue; - result[kSOURCE_DIRECTORY_KEY] = sourceDir; - result[kCMAKE_ROOT_DIRECTORY_KEY] = cmakeRootDir; - result[kBUILD_FILES_KEY] = cmDumpCMakeInputs(cm); - return request.Reply(result); -} - -cmServerResponse cmServerProtocol1::ProcessCodeModel( - const cmServerRequest& request) -{ - if (this->m_State != STATE_COMPUTED) { - return request.ReportError("No build system was generated yet."); - } - - return request.Reply(cmDumpCodeModel(this->CMakeInstance())); -} - -cmServerResponse cmServerProtocol1::ProcessCompute( - const cmServerRequest& request) -{ - if (this->m_State > STATE_CONFIGURED) { - return request.ReportError("This build system was already generated."); - } - if (this->m_State < STATE_CONFIGURED) { - return request.ReportError("This project was not configured yet."); - } - - cmake* cm = this->CMakeInstance(); - int ret = cm->Generate(); - - if (ret < 0) { - return request.ReportError("Failed to compute build system."); - } - m_State = STATE_COMPUTED; - return request.Reply(Json::Value()); -} - -cmServerResponse cmServerProtocol1::ProcessConfigure( - const cmServerRequest& request) -{ - if (this->m_State == STATE_INACTIVE) { - return request.ReportError("This instance is inactive."); - } - - FileMonitor()->StopMonitoring(); - - std::string errorMessage; - cmake* cm = this->CMakeInstance(); - this->GeneratorInfo.SetupGenerator(cm, &errorMessage); - if (!errorMessage.empty()) { - return request.ReportError(errorMessage); - } - - // Make sure the types of cacheArguments matches (if given): - std::vector<std::string> cacheArgs = { "unused" }; - bool cacheArgumentsError = false; - const Json::Value passedArgs = request.Data[kCACHE_ARGUMENTS_KEY]; - if (!passedArgs.isNull()) { - if (passedArgs.isString()) { - cacheArgs.push_back(passedArgs.asString()); - } else if (passedArgs.isArray()) { - for (auto const& arg : passedArgs) { - if (!arg.isString()) { - cacheArgumentsError = true; - break; - } - cacheArgs.push_back(arg.asString()); - } - } else { - cacheArgumentsError = true; - } - } - if (cacheArgumentsError) { - request.ReportError( - "cacheArguments must be unset, a string or an array of strings."); - } - - std::string sourceDir = cm->GetHomeDirectory(); - const std::string buildDir = cm->GetHomeOutputDirectory(); - - cmGlobalGenerator* gg = cm->GetGlobalGenerator(); - - if (buildDir.empty()) { - return request.ReportError("No build directory set via Handshake."); - } - - if (cm->LoadCache(buildDir)) { - // build directory has been set up before - cmProp cachedSourceDir = - cm->GetState()->GetInitializedCacheValue("CMAKE_HOME_DIRECTORY"); - if (!cachedSourceDir) { - return request.ReportError("No CMAKE_HOME_DIRECTORY found in cache."); - } - if (sourceDir.empty()) { - sourceDir = *cachedSourceDir; - cm->SetHomeDirectory(sourceDir); - } - - cmProp cachedGenerator = - cm->GetState()->GetInitializedCacheValue("CMAKE_GENERATOR"); - if (cachedGenerator) { - if (gg && gg->GetName() != *cachedGenerator) { - return request.ReportError("Configured generator does not match with " - "CMAKE_GENERATOR found in cache."); - } - } - } else { - // build directory has not been set up before - if (sourceDir.empty()) { - return request.ReportError("No sourceDirectory set via " - "setGlobalSettings and no cache found in " - "buildDirectory."); - } - } - - cmSystemTools::ResetErrorOccuredFlag(); // Reset error state - - if (cm->AddCMakePaths() != 1) { - return request.ReportError("Failed to set CMake paths."); - } - - if (!cm->SetCacheArgs(cacheArgs)) { - return request.ReportError("cacheArguments could not be set."); - } - - int ret = cm->Configure(); - cm->IssueMessage( - MessageType::DEPRECATION_WARNING, - "The 'cmake-server(7)' is deprecated. " - "Please port clients to use the 'cmake-file-api(7)' instead."); - if (ret < 0) { - return request.ReportError("Configuration failed."); - } - - std::vector<std::string> toWatchList; - cmGetCMakeInputs(gg, std::string(), buildDir, nullptr, &toWatchList, - nullptr); - - FileMonitor()->MonitorPaths(toWatchList, - [this](const std::string& p, int e, int s) { - this->HandleCMakeFileChanges(p, e, s); - }); - - m_State = STATE_CONFIGURED; - m_isDirty = false; - return request.Reply(Json::Value()); -} - -cmServerResponse cmServerProtocol1::ProcessGlobalSettings( - const cmServerRequest& request) -{ - cmake* cm = this->CMakeInstance(); - Json::Value obj = Json::objectValue; - - // Capabilities information: - obj[kCAPABILITIES_KEY] = cm->ReportCapabilitiesJson(); - - obj[kDEBUG_OUTPUT_KEY] = cm->GetDebugOutput(); - obj[kTRACE_KEY] = cm->GetTrace(); - obj[kTRACE_EXPAND_KEY] = cm->GetTraceExpand(); - obj[kWARN_UNINITIALIZED_KEY] = cm->GetWarnUninitialized(); - obj[kWARN_UNUSED_KEY] = m_WarnUnused; - obj[kWARN_UNUSED_CLI_KEY] = cm->GetWarnUnusedCli(); - obj[kCHECK_SYSTEM_VARS_KEY] = cm->GetCheckSystemVars(); - - obj[kSOURCE_DIRECTORY_KEY] = this->GeneratorInfo.SourceDirectory; - obj[kBUILD_DIRECTORY_KEY] = this->GeneratorInfo.BuildDirectory; - - // Currently used generator: - obj[kGENERATOR_KEY] = this->GeneratorInfo.GeneratorName; - obj[kEXTRA_GENERATOR_KEY] = this->GeneratorInfo.ExtraGeneratorName; - - return request.Reply(obj); -} - -static void setBool(const cmServerRequest& request, const std::string& key, - std::function<void(bool)> const& setter) -{ - if (request.Data[key].isNull()) { - return; - } - setter(request.Data[key].asBool()); -} - -cmServerResponse cmServerProtocol1::ProcessSetGlobalSettings( - const cmServerRequest& request) -{ - const std::vector<std::string> boolValues = { - kDEBUG_OUTPUT_KEY, kTRACE_KEY, kTRACE_EXPAND_KEY, - kWARN_UNINITIALIZED_KEY, kWARN_UNUSED_KEY, kWARN_UNUSED_CLI_KEY, - kCHECK_SYSTEM_VARS_KEY - }; - for (std::string const& i : boolValues) { - if (!request.Data[i].isNull() && !request.Data[i].isBool()) { - return request.ReportError("\"" + i + - "\" must be unset or a bool value."); - } - } - - cmake* cm = this->CMakeInstance(); - - setBool(request, kDEBUG_OUTPUT_KEY, - [cm](bool e) { cm->SetDebugOutputOn(e); }); - setBool(request, kTRACE_KEY, [cm](bool e) { cm->SetTrace(e); }); - setBool(request, kTRACE_EXPAND_KEY, [cm](bool e) { cm->SetTraceExpand(e); }); - setBool(request, kWARN_UNINITIALIZED_KEY, - [cm](bool e) { cm->SetWarnUninitialized(e); }); - setBool(request, kWARN_UNUSED_KEY, [this](bool e) { m_WarnUnused = e; }); - setBool(request, kWARN_UNUSED_CLI_KEY, - [cm](bool e) { cm->SetWarnUnusedCli(e); }); - setBool(request, kCHECK_SYSTEM_VARS_KEY, - [cm](bool e) { cm->SetCheckSystemVars(e); }); - - return request.Reply(Json::Value()); -} - -cmServerResponse cmServerProtocol1::ProcessFileSystemWatchers( - const cmServerRequest& request) -{ - const cmFileMonitor* const fm = FileMonitor(); - Json::Value result = Json::objectValue; - Json::Value files = Json::arrayValue; - for (auto const& f : fm->WatchedFiles()) { - files.append(f); - } - Json::Value directories = Json::arrayValue; - for (auto const& d : fm->WatchedDirectories()) { - directories.append(d); - } - result[kWATCHED_FILES_KEY] = files; - result[kWATCHED_DIRECTORIES_KEY] = directories; - - return request.Reply(result); -} - -cmServerResponse cmServerProtocol1::ProcessCTests( - const cmServerRequest& request) -{ - if (this->m_State < STATE_COMPUTED) { - return request.ReportError("This instance was not yet computed."); - } - - return request.Reply(cmDumpCTestInfo(this->CMakeInstance())); -} - -cmServerProtocol1::GeneratorInformation::GeneratorInformation( - std::string generatorName, std::string extraGeneratorName, - std::string toolset, std::string platform, std::string sourceDirectory, - std::string buildDirectory) - : GeneratorName(std::move(generatorName)) - , ExtraGeneratorName(std::move(extraGeneratorName)) - , Toolset(std::move(toolset)) - , Platform(std::move(platform)) - , SourceDirectory(std::move(sourceDirectory)) - , BuildDirectory(std::move(buildDirectory)) -{ -} - -void cmServerProtocol1::GeneratorInformation::SetupGenerator( - cmake* cm, std::string* errorMessage) -{ - const std::string fullGeneratorName = - cmExternalMakefileProjectGenerator::CreateFullGeneratorName( - GeneratorName, ExtraGeneratorName); - - cm->SetHomeDirectory(SourceDirectory); - cm->SetHomeOutputDirectory(BuildDirectory); - - auto gg = cm->CreateGlobalGenerator(fullGeneratorName); - if (!gg) { - setErrorMessage( - errorMessage, - std::string("Could not set up the requested combination of \"") + - kGENERATOR_KEY + "\" and \"" + kEXTRA_GENERATOR_KEY + "\""); - return; - } - - cm->SetGlobalGenerator(std::move(gg)); - - cm->SetGeneratorToolset(Toolset); - cm->SetGeneratorPlatform(Platform); -} diff --git a/Source/cmServerProtocol.h b/Source/cmServerProtocol.h deleted file mode 100644 index 6009e23..0000000 --- a/Source/cmServerProtocol.h +++ /dev/null @@ -1,162 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#pragma once - -#include "cmConfigure.h" // IWYU pragma: keep - -#include <memory> -#include <string> -#include <utility> - -#include <cm3p/json/value.h> - -#include "cmake.h" - -class cmConnection; -class cmFileMonitor; -class cmServer; -class cmServerRequest; - -class cmServerResponse -{ -public: - explicit cmServerResponse(const cmServerRequest& request); - - void SetData(const Json::Value& data); - void SetError(const std::string& message); - - bool IsComplete() const; - bool IsError() const; - std::string ErrorMessage() const; - Json::Value Data() const; - - const std::string Type; - const std::string Cookie; - -private: - enum PayLoad - { - PAYLOAD_UNKNOWN, - PAYLOAD_ERROR, - PAYLOAD_DATA - }; - PayLoad m_Payload = PAYLOAD_UNKNOWN; - std::string m_ErrorMessage; - Json::Value m_Data; -}; - -class cmServerRequest -{ -public: - cmServerResponse Reply(const Json::Value& data) const; - cmServerResponse ReportError(const std::string& message) const; - - const std::string Type; - const std::string Cookie; - const Json::Value Data; - cmConnection* Connection; - -private: - cmServerRequest(cmServer* server, cmConnection* connection, std::string t, - std::string c, Json::Value d); - - void ReportProgress(int min, int current, int max, - const std::string& message) const; - void ReportMessage(const std::string& message, - const std::string& title) const; - - cmServer* m_Server; - - friend class cmServer; -}; - -class cmServerProtocol -{ -public: - cmServerProtocol() = default; - virtual ~cmServerProtocol() = default; - - cmServerProtocol(cmServerProtocol const&) = delete; - cmServerProtocol& operator=(cmServerProtocol const&) = delete; - - virtual std::pair<int, int> ProtocolVersion() const = 0; - virtual bool IsExperimental() const = 0; - virtual cmServerResponse Process(const cmServerRequest& request) = 0; - - bool Activate(cmServer* server, const cmServerRequest& request, - std::string* errorMessage); - - cmFileMonitor* FileMonitor() const; - void SendSignal(const std::string& name, const Json::Value& data) const; - -protected: - cmake* CMakeInstance() const; - // Implement protocol specific activation tasks here. Called from Activate(). - virtual bool DoActivate(const cmServerRequest& request, - std::string* errorMessage); - bool m_WarnUnused = false; // storage for legacy option - -private: - std::unique_ptr<cmake> m_CMakeInstance; - cmServer* m_Server = nullptr; // not owned! - - friend class cmServer; -}; - -class cmServerProtocol1 : public cmServerProtocol -{ -public: - std::pair<int, int> ProtocolVersion() const override; - bool IsExperimental() const override; - cmServerResponse Process(const cmServerRequest& request) override; - -private: - bool DoActivate(const cmServerRequest& request, - std::string* errorMessage) override; - - void HandleCMakeFileChanges(const std::string& path, int event, int status); - - // Handle requests: - cmServerResponse ProcessCache(const cmServerRequest& request); - cmServerResponse ProcessCMakeInputs(const cmServerRequest& request); - cmServerResponse ProcessCodeModel(const cmServerRequest& request); - cmServerResponse ProcessCompute(const cmServerRequest& request); - cmServerResponse ProcessConfigure(const cmServerRequest& request); - cmServerResponse ProcessGlobalSettings(const cmServerRequest& request); - cmServerResponse ProcessSetGlobalSettings(const cmServerRequest& request); - cmServerResponse ProcessFileSystemWatchers(const cmServerRequest& request); - cmServerResponse ProcessCTests(const cmServerRequest& request); - - enum State - { - STATE_INACTIVE, - STATE_ACTIVE, - STATE_CONFIGURED, - STATE_COMPUTED - }; - State m_State = STATE_INACTIVE; - - bool m_isDirty = false; - - struct GeneratorInformation - { - public: - GeneratorInformation() = default; - GeneratorInformation(std::string generatorName, - std::string extraGeneratorName, std::string toolset, - std::string platform, std::string sourceDirectory, - std::string buildDirectory); - - void SetupGenerator(cmake* cm, std::string* errorMessage); - - std::string GeneratorName; - std::string ExtraGeneratorName; - std::string Toolset; - std::string Platform; - - std::string SourceDirectory; - std::string BuildDirectory; - }; - - GeneratorInformation GeneratorInfo; -}; diff --git a/Source/cmSourceFile.cxx b/Source/cmSourceFile.cxx index ef44a57..39074a5 100644 --- a/Source/cmSourceFile.cxx +++ b/Source/cmSourceFile.cxx @@ -8,6 +8,7 @@ #include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmPolicies.h" #include "cmProperty.h" #include "cmState.h" #include "cmStringAlgorithms.h" @@ -93,10 +94,11 @@ cmSourceFileLocation const& cmSourceFile::GetLocation() const return this->Location; } -std::string const& cmSourceFile::ResolveFullPath(std::string* error) +std::string const& cmSourceFile::ResolveFullPath(std::string* error, + std::string* cmp0115Warning) { if (this->FullPath.empty()) { - if (this->FindFullPath(error)) { + if (this->FindFullPath(error, cmp0115Warning)) { this->CheckExtension(); } } @@ -108,7 +110,8 @@ std::string const& cmSourceFile::GetFullPath() const return this->FullPath; } -bool cmSourceFile::FindFullPath(std::string* error) +bool cmSourceFile::FindFullPath(std::string* error, + std::string* cmp0115Warning) { // If the file is generated compute the location without checking on disk. if (this->GetIsGenerated()) { @@ -131,9 +134,11 @@ bool cmSourceFile::FindFullPath(std::string* error) // List of extension lists std::vector<std::string> exts = makefile->GetCMakeInstance()->GetAllExtensions(); + auto cmp0115 = makefile->GetPolicyStatus(cmPolicies::CMP0115); // Tries to find the file in a given directory - auto findInDir = [this, &exts, &lPath](std::string const& dir) -> bool { + auto findInDir = [this, &exts, &lPath, cmp0115, cmp0115Warning, + makefile](std::string const& dir) -> bool { // Compute full path std::string const fullPath = cmSystemTools::CollapseFullPath(lPath, dir); // Try full path @@ -141,13 +146,29 @@ bool cmSourceFile::FindFullPath(std::string* error) this->FullPath = fullPath; return true; } - // Try full path with extension - for (std::string const& ext : exts) { - if (!ext.empty()) { - std::string extPath = cmStrCat(fullPath, '.', ext); - if (cmSystemTools::FileExists(extPath)) { - this->FullPath = extPath; - return true; + // This has to be an if statement due to a bug in Oracle Developer Studio. + // See https://community.oracle.com/tech/developers/discussion/4476246/ + // for details. + if (cmp0115 == cmPolicies::OLD || cmp0115 == cmPolicies::WARN) { + // Try full path with extension + for (std::string const& ext : exts) { + if (!ext.empty()) { + std::string extPath = cmStrCat(fullPath, '.', ext); + if (cmSystemTools::FileExists(extPath)) { + this->FullPath = extPath; + if (cmp0115 == cmPolicies::WARN) { + std::string warning = + cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0115), + "\nFile:\n ", extPath); + if (cmp0115Warning) { + *cmp0115Warning = std::move(warning); + } else { + makefile->GetCMakeInstance()->IssueMessage( + MessageType::AUTHOR_WARNING, warning); + } + } + return true; + } } } } @@ -168,11 +189,19 @@ bool cmSourceFile::FindFullPath(std::string* error) } // Compose error - std::string err = - cmStrCat("Cannot find source file:\n ", lPath, "\nTried extensions"); - for (std::string const& ext : exts) { - err += " ."; - err += ext; + std::string err = cmStrCat("Cannot find source file:\n ", lPath); + switch (cmp0115) { + case cmPolicies::OLD: + case cmPolicies::WARN: + err = cmStrCat(err, "\nTried extensions"); + for (auto const& ext : exts) { + err = cmStrCat(err, " .", ext); + } + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + break; } if (error != nullptr) { *error = std::move(err); @@ -281,7 +310,7 @@ void cmSourceFile::AppendProperty(const std::string& prop, } } -const char* cmSourceFile::GetPropertyForUser(const std::string& prop) +cmProp cmSourceFile::GetPropertyForUser(const std::string& prop) { // This method is a consequence of design history and backwards // compatibility. GetProperty is (and should be) a const method. @@ -305,13 +334,12 @@ const char* cmSourceFile::GetPropertyForUser(const std::string& prop) // Similarly, LANGUAGE can be determined by the file extension // if it is requested by the user. if (prop == propLANGUAGE) { - // The c_str pointer is valid until `this->Language` is modified. - return this->GetOrDetermineLanguage().c_str(); + // The pointer is valid until `this->Language` is modified. + return &this->GetOrDetermineLanguage(); } // Perform the normal property lookup. - cmProp p = this->GetProperty(prop); - return p ? p->c_str() : nullptr; + return this->GetProperty(prop); } cmProp cmSourceFile::GetProperty(const std::string& prop) const @@ -369,13 +397,15 @@ cmProp cmSourceFile::GetProperty(const std::string& prop) const return retVal; } -const char* cmSourceFile::GetSafeProperty(const std::string& prop) const +const std::string& cmSourceFile::GetSafeProperty(const std::string& prop) const { cmProp ret = this->GetProperty(prop); - if (!ret) { - return ""; + if (ret) { + return *ret; } - return ret->c_str(); + + static std::string const s_empty; + return s_empty; } bool cmSourceFile::GetPropertyAsBool(const std::string& prop) const diff --git a/Source/cmSourceFile.h b/Source/cmSourceFile.h index 39ea8e3..3ad2664 100644 --- a/Source/cmSourceFile.h +++ b/Source/cmSourceFile.h @@ -47,12 +47,12 @@ public: //! Might return a nullptr if the property is not set or invalid cmProp GetProperty(const std::string& prop) const; //! Always returns a valid pointer - const char* GetSafeProperty(const std::string& prop) const; + const std::string& GetSafeProperty(const std::string& prop) const; bool GetPropertyAsBool(const std::string& prop) const; /** Implement getting a property when called from a CMake language command like get_property or get_source_file_property. */ - const char* GetPropertyForUser(const std::string& prop); + cmProp GetPropertyForUser(const std::string& prop); //! Checks is the GENERATED property is set and true /// @return Equivalent to GetPropertyAsBool("GENERATED") @@ -77,7 +77,8 @@ public: * Resolves the full path to the file. Attempts to locate the file on disk * and finalizes its location. */ - std::string const& ResolveFullPath(std::string* error = nullptr); + std::string const& ResolveFullPath(std::string* error = nullptr, + std::string* cmp0115Warning = nullptr); /** * The resolved full path to the file. The returned file name might be empty @@ -138,7 +139,7 @@ private: bool FindFullPathFailed = false; bool IsGenerated = false; - bool FindFullPath(std::string* error); + bool FindFullPath(std::string* error, std::string* cmp0115Warning); void CheckExtension(); void CheckLanguage(std::string const& ext); diff --git a/Source/cmSourceFileLocation.cxx b/Source/cmSourceFileLocation.cxx index 222bafa..921eb0e 100644 --- a/Source/cmSourceFileLocation.cxx +++ b/Source/cmSourceFileLocation.cxx @@ -33,8 +33,7 @@ cmSourceFileLocation::cmSourceFileLocation(cmMakefile const* mf, this->AmbiguousExtension = true; this->Directory = cmSystemTools::GetFilenamePath(name); if (cmSystemTools::FileIsFullPath(this->Directory)) { - this->Directory = cmSystemTools::CollapseFullPath( - this->Directory, mf->GetHomeOutputDirectory()); + this->Directory = cmSystemTools::CollapseFullPath(this->Directory); } this->Name = cmSystemTools::GetFilenameName(name); if (kind == cmSourceFileLocationKind::Known) { diff --git a/Source/cmState.cxx b/Source/cmState.cxx index d268e62..3692a01 100644 --- a/Source/cmState.cxx +++ b/Source/cmState.cxx @@ -441,6 +441,19 @@ void cmState::AddBuiltinCommand(std::string const& name, }); } +void cmState::AddFlowControlCommand(std::string const& name, Command command) +{ + this->FlowControlCommands.insert(name); + this->AddBuiltinCommand(name, std::move(command)); +} + +void cmState::AddFlowControlCommand(std::string const& name, + BuiltinCommand command) +{ + this->FlowControlCommands.insert(name); + this->AddBuiltinCommand(name, command); +} + void cmState::AddDisallowedCommand(std::string const& name, BuiltinCommand command, cmPolicies::PolicyID policy, @@ -470,7 +483,7 @@ void cmState::AddDisallowedCommand(std::string const& name, void cmState::AddUnexpectedCommand(std::string const& name, const char* error) { - this->AddBuiltinCommand( + this->AddFlowControlCommand( name, [name, error](std::vector<cmListFileArgument> const&, cmExecutionStatus& status) -> bool { @@ -485,16 +498,28 @@ void cmState::AddUnexpectedCommand(std::string const& name, const char* error) }); } -void cmState::AddScriptedCommand(std::string const& name, Command command) +bool cmState::AddScriptedCommand(std::string const& name, BT<Command> command, + cmMakefile& mf) { std::string sName = cmSystemTools::LowerCase(name); + if (this->FlowControlCommands.count(sName)) { + mf.GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Built-in flow control command \"", sName, + "\" cannot be overridden."), + command.Backtrace); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + // if the command already exists, give a new name to the old command. if (Command oldCmd = this->GetCommandByExactName(sName)) { this->ScriptedCommands["_" + sName] = oldCmd; } - this->ScriptedCommands[sName] = std::move(command); + this->ScriptedCommands[sName] = std::move(command.Value); + return true; } cmState::Command cmState::GetCommand(std::string const& name) const diff --git a/Source/cmState.h b/Source/cmState.h index e4c9eb5..4e41156 100644 --- a/Source/cmState.h +++ b/Source/cmState.h @@ -9,6 +9,7 @@ #include <set> #include <string> #include <unordered_map> +#include <unordered_set> #include <vector> #include "cmDefinitions.h" @@ -24,6 +25,7 @@ class cmCacheManager; class cmCommand; class cmGlobVerificationManager; +class cmMakefile; class cmStateSnapshot; class cmMessenger; class cmExecutionStatus; @@ -159,10 +161,13 @@ public: std::unique_ptr<cmCommand> command); void AddBuiltinCommand(std::string const& name, Command command); void AddBuiltinCommand(std::string const& name, BuiltinCommand command); + void AddFlowControlCommand(std::string const& name, Command command); + void AddFlowControlCommand(std::string const& name, BuiltinCommand command); void AddDisallowedCommand(std::string const& name, BuiltinCommand command, cmPolicies::PolicyID policy, const char* message); void AddUnexpectedCommand(std::string const& name, const char* error); - void AddScriptedCommand(std::string const& name, Command command); + bool AddScriptedCommand(std::string const& name, BT<Command> command, + cmMakefile& mf); void RemoveBuiltinCommand(std::string const& name); void RemoveUserDefinedCommands(); std::vector<std::string> GetCommandNames() const; @@ -225,6 +230,7 @@ private: std::vector<std::string> EnabledLanguages; std::unordered_map<std::string, Command> BuiltinCommands; std::unordered_map<std::string, Command> ScriptedCommands; + std::unordered_set<std::string> FlowControlCommands; cmPropertyMap GlobalProperties; std::unique_ptr<cmCacheManager> CacheManager; std::unique_ptr<cmGlobVerificationManager> GlobVerificationManager; diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index e076d1e..35224eb 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -213,7 +213,7 @@ public: bool CheckImportedLibName(std::string const& prop, std::string const& value) const; - std::string ProcessSourceItemCMP0049(const std::string& s); + std::string ProcessSourceItemCMP0049(const std::string& s) const; }; namespace { @@ -372,6 +372,8 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, initProp("ISPC_INSTRUCTION_SETS"); initProp("LINK_SEARCH_START_STATIC"); initProp("LINK_SEARCH_END_STATIC"); + initProp("OBJC_CLANG_TIDY"); + initProp("OBJCXX_CLANG_TIDY"); initProp("Swift_LANGUAGE_VERSION"); initProp("Swift_MODULE_DIRECTORY"); initProp("VS_JUST_MY_CODE_DEBUGGING"); @@ -623,6 +625,11 @@ void cmTarget::AddUtility(std::string const& name, bool cross, cmMakefile* mf) { name, cross }, mf ? mf->GetBacktrace() : cmListFileBacktrace())); } +void cmTarget::AddUtility(BT<std::pair<std::string, bool>> util) +{ + impl->Utilities.emplace(std::move(util)); +} + std::set<BT<std::pair<std::string, bool>>> const& cmTarget::GetUtilities() const { @@ -740,7 +747,8 @@ void cmTarget::AddSources(std::vector<std::string> const& srcs) } } -std::string cmTargetInternals::ProcessSourceItemCMP0049(const std::string& s) +std::string cmTargetInternals::ProcessSourceItemCMP0049( + const std::string& s) const { std::string src = s; @@ -791,7 +799,7 @@ struct CreateLocation { } - cmSourceFileLocation operator()(const std::string& filename) + cmSourceFileLocation operator()(const std::string& filename) const { return cmSourceFileLocation(this->Makefile, filename); } @@ -857,7 +865,7 @@ cmSourceFile* cmTarget::AddSource(const std::string& src, bool before) cmSourceFileLocationKind::Known); } -void cmTarget::ClearDependencyInformation(cmMakefile& mf) +void cmTarget::ClearDependencyInformation(cmMakefile& mf) const { std::string depname = cmStrCat(this->GetName(), "_LIB_DEPENDS"); mf.RemoveCacheDefinition(depname); diff --git a/Source/cmTarget.h b/Source/cmTarget.h index d8f66bc..27f0c59 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -114,7 +114,7 @@ public: LinkLibraryVectorType const& GetOriginalLinkLibraries() const; //! Clear the dependency information recorded for this target, if any. - void ClearDependencyInformation(cmMakefile& mf); + void ClearDependencyInformation(cmMakefile& mf) const; void AddLinkLibrary(cmMakefile& mf, std::string const& lib, cmTargetLinkLibraryType llt); @@ -164,6 +164,7 @@ public: */ void AddUtility(std::string const& name, bool cross, cmMakefile* mf = nullptr); + void AddUtility(BT<std::pair<std::string, bool>> util); //! Get the utilities used by this target std::set<BT<std::pair<std::string, bool>>> const& GetUtilities() const; diff --git a/Source/cmTargetPropCommandBase.cxx b/Source/cmTargetPropCommandBase.cxx index 9e30136..b050a58 100644 --- a/Source/cmTargetPropCommandBase.cxx +++ b/Source/cmTargetPropCommandBase.cxx @@ -45,15 +45,26 @@ bool cmTargetPropCommandBase::HandleArguments( this->HandleMissingTarget(args[0]); return false; } - if ((this->Target->GetType() != cmStateEnums::EXECUTABLE) && - (this->Target->GetType() != cmStateEnums::STATIC_LIBRARY) && - (this->Target->GetType() != cmStateEnums::SHARED_LIBRARY) && - (this->Target->GetType() != cmStateEnums::MODULE_LIBRARY) && - (this->Target->GetType() != cmStateEnums::OBJECT_LIBRARY) && - (this->Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) && - (this->Target->GetType() != cmStateEnums::UNKNOWN_LIBRARY)) { - this->SetError("called with non-compilable target type"); - return false; + const bool isRegularTarget = + (this->Target->GetType() == cmStateEnums::EXECUTABLE) || + (this->Target->GetType() == cmStateEnums::STATIC_LIBRARY) || + (this->Target->GetType() == cmStateEnums::SHARED_LIBRARY) || + (this->Target->GetType() == cmStateEnums::MODULE_LIBRARY) || + (this->Target->GetType() == cmStateEnums::OBJECT_LIBRARY) || + (this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY) || + (this->Target->GetType() == cmStateEnums::UNKNOWN_LIBRARY); + const bool isCustomTarget = this->Target->GetType() == cmStateEnums::UTILITY; + + if (prop == "SOURCES") { + if (!isRegularTarget && !isCustomTarget) { + this->SetError("called with non-compilable target type"); + return false; + } + } else { + if (!isRegularTarget) { + this->SetError("called with non-compilable target type"); + return false; + } } bool system = false; @@ -131,6 +142,11 @@ bool cmTargetPropCommandBase::ProcessContentArgs( this->SetError("may only set INTERFACE properties on IMPORTED targets"); return false; } + if (this->Target->GetType() == cmStateEnums::UTILITY && + scope != "PRIVATE") { + this->SetError("may only set PRIVATE properties on custom targets"); + return false; + } } return this->PopulateTargetProperies(scope, content, prepend, system); } diff --git a/Source/cmTransformDepfile.cxx b/Source/cmTransformDepfile.cxx new file mode 100644 index 0000000..e1f8753 --- /dev/null +++ b/Source/cmTransformDepfile.cxx @@ -0,0 +1,114 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmTransformDepfile.h" + +#include <string> +#include <type_traits> +#include <utility> +#include <vector> + +#include <cm/optional> + +#include "cmsys/FStream.hxx" + +#include "cmGccDepfileReader.h" +#include "cmGccDepfileReaderTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" + +namespace { +void WriteFilenameGcc(cmsys::ofstream& fout, const std::string& filename) +{ + for (auto c : filename) { + switch (c) { + case ' ': + fout << "\\ "; + break; + case '\\': + fout << "\\\\"; + break; + default: + fout << c; + break; + } + } +} + +void WriteGccDepfile(cmsys::ofstream& fout, const cmGccDepfileContent& content) +{ + for (auto const& dep : content) { + bool first = true; + for (auto const& rule : dep.rules) { + if (!first) { + fout << " \\\n "; + } + first = false; + WriteFilenameGcc(fout, rule); + } + fout << ':'; + for (auto const& path : dep.paths) { + fout << " \\\n " << path; + } + fout << '\n'; + } +} + +void WriteVsTlog(cmsys::ofstream& fout, const cmGccDepfileContent& content) +{ + for (auto const& dep : content) { + fout << '^'; + bool first = true; + for (auto const& rule : dep.rules) { + if (!first) { + fout << '|'; + } + first = false; + fout << cmSystemTools::ConvertToOutputPath(rule); + } + fout << "\r\n"; + for (auto const& path : dep.paths) { + fout << cmSystemTools::ConvertToOutputPath(path) << "\r\n"; + } + } +} +} + +bool cmTransformDepfile(cmDepfileFormat format, const std::string& prefix, + const std::string& infile, const std::string& outfile) +{ + cmGccDepfileContent content; + if (cmSystemTools::FileExists(infile)) { + auto result = cmReadGccDepfile(infile.c_str()); + if (!result) { + return false; + } + content = *std::move(result); + } + + for (auto& dep : content) { + for (auto& rule : dep.rules) { + if (!cmSystemTools::FileIsFullPath(rule)) { + rule = cmStrCat(prefix, rule); + } + } + for (auto& path : dep.paths) { + if (!cmSystemTools::FileIsFullPath(path)) { + path = cmStrCat(prefix, path); + } + } + } + + cmsys::ofstream fout(outfile.c_str()); + if (!fout) { + return false; + } + switch (format) { + case cmDepfileFormat::GccDepfile: + WriteGccDepfile(fout, content); + break; + case cmDepfileFormat::VsTlog: + WriteVsTlog(fout, content); + break; + } + return true; +} diff --git a/Source/cmTransformDepfile.h b/Source/cmTransformDepfile.h new file mode 100644 index 0000000..792c1aa --- /dev/null +++ b/Source/cmTransformDepfile.h @@ -0,0 +1,14 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <string> + +enum class cmDepfileFormat +{ + GccDepfile, + VsTlog, +}; + +bool cmTransformDepfile(cmDepfileFormat format, const std::string& prefix, + const std::string& infile, const std::string& outfile); diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index a482ed6..ab66fd0 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -676,7 +676,7 @@ void cmVisualStudio10TargetGenerator::Generate() cmStrCat(this->DefaultArtifactDir, "\\nasm.props"); ConvertToWindowsSlash(propsLocal); this->Makefile->ConfigureFile(propsTemplate, propsLocal, false, true, - true, true); + true); Elem(e1, "Import").Attribute("Project", propsLocal); } } @@ -1020,9 +1020,9 @@ void cmVisualStudio10TargetGenerator::WriteEmbeddedResourceGroup(Elem& e0) cm::string_view tagName = cm::string_view(p).substr(propNamePrefix.length()); if (!tagName.empty()) { - const std::string& value = *props.GetPropertyValue(p); - if (!value.empty()) { - e2.Element(tagName, value); + cmProp value = props.GetPropertyValue(p); + if (cmNonempty(value)) { + e2.Element(tagName, *value); } } } @@ -2402,27 +2402,28 @@ void cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags( configDefines += *ccdefs; } - // Add precompile headers compile options. - std::string customAndPchOptions = options; + // We have pch state in the following situation: + // 1. We have SKIP_PRECOMPILE_HEADERS == true + // 2. We are creating the pre-compiled header + // 3. We are a different language than the linker language AND pch is + // enabled const std::string pchSource = this->GeneratorTarget->GetPchSource(config, lang); - if (!pchSource.empty() && !sf.GetProperty("SKIP_PRECOMPILE_HEADERS")) { - std::string pchOptions; - if (sf.GetFullPath() == pchSource) { - pchOptions = - this->GeneratorTarget->GetPchCreateCompileOptions(config, lang); - } else { - pchOptions = - this->GeneratorTarget->GetPchUseCompileOptions(config, lang); - } - customAndPchOptions = cmStrCat(customAndPchOptions, ';', pchOptions); - } + const bool skipPCH = + pchSource.empty() || sf.GetPropertyAsBool("SKIP_PRECOMPILE_HEADERS"); + const bool makePCH = (sf.GetFullPath() == pchSource); + const bool useSharedPCH = + !skipPCH && (lang == this->GeneratorTarget->GetLinkerLanguage(config)); + const bool useDifferentLangPCH = + !skipPCH && (lang != this->GeneratorTarget->GetLinkerLanguage(config)); + const bool needsPCHFlags = + (makePCH || useSharedPCH || useDifferentLangPCH); // if we have flags or defines for this config then // use them if (!flags.empty() || !options.empty() || !configDefines.empty() || - !includes.empty() || compileAs || noWinRT || - !customAndPchOptions.empty()) { + !includes.empty() || compileAs || noWinRT || !options.empty() || + needsPCHFlags) { cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator; cmIDEFlagTable const* flagtable = nullptr; const std::string& srclang = source->GetLanguage(); @@ -2455,15 +2456,35 @@ void cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags( } else { clOptions.Parse(flags); } - if (!customAndPchOptions.empty()) { + + if (needsPCHFlags) { + // Add precompile headers compile options. + std::string expandedOptions; + std::string pchOptions; + if (makePCH) { + pchOptions = + this->GeneratorTarget->GetPchCreateCompileOptions(config, lang); + } else if (useSharedPCH) { + std::string pchHeader = + this->GeneratorTarget->GetPchHeader(config, lang); + clOptions.AddFlag("ForcedIncludeFiles", pchHeader); + } else if (useDifferentLangPCH) { + pchOptions = + this->GeneratorTarget->GetPchUseCompileOptions(config, lang); + } + this->LocalGenerator->AppendCompileOptions(expandedOptions, + pchOptions); + clOptions.Parse(expandedOptions); + } + + if (!options.empty()) { std::string expandedOptions; if (configDependentOptions) { this->LocalGenerator->AppendCompileOptions( expandedOptions, - genexInterpreter.Evaluate(customAndPchOptions, "COMPILE_OPTIONS")); + genexInterpreter.Evaluate(options, "COMPILE_OPTIONS")); } else { - this->LocalGenerator->AppendCompileOptions(expandedOptions, - customAndPchOptions); + this->LocalGenerator->AppendCompileOptions(expandedOptions, options); } clOptions.Parse(expandedOptions); } @@ -2786,6 +2807,13 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( this->GeneratorTarget->GetPchHeader(configName, linkLanguage); if (this->MSTools && vcxproj == this->ProjectType && pchHeader.empty()) { clOptions.AddFlag("PrecompiledHeader", "NotUsing"); + } else if (this->MSTools && vcxproj == this->ProjectType && + !pchHeader.empty()) { + clOptions.AddFlag("PrecompiledHeader", "Use"); + clOptions.AddFlag("PrecompiledHeaderFile", pchHeader); + std::string pchFile = + this->GeneratorTarget->GetPchFile(configName, linkLanguage); + clOptions.AddFlag("PrecompiledHeaderOutputFile", pchFile); } // Get preprocessor definitions for this directory. @@ -4182,8 +4210,7 @@ void cmVisualStudio10TargetGenerator::WriteProjectReferences(Elem& e0) cmLocalGenerator* lg = dt->GetLocalGenerator(); std::string name = dt->GetName(); std::string path; - cmProp p = dt->GetProperty("EXTERNAL_MSPROJECT"); - if (p) { + if (cmProp p = dt->GetProperty("EXTERNAL_MSPROJECT")) { path = *p; } else { path = cmStrCat(lg->GetCurrentBinaryDirectory(), '/', dt->GetName(), @@ -4971,9 +4998,9 @@ void cmVisualStudio10TargetGenerator::GetCSharpSourceProperties( if (cmHasPrefix(p, propNamePrefix)) { std::string tagName = p.substr(propNamePrefix.length()); if (!tagName.empty()) { - const std::string& val = *props.GetPropertyValue(p); - if (!val.empty()) { - tags[tagName] = val; + cmProp val = props.GetPropertyValue(p); + if (cmNonempty(val)) { + tags[tagName] = *val; } else { tags.erase(tagName); } diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 60a493c..4a2bb49 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -263,7 +263,7 @@ Json::Value cmake::ReportCapabilitiesJson() const } obj["generators"] = generators; obj["fileApi"] = cmFileAPI::ReportCapabilities(); - obj["serverMode"] = true; + obj["serverMode"] = false; return obj; } @@ -1949,7 +1949,7 @@ int cmake::ActualConfigure() } } - auto& mf = this->GlobalGenerator->GetMakefiles()[0]; + const auto& mf = this->GlobalGenerator->GetMakefiles()[0]; if (mf->IsOn("CTEST_USE_LAUNCHERS") && !this->State->GetGlobalProperty("RULE_LAUNCH_COMPILE")) { cmSystemTools::Error( @@ -2291,12 +2291,12 @@ cmProp cmake::GetCacheDefinition(const std::string& name) const return this->State->GetInitializedCacheValue(name); } -void cmake::AddScriptingCommands() +void cmake::AddScriptingCommands() const { GetScriptingCommands(this->GetState()); } -void cmake::AddProjectCommands() +void cmake::AddProjectCommands() const { GetProjectCommands(this->GetState()); } @@ -2571,8 +2571,7 @@ int cmake::CheckBuildSystem() if (this->ClearBuildSystem) { // Get the generator used for this build system. - const char* genName = - cmToCStr(mf.GetDefinition("CMAKE_DEPENDS_GENERATOR")); + std::string genName = mf.GetSafeDefinition("CMAKE_DEPENDS_GENERATOR"); if (!cmNonempty(genName)) { genName = "Unix Makefiles"; } diff --git a/Source/cmake.h b/Source/cmake.h index 1ecf2c2..914b827 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -411,7 +411,7 @@ public: WorkingMode GetWorkingMode() { return this->CurrentWorkingMode; } //! Debug the try compile stuff by not deleting the files - bool GetDebugTryCompile() { return this->DebugTryCompile; } + bool GetDebugTryCompile() const { return this->DebugTryCompile; } void DebugTryCompileOn() { this->DebugTryCompile = true; } /** @@ -456,11 +456,11 @@ public: void SetShowLogContext(bool b) { this->LogContext = b; } //! Do we want debug output during the cmake run. - bool GetDebugOutput() { return this->DebugOutput; } + bool GetDebugOutput() const { return this->DebugOutput; } void SetDebugOutputOn(bool b) { this->DebugOutput = b; } //! Do we want debug output from the find commands during the cmake run. - bool GetDebugFindOutput() { return this->DebugFindOutput; } + bool GetDebugFindOutput() const { return this->DebugFindOutput; } void SetDebugFindOutputOn(bool b) { this->DebugFindOutput = b; } //! Do we want trace output during the cmake run. @@ -482,11 +482,11 @@ public: void SetTraceFile(std::string const& file); void PrintTraceFormatVersion(); - bool GetWarnUninitialized() { return this->WarnUninitialized; } + bool GetWarnUninitialized() const { return this->WarnUninitialized; } void SetWarnUninitialized(bool b) { this->WarnUninitialized = b; } - bool GetWarnUnusedCli() { return this->WarnUnusedCli; } + bool GetWarnUnusedCli() const { return this->WarnUnusedCli; } void SetWarnUnusedCli(bool b) { this->WarnUnusedCli = b; } - bool GetCheckSystemVars() { return this->CheckSystemVars; } + bool GetCheckSystemVars() const { return this->CheckSystemVars; } void SetCheckSystemVars(bool b) { this->CheckSystemVars = b; } void MarkCliAsUsed(const std::string& variable); @@ -591,8 +591,8 @@ protected: using RegisteredExtraGeneratorsVector = std::vector<cmExternalMakefileProjectGeneratorFactory*>; RegisteredExtraGeneratorsVector ExtraGenerators; - void AddScriptingCommands(); - void AddProjectCommands(); + void AddScriptingCommands() const; + void AddProjectCommands() const; void AddDefaultGenerators(); void AddDefaultExtraGenerators(); diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index e2ff8b7..a611dd7 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -20,6 +20,7 @@ #include "cmStateSnapshot.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmTransformDepfile.h" #include "cmUVProcessChain.h" #include "cmUtils.hxx" #include "cmVersion.h" @@ -28,8 +29,6 @@ #if !defined(CMAKE_BOOTSTRAP) # include "cmDependsFortran.h" // For -E cmake_copy_f90_mod callback. # include "cmFileTime.h" -# include "cmServer.h" -# include "cmServerConnection.h" # include "bindexplib.h" #endif @@ -59,10 +58,9 @@ #include "cmsys/Directory.hxx" #include "cmsys/FStream.hxx" #include "cmsys/Process.h" +#include "cmsys/RegularExpression.hxx" #include "cmsys/Terminal.h" -class cmConnection; - int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg, std::vector<std::string>::const_iterator argEnd); int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, @@ -121,7 +119,6 @@ void CMakeCommandUsage(const char* program) "(on one volume)\n" << " rm [-rRf] <file/dir>... - remove files or directories, use -f to " "force it, r or R to remove directories and their contents recursively\n" - << " server - start cmake in server mode\n" << " sleep <number>... - sleep for given number of seconds\n" << " tar [cxt][vf][zjJ] file.tar [file/dir1 file/dir2 ...]\n" << " - create or extract a tar or zip archive\n" @@ -1359,47 +1356,8 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, } if (args[1] == "server") { - const std::string pipePrefix = "--pipe="; - bool supportExperimental = false; - bool isDebug = false; - std::string pipe; - - for (auto const& arg : cmMakeRange(args).advance(2)) { - if (arg == "--experimental") { - supportExperimental = true; - } else if (arg == "--debug") { - pipe.clear(); - isDebug = true; - } else if (cmHasPrefix(arg, pipePrefix)) { - isDebug = false; - pipe = arg.substr(pipePrefix.size()); - if (pipe.empty()) { - cmSystemTools::Error("No pipe given after --pipe="); - return 2; - } - } else { - cmSystemTools::Error("Unknown argument for server mode"); - return 1; - } - } -#if !defined(CMAKE_BOOTSTRAP) - cmConnection* conn; - if (isDebug) { - conn = new cmServerStdIoConnection; - } else { - conn = new cmServerPipeConnection(pipe); - } - cmServer server(conn, supportExperimental); - std::string errorMessage; - if (server.Serve(&errorMessage)) { - return 0; - } - cmSystemTools::Error(errorMessage); -#else - static_cast<void>(supportExperimental); - static_cast<void>(isDebug); - cmSystemTools::Error("CMake was not built with server mode enabled"); -#endif + cmSystemTools::Error( + "CMake server mode has been removed in favor of the file-api."); return 1; } @@ -1435,6 +1393,23 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, return cmcmd::WindowsCEEnvironment("9.0", args[2]); } #endif + + // Internal depfile transformation + if (args[1] == "cmake_transform_depfile" && args.size() == 6) { + auto format = cmDepfileFormat::GccDepfile; + if (args[2] == "gccdepfile") { + format = cmDepfileFormat::GccDepfile; + } else if (args[2] == "vstlog") { + format = cmDepfileFormat::VsTlog; + } else { + return 1; + } + std::string prefix = args[3]; + if (prefix == "./") { + prefix.clear(); + } + return cmTransformDepfile(format, prefix, args[4], args[5]) ? 0 : 1; + } } ::CMakeCommandUsage(args[0].c_str()); @@ -1737,7 +1712,6 @@ int cmcmd::WindowsCEEnvironment(const char* version, const std::string& name) int cmcmd::RunPreprocessor(const std::vector<std::string>& command, const std::string& intermediate_file) { - cmUVProcessChainBuilder builder; uv_fs_t fs_req; @@ -1769,7 +1743,6 @@ int cmcmd::RunPreprocessor(const std::vector<std::string>& command, return 1; } - return 0; } @@ -1787,19 +1760,56 @@ int cmcmd::RunLLVMRC(std::vector<std::string> const& args) std::cerr << "Invalid cmake_llvm_rc arguments"; return 1; } + const std::string& intermediate_file = args[3]; const std::string& source_file = args[2]; std::vector<std::string> preprocess; std::vector<std::string> resource_compile; std::vector<std::string>* pArgTgt = &preprocess; + + static const cmsys::RegularExpression llvm_rc_only_single_arg("^[-/](N|Y)"); + static const cmsys::RegularExpression llvm_rc_only_double_arg( + "^[-/](C|LN|L)(.)?"); + static const cmsys::RegularExpression common_double_arg( + "^[-/](D|U|I|FO|fo|Fo)(.)?"); + bool acceptNextArg = false; + bool skipNextArg = false; for (std::string const& arg : cmMakeRange(args).advance(4)) { + if (skipNextArg) { + skipNextArg = false; + continue; + } // We use ++ as seperator between the preprocessing step definition and the // rc compilation step becase we need to prepend a -- to seperate the // source file properly from other options when using clang-cl for // preprocessing. if (arg == "++") { pArgTgt = &resource_compile; + skipNextArg = false; + acceptNextArg = true; } else { + cmsys::RegularExpressionMatch match; + if (!acceptNextArg) { + if (common_double_arg.find(arg.c_str(), match)) { + acceptNextArg = match.match(2).empty(); + } else { + if (llvm_rc_only_single_arg.find(arg.c_str(), match)) { + if (pArgTgt == &preprocess) { + continue; + } + } else if (llvm_rc_only_double_arg.find(arg.c_str(), match)) { + if (pArgTgt == &preprocess) { + skipNextArg = match.match(2).empty(); + continue; + } + acceptNextArg = match.match(2).empty(); + } else if (pArgTgt == &resource_compile) { + continue; + } + } + } else { + acceptNextArg = false; + } if (arg.find("SOURCE_DIR") != std::string::npos) { std::string sourceDirArg = arg; cmSystemTools::ReplaceString( @@ -1819,10 +1829,14 @@ int cmcmd::RunLLVMRC(std::vector<std::string> const& args) std::cerr << "Empty resource compilation command"; return 1; } + // Since we might have skipped the last argument to llvm-rc + // we need to make sure the llvm-rc source file is present in the commandline + if (resource_compile.back() != intermediate_file) { + resource_compile.push_back(intermediate_file); + } auto result = RunPreprocessor(preprocess, intermediate_file); if (result != 0) { - cmSystemTools::RemoveFile(intermediate_file); return result; } @@ -1986,7 +2000,7 @@ static bool RunCommand(const char* comment, << NumberFormatter(exitFormat, retCode) << ") with the following output:\n" << output; - } else { + } else if (verbose) { // always print the output of the command, unless // it is the dumb rc command banner if (output.find("Resource Compiler Version") == std::string::npos) { |