diff options
Diffstat (limited to 'Source')
134 files changed, 6014 insertions, 2928 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 95b07cb..b6f7c85 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -197,6 +197,8 @@ set(SRCS cmCustomCommandLines.cxx cmCustomCommandLines.h cmCustomCommandTypes.h + cmCxxModuleMapper.cxx + cmCxxModuleMapper.h cmDefinitions.cxx cmDefinitions.h cmDependencyProvider.h @@ -543,6 +545,8 @@ set(SRCS cmExecuteProcessCommand.h cmExpandedCommandArgument.cxx cmExpandedCommandArgument.h + cmExperimental.cxx + cmExperimental.h cmExportCommand.cxx cmExportCommand.h cmExportLibraryDependenciesCommand.cxx @@ -567,6 +571,8 @@ set(SRCS cmFindProgramCommand.h cmForEachCommand.cxx cmForEachCommand.h + cmBlockCommand.cxx + cmBlockCommand.h cmFunctionBlocker.cxx cmFunctionBlocker.h cmFunctionCommand.cxx @@ -603,6 +609,8 @@ set(SRCS cmInstallCommand.h cmInstallCommandArguments.cxx cmInstallCommandArguments.h + cmInstallCxxModuleBmiGenerator.cxx + cmInstallCxxModuleBmiGenerator.h cmInstallFilesCommand.cxx cmInstallFilesCommand.h cmInstallProgramsCommand.cxx diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 6851aa2..91c9513 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,7 +1,7 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 24) -set(CMake_VERSION_PATCH 1) +set(CMake_VERSION_PATCH 20220831) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) diff --git a/Source/CPack/cmCPackFreeBSDGenerator.cxx b/Source/CPack/cmCPackFreeBSDGenerator.cxx index 607d797..162dfc7 100644 --- a/Source/CPack/cmCPackFreeBSDGenerator.cxx +++ b/Source/CPack/cmCPackFreeBSDGenerator.cxx @@ -23,8 +23,6 @@ // Suffix used to tell libpkg what compression to use static const char FreeBSDPackageCompression[] = "txz"; -// Resulting package file-suffix, for < 1.17 and >= 1.17 versions of libpkg -static const char FreeBSDPackageSuffix_10[] = ".txz"; static const char FreeBSDPackageSuffix_17[] = ".pkg"; cmCPackFreeBSDGenerator::cmCPackFreeBSDGenerator() @@ -83,6 +81,14 @@ public: { if (!isValid()) return false; + // The API in the FreeBSD sources (the header has no documentation), + // is as follows: + // + // int pkg_create(struct pkg_create *pc, const char *metadata, const char + // *plist, bool hash) + // + // We let the plist be determined from what is installed, and all + // the rest comes from the manifest data. int r = pkg_create(d, manifest.c_str(), nullptr, false); return r == 0; } @@ -402,7 +408,8 @@ int cmCPackFreeBSDGenerator::PackageFiles() return 0; } - std::string output_dir = cmSystemTools::CollapseFullPath("../", toplevel); + const std::string output_dir = + cmSystemTools::CollapseFullPath("../", toplevel); PkgCreate package(output_dir, toplevel, manifestname); if (package.isValid()) { if (!package.Create()) { @@ -416,40 +423,33 @@ int cmCPackFreeBSDGenerator::PackageFiles() return 0; } - // Specifically looking for packages suffixed with the TAG, either extension - std::string broken_suffix_10 = - cmStrCat('-', var_lookup("CPACK_TOPLEVEL_TAG"), FreeBSDPackageSuffix_10); + // Specifically looking for packages suffixed with the TAG std::string broken_suffix_17 = cmStrCat('-', var_lookup("CPACK_TOPLEVEL_TAG"), FreeBSDPackageSuffix_17); for (std::string& name : packageFileNames) { cmCPackLogger(cmCPackLog::LOG_DEBUG, "Packagefile " << name << std::endl); - if (cmHasSuffix(name, broken_suffix_10)) { - name.replace(name.size() - broken_suffix_10.size(), std::string::npos, - FreeBSDPackageSuffix_10); - break; - } if (cmHasSuffix(name, broken_suffix_17)) { name.replace(name.size() - broken_suffix_17.size(), std::string::npos, FreeBSDPackageSuffix_17); break; } } - // If the name uses a *new* style name, which doesn't exist, but there - // is an *old* style name, then use that instead. This indicates we used - // an older libpkg, which still creates .txz instead of .pkg files. - for (std::string& name : packageFileNames) { - if (cmHasSuffix(name, FreeBSDPackageSuffix_17) && - !cmSystemTools::FileExists(name)) { - const std::string badSuffix(FreeBSDPackageSuffix_17); - const std::string goodSuffix(FreeBSDPackageSuffix_10); - std::string repairedName(name); - repairedName.replace(repairedName.size() - badSuffix.size(), - std::string::npos, goodSuffix); - if (cmSystemTools::FileExists(repairedName)) { - name = repairedName; - cmCPackLogger(cmCPackLog::LOG_DEBUG, - "Repaired packagefile " << name << std::endl); - } + + const std::string packageFileName = + var_lookup("CPACK_PACKAGE_FILE_NAME") + FreeBSDPackageSuffix_17; + if (packageFileNames.size() == 1 && !packageFileName.empty() && + packageFileNames[0] != packageFileName) { + // Since libpkg always writes <name>-<version>.<suffix>, + // if there is a CPACK_PACKAGE_FILE_NAME set, we need to + // rename, and then re-set the name. + const std::string sourceFile = packageFileNames[0]; + const std::string packageSubDirectory = + cmSystemTools::GetParentDirectory(sourceFile); + const std::string targetFileName = + packageSubDirectory + '/' + packageFileName; + if (cmSystemTools::RenameFile(sourceFile, targetFileName)) { + this->packageFileNames.clear(); + this->packageFileNames.emplace_back(targetFileName); } } diff --git a/Source/CPack/cpack.cxx b/Source/CPack/cpack.cxx index 221f7dd..4d9a42a 100644 --- a/Source/CPack/cpack.cxx +++ b/Source/CPack/cpack.cxx @@ -50,7 +50,7 @@ const char* cmDocumentationOptions[][2] = { { "-C <Configuration>", "Specify the project configuration" }, { "-D <var>=<value>", "Set a CPack variable." }, { "--config <configFile>", "Specify the config file." }, - { "--verbose,-V", "Enable verbose output" }, + { "-V,--verbose", "Enable verbose output" }, { "--trace", "Put underlying cmake scripts in trace mode." }, { "--trace-expand", "Put underlying cmake scripts in expanded trace mode." }, { "--debug", "Enable debug output (for CPack developers)" }, diff --git a/Source/CTest/cmCTestCoverageCommand.cxx b/Source/CTest/cmCTestCoverageCommand.cxx index 7432d08..97b0a89 100644 --- a/Source/CTest/cmCTestCoverageCommand.cxx +++ b/Source/CTest/cmCTestCoverageCommand.cxx @@ -4,7 +4,6 @@ #include <set> -#include <cmext/algorithm> #include <cmext/string_view> #include "cmCTest.h" @@ -18,13 +17,6 @@ void cmCTestCoverageCommand::BindArguments() this->Bind("LABELS"_s, this->Labels); } -void cmCTestCoverageCommand::CheckArguments( - std::vector<std::string> const& keywords) -{ - this->LabelsMentioned = - !this->Labels.empty() || cm::contains(keywords, "LABELS"); -} - cmCTestGenericHandler* cmCTestCoverageCommand::InitializeHandler() { this->CTest->SetCTestConfigurationFromCMakeVariable( @@ -36,9 +28,9 @@ cmCTestGenericHandler* cmCTestCoverageCommand::InitializeHandler() handler->Initialize(); // If a LABELS option was given, select only files with the labels. - if (this->LabelsMentioned) { + if (this->Labels) { handler->SetLabelFilter( - std::set<std::string>(this->Labels.begin(), this->Labels.end())); + std::set<std::string>(this->Labels->begin(), this->Labels->end())); } handler->SetQuiet(this->Quiet); diff --git a/Source/CTest/cmCTestCoverageCommand.h b/Source/CTest/cmCTestCoverageCommand.h index 9344852..55c68b2 100644 --- a/Source/CTest/cmCTestCoverageCommand.h +++ b/Source/CTest/cmCTestCoverageCommand.h @@ -9,7 +9,9 @@ #include <vector> #include <cm/memory> +#include <cm/optional> +#include "cmArgumentParserTypes.h" // IWYU pragma: keep #include "cmCTestHandlerCommand.h" #include "cmCommand.h" @@ -41,9 +43,7 @@ public: protected: void BindArguments() override; - void CheckArguments(std::vector<std::string> const& keywords) override; cmCTestGenericHandler* InitializeHandler() override; - bool LabelsMentioned; - std::vector<std::string> Labels; + cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Labels; }; diff --git a/Source/CTest/cmCTestHandlerCommand.cxx b/Source/CTest/cmCTestHandlerCommand.cxx index 5494d20..be952cd 100644 --- a/Source/CTest/cmCTestHandlerCommand.cxx +++ b/Source/CTest/cmCTestHandlerCommand.cxx @@ -7,6 +7,7 @@ #include <cstring> #include <sstream> +#include <cm/string_view> #include <cmext/string_view> #include "cmCTest.h" @@ -81,15 +82,13 @@ bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args, // Process input arguments. std::vector<std::string> unparsedArguments; - std::vector<std::string> keywordsMissingValue; - std::vector<std::string> parsedKeywords; - this->Parse(args, &unparsedArguments, &keywordsMissingValue, - &parsedKeywords); - this->CheckArguments(keywordsMissingValue); - - std::sort(parsedKeywords.begin(), parsedKeywords.end()); - auto it = std::adjacent_find(parsedKeywords.begin(), parsedKeywords.end()); - if (it != parsedKeywords.end()) { + this->Parse(args, &unparsedArguments); + this->CheckArguments(); + + std::sort(this->ParsedKeywords.begin(), this->ParsedKeywords.end()); + auto it = std::adjacent_find(this->ParsedKeywords.begin(), + this->ParsedKeywords.end()); + if (it != this->ParsedKeywords.end()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, cmStrCat("Called with more than one value for ", *it)); @@ -233,6 +232,7 @@ void cmCTestHandlerCommand::ProcessAdditionalValues(cmCTestGenericHandler*) void cmCTestHandlerCommand::BindArguments() { + this->BindParsedKeywords(this->ParsedKeywords); this->Bind("APPEND"_s, this->Append); this->Bind("QUIET"_s, this->Quiet); this->Bind("RETURN_VALUE"_s, this->ReturnValue); @@ -242,6 +242,6 @@ void cmCTestHandlerCommand::BindArguments() this->Bind("SUBMIT_INDEX"_s, this->SubmitIndex); } -void cmCTestHandlerCommand::CheckArguments(std::vector<std::string> const&) +void cmCTestHandlerCommand::CheckArguments() { } diff --git a/Source/CTest/cmCTestHandlerCommand.h b/Source/CTest/cmCTestHandlerCommand.h index 756952d..ed6d9af 100644 --- a/Source/CTest/cmCTestHandlerCommand.h +++ b/Source/CTest/cmCTestHandlerCommand.h @@ -7,6 +7,8 @@ #include <string> #include <vector> +#include <cm/string_view> + #include "cmArgumentParser.h" #include "cmCTestCommand.h" @@ -42,8 +44,9 @@ protected: // Command argument handling. virtual void BindArguments(); - virtual void CheckArguments(std::vector<std::string> const& keywords); + virtual void CheckArguments(); + std::vector<cm::string_view> ParsedKeywords; bool Append = false; bool Quiet = false; std::string CaptureCMakeError; diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx index 2a2cb1c..5efe69f 100644 --- a/Source/CTest/cmCTestRunTest.cxx +++ b/Source/CTest/cmCTestRunTest.cxx @@ -8,16 +8,12 @@ #include <cstdint> #include <cstdio> #include <cstring> -#include <functional> #include <iomanip> #include <ratio> #include <sstream> #include <utility> #include <cm/memory> -#include <cm/optional> -#include <cm/string_view> -#include <cmext/string_view> #include "cmsys/RegularExpression.hxx" @@ -787,149 +783,31 @@ bool cmCTestRunTest::ForkProcess( this->TestProcess->SetTimeout(timeout); -#ifndef CMAKE_BOOTSTRAP cmSystemTools::SaveRestoreEnvironment sre; -#endif - std::ostringstream envMeasurement; + + // We split processing ENVIRONMENT and ENVIRONMENT_MODIFICATION into two + // phases to ensure that MYVAR=reset: in the latter phase resets to the + // former phase's settings, rather than to the original environment. if (environment && !environment->empty()) { - // Environment modification works on the assumption that the environment is - // actually modified here. If another strategy is used, there will need to - // be updates below in `apply_diff`. - cmSystemTools::AppendEnv(*environment); - for (auto const& var : *environment) { - envMeasurement << var << std::endl; - } + cmSystemTools::EnvDiff diff; + diff.AppendEnv(*environment); + diff.ApplyToCurrentEnv(&envMeasurement); } if (environment_modification && !environment_modification->empty()) { - std::map<std::string, cm::optional<std::string>> env_application; - -#ifdef _WIN32 - char path_sep = ';'; -#else - char path_sep = ':'; -#endif - - auto apply_diff = - [&env_application](const std::string& name, - std::function<void(std::string&)> const& apply) { - cm::optional<std::string> old_value = env_application[name]; - std::string output; - if (old_value) { - output = *old_value; - } else { - // This only works because the environment is actually modified above - // (`AppendEnv`). If CTest ever just creates an environment block - // directly, that block will need to be queried for the subprocess' - // value instead. - const char* curval = cmSystemTools::GetEnv(name); - if (curval) { - output = curval; - } - } - apply(output); - env_application[name] = output; - }; - - bool err_occurred = false; + cmSystemTools::EnvDiff diff; + bool env_ok = true; for (auto const& envmod : *environment_modification) { - // Split on `=` - auto const eq_loc = envmod.find_first_of('='); - if (eq_loc == std::string::npos) { - cmCTestLog(this->CTest, ERROR_MESSAGE, - "Error: Missing `=` after the variable name in: " - << envmod << std::endl); - err_occurred = true; - continue; - } - auto const name = envmod.substr(0, eq_loc); - - // Split value on `:` - auto const op_value_start = eq_loc + 1; - auto const colon_loc = envmod.find_first_of(':', op_value_start); - if (colon_loc == std::string::npos) { - cmCTestLog(this->CTest, ERROR_MESSAGE, - "Error: Missing `:` after the operation in: " << envmod - << std::endl); - err_occurred = true; - continue; - } - auto const op = - envmod.substr(op_value_start, colon_loc - op_value_start); - - auto const value_start = colon_loc + 1; - auto const value = envmod.substr(value_start); - - // Determine what to do with the operation. - if (op == "reset"_s) { - auto entry = env_application.find(name); - if (entry != env_application.end()) { - env_application.erase(entry); - } - } else if (op == "set"_s) { - env_application[name] = value; - } else if (op == "unset"_s) { - env_application[name] = {}; - } else if (op == "string_append"_s) { - apply_diff(name, [&value](std::string& output) { output += value; }); - } else if (op == "string_prepend"_s) { - apply_diff(name, - [&value](std::string& output) { output.insert(0, value); }); - } else if (op == "path_list_append"_s) { - apply_diff(name, [&value, path_sep](std::string& output) { - if (!output.empty()) { - output += path_sep; - } - output += value; - }); - } else if (op == "path_list_prepend"_s) { - apply_diff(name, [&value, path_sep](std::string& output) { - if (!output.empty()) { - output.insert(output.begin(), path_sep); - } - output.insert(0, value); - }); - } else if (op == "cmake_list_append"_s) { - apply_diff(name, [&value](std::string& output) { - if (!output.empty()) { - output += ';'; - } - output += value; - }); - } else if (op == "cmake_list_prepend"_s) { - apply_diff(name, [&value](std::string& output) { - if (!output.empty()) { - output.insert(output.begin(), ';'); - } - output.insert(0, value); - }); - } else { - cmCTestLog(this->CTest, ERROR_MESSAGE, - "Error: Unrecognized environment manipulation argument: " - << op << std::endl); - err_occurred = true; - continue; - } + env_ok &= diff.ParseOperation(envmod); } - if (err_occurred) { + if (!env_ok) { return false; } - for (auto const& env_apply : env_application) { - if (env_apply.second) { - auto const env_update = - cmStrCat(env_apply.first, '=', *env_apply.second); - cmSystemTools::PutEnv(env_update); - envMeasurement << env_update << std::endl; - } else { - cmSystemTools::UnsetEnv(env_apply.first.c_str()); - // Signify that this variable is being actively unset - envMeasurement << "#" << env_apply.first << "=" << std::endl; - } - } + diff.ApplyToCurrentEnv(&envMeasurement); } if (this->UseAllocatedResources) { diff --git a/Source/CTest/cmCTestSubmitCommand.cxx b/Source/CTest/cmCTestSubmitCommand.cxx index a2dc615..a1933cc 100644 --- a/Source/CTest/cmCTestSubmitCommand.cxx +++ b/Source/CTest/cmCTestSubmitCommand.cxx @@ -8,7 +8,6 @@ #include <cm/memory> #include <cm/vector> -#include <cmext/algorithm> #include <cmext/string_view> #include "cmCTest.h" @@ -87,7 +86,7 @@ cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler() // If FILES are given, but not PARTS, only the FILES are submitted // and *no* PARTS are submitted. // (This is why we select the empty "noParts" set in the - // FilesMentioned block below...) + // if(this->Files) block below...) // // If PARTS are given, only the selected PARTS are submitted. // @@ -96,7 +95,7 @@ cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler() // If given explicit FILES to submit, pass them to the handler. // - if (this->FilesMentioned) { + if (this->Files) { // Intentionally select *no* PARTS. (Pass an empty set.) If PARTS // were also explicitly mentioned, they will be selected below... // But FILES with no PARTS mentioned should just submit the FILES @@ -104,14 +103,14 @@ cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler() // handler->SelectParts(std::set<cmCTest::Part>()); handler->SelectFiles( - std::set<std::string>(this->Files.begin(), this->Files.end())); + std::set<std::string>(this->Files->begin(), this->Files->end())); } // If a PARTS option was given, select only the named parts for submission. // - if (this->PartsMentioned) { + if (this->Parts) { auto parts = - cmMakeRange(this->Parts).transform([this](std::string const& arg) { + cmMakeRange(*(this->Parts)).transform([this](std::string const& arg) { return this->CTest->GetPartFromName(arg); }); handler->SelectParts(std::set<cmCTest::Part>(parts.begin(), parts.end())); @@ -172,33 +171,31 @@ void cmCTestSubmitCommand::BindArguments() this->cmCTestHandlerCommand::BindArguments(); } -void cmCTestSubmitCommand::CheckArguments( - std::vector<std::string> const& keywords) +void cmCTestSubmitCommand::CheckArguments() { - this->PartsMentioned = - !this->Parts.empty() || cm::contains(keywords, "PARTS"); - this->FilesMentioned = - !this->Files.empty() || cm::contains(keywords, "FILES"); - - cm::erase_if(this->Parts, [this](std::string const& arg) -> bool { - cmCTest::Part p = this->CTest->GetPartFromName(arg); - if (p == cmCTest::PartCount) { - std::ostringstream e; - e << "Part name \"" << arg << "\" is invalid."; - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return true; - } - return false; - }); - - cm::erase_if(this->Files, [this](std::string const& arg) -> bool { - if (!cmSystemTools::FileExists(arg)) { - std::ostringstream e; - e << "File \"" << arg << "\" does not exist. Cannot submit " - << "a non-existent file."; - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return true; - } - return false; - }); + if (this->Parts) { + cm::erase_if(*(this->Parts), [this](std::string const& arg) -> bool { + cmCTest::Part p = this->CTest->GetPartFromName(arg); + if (p == cmCTest::PartCount) { + std::ostringstream e; + e << "Part name \"" << arg << "\" is invalid."; + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return true; + } + return false; + }); + } + + if (this->Files) { + cm::erase_if(*(this->Files), [this](std::string const& arg) -> bool { + if (!cmSystemTools::FileExists(arg)) { + std::ostringstream e; + e << "File \"" << arg << "\" does not exist. Cannot submit " + << "a non-existent file."; + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return true; + } + return false; + }); + } } diff --git a/Source/CTest/cmCTestSubmitCommand.h b/Source/CTest/cmCTestSubmitCommand.h index c5d11df..b67f182 100644 --- a/Source/CTest/cmCTestSubmitCommand.h +++ b/Source/CTest/cmCTestSubmitCommand.h @@ -8,6 +8,9 @@ #include <string> #include <vector> +#include <cm/optional> + +#include "cmArgumentParserTypes.h" #include "cmCTestHandlerCommand.h" class cmCommand; @@ -35,13 +38,11 @@ public: protected: void BindArguments() override; - void CheckArguments(std::vector<std::string> const& keywords) override; + void CheckArguments() override; cmCTestGenericHandler* InitializeHandler() override; bool CDashUpload = false; - bool FilesMentioned = false; bool InternalTest = false; - bool PartsMentioned = false; std::string BuildID; std::string CDashUploadFile; @@ -50,7 +51,7 @@ protected: std::string RetryDelay; std::string SubmitURL; - std::vector<std::string> Files; - std::vector<std::string> HttpHeaders; - std::vector<std::string> Parts; + cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Files; + ArgumentParser::MaybeEmpty<std::vector<std::string>> HttpHeaders; + cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Parts; }; diff --git a/Source/CTest/cmCTestUploadCommand.cxx b/Source/CTest/cmCTestUploadCommand.cxx index f86ee0d..2ed671c 100644 --- a/Source/CTest/cmCTestUploadCommand.cxx +++ b/Source/CTest/cmCTestUploadCommand.cxx @@ -21,7 +21,7 @@ void cmCTestUploadCommand::BindArguments() this->Bind("CAPTURE_CMAKE_ERROR"_s, this->CaptureCMakeError); } -void cmCTestUploadCommand::CheckArguments(std::vector<std::string> const&) +void cmCTestUploadCommand::CheckArguments() { cm::erase_if(this->Files, [this](std::string const& arg) -> bool { if (!cmSystemTools::FileExists(arg)) { diff --git a/Source/CTest/cmCTestUploadCommand.h b/Source/CTest/cmCTestUploadCommand.h index fe155f6..a9d1dd2 100644 --- a/Source/CTest/cmCTestUploadCommand.h +++ b/Source/CTest/cmCTestUploadCommand.h @@ -10,6 +10,7 @@ #include <cm/memory> +#include "cmArgumentParserTypes.h" #include "cmCTestHandlerCommand.h" #include "cmCommand.h" @@ -42,8 +43,8 @@ public: protected: void BindArguments() override; - void CheckArguments(std::vector<std::string> const&) override; + void CheckArguments() override; cmCTestGenericHandler* InitializeHandler() override; - std::vector<std::string> Files; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Files; }; diff --git a/Source/CursesDialog/cmCursesLongMessageForm.cxx b/Source/CursesDialog/cmCursesLongMessageForm.cxx index b14a751..6e6f0d6 100644 --- a/Source/CursesDialog/cmCursesLongMessageForm.cxx +++ b/Source/CursesDialog/cmCursesLongMessageForm.cxx @@ -85,7 +85,7 @@ void cmCursesLongMessageForm::UpdateStatusBar() for (size_t i = 0; i < sideSpace; i++) { version[i] = ' '; } - sprintf(version + sideSpace, "%s", vertmp); + snprintf(version + sideSpace, sizeof(version) - sideSpace, "%s", vertmp); version[width] = '\0'; char fmt_s[] = "%s"; diff --git a/Source/CursesDialog/form/.gitattributes b/Source/CursesDialog/form/.gitattributes index 12ede74..6dfa627 100644 --- a/Source/CursesDialog/form/.gitattributes +++ b/Source/CursesDialog/form/.gitattributes @@ -1 +1,2 @@ +* -whitespace * -format.clang-format-6.0 diff --git a/Source/CursesDialog/form/fty_int.c b/Source/CursesDialog/form/fty_int.c index 7107fcc..7aeb4b8 100644 --- a/Source/CursesDialog/form/fty_int.c +++ b/Source/CursesDialog/form/fty_int.c @@ -117,7 +117,7 @@ static bool Check_Integer_Field(FIELD * field, const void * argp) { if (val<low || val>high) return FALSE; } - sprintf(buf,"%.*ld",(prec>0?prec:0),val); + snprintf(buf,sizeof(buf),"%.*ld",(prec>0?prec:0),val); set_field_buffer(field,0,buf); return TRUE; } diff --git a/Source/CursesDialog/form/fty_num.c b/Source/CursesDialog/form/fty_num.c index 7809599..4109b6f 100644 --- a/Source/CursesDialog/form/fty_num.c +++ b/Source/CursesDialog/form/fty_num.c @@ -140,7 +140,7 @@ static bool Check_Numeric_Field(FIELD * field, const void * argp) { if (val<low || val>high) return FALSE; } - sprintf(buf,"%.*f",(prec>0?prec:0),val); + snprintf(buf,sizeof(buf),"%.*f",(prec>0?prec:0),val); set_field_buffer(field,0,buf); return TRUE; } diff --git a/Source/LexerParser/cmFortranLexer.cxx b/Source/LexerParser/cmFortranLexer.cxx index c3d0000..5703de1 100644 --- a/Source/LexerParser/cmFortranLexer.cxx +++ b/Source/LexerParser/cmFortranLexer.cxx @@ -557,31 +557,32 @@ struct yy_trans_info flex_int32_t yy_verify; flex_int32_t yy_nxt; }; -static const flex_int16_t yy_accept[210] = +static const flex_int16_t yy_accept[216] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 49, 51, 50, 53, 1, 49, 33, 2, 47, 48, 35, 37, 50, 39, 49, 46, 46, 46, 46, - 46, 46, 49, 46, 51, 49, 50, 49, 46, 9, - 8, 9, 4, 3, 49, 0, 10, 0, 0, 0, - 0, 0, 33, 33, 34, 36, 39, 49, 46, 46, - 46, 46, 46, 46, 0, 52, 46, 0, 0, 0, - 12, 0, 0, 0, 0, 0, 0, 49, 0, 11, - 46, 0, 0, 5, 0, 0, 0, 0, 29, 0, - 33, 33, 33, 33, 0, 0, 40, 46, 46, 46, - - 46, 45, 12, 12, 0, 0, 0, 23, 0, 0, - 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 46, 46, 46, 46, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 30, 31, 0, 0, 0, 0, 0, 46, 46, - 46, 46, 0, 24, 25, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 20, 32, 27, 0, 0, 0, - 46, 46, 43, 46, 0, 26, 21, 0, 0, 0, - 19, 0, 0, 18, 28, 0, 0, 41, 46, 46, - 17, 22, 0, 7, 38, 7, 15, 0, 46, 46, - - 14, 16, 42, 44, 0, 0, 0, 13, 0 + 46, 46, 49, 46, 51, 49, 50, 51, 49, 46, + 9, 8, 9, 9, 4, 3, 49, 0, 10, 0, + 0, 0, 0, 0, 33, 33, 34, 36, 39, 49, + 46, 46, 46, 46, 46, 46, 0, 52, 0, 46, + 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, + 0, 49, 0, 11, 46, 0, 0, 0, 5, 0, + 0, 0, 0, 0, 29, 0, 33, 33, 33, 33, + + 0, 0, 40, 46, 46, 46, 46, 45, 12, 12, + 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 46, 46, 46, 46, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 30, 31, 0, + 0, 0, 0, 0, 46, 46, 46, 46, 0, 24, + 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 20, 32, 27, 0, 0, 0, 46, 46, 43, 46, + 0, 26, 21, 0, 0, 0, 19, 0, 0, 18, + 28, 0, 0, 41, 46, 46, 17, 22, 0, 7, + + 38, 7, 15, 0, 46, 46, 14, 16, 42, 44, + 0, 0, 0, 13, 0 } ; static const YY_CHAR yy_ec[256] = @@ -618,189 +619,197 @@ static const YY_CHAR yy_ec[256] = static const YY_CHAR yy_meta[50] = { 0, - 1, 2, 2, 3, 4, 3, 3, 1, 1, 3, - 3, 3, 3, 1, 3, 5, 3, 3, 1, 3, + 1, 2, 2, 2, 3, 4, 4, 1, 1, 4, + 4, 4, 4, 1, 4, 5, 4, 4, 1, 4, 6, 1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, 5, 7, 7, 7, 7, 7, 7, 7, 7, 7 } ; -static const flex_int16_t yy_base[219] = +static const flex_int16_t yy_base[225] = { 0, - 0, 48, 0, 49, 464, 56, 52, 57, 62, 68, - 466, 0, 468, 468, 462, 468, 74, 81, 468, 468, - 468, 468, 447, 468, 442, 440, 0, 19, 41, 427, - 47, 41, 90, 119, 97, 158, 455, 207, 247, 468, - 454, 101, 468, 468, 0, 455, 468, 105, 430, 423, - 62, 67, 119, 151, 468, 468, 468, 121, 0, 90, - 93, 110, 431, 112, 142, 468, 0, 160, 295, 0, - 162, 411, 123, 102, 408, 405, 446, 344, 447, 468, - 0, 444, 170, 174, 420, 421, 132, 404, 95, 404, - 180, 186, 192, 228, 297, 397, 0, 168, 144, 52, - - 411, 0, 204, 217, 397, 179, 390, 170, 389, 378, - 364, 390, 389, 230, 468, 363, 355, 337, 337, 334, - 335, 335, 330, 334, 187, 339, 267, 339, 327, 327, - 327, 324, 325, 325, 318, 319, 318, 354, 352, 323, - 327, 468, 468, 310, 307, 305, 297, 297, 275, 275, - 277, 279, 287, 468, 468, 286, 283, 273, 196, 307, - 200, 238, 234, 210, 468, 468, 468, 174, 171, 162, - 279, 182, 0, 269, 150, 468, 468, 137, 109, 323, - 468, 239, 0, 468, 468, 72, 71, 0, 283, 283, - 468, 468, 51, 468, 468, 468, 468, 37, 283, 288, - - 330, 468, 0, 0, 331, 0, 52, 468, 468, 384, - 391, 397, 400, 407, 414, 421, 428, 435 + 0, 48, 0, 49, 55, 58, 64, 66, 75, 83, + 491, 0, 492, 492, 487, 492, 86, 92, 492, 492, + 492, 492, 472, 492, 467, 465, 0, 56, 59, 452, + 66, 16, 105, 131, 109, 170, 480, 481, 219, 259, + 492, 478, 479, 116, 492, 492, 0, 478, 492, 111, + 453, 446, 34, 78, 155, 174, 492, 492, 492, 121, + 0, 29, 105, 101, 454, 101, 131, 492, 474, 0, + 180, 307, 0, 146, 433, 117, 94, 430, 427, 468, + 467, 356, 468, 492, 0, 465, 464, 187, 191, 465, + 439, 440, 149, 423, 126, 423, 200, 240, 311, 322, + + 206, 416, 0, 152, 180, 176, 430, 0, 216, 224, + 417, 186, 418, 127, 418, 411, 415, 451, 450, 247, + 492, 423, 416, 398, 393, 373, 364, 364, 359, 353, + 198, 358, 178, 358, 346, 346, 346, 343, 344, 344, + 338, 340, 339, 376, 374, 343, 346, 492, 492, 329, + 325, 324, 313, 315, 211, 211, 291, 293, 313, 492, + 492, 314, 304, 304, 261, 328, 212, 249, 243, 203, + 492, 492, 492, 173, 158, 150, 293, 172, 0, 273, + 144, 492, 492, 137, 125, 335, 492, 339, 0, 492, + 492, 112, 63, 0, 304, 300, 492, 492, 58, 492, + + 492, 492, 492, 30, 311, 312, 361, 492, 0, 0, + 366, 0, 44, 492, 492, 396, 403, 409, 412, 419, + 426, 433, 440, 447 } ; -static const flex_int16_t yy_def[219] = +static const flex_int16_t yy_def[225] = { 0, - 209, 1, 1, 1, 1, 1, 210, 210, 210, 210, - 209, 211, 209, 209, 212, 209, 211, 209, 209, 209, - 209, 209, 209, 209, 209, 211, 213, 213, 213, 213, - 213, 213, 211, 213, 209, 211, 209, 214, 209, 209, - 209, 209, 209, 209, 211, 212, 209, 209, 209, 209, - 209, 209, 209, 215, 209, 209, 209, 211, 213, 213, - 213, 213, 213, 213, 209, 209, 34, 209, 209, 69, - 211, 209, 209, 209, 209, 209, 209, 214, 214, 209, - 39, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 215, 215, 215, 215, 209, 209, 213, 213, 213, 213, - - 213, 213, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 213, 213, 213, 213, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 213, 213, - 213, 213, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 213, 213, 213, 213, 209, 209, 209, 209, 209, 209, - 209, 216, 217, 209, 209, 209, 209, 213, 213, 213, - 209, 209, 209, 209, 209, 209, 209, 209, 213, 213, - - 209, 209, 213, 213, 209, 218, 218, 209, 0, 209, - 209, 209, 209, 209, 209, 209, 209, 209 + 215, 1, 1, 1, 1, 1, 216, 216, 216, 216, + 215, 217, 215, 215, 218, 215, 217, 215, 215, 215, + 215, 215, 215, 215, 215, 217, 219, 219, 219, 219, + 219, 219, 217, 219, 215, 217, 215, 215, 220, 215, + 215, 215, 215, 215, 215, 215, 217, 218, 215, 215, + 215, 215, 215, 215, 215, 221, 215, 215, 215, 217, + 219, 219, 219, 219, 219, 219, 215, 215, 215, 34, + 215, 215, 72, 217, 215, 215, 215, 215, 215, 215, + 215, 220, 220, 215, 40, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 221, 221, 221, 221, + + 215, 215, 219, 219, 219, 219, 219, 219, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 219, 219, 219, 219, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 219, 219, 219, 219, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 219, 219, 219, 219, + 215, 215, 215, 215, 215, 215, 215, 222, 223, 215, + 215, 215, 215, 219, 219, 219, 215, 215, 215, 215, + + 215, 215, 215, 215, 219, 219, 215, 215, 219, 219, + 215, 224, 224, 215, 0, 215, 215, 215, 215, 215, + 215, 215, 215, 215 } ; -static const flex_int16_t yy_nxt[518] = +static const flex_int16_t yy_nxt[542] = { 0, 12, 13, 14, 13, 13, 15, 16, 12, 17, 18, 19, 20, 21, 12, 22, 12, 23, 24, 12, 25, 12, 26, 27, 27, 27, 27, 28, 27, 27, 29, 27, 30, 27, 27, 27, 31, 27, 32, 33, 34, 27, 27, 28, 27, 29, 27, 27, 31, 32, 35, - 35, 60, 35, 35, 41, 36, 36, 35, 37, 41, - 35, 42, 43, 36, 41, 60, 42, 43, 44, 38, - 41, 42, 208, 61, 44, 48, 64, 42, 48, 202, - 39, 39, 53, 53, 63, 53, 54, 61, 64, 127, - 55, 65, 66, 201, 65, 63, 39, 39, 68, 49, - - 127, 68, 83, 84, 69, 83, 48, 87, 88, 48, - 89, 50, 198, 90, 197, 97, 51, 98, 52, 45, - 53, 53, 95, 53, 54, 95, 45, 45, 55, 99, - 49, 97, 45, 98, 67, 100, 121, 45, 102, 45, - 45, 122, 50, 65, 66, 108, 65, 51, 109, 52, - 193, 100, 92, 53, 102, 92, 93, 45, 67, 70, - 94, 68, 70, 104, 68, 96, 104, 69, 106, 107, - 126, 83, 84, 71, 83, 114, 118, 71, 114, 119, - 192, 92, 53, 115, 92, 93, 126, 92, 53, 94, - 92, 93, 191, 92, 53, 94, 92, 93, 125, 72, - - 73, 94, 74, 75, 189, 104, 76, 78, 104, 80, - 187, 133, 186, 125, 78, 78, 134, 185, 104, 103, - 78, 104, 78, 130, 149, 78, 131, 78, 78, 92, - 53, 114, 92, 93, 114, 149, 184, 94, 183, 115, - 195, 195, 182, 181, 179, 78, 78, 79, 79, 80, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 81, 79, 79, 79, 79, 79, 79, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 79, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 70, 151, 95, 70, - - 171, 95, 172, 173, 174, 188, 190, 199, 180, 203, - 103, 180, 151, 200, 204, 178, 171, 190, 172, 173, - 174, 188, 103, 199, 180, 203, 177, 180, 200, 176, - 204, 205, 205, 175, 205, 205, 72, 73, 103, 74, - 75, 96, 170, 76, 78, 169, 80, 168, 206, 206, - 167, 78, 78, 166, 165, 164, 163, 78, 162, 78, - 161, 160, 78, 159, 78, 78, 158, 157, 156, 155, - 154, 153, 152, 150, 148, 147, 146, 145, 144, 143, - 142, 141, 78, 78, 40, 40, 40, 40, 40, 40, - 40, 45, 140, 139, 138, 45, 45, 46, 46, 46, - - 46, 46, 46, 46, 59, 137, 59, 79, 79, 79, - 79, 79, 79, 79, 91, 91, 91, 91, 91, 91, - 91, 194, 194, 194, 136, 194, 194, 194, 196, 135, - 196, 132, 196, 196, 196, 207, 207, 207, 207, 207, - 129, 207, 128, 124, 123, 120, 117, 116, 113, 80, - 112, 111, 110, 105, 101, 86, 85, 47, 82, 77, - 62, 58, 57, 56, 47, 209, 37, 11, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209 + 35, 66, 35, 35, 103, 36, 36, 37, 38, 35, + 37, 38, 35, 66, 214, 36, 42, 43, 42, 43, + 103, 39, 208, 44, 45, 44, 45, 42, 43, 93, + 94, 46, 40, 40, 44, 42, 43, 50, 62, 46, + 50, 63, 44, 55, 55, 55, 55, 56, 40, 40, + + 207, 57, 62, 65, 204, 63, 67, 68, 69, 67, + 71, 51, 50, 71, 65, 50, 72, 88, 89, 90, + 88, 95, 101, 52, 96, 101, 106, 108, 53, 104, + 54, 47, 67, 68, 69, 67, 51, 114, 47, 47, + 115, 105, 106, 108, 47, 104, 70, 110, 52, 47, + 110, 47, 47, 53, 203, 54, 55, 55, 55, 55, + 56, 74, 112, 113, 57, 102, 199, 127, 139, 47, + 70, 73, 128, 140, 73, 98, 55, 98, 98, 99, + 198, 71, 131, 100, 71, 74, 197, 72, 88, 89, + 90, 88, 120, 124, 195, 120, 125, 131, 193, 192, + + 121, 98, 55, 98, 98, 99, 132, 101, 157, 100, + 101, 75, 76, 133, 77, 78, 191, 110, 79, 82, + 110, 84, 132, 157, 133, 110, 82, 82, 110, 190, + 136, 109, 82, 137, 82, 155, 177, 82, 178, 82, + 82, 98, 55, 98, 98, 99, 155, 189, 120, 100, + 102, 120, 177, 188, 178, 187, 121, 82, 82, 83, + 83, 84, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 85, 83, 83, 83, 83, 83, + 83, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 83, 85, 85, + + 85, 85, 85, 85, 85, 85, 85, 85, 73, 185, + 196, 73, 98, 55, 98, 98, 99, 179, 180, 194, + 100, 196, 109, 98, 55, 98, 98, 99, 205, 186, + 206, 100, 186, 179, 180, 194, 186, 209, 210, 186, + 201, 201, 201, 109, 205, 206, 184, 183, 75, 76, + 109, 77, 78, 209, 210, 79, 82, 182, 84, 181, + 176, 175, 211, 82, 82, 211, 174, 211, 173, 82, + 211, 82, 172, 171, 82, 170, 82, 82, 169, 212, + 168, 167, 166, 165, 212, 164, 163, 162, 161, 160, + 159, 158, 156, 154, 82, 82, 41, 41, 41, 41, + + 41, 41, 41, 47, 153, 152, 151, 47, 47, 48, + 48, 48, 48, 48, 48, 48, 61, 150, 61, 83, + 83, 83, 83, 83, 83, 83, 97, 97, 97, 97, + 97, 97, 97, 200, 200, 149, 200, 200, 200, 200, + 202, 148, 147, 202, 202, 202, 202, 213, 213, 213, + 213, 213, 146, 213, 145, 144, 143, 142, 141, 138, + 135, 134, 130, 129, 126, 123, 122, 89, 86, 119, + 84, 80, 118, 117, 116, 111, 68, 107, 92, 91, + 49, 87, 86, 81, 80, 64, 60, 59, 58, 49, + 215, 11, 215, 215, 215, 215, 215, 215, 215, 215, + + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215 } ; -static const flex_int16_t yy_chk[518] = +static const flex_int16_t yy_chk[542] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, - 4, 28, 2, 4, 7, 2, 4, 6, 6, 8, - 6, 7, 7, 6, 9, 28, 8, 8, 9, 6, - 10, 9, 207, 29, 10, 17, 32, 10, 17, 198, - 6, 6, 18, 18, 31, 18, 18, 29, 32, 100, - 18, 33, 33, 193, 33, 31, 6, 6, 35, 17, - - 100, 35, 42, 42, 35, 42, 48, 51, 51, 48, - 52, 17, 187, 52, 186, 60, 17, 61, 17, 34, - 53, 53, 58, 53, 53, 58, 34, 34, 53, 61, - 48, 60, 34, 61, 34, 62, 89, 34, 64, 34, - 34, 89, 48, 65, 65, 74, 65, 48, 74, 48, - 179, 62, 54, 54, 64, 54, 54, 34, 34, 36, - 54, 68, 36, 71, 68, 58, 71, 68, 73, 73, - 99, 83, 83, 36, 83, 84, 87, 71, 84, 87, - 178, 91, 91, 84, 91, 91, 99, 92, 92, 91, - 92, 92, 175, 93, 93, 92, 93, 93, 98, 36, - - 36, 93, 36, 36, 172, 103, 36, 38, 103, 38, - 170, 108, 169, 98, 38, 38, 108, 168, 104, 103, - 38, 104, 38, 106, 125, 38, 106, 38, 38, 94, - 94, 114, 94, 94, 114, 125, 164, 94, 163, 114, - 182, 182, 162, 161, 159, 38, 38, 39, 39, 39, - 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 39, 39, 39, 39, 69, 127, 95, 69, - - 149, 95, 150, 151, 152, 171, 174, 189, 160, 199, - 69, 160, 127, 190, 200, 158, 149, 174, 150, 151, - 152, 171, 160, 189, 180, 199, 157, 180, 190, 156, - 200, 201, 205, 153, 201, 205, 69, 69, 180, 69, - 69, 95, 148, 69, 78, 147, 78, 146, 201, 205, - 145, 78, 78, 144, 141, 140, 139, 78, 138, 78, - 137, 136, 78, 135, 78, 78, 134, 133, 132, 131, - 130, 129, 128, 126, 124, 123, 122, 121, 120, 119, - 118, 117, 78, 78, 210, 210, 210, 210, 210, 210, - 210, 211, 116, 113, 112, 211, 211, 212, 212, 212, - - 212, 212, 212, 212, 213, 111, 213, 214, 214, 214, - 214, 214, 214, 214, 215, 215, 215, 215, 215, 215, - 215, 216, 216, 216, 110, 216, 216, 216, 217, 109, - 217, 107, 217, 217, 217, 218, 218, 218, 218, 218, - 105, 218, 101, 96, 90, 88, 86, 85, 82, 79, - 77, 76, 75, 72, 63, 50, 49, 46, 41, 37, - 30, 26, 25, 23, 15, 11, 5, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209 + 4, 32, 2, 4, 62, 2, 4, 5, 5, 6, + 6, 6, 6, 32, 213, 6, 7, 7, 8, 8, + 62, 6, 204, 7, 7, 8, 8, 9, 9, 53, + 53, 9, 6, 6, 9, 10, 10, 17, 28, 10, + 17, 29, 10, 18, 18, 18, 18, 18, 6, 6, + + 199, 18, 28, 31, 193, 29, 33, 33, 33, 33, + 35, 17, 50, 35, 31, 50, 35, 44, 44, 44, + 44, 54, 60, 17, 54, 60, 64, 66, 17, 63, + 17, 34, 67, 67, 67, 67, 50, 77, 34, 34, + 77, 63, 64, 66, 34, 63, 34, 74, 50, 34, + 74, 34, 34, 50, 192, 50, 55, 55, 55, 55, + 55, 74, 76, 76, 55, 60, 185, 95, 114, 34, + 34, 36, 95, 114, 36, 56, 56, 56, 56, 56, + 184, 71, 104, 56, 71, 36, 181, 71, 88, 88, + 88, 88, 89, 93, 178, 89, 93, 104, 176, 175, + + 89, 97, 97, 97, 97, 97, 105, 101, 133, 97, + 101, 36, 36, 106, 36, 36, 174, 109, 36, 39, + 109, 39, 105, 133, 106, 110, 39, 39, 110, 170, + 112, 109, 39, 112, 39, 131, 155, 39, 156, 39, + 39, 98, 98, 98, 98, 98, 131, 169, 120, 98, + 101, 120, 155, 168, 156, 167, 120, 39, 39, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + + 40, 40, 40, 40, 40, 40, 40, 40, 72, 165, + 180, 72, 99, 99, 99, 99, 99, 157, 158, 177, + 99, 180, 72, 100, 100, 100, 100, 100, 195, 166, + 196, 100, 166, 157, 158, 177, 186, 205, 206, 186, + 188, 188, 188, 166, 195, 196, 164, 163, 72, 72, + 186, 72, 72, 205, 206, 72, 82, 162, 82, 159, + 154, 153, 207, 82, 82, 207, 152, 211, 151, 82, + 211, 82, 150, 147, 82, 146, 82, 82, 145, 207, + 144, 143, 142, 141, 211, 140, 139, 138, 137, 136, + 135, 134, 132, 130, 82, 82, 216, 216, 216, 216, + + 216, 216, 216, 217, 129, 128, 127, 217, 217, 218, + 218, 218, 218, 218, 218, 218, 219, 126, 219, 220, + 220, 220, 220, 220, 220, 220, 221, 221, 221, 221, + 221, 221, 221, 222, 222, 125, 222, 222, 222, 222, + 223, 124, 123, 223, 223, 223, 223, 224, 224, 224, + 224, 224, 122, 224, 119, 118, 117, 116, 115, 113, + 111, 107, 102, 96, 94, 92, 91, 90, 87, 86, + 83, 81, 80, 79, 78, 75, 69, 65, 52, 51, + 48, 43, 42, 38, 37, 30, 26, 25, 23, 15, + 11, 215, 215, 215, 215, 215, 215, 215, 215, 215, + + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215 } ; /* The intent behind this definition is that it'll catch @@ -1139,13 +1148,13 @@ yy_match: while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 210 ) + if ( yy_current_state >= 216 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; ++yy_cp; } - while ( yy_base[yy_current_state] != 468 ); + while ( yy_base[yy_current_state] != 492 ); yy_find_action: yy_act = yy_accept[yy_current_state]; @@ -1733,7 +1742,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner) while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 210 ) + if ( yy_current_state >= 216 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; @@ -1762,11 +1771,11 @@ static int yy_get_next_buffer (yyscan_t yyscanner) while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 210 ) + if ( yy_current_state >= 216 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; - yy_is_jam = (yy_current_state == 209); + yy_is_jam = (yy_current_state == 215); (void)yyg; return yy_is_jam ? 0 : yy_current_state; diff --git a/Source/LexerParser/cmFortranLexer.in.l b/Source/LexerParser/cmFortranLexer.in.l index 05769a1..fac3181 100644 --- a/Source/LexerParser/cmFortranLexer.in.l +++ b/Source/LexerParser/cmFortranLexer.in.l @@ -75,10 +75,10 @@ Modify cmFortranLexer.cxx: return STRING; } -<str_dq,str_sq>&[ \t]*\n | -<str_dq,str_sq>&[ \t]*\n[ \t]*& /* Ignore (continued strings, free fmt) */ +<str_dq,str_sq>&[ \t]*\r?\n | +<str_dq,str_sq>&[ \t]*\r?\n[ \t]*& /* Ignore (continued strings, free fmt) */ -<fixed_fmt,str_dq,str_sq>\n[ ]{5}[^ \t\n] { +<fixed_fmt,str_dq,str_sq>\r?\n[ ]{5}[^ \t\r\n] { if (cmFortranParser_GetOldStartcond(yyextra) == fixed_fmt) ; /* Ignore (cont. strings, fixed fmt) */ else @@ -132,15 +132,15 @@ $[ \t]*else { return F90PPR_ELSE; } $[ \t]*endif { return F90PPR_ENDIF; } /* Line continuations, possible involving comments. */ -&([ \t\n]*|!.*)* -&([ \t\n]*|!.*)*& +&([ \t\r\n]*|!.*)* +&([ \t\r\n]*|!.*)*& , { return COMMA; } :: { return DCOLON; } : { return COLON; } -<fixed_fmt>\n[ ]{5}[^ ] { return GARBAGE; } +<fixed_fmt>\r?\n[ ]{5}[^ ] { return GARBAGE; } =|=> { return ASSIGNMENT_OP; } @@ -159,13 +159,13 @@ $[ \t]*endif { return F90PPR_ENDIF; } \( { return LPAREN; } \) { return RPAREN; } -[^ \t\n\r:;,!'"a-zA-Z=&()]+ { return GARBAGE; } +[^ \t\r\n:;,!'"a-zA-Z=&()]+ { return GARBAGE; } ;|\n { return EOSTMT; } [ \t\r,] /* Ignore */ -\\[ \t]*\n /* Ignore line-endings preceded by \ */ +\\[ \t]*\r?\n /* Ignore line-endings preceded by \ */ . { return *yytext; } diff --git a/Source/LexerParser/cmFortranParser.cxx b/Source/LexerParser/cmFortranParser.cxx index b177296..5e5cc0b 100644 --- a/Source/LexerParser/cmFortranParser.cxx +++ b/Source/LexerParser/cmFortranParser.cxx @@ -548,7 +548,7 @@ union yyalloc /* YYFINAL -- State number of the termination state. */ #define YYFINAL 2 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 594 +#define YYLAST 432 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 41 @@ -557,7 +557,7 @@ union yyalloc /* YYNRULES -- Number of rules. */ #define YYNRULES 64 /* YYNSTATES -- Number of states. */ -#define YYNSTATES 127 +#define YYNSTATES 121 /* YYMAXUTOK -- Last valid token kind. */ #define YYMAXUTOK 295 @@ -608,15 +608,15 @@ static const yytype_int8 yytranslate[] = #if YYDEBUG /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ -static const yytype_int16 yyrline[] = +static const yytype_uint8 yyrline[] = { - 0, 106, 106, 106, 109, 113, 118, 127, 133, 140, - 145, 149, 154, 166, 171, 176, 181, 186, 191, 196, - 201, 206, 210, 214, 218, 222, 223, 228, 228, 228, - 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, - 234, 234, 235, 235, 236, 236, 237, 237, 240, 241, - 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, - 252, 253, 254, 255, 256 + 0, 106, 106, 106, 109, 113, 118, 123, 129, 136, + 141, 145, 150, 162, 167, 172, 177, 182, 187, 192, + 197, 202, 206, 210, 214, 218, 219, 224, 224, 224, + 225, 225, 226, 226, 227, 227, 228, 228, 229, 229, + 230, 230, 231, 231, 232, 232, 233, 233, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252 }; #endif @@ -666,19 +666,19 @@ yysymbol_name (yysymbol_kind_t yysymbol) STATE-NUM. */ static const yytype_int16 yypact[] = { - -39, 21, -39, 1, -39, -20, -39, -39, -39, -39, + -39, 21, -39, 5, -39, -23, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, - -39, -39, -39, -39, -39, -39, -24, -18, 20, -8, - -3, 40, -39, 15, 16, 17, 19, 34, -39, -39, - -39, -39, -39, -39, 59, -39, -39, -39, -39, -39, - 36, 37, 38, -39, -39, -39, -39, -39, -39, 77, - 115, 130, 168, 183, -39, -39, -39, -39, -39, -39, + -39, -39, -39, -39, -39, -39, -25, -19, 20, -8, + -15, -22, -39, -6, 14, 15, 16, 17, -39, -39, + -39, -39, -39, -39, 59, 49, 51, -39, 63, 64, + 35, 36, 37, -39, -39, -39, -39, -39, -39, 74, + 112, 127, 165, 180, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, - -39, -39, -39, 221, 236, 274, 289, -21, 26, -39, - 327, 342, 380, 395, 433, 448, -39, -39, -39, -39, - -39, -39, -39, -39, -39, 39, 41, 42, 486, -39, - -39, -39, -39, -39, -39, 18, -39, -39, -39, 43, - 501, 539, -39, -39, -39, 554, -39 + -39, -39, -39, -39, -39, -39, -39, -20, 43, -39, + 218, 233, 271, 286, 324, 339, -39, -39, -39, -39, + -39, 39, 40, 41, 377, -39, -39, -39, -39, -39, + -39, 46, 78, -39, -39, 50, -39, 392, 79, -39, + -39 }; /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. @@ -690,15 +690,15 @@ static const yytype_int8 yydefact[] = 30, 33, 32, 34, 36, 38, 42, 40, 44, 35, 37, 39, 43, 41, 45, 46, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 46, 46, - 46, 46, 26, 46, 0, 46, 46, 4, 46, 46, + 46, 46, 26, 46, 0, 0, 0, 4, 0, 0, 0, 0, 0, 46, 46, 46, 46, 46, 46, 0, 0, 0, 0, 0, 15, 57, 56, 64, 62, 58, 59, 60, 61, 63, 55, 48, 49, 50, 51, 52, - 53, 54, 47, 0, 0, 0, 0, 0, 0, 46, + 53, 54, 47, 10, 13, 9, 6, 0, 0, 46, 0, 0, 0, 0, 0, 0, 21, 22, 23, 24, - 14, 10, 13, 9, 6, 0, 0, 0, 0, 5, - 16, 17, 18, 19, 20, 0, 46, 46, 11, 0, - 0, 0, 46, 7, 12, 0, 8 + 14, 0, 0, 0, 0, 5, 16, 17, 18, 19, + 20, 0, 0, 46, 11, 0, 7, 0, 0, 12, + 8 }; /* YYPGOTO[NTERM-NUM]. */ @@ -720,81 +720,70 @@ static const yytype_int8 yydefgoto[] = number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_int8 yytable[] = { - 59, 60, 61, 62, 42, 63, 105, 83, 84, 106, - 85, 86, 43, 45, 46, 90, 91, 92, 93, 94, - 95, 2, 3, 47, 4, 49, 50, 5, 6, 7, + 59, 60, 61, 62, 51, 63, 52, 101, 42, 43, + 102, 53, 45, 46, 50, 90, 91, 92, 93, 94, + 95, 2, 3, 47, 4, 49, 54, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, - 18, 19, 20, 21, 22, 23, 24, 54, 119, 55, - 56, 108, 57, 48, 107, 25, 26, 27, 28, 29, - 30, 31, 64, 65, 66, 67, 51, 58, 52, 87, - 88, 89, 115, 53, 116, 117, 122, 0, 120, 121, - 96, 65, 66, 67, 125, 68, 69, 70, 71, 72, + 18, 19, 20, 21, 22, 23, 24, 55, 56, 57, + 58, 104, 83, 48, 84, 25, 26, 27, 28, 29, + 30, 31, 64, 65, 66, 67, 85, 86, 87, 88, + 89, 103, 111, 112, 113, 117, 115, 96, 65, 66, + 67, 116, 120, 118, 0, 68, 69, 70, 71, 72, 73, 74, 75, 0, 76, 77, 78, 79, 80, 81, - 0, 0, 0, 68, 69, 70, 71, 72, 73, 74, - 75, 0, 76, 77, 78, 79, 80, 81, 97, 65, + 68, 69, 70, 71, 72, 73, 74, 75, 0, 76, + 77, 78, 79, 80, 81, 97, 65, 66, 67, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 98, 65, 66, 67, 0, 0, 0, 0, 68, 69, + 70, 71, 72, 73, 74, 75, 0, 76, 77, 78, + 79, 80, 81, 68, 69, 70, 71, 72, 73, 74, + 75, 0, 76, 77, 78, 79, 80, 81, 99, 65, 66, 67, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 98, 65, 66, 67, 0, 0, 0, + 0, 0, 0, 100, 65, 66, 67, 0, 0, 0, 0, 68, 69, 70, 71, 72, 73, 74, 75, 0, 76, 77, 78, 79, 80, 81, 68, 69, 70, 71, 72, 73, 74, 75, 0, 76, 77, 78, 79, 80, - 81, 99, 65, 66, 67, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 100, 65, 66, 67, + 81, 105, 65, 66, 67, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 106, 65, 66, 67, 0, 0, 0, 0, 68, 69, 70, 71, 72, 73, 74, 75, 0, 76, 77, 78, 79, 80, 81, 68, 69, 70, 71, 72, 73, 74, 75, 0, 76, 77, - 78, 79, 80, 81, 101, 65, 66, 67, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 102, + 78, 79, 80, 81, 107, 65, 66, 67, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 108, 65, 66, 67, 0, 0, 0, 0, 68, 69, 70, 71, 72, 73, 74, 75, 0, 76, 77, 78, 79, 80, 81, 68, 69, 70, 71, 72, 73, 74, 75, - 0, 76, 77, 78, 79, 80, 81, 103, 65, 66, + 0, 76, 77, 78, 79, 80, 81, 109, 65, 66, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 104, 65, 66, 67, 0, 0, 0, 0, + 0, 0, 110, 65, 66, 67, 0, 0, 0, 0, 68, 69, 70, 71, 72, 73, 74, 75, 0, 76, 77, 78, 79, 80, 81, 68, 69, 70, 71, 72, 73, 74, 75, 0, 76, 77, 78, 79, 80, 81, - 109, 65, 66, 67, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 110, 65, 66, 67, 0, + 114, 65, 66, 67, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 119, 65, 66, 67, 0, 0, 0, 0, 68, 69, 70, 71, 72, 73, 74, 75, 0, 76, 77, 78, 79, 80, 81, 68, 69, 70, 71, 72, 73, 74, 75, 0, 76, 77, 78, - 79, 80, 81, 111, 65, 66, 67, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 112, 65, - 66, 67, 0, 0, 0, 0, 68, 69, 70, 71, - 72, 73, 74, 75, 0, 76, 77, 78, 79, 80, - 81, 68, 69, 70, 71, 72, 73, 74, 75, 0, - 76, 77, 78, 79, 80, 81, 113, 65, 66, 67, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 114, 65, 66, 67, 0, 0, 0, 0, 68, - 69, 70, 71, 72, 73, 74, 75, 0, 76, 77, - 78, 79, 80, 81, 68, 69, 70, 71, 72, 73, - 74, 75, 0, 76, 77, 78, 79, 80, 81, 118, - 65, 66, 67, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 123, 65, 66, 67, 0, 0, - 0, 0, 68, 69, 70, 71, 72, 73, 74, 75, - 0, 76, 77, 78, 79, 80, 81, 68, 69, 70, - 71, 72, 73, 74, 75, 0, 76, 77, 78, 79, - 80, 81, 124, 65, 66, 67, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 126, 65, 66, - 67, 0, 0, 0, 0, 68, 69, 70, 71, 72, - 73, 74, 75, 0, 76, 77, 78, 79, 80, 81, - 68, 69, 70, 71, 72, 73, 74, 75, 0, 76, - 77, 78, 79, 80, 81 + 79, 80, 81 }; static const yytype_int8 yycheck[] = { - 38, 39, 40, 41, 3, 43, 27, 45, 46, 30, - 48, 49, 32, 37, 32, 53, 54, 55, 56, 57, - 58, 0, 1, 3, 3, 33, 29, 6, 7, 8, + 38, 39, 40, 41, 26, 43, 28, 27, 3, 32, + 30, 33, 37, 32, 29, 53, 54, 55, 56, 57, + 58, 0, 1, 3, 3, 33, 32, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, - 19, 20, 21, 22, 23, 24, 25, 32, 30, 33, - 33, 89, 33, 33, 28, 34, 35, 36, 37, 38, - 39, 40, 3, 4, 5, 6, 26, 33, 28, 33, - 33, 33, 33, 33, 33, 33, 33, -1, 116, 117, - 3, 4, 5, 6, 122, 26, 27, 28, 29, 30, + 19, 20, 21, 22, 23, 24, 25, 33, 33, 33, + 33, 89, 3, 33, 3, 34, 35, 36, 37, 38, + 39, 40, 3, 4, 5, 6, 3, 3, 33, 33, + 33, 28, 33, 33, 33, 113, 30, 3, 4, 5, + 6, 3, 3, 33, -1, 26, 27, 28, 29, 30, 31, 32, 33, -1, 35, 36, 37, 38, 39, 40, - -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, + 26, 27, 28, 29, 30, 31, 32, 33, -1, 35, + 36, 37, 38, 39, 40, 3, 4, 5, 6, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 3, 4, 5, 6, -1, -1, -1, -1, 26, 27, + 28, 29, 30, 31, 32, 33, -1, 35, 36, 37, + 38, 39, 40, 26, 27, 28, 29, 30, 31, 32, 33, -1, 35, 36, 37, 38, 39, 40, 3, 4, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, -1, -1, -1, @@ -822,28 +811,7 @@ static const yytype_int8 yycheck[] = -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, -1, 35, 36, 37, 38, 39, 40, 26, 27, 28, 29, 30, 31, 32, 33, -1, 35, 36, 37, - 38, 39, 40, 3, 4, 5, 6, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 3, 4, - 5, 6, -1, -1, -1, -1, 26, 27, 28, 29, - 30, 31, 32, 33, -1, 35, 36, 37, 38, 39, - 40, 26, 27, 28, 29, 30, 31, 32, 33, -1, - 35, 36, 37, 38, 39, 40, 3, 4, 5, 6, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 3, 4, 5, 6, -1, -1, -1, -1, 26, - 27, 28, 29, 30, 31, 32, 33, -1, 35, 36, - 37, 38, 39, 40, 26, 27, 28, 29, 30, 31, - 32, 33, -1, 35, 36, 37, 38, 39, 40, 3, - 4, 5, 6, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 3, 4, 5, 6, -1, -1, - -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, - -1, 35, 36, 37, 38, 39, 40, 26, 27, 28, - 29, 30, 31, 32, 33, -1, 35, 36, 37, 38, - 39, 40, 3, 4, 5, 6, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 3, 4, 5, - 6, -1, -1, -1, -1, 26, 27, 28, 29, 30, - 31, 32, 33, -1, 35, 36, 37, 38, 39, 40, - 26, 27, 28, 29, 30, 31, 32, 33, -1, 35, - 36, 37, 38, 39, 40 + 38, 39, 40 }; /* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of @@ -858,11 +826,11 @@ static const yytype_int8 yystos[] = 29, 26, 28, 33, 32, 33, 33, 33, 33, 53, 53, 53, 53, 53, 3, 4, 5, 6, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 38, - 39, 40, 54, 53, 53, 53, 53, 33, 33, 33, + 39, 40, 54, 3, 3, 3, 3, 33, 33, 33, 53, 53, 53, 53, 53, 53, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 27, 30, 28, 53, 3, - 3, 3, 3, 3, 3, 33, 33, 33, 3, 30, - 53, 53, 33, 3, 3, 53, 3 + 3, 27, 30, 28, 53, 3, 3, 3, 3, 3, + 3, 33, 33, 33, 3, 30, 3, 53, 33, 3, + 3 }; /* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */ @@ -880,8 +848,8 @@ static const yytype_int8 yyr1[] = /* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */ static const yytype_int8 yyr2[] = { - 0, 2, 0, 2, 2, 4, 4, 7, 9, 4, - 4, 5, 7, 4, 4, 3, 4, 4, 4, 4, + 0, 2, 0, 2, 2, 4, 3, 6, 8, 3, + 3, 5, 7, 3, 4, 3, 4, 4, 4, 4, 4, 3, 3, 3, 3, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 1, 1, @@ -1361,19 +1329,19 @@ yydestruct (const char *yymsg, case YYSYMBOL_STRING: /* STRING */ #line 100 "cmFortranParser.y" { free(((*yyvaluep).string)); } -#line 1365 "cmFortranParser.cxx" +#line 1333 "cmFortranParser.cxx" break; case YYSYMBOL_WORD: /* WORD */ #line 100 "cmFortranParser.y" { free(((*yyvaluep).string)); } -#line 1371 "cmFortranParser.cxx" +#line 1339 "cmFortranParser.cxx" break; case YYSYMBOL_CPP_INCLUDE_ANGLE: /* CPP_INCLUDE_ANGLE */ #line 100 "cmFortranParser.y" { free(((*yyvaluep).string)); } -#line 1377 "cmFortranParser.cxx" +#line 1345 "cmFortranParser.cxx" break; default: @@ -1655,7 +1623,7 @@ yyreduce: cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_SetInInterface(parser, true); } -#line 1659 "cmFortranParser.cxx" +#line 1627 "cmFortranParser.cxx" break; case 5: /* stmt: USE WORD other EOSTMT */ @@ -1665,77 +1633,73 @@ yyreduce: cmFortranParser_RuleUse(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1669 "cmFortranParser.cxx" +#line 1637 "cmFortranParser.cxx" break; - case 6: /* stmt: MODULE WORD other EOSTMT */ + case 6: /* stmt: MODULE WORD EOSTMT */ #line 118 "cmFortranParser.y" - { + { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); - if (cmsysString_strcasecmp((yyvsp[-2].string), "function") != 0 && - cmsysString_strcasecmp((yyvsp[-2].string), "procedure") != 0 && - cmsysString_strcasecmp((yyvsp[-2].string), "subroutine") != 0) { - cmFortranParser_RuleModule(parser, (yyvsp[-2].string)); - } - free((yyvsp[-2].string)); + cmFortranParser_RuleModule(parser, (yyvsp[-1].string)); + free((yyvsp[-1].string)); } -#line 1683 "cmFortranParser.cxx" +#line 1647 "cmFortranParser.cxx" break; - case 7: /* stmt: SUBMODULE LPAREN WORD RPAREN WORD other EOSTMT */ -#line 127 "cmFortranParser.y" - { + case 7: /* stmt: SUBMODULE LPAREN WORD RPAREN WORD EOSTMT */ +#line 123 "cmFortranParser.y" + { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); - cmFortranParser_RuleSubmodule(parser, (yyvsp[-4].string), (yyvsp[-2].string)); - free((yyvsp[-4].string)); - free((yyvsp[-2].string)); + cmFortranParser_RuleSubmodule(parser, (yyvsp[-3].string), (yyvsp[-1].string)); + free((yyvsp[-3].string)); + free((yyvsp[-1].string)); } -#line 1694 "cmFortranParser.cxx" +#line 1658 "cmFortranParser.cxx" break; - case 8: /* stmt: SUBMODULE LPAREN WORD COLON WORD RPAREN WORD other EOSTMT */ -#line 133 "cmFortranParser.y" - { + case 8: /* stmt: SUBMODULE LPAREN WORD COLON WORD RPAREN WORD EOSTMT */ +#line 129 "cmFortranParser.y" + { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); - cmFortranParser_RuleSubmoduleNested(parser, (yyvsp[-6].string), (yyvsp[-4].string), (yyvsp[-2].string)); - free((yyvsp[-6].string)); - free((yyvsp[-4].string)); - free((yyvsp[-2].string)); + cmFortranParser_RuleSubmoduleNested(parser, (yyvsp[-5].string), (yyvsp[-3].string), (yyvsp[-1].string)); + free((yyvsp[-5].string)); + free((yyvsp[-3].string)); + free((yyvsp[-1].string)); } -#line 1706 "cmFortranParser.cxx" +#line 1670 "cmFortranParser.cxx" break; - case 9: /* stmt: INTERFACE WORD other EOSTMT */ -#line 140 "cmFortranParser.y" - { + case 9: /* stmt: INTERFACE WORD EOSTMT */ +#line 136 "cmFortranParser.y" + { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_SetInInterface(parser, true); - free((yyvsp[-2].string)); + free((yyvsp[-1].string)); } -#line 1716 "cmFortranParser.cxx" +#line 1680 "cmFortranParser.cxx" break; - case 10: /* stmt: END INTERFACE other EOSTMT */ -#line 145 "cmFortranParser.y" - { + case 10: /* stmt: END INTERFACE EOSTMT */ +#line 141 "cmFortranParser.y" + { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_SetInInterface(parser, false); } -#line 1725 "cmFortranParser.cxx" +#line 1689 "cmFortranParser.cxx" break; case 11: /* stmt: USE DCOLON WORD other EOSTMT */ -#line 149 "cmFortranParser.y" +#line 145 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleUse(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1735 "cmFortranParser.cxx" +#line 1699 "cmFortranParser.cxx" break; case 12: /* stmt: USE COMMA WORD DCOLON WORD other EOSTMT */ -#line 154 "cmFortranParser.y" +#line 150 "cmFortranParser.y" { if (cmsysString_strcasecmp((yyvsp[-4].string), "non_intrinsic") == 0) { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); @@ -1748,139 +1712,139 @@ yyreduce: free((yyvsp[-4].string)); free((yyvsp[-2].string)); } -#line 1752 "cmFortranParser.cxx" +#line 1716 "cmFortranParser.cxx" break; - case 13: /* stmt: INCLUDE STRING other EOSTMT */ -#line 166 "cmFortranParser.y" - { + case 13: /* stmt: INCLUDE STRING EOSTMT */ +#line 162 "cmFortranParser.y" + { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); - cmFortranParser_RuleInclude(parser, (yyvsp[-2].string)); - free((yyvsp[-2].string)); + cmFortranParser_RuleInclude(parser, (yyvsp[-1].string)); + free((yyvsp[-1].string)); } -#line 1762 "cmFortranParser.cxx" +#line 1726 "cmFortranParser.cxx" break; case 14: /* stmt: CPP_LINE_DIRECTIVE STRING other EOSTMT */ -#line 171 "cmFortranParser.y" +#line 167 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleLineDirective(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1772 "cmFortranParser.cxx" +#line 1736 "cmFortranParser.cxx" break; case 15: /* stmt: CPP_INCLUDE_ANGLE other EOSTMT */ -#line 176 "cmFortranParser.y" +#line 172 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleInclude(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1782 "cmFortranParser.cxx" +#line 1746 "cmFortranParser.cxx" break; case 16: /* stmt: include STRING other EOSTMT */ -#line 181 "cmFortranParser.y" +#line 177 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleInclude(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1792 "cmFortranParser.cxx" +#line 1756 "cmFortranParser.cxx" break; case 17: /* stmt: define WORD other EOSTMT */ -#line 186 "cmFortranParser.y" +#line 182 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleDefine(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1802 "cmFortranParser.cxx" +#line 1766 "cmFortranParser.cxx" break; case 18: /* stmt: undef WORD other EOSTMT */ -#line 191 "cmFortranParser.y" +#line 187 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleUndef(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1812 "cmFortranParser.cxx" +#line 1776 "cmFortranParser.cxx" break; case 19: /* stmt: ifdef WORD other EOSTMT */ -#line 196 "cmFortranParser.y" +#line 192 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleIfdef(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1822 "cmFortranParser.cxx" +#line 1786 "cmFortranParser.cxx" break; case 20: /* stmt: ifndef WORD other EOSTMT */ -#line 201 "cmFortranParser.y" +#line 197 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleIfndef(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1832 "cmFortranParser.cxx" +#line 1796 "cmFortranParser.cxx" break; case 21: /* stmt: if other EOSTMT */ -#line 206 "cmFortranParser.y" +#line 202 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleIf(parser); } -#line 1841 "cmFortranParser.cxx" +#line 1805 "cmFortranParser.cxx" break; case 22: /* stmt: elif other EOSTMT */ -#line 210 "cmFortranParser.y" +#line 206 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleElif(parser); } -#line 1850 "cmFortranParser.cxx" +#line 1814 "cmFortranParser.cxx" break; case 23: /* stmt: else other EOSTMT */ -#line 214 "cmFortranParser.y" +#line 210 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleElse(parser); } -#line 1859 "cmFortranParser.cxx" +#line 1823 "cmFortranParser.cxx" break; case 24: /* stmt: endif other EOSTMT */ -#line 218 "cmFortranParser.y" +#line 214 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleEndif(parser); } -#line 1868 "cmFortranParser.cxx" +#line 1832 "cmFortranParser.cxx" break; case 48: /* misc_code: WORD */ -#line 240 "cmFortranParser.y" +#line 236 "cmFortranParser.y" { free ((yyvsp[0].string)); } -#line 1874 "cmFortranParser.cxx" +#line 1838 "cmFortranParser.cxx" break; case 55: /* misc_code: STRING */ -#line 247 "cmFortranParser.y" +#line 243 "cmFortranParser.y" { free ((yyvsp[0].string)); } -#line 1880 "cmFortranParser.cxx" +#line 1844 "cmFortranParser.cxx" break; -#line 1884 "cmFortranParser.cxx" +#line 1848 "cmFortranParser.cxx" default: break; } @@ -2104,6 +2068,6 @@ yyreturnlab: return yyresult; } -#line 259 "cmFortranParser.y" +#line 255 "cmFortranParser.y" /* End of grammar */ diff --git a/Source/LexerParser/cmFortranParser.y b/Source/LexerParser/cmFortranParser.y index 07ca630..a5c8976 100644 --- a/Source/LexerParser/cmFortranParser.y +++ b/Source/LexerParser/cmFortranParser.y @@ -115,34 +115,30 @@ stmt: cmFortranParser_RuleUse(parser, $2); free($2); } -| MODULE WORD other EOSTMT { +| MODULE WORD EOSTMT { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); - if (cmsysString_strcasecmp($2, "function") != 0 && - cmsysString_strcasecmp($2, "procedure") != 0 && - cmsysString_strcasecmp($2, "subroutine") != 0) { - cmFortranParser_RuleModule(parser, $2); - } + cmFortranParser_RuleModule(parser, $2); free($2); } -| SUBMODULE LPAREN WORD RPAREN WORD other EOSTMT { +| SUBMODULE LPAREN WORD RPAREN WORD EOSTMT { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleSubmodule(parser, $3, $5); free($3); free($5); } -| SUBMODULE LPAREN WORD COLON WORD RPAREN WORD other EOSTMT { +| SUBMODULE LPAREN WORD COLON WORD RPAREN WORD EOSTMT { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleSubmoduleNested(parser, $3, $5, $7); free($3); free($5); free($7); } -| INTERFACE WORD other EOSTMT { +| INTERFACE WORD EOSTMT { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_SetInInterface(parser, true); free($2); } -| END INTERFACE other EOSTMT { +| END INTERFACE EOSTMT { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_SetInInterface(parser, false); } @@ -163,7 +159,7 @@ stmt: free($3); free($5); } -| INCLUDE STRING other EOSTMT { +| INCLUDE STRING EOSTMT { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleInclude(parser, $2); free($2); diff --git a/Source/cmArgumentParser.cxx b/Source/cmArgumentParser.cxx index 4624f1c..ad57a88 100644 --- a/Source/cmArgumentParser.cxx +++ b/Source/cmArgumentParser.cxx @@ -4,9 +4,14 @@ #include <algorithm> +#include "cmArgumentParserTypes.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmStringAlgorithms.h" + namespace ArgumentParser { -auto ActionMap::Emplace(cm::string_view name, Action action) +auto KeywordActionMap::Emplace(cm::string_view name, KeywordAction action) -> std::pair<iterator, bool> { auto const it = @@ -19,7 +24,7 @@ auto ActionMap::Emplace(cm::string_view name, Action action) : std::make_pair(this->emplace(it, name, std::move(action)), true); } -auto ActionMap::Find(cm::string_view name) const -> const_iterator +auto KeywordActionMap::Find(cm::string_view name) const -> const_iterator { auto const it = std::lower_bound(this->begin(), this->end(), name, @@ -29,68 +34,171 @@ auto ActionMap::Find(cm::string_view name) const -> const_iterator return (it != this->end() && it->first == name) ? it : this->end(); } +auto PositionActionMap::Emplace(std::size_t pos, PositionAction action) + -> std::pair<iterator, bool> +{ + auto const it = std::lower_bound( + this->begin(), this->end(), pos, + [](value_type const& elem, std::size_t k) { return elem.first < k; }); + return (it != this->end() && it->first == pos) + ? std::make_pair(it, false) + : std::make_pair(this->emplace(it, pos, std::move(action)), true); +} + +auto PositionActionMap::Find(std::size_t pos) const -> const_iterator +{ + auto const it = std::lower_bound( + this->begin(), this->end(), pos, + [](value_type const& elem, std::size_t k) { return elem.first < k; }); + return (it != this->end() && it->first == pos) ? it : this->end(); +} + +void Instance::Bind(std::function<Continue(cm::string_view)> f, + ExpectAtLeast expect) +{ + this->KeywordValueFunc = std::move(f); + this->KeywordValuesExpected = expect.Count; +} + void Instance::Bind(bool& val) { val = true; - this->CurrentString = nullptr; - this->CurrentList = nullptr; - this->ExpectValue = false; + this->Bind(nullptr, ExpectAtLeast{ 0 }); } void Instance::Bind(std::string& val) { - this->CurrentString = &val; - this->CurrentList = nullptr; - this->ExpectValue = true; + this->Bind( + [&val](cm::string_view arg) -> Continue { + val = std::string(arg); + return Continue::No; + }, + ExpectAtLeast{ 1 }); +} + +void Instance::Bind(NonEmpty<std::string>& val) +{ + this->Bind( + [this, &val](cm::string_view arg) -> Continue { + if (arg.empty() && this->ParseResults) { + this->ParseResults->AddKeywordError(this->Keyword, + " empty string not allowed\n"); + } + val.assign(std::string(arg)); + return Continue::No; + }, + ExpectAtLeast{ 1 }); } -void Instance::Bind(StringList& val) +void Instance::Bind(Maybe<std::string>& val) { - this->CurrentString = nullptr; - this->CurrentList = &val; - this->ExpectValue = true; + this->Bind( + [&val](cm::string_view arg) -> Continue { + static_cast<std::string&>(val) = std::string(arg); + return Continue::No; + }, + ExpectAtLeast{ 0 }); } -void Instance::Bind(MultiStringList& val) +void Instance::Bind(MaybeEmpty<std::vector<std::string>>& val) { - this->CurrentString = nullptr; - this->CurrentList = (static_cast<void>(val.emplace_back()), &val.back()); - this->ExpectValue = false; + this->Bind( + [&val](cm::string_view arg) -> Continue { + val.emplace_back(arg); + return Continue::Yes; + }, + ExpectAtLeast{ 0 }); } -void Instance::Consume(cm::string_view arg, void* result, - std::vector<std::string>* unparsedArguments, - std::vector<std::string>* keywordsMissingValue, - std::vector<std::string>* parsedKeywords) +void Instance::Bind(NonEmpty<std::vector<std::string>>& val) { - auto const it = this->Bindings.Find(arg); - if (it != this->Bindings.end()) { - if (parsedKeywords != nullptr) { - parsedKeywords->emplace_back(arg); + this->Bind( + [&val](cm::string_view arg) -> Continue { + val.emplace_back(arg); + return Continue::Yes; + }, + ExpectAtLeast{ 1 }); +} + +void Instance::Bind(std::vector<std::vector<std::string>>& multiVal) +{ + multiVal.emplace_back(); + std::vector<std::string>& val = multiVal.back(); + this->Bind( + [&val](cm::string_view arg) -> Continue { + val.emplace_back(arg); + return Continue::Yes; + }, + ExpectAtLeast{ 0 }); +} + +void Instance::Consume(std::size_t pos, cm::string_view arg) +{ + auto const it = this->Bindings.Keywords.Find(arg); + if (it != this->Bindings.Keywords.end()) { + this->FinishKeyword(); + this->Keyword = it->first; + this->KeywordValuesSeen = 0; + this->DoneWithPositional = true; + if (this->Bindings.ParsedKeyword) { + this->Bindings.ParsedKeyword(*this, it->first); } - it->second(*this, result); - if (this->ExpectValue && keywordsMissingValue != nullptr) { - keywordsMissingValue->emplace_back(arg); + it->second(*this); + return; + } + + if (this->KeywordValueFunc) { + switch (this->KeywordValueFunc(arg)) { + case Continue::Yes: + break; + case Continue::No: + this->KeywordValueFunc = nullptr; + break; } + ++this->KeywordValuesSeen; return; } - if (this->CurrentString != nullptr) { - this->CurrentString->assign(std::string(arg)); - this->CurrentString = nullptr; - this->CurrentList = nullptr; - } else if (this->CurrentList != nullptr) { - this->CurrentList->emplace_back(arg); - } else if (unparsedArguments != nullptr) { - unparsedArguments->emplace_back(arg); + if (!this->DoneWithPositional) { + auto const pit = this->Bindings.Positions.Find(pos); + if (pit != this->Bindings.Positions.end()) { + pit->second(*this, pos, arg); + return; + } + } + + if (this->UnparsedArguments != nullptr) { + this->UnparsedArguments->emplace_back(arg); } +} - if (this->ExpectValue) { - if (keywordsMissingValue != nullptr) { - keywordsMissingValue->pop_back(); +void Instance::FinishKeyword() +{ + if (this->Keyword.empty()) { + return; + } + if (this->KeywordValuesSeen < this->KeywordValuesExpected) { + if (this->ParseResults != nullptr) { + this->ParseResults->AddKeywordError(this->Keyword, + " missing required value\n"); } - this->ExpectValue = false; + if (this->Bindings.KeywordMissingValue) { + this->Bindings.KeywordMissingValue(*this, this->Keyword); + } + } +} + +bool ParseResult::MaybeReportError(cmMakefile& mf) const +{ + if (*this) { + return false; + } + std::string e; + for (auto const& ke : this->KeywordErrors) { + e = cmStrCat(e, "Error after keyword \"", ke.first, "\":\n", ke.second); } + mf.IssueMessage(MessageType::FATAL_ERROR, e); + return true; } } // namespace ArgumentParser diff --git a/Source/cmArgumentParser.h b/Source/cmArgumentParser.h index 71ed844..fdf54fb 100644 --- a/Source/cmArgumentParser.h +++ b/Source/cmArgumentParser.h @@ -5,59 +5,220 @@ #include "cmConfigure.h" // IWYU pragma: keep #include <cassert> +#include <cstddef> #include <functional> +#include <map> #include <string> #include <utility> #include <vector> +#include <cm/optional> #include <cm/string_view> +#include <cm/type_traits> #include <cmext/string_view> +#include "cmArgumentParserTypes.h" // IWYU pragma: keep + +template <typename Result> +class cmArgumentParser; // IWYU pragma: keep + +class cmMakefile; + namespace ArgumentParser { -using StringList = std::vector<std::string>; -using MultiStringList = std::vector<StringList>; +class ParseResult +{ + std::map<cm::string_view, std::string> KeywordErrors; + +public: + explicit operator bool() const { return this->KeywordErrors.empty(); } + + void AddKeywordError(cm::string_view key, cm::string_view text) + + { + this->KeywordErrors[key] += text; + } + + std::map<cm::string_view, std::string> const& GetKeywordErrors() const + { + return this->KeywordErrors; + } + + bool MaybeReportError(cmMakefile& mf) const; +}; + +template <typename Result> +typename std::enable_if<std::is_base_of<ParseResult, Result>::value, + ParseResult*>::type +AsParseResultPtr(Result& result) +{ + return &result; +} + +template <typename Result> +typename std::enable_if<!std::is_base_of<ParseResult, Result>::value, + ParseResult*>::type +AsParseResultPtr(Result&) +{ + return nullptr; +} + +enum class Continue +{ + No, + Yes, +}; + +struct ExpectAtLeast +{ + std::size_t Count = 0; + + ExpectAtLeast(std::size_t count) + : Count(count) + { + } +}; class Instance; -using Action = std::function<void(Instance&, void*)>; +using KeywordAction = std::function<void(Instance&)>; +using KeywordNameAction = std::function<void(Instance&, cm::string_view)>; +using PositionAction = + std::function<void(Instance&, std::size_t, cm::string_view)>; -// using ActionMap = cm::flat_map<cm::string_view, Action>; -class ActionMap : public std::vector<std::pair<cm::string_view, Action>> +// using KeywordActionMap = cm::flat_map<cm::string_view, KeywordAction>; +class KeywordActionMap + : public std::vector<std::pair<cm::string_view, KeywordAction>> { public: - std::pair<iterator, bool> Emplace(cm::string_view name, Action action); + std::pair<iterator, bool> Emplace(cm::string_view name, + KeywordAction action); const_iterator Find(cm::string_view name) const; }; +// using PositionActionMap = cm::flat_map<cm::string_view, PositionAction>; +class PositionActionMap + : public std::vector<std::pair<std::size_t, PositionAction>> +{ +public: + std::pair<iterator, bool> Emplace(std::size_t pos, PositionAction action); + const_iterator Find(std::size_t pos) const; +}; + +class ActionMap +{ +public: + KeywordActionMap Keywords; + KeywordNameAction KeywordMissingValue; + KeywordNameAction ParsedKeyword; + PositionActionMap Positions; +}; + +class Base +{ +public: + using ExpectAtLeast = ArgumentParser::ExpectAtLeast; + using Continue = ArgumentParser::Continue; + using Instance = ArgumentParser::Instance; + using ParseResult = ArgumentParser::ParseResult; + + ArgumentParser::ActionMap Bindings; + + bool MaybeBind(cm::string_view name, KeywordAction action) + { + return this->Bindings.Keywords.Emplace(name, std::move(action)).second; + } + + void Bind(cm::string_view name, KeywordAction action) + { + bool const inserted = this->MaybeBind(name, std::move(action)); + assert(inserted); + static_cast<void>(inserted); + } + + void BindParsedKeyword(KeywordNameAction action) + { + assert(!this->Bindings.ParsedKeyword); + this->Bindings.ParsedKeyword = std::move(action); + } + + void BindKeywordMissingValue(KeywordNameAction action) + { + assert(!this->Bindings.KeywordMissingValue); + this->Bindings.KeywordMissingValue = std::move(action); + } + + void Bind(std::size_t pos, PositionAction action) + { + bool const inserted = + this->Bindings.Positions.Emplace(pos, std::move(action)).second; + assert(inserted); + static_cast<void>(inserted); + } +}; + class Instance { public: - Instance(ActionMap const& bindings) + Instance(ActionMap const& bindings, ParseResult* parseResult, + std::vector<std::string>* unparsedArguments, void* result = nullptr) : Bindings(bindings) + , ParseResults(parseResult) + , UnparsedArguments(unparsedArguments) + , Result(result) { } + void Bind(std::function<Continue(cm::string_view)> f, ExpectAtLeast expect); void Bind(bool& val); void Bind(std::string& val); - void Bind(StringList& val); - void Bind(MultiStringList& val); + void Bind(NonEmpty<std::string>& val); + void Bind(Maybe<std::string>& val); + void Bind(MaybeEmpty<std::vector<std::string>>& val); + void Bind(NonEmpty<std::vector<std::string>>& val); + void Bind(std::vector<std::vector<std::string>>& val); + + // cm::optional<> records the presence the keyword to which it binds. + template <typename T> + void Bind(cm::optional<T>& optVal) + { + if (!optVal) { + optVal.emplace(); + } + this->Bind(*optVal); + } - void Consume(cm::string_view arg, void* result, - std::vector<std::string>* unparsedArguments, - std::vector<std::string>* keywordsMissingValue, - std::vector<std::string>* parsedKeywords); + template <typename Range> + void Parse(Range const& args, std::size_t pos = 0) + { + for (cm::string_view arg : args) { + this->Consume(pos++, arg); + } + this->FinishKeyword(); + } private: ActionMap const& Bindings; - std::string* CurrentString = nullptr; - StringList* CurrentList = nullptr; - bool ExpectValue = false; + ParseResult* ParseResults = nullptr; + std::vector<std::string>* UnparsedArguments = nullptr; + void* Result = nullptr; + + cm::string_view Keyword; + std::size_t KeywordValuesSeen = 0; + std::size_t KeywordValuesExpected = 0; + std::function<Continue(cm::string_view)> KeywordValueFunc; + bool DoneWithPositional = false; + + void Consume(std::size_t pos, cm::string_view arg); + void FinishKeyword(); + + template <typename Result> + friend class ::cmArgumentParser; }; } // namespace ArgumentParser template <typename Result> -class cmArgumentParser +class cmArgumentParser : private ArgumentParser::Base { public: // I *think* this function could be made `constexpr` when the code is @@ -65,83 +226,194 @@ public: template <typename T> cmArgumentParser& Bind(cm::static_string_view name, T Result::*member) { - bool const inserted = - this->Bindings - .Emplace(name, - [member](ArgumentParser::Instance& instance, void* result) { - instance.Bind(static_cast<Result*>(result)->*member); - }) - .second; - assert(inserted), (void)inserted; + this->Base::Bind(name, [member](Instance& instance) { + instance.Bind(static_cast<Result*>(instance.Result)->*member); + }); + return *this; + } + + cmArgumentParser& Bind(cm::static_string_view name, + Continue (Result::*member)(cm::string_view), + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [member, expect](Instance& instance) { + Result* result = static_cast<Result*>(instance.Result); + instance.Bind( + [result, member](cm::string_view arg) -> Continue { + return (result->*member)(arg); + }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind(cm::static_string_view name, + Continue (Result::*member)(cm::string_view, + cm::string_view), + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [member, expect](Instance& instance) { + Result* result = static_cast<Result*>(instance.Result); + cm::string_view keyword = instance.Keyword; + instance.Bind( + [result, member, keyword](cm::string_view arg) -> Continue { + return (result->*member)(keyword, arg); + }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind(cm::static_string_view name, + std::function<Continue(Result&, cm::string_view)> f, + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [f, expect](Instance& instance) { + Result* result = static_cast<Result*>(instance.Result); + instance.Bind( + [result, &f](cm::string_view arg) -> Continue { + return f(*result, arg); + }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind( + cm::static_string_view name, + std::function<Continue(Result&, cm::string_view, cm::string_view)> f, + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [f, expect](Instance& instance) { + Result* result = static_cast<Result*>(instance.Result); + cm::string_view keyword = instance.Keyword; + instance.Bind( + [result, keyword, &f](cm::string_view arg) -> Continue { + return f(*result, keyword, arg); + }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind(std::size_t position, + cm::optional<std::string> Result::*member) + { + this->Base::Bind( + position, + [member](Instance& instance, std::size_t, cm::string_view arg) { + Result* result = static_cast<Result*>(instance.Result); + result->*member = arg; + }); + return *this; + } + + cmArgumentParser& BindParsedKeywords( + std::vector<cm::string_view> Result::*member) + { + this->Base::BindParsedKeyword( + [member](Instance& instance, cm::string_view arg) { + (static_cast<Result*>(instance.Result)->*member).emplace_back(arg); + }); return *this; } template <typename Range> - void Parse(Result& result, Range const& args, - std::vector<std::string>* unparsedArguments = nullptr, - std::vector<std::string>* keywordsMissingValue = nullptr, - std::vector<std::string>* parsedKeywords = nullptr) const + bool Parse(Result& result, Range const& args, + std::vector<std::string>* unparsedArguments, + std::size_t pos = 0) const { - ArgumentParser::Instance instance(this->Bindings); - for (cm::string_view arg : args) { - instance.Consume(arg, &result, unparsedArguments, keywordsMissingValue, - parsedKeywords); - } + using ArgumentParser::AsParseResultPtr; + ParseResult* parseResultPtr = AsParseResultPtr(result); + Instance instance(this->Bindings, parseResultPtr, unparsedArguments, + &result); + instance.Parse(args, pos); + return parseResultPtr ? static_cast<bool>(*parseResultPtr) : true; } template <typename Range> - Result Parse(Range const& args, - std::vector<std::string>* unparsedArguments = nullptr, - std::vector<std::string>* keywordsMissingValue = nullptr, - std::vector<std::string>* parsedKeywords = nullptr) const + Result Parse(Range const& args, std::vector<std::string>* unparsedArguments, + std::size_t pos = 0) const { Result result; - this->Parse(result, args, unparsedArguments, keywordsMissingValue, - parsedKeywords); + this->Parse(result, args, unparsedArguments, pos); return result; } - -private: - ArgumentParser::ActionMap Bindings; }; template <> -class cmArgumentParser<void> +class cmArgumentParser<void> : private ArgumentParser::Base { public: template <typename T> cmArgumentParser& Bind(cm::static_string_view name, T& ref) { - bool const inserted = this->Bind(cm::string_view(name), ref); - assert(inserted), (void)inserted; + this->Base::Bind(name, [&ref](Instance& instance) { instance.Bind(ref); }); + return *this; + } + + cmArgumentParser& Bind(cm::static_string_view name, + std::function<Continue(cm::string_view)> f, + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [f, expect](Instance& instance) { + instance.Bind([&f](cm::string_view arg) -> Continue { return f(arg); }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind( + cm::static_string_view name, + std::function<Continue(cm::string_view, cm::string_view)> f, + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [f, expect](Instance& instance) { + cm::string_view keyword = instance.Keyword; + instance.Bind( + [keyword, &f](cm::string_view arg) -> Continue { + return f(keyword, arg); + }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind(std::size_t position, cm::optional<std::string>& ref) + { + this->Base::Bind(position, + [&ref](Instance&, std::size_t, cm::string_view arg) { + ref = std::string(arg); + }); + return *this; + } + + cmArgumentParser& BindParsedKeywords(std::vector<cm::string_view>& ref) + { + this->Base::BindParsedKeyword( + [&ref](Instance&, cm::string_view arg) { ref.emplace_back(arg); }); return *this; } template <typename Range> - void Parse(Range const& args, - std::vector<std::string>* unparsedArguments = nullptr, - std::vector<std::string>* keywordsMissingValue = nullptr, - std::vector<std::string>* parsedKeywords = nullptr) const + ParseResult Parse(Range const& args, + std::vector<std::string>* unparsedArguments, + std::size_t pos = 0) const { - ArgumentParser::Instance instance(this->Bindings); - for (cm::string_view arg : args) { - instance.Consume(arg, nullptr, unparsedArguments, keywordsMissingValue, - parsedKeywords); - } + ParseResult parseResult; + Instance instance(this->Bindings, &parseResult, unparsedArguments); + instance.Parse(args, pos); + return parseResult; } protected: + using Base::Instance; + using Base::BindKeywordMissingValue; + template <typename T> bool Bind(cm::string_view name, T& ref) { - return this->Bindings - .Emplace(name, - [&ref](ArgumentParser::Instance& instance, void*) { - instance.Bind(ref); - }) - .second; + return this->MaybeBind(name, + [&ref](Instance& instance) { instance.Bind(ref); }); } - -private: - ArgumentParser::ActionMap Bindings; }; diff --git a/Source/cmArgumentParserTypes.h b/Source/cmArgumentParserTypes.h new file mode 100644 index 0000000..7daae09 --- /dev/null +++ b/Source/cmArgumentParserTypes.h @@ -0,0 +1,69 @@ +/* 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 + +#if defined(__SUNPRO_CC) + +# include <string> +# include <vector> + +namespace ArgumentParser { + +template <typename T> +struct Maybe; +template <> +struct Maybe<std::string> : public std::string +{ + using std::string::basic_string; +}; + +template <typename T> +struct MaybeEmpty; +template <typename T> +struct MaybeEmpty<std::vector<T>> : public std::vector<T> +{ + using std::vector<T>::vector; +}; + +template <typename T> +struct NonEmpty; +template <typename T> +struct NonEmpty<std::vector<T>> : public std::vector<T> +{ + using std::vector<T>::vector; +}; +template <> +struct NonEmpty<std::string> : public std::string +{ + using std::string::basic_string; +}; + +} // namespace ArgumentParser + +#else + +namespace ArgumentParser { + +template <typename T> +struct Maybe : public T +{ + using T::T; +}; + +template <typename T> +struct MaybeEmpty : public T +{ + using T::T; +}; + +template <typename T> +struct NonEmpty : public T +{ + using T::T; +}; + +} // namespace ArgumentParser + +#endif diff --git a/Source/cmBlockCommand.cxx b/Source/cmBlockCommand.cxx new file mode 100644 index 0000000..c358aa2 --- /dev/null +++ b/Source/cmBlockCommand.cxx @@ -0,0 +1,200 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBlockCommand.h" + +#include <cstdint> // IWYU pragma: keep +#include <utility> + +#include <cm/memory> +#include <cm/optional> +#include <cm/string_view> +#include <cmext/enum_set> +#include <cmext/string_view> + +#include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" +#include "cmExecutionStatus.h" +#include "cmFunctionBlocker.h" +#include "cmListFileCache.h" +#include "cmMakefile.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" + +namespace { +enum class ScopeType : std::uint8_t +{ + VARIABLES, + POLICIES +}; +using ScopeSet = cm::enum_set<ScopeType>; + +class BlockScopePushPop +{ +public: + BlockScopePushPop(cmMakefile* m, const ScopeSet& scopes); + ~BlockScopePushPop() = default; + + BlockScopePushPop(const BlockScopePushPop&) = delete; + BlockScopePushPop& operator=(const BlockScopePushPop&) = delete; + +private: + std::unique_ptr<cmMakefile::PolicyPushPop> PolicyScope; + std::unique_ptr<cmMakefile::VariablePushPop> VariableScope; +}; + +BlockScopePushPop::BlockScopePushPop(cmMakefile* mf, const ScopeSet& scopes) +{ + if (scopes.contains(ScopeType::POLICIES)) { + this->PolicyScope = cm::make_unique<cmMakefile::PolicyPushPop>(mf); + } + if (scopes.contains(ScopeType::VARIABLES)) { + this->VariableScope = cm::make_unique<cmMakefile::VariablePushPop>(mf); + } +} + +class cmBlockFunctionBlocker : public cmFunctionBlocker +{ +public: + cmBlockFunctionBlocker(cmMakefile* mf, const ScopeSet& scopes, + std::vector<std::string> variableNames); + ~cmBlockFunctionBlocker() override; + + cm::string_view StartCommandName() const override { return "block"_s; } + cm::string_view EndCommandName() const override { return "endblock"_s; } + + bool EndCommandSupportsArguments() const override { return false; } + + bool ArgumentsMatch(cmListFileFunction const& lff, + cmMakefile& mf) const override; + + bool Replay(std::vector<cmListFileFunction> functions, + cmExecutionStatus& inStatus) override; + +private: + cmMakefile* Makefile; + BlockScopePushPop BlockScope; + std::vector<std::string> VariableNames; +}; + +cmBlockFunctionBlocker::cmBlockFunctionBlocker( + cmMakefile* const mf, const ScopeSet& scopes, + std::vector<std::string> variableNames) + : Makefile{ mf } + , BlockScope{ mf, scopes } + , VariableNames{ std::move(variableNames) } +{ +} + +cmBlockFunctionBlocker::~cmBlockFunctionBlocker() +{ + for (auto const& varName : this->VariableNames) { + if (this->Makefile->IsNormalDefinitionSet(varName)) { + this->Makefile->RaiseScope(varName, + this->Makefile->GetDefinition(varName)); + } else { + // unset variable in parent scope + this->Makefile->RaiseScope(varName, nullptr); + } + } +} + +bool cmBlockFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff, + cmMakefile&) const +{ + // no arguments expected for endblock() + // but this method should not be called because EndCommandHasArguments() + // returns false. + return lff.Arguments().empty(); +} + +bool cmBlockFunctionBlocker::Replay(std::vector<cmListFileFunction> functions, + cmExecutionStatus& inStatus) +{ + auto& mf = inStatus.GetMakefile(); + + // Invoke all the functions that were collected in the block. + for (cmListFileFunction const& fn : functions) { + cmExecutionStatus status(mf); + mf.ExecuteCommand(fn, status); + if (status.GetReturnInvoked()) { + inStatus.SetReturnInvoked(); + return true; + } + if (status.GetBreakInvoked()) { + inStatus.SetBreakInvoked(); + return true; + } + if (status.GetContinueInvoked()) { + inStatus.SetContinueInvoked(); + return true; + } + if (cmSystemTools::GetFatalErrorOccurred()) { + return true; + } + } + return true; +} + +} // anonymous namespace + +bool cmBlockCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + struct Arguments : public ArgumentParser::ParseResult + { + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> ScopeFor; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Propagate; + }; + static auto const parser = cmArgumentParser<Arguments>{} + .Bind("SCOPE_FOR"_s, &Arguments::ScopeFor) + .Bind("PROPAGATE"_s, &Arguments::Propagate); + std::vector<std::string> unrecognizedArguments; + auto parsedArgs = parser.Parse(args, &unrecognizedArguments); + + if (!unrecognizedArguments.empty()) { + status.SetError(cmStrCat("called with unsupported argument \"", + unrecognizedArguments[0], '"')); + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + + if (parsedArgs.MaybeReportError(status.GetMakefile())) { + cmSystemTools::SetFatalErrorOccurred(); + return true; + } + + ScopeSet scopes; + + if (parsedArgs.ScopeFor) { + for (auto const& scope : *parsedArgs.ScopeFor) { + if (scope == "VARIABLES"_s) { + scopes.insert(ScopeType::VARIABLES); + continue; + } + if (scope == "POLICIES"_s) { + scopes.insert(ScopeType::POLICIES); + continue; + } + status.SetError(cmStrCat("SCOPE_FOR unsupported scope \"", scope, '"')); + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + } else { + scopes = { ScopeType::VARIABLES, ScopeType::POLICIES }; + } + if (!scopes.contains(ScopeType::VARIABLES) && + !parsedArgs.Propagate.empty()) { + status.SetError( + "PROPAGATE cannot be specified without a new scope for VARIABLES"); + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + + // create a function blocker + auto fb = cm::make_unique<cmBlockFunctionBlocker>( + &status.GetMakefile(), scopes, parsedArgs.Propagate); + status.GetMakefile().AddFunctionBlocker(std::move(fb)); + + return true; +} diff --git a/Source/cmBlockCommand.h b/Source/cmBlockCommand.h new file mode 100644 index 0000000..5fd8f42 --- /dev/null +++ b/Source/cmBlockCommand.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 "cmConfigure.h" // IWYU pragma: keep + +#include <string> +#include <vector> + +class cmExecutionStatus; + +/// Starts block() ... endblock() block +bool cmBlockCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); diff --git a/Source/cmCMakeHostSystemInformationCommand.cxx b/Source/cmCMakeHostSystemInformationCommand.cxx index 0750eea..58129a0 100644 --- a/Source/cmCMakeHostSystemInformationCommand.cxx +++ b/Source/cmCMakeHostSystemInformationCommand.cxx @@ -474,7 +474,7 @@ bool QueryWindowsRegistry(Range args, cmExecutionStatus& status, } std::string const& key = *args.begin(); - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { std::string ValueName; bool ValueNames = false; @@ -491,19 +491,15 @@ bool QueryWindowsRegistry(Range args, cmExecutionStatus& status, .Bind("SEPARATOR"_s, &Arguments::Separator) .Bind("ERROR_VARIABLE"_s, &Arguments::ErrorVariable); std::vector<std::string> invalidArgs; - std::vector<std::string> keywordsMissingValue; - Arguments const arguments = - parser.Parse(args.advance(1), &invalidArgs, &keywordsMissingValue); + Arguments const arguments = parser.Parse(args.advance(1), &invalidArgs); if (!invalidArgs.empty()) { status.SetError(cmStrCat("given invalid argument(s) \"", cmJoin(invalidArgs, ", "_s), "\".")); return false; } - if (!keywordsMissingValue.empty()) { - status.SetError(cmStrCat("missing expected value for argument(s) \"", - cmJoin(keywordsMissingValue, ", "_s), "\".")); - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if ((!arguments.ValueName.empty() && (arguments.ValueNames || arguments.SubKeys)) || diff --git a/Source/cmCMakeLanguageCommand.cxx b/Source/cmCMakeLanguageCommand.cxx index a2aaa2a..68e658c 100644 --- a/Source/cmCMakeLanguageCommand.cxx +++ b/Source/cmCMakeLanguageCommand.cxx @@ -14,15 +14,18 @@ #include <cmext/string_view> #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmDependencyProvider.h" #include "cmExecutionStatus.h" #include "cmGlobalGenerator.h" #include "cmListFileCache.h" #include "cmMakefile.h" +#include "cmMessageType.h" #include "cmRange.h" #include "cmState.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmake.h" namespace { @@ -33,13 +36,14 @@ bool FatalError(cmExecutionStatus& status, std::string const& error) return false; } -std::array<cm::static_string_view, 12> InvalidCommands{ +std::array<cm::static_string_view, 14> InvalidCommands{ { // clang-format off "function"_s, "endfunction"_s, "macro"_s, "endmacro"_s, "if"_s, "elseif"_s, "else"_s, "endif"_s, "while"_s, "endwhile"_s, - "foreach"_s, "endforeach"_s + "foreach"_s, "endforeach"_s, + "block"_s, "endblock"_s } // clang-format on }; @@ -235,7 +239,7 @@ bool cmCMakeLanguageCommandSET_DEPENDENCY_PROVIDER( struct SetProviderArgs { std::string Command; - std::vector<std::string> Methods; + ArgumentParser::NonEmpty<std::vector<std::string>> Methods; }; auto const ArgsParser = @@ -303,6 +307,27 @@ bool cmCMakeLanguageCommandSET_DEPENDENCY_PROVIDER( return true; } + +bool cmCMakeLanguageCommandGET_MESSAGE_LOG_LEVEL( + std::vector<cmListFileArgument> const& args, cmExecutionStatus& status) +{ + cmMakefile& makefile = status.GetMakefile(); + std::vector<std::string> expandedArgs; + makefile.ExpandArguments(args, expandedArgs); + + if (args.size() < 2 || expandedArgs.size() > 2) { + return FatalError( + status, + "sub-command GET_MESSAGE_LOG_LEVEL expects exactly one argument"); + } + + Message::LogLevel logLevel = makefile.GetCurrentLogLevel(); + std::string outputValue = cmake::LogLevelToString(logLevel); + + const std::string& outputVariable = expandedArgs[1]; + makefile.AddDefinition(outputVariable, outputValue); + return true; +} } bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args, @@ -451,5 +476,9 @@ bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args, return cmCMakeLanguageCommandEVAL(args, status); } + if (expArgs[expArg] == "GET_MESSAGE_LOG_LEVEL") { + return cmCMakeLanguageCommandGET_MESSAGE_LOG_LEVEL(args, status); + } + return FatalError(status, "called with unknown meta-operation"); } diff --git a/Source/cmCMakePathCommand.cxx b/Source/cmCMakePathCommand.cxx index bf94c2d..7755082 100644 --- a/Source/cmCMakePathCommand.cxx +++ b/Source/cmCMakePathCommand.cxx @@ -2,7 +2,6 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCMakePathCommand.h" -#include <algorithm> #include <functional> #include <iomanip> #include <map> @@ -11,10 +10,12 @@ #include <utility> #include <vector> +#include <cm/optional> #include <cm/string_view> #include <cmext/string_view> #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmCMakePath.h" #include "cmExecutionStatus.h" #include "cmMakefile.h" @@ -43,15 +44,12 @@ public: } template <int Advance = 2> - Result Parse(std::vector<std::string> const& args, - std::vector<std::string>* keywordsMissingValue = nullptr, - std::vector<std::string>* parsedKeywords = nullptr) const + Result Parse(std::vector<std::string> const& args) const { this->Inputs.clear(); return this->cmArgumentParser<Result>::Parse( - cmMakeRange(args).advance(Advance), &this->Inputs, keywordsMissingValue, - parsedKeywords); + cmMakeRange(args).advance(Advance), &this->Inputs); } const std::vector<std::string>& GetInputs() const { return this->Inputs; } @@ -82,52 +80,14 @@ public: template <int Advance = 2> Result Parse(std::vector<std::string> const& args) const { - this->KeywordsMissingValue.clear(); - this->ParsedKeywords.clear(); - return this->CMakePathArgumentParser<Result>::template Parse<Advance>( - args, &this->KeywordsMissingValue, &this->ParsedKeywords); - } - - const std::vector<std::string>& GetKeywordsMissingValue() const - { - return this->KeywordsMissingValue; - } - const std::vector<std::string>& GetParsedKeywords() const - { - return this->ParsedKeywords; - } - - bool checkOutputVariable(const Result& arguments, - cmExecutionStatus& status) const - { - if (std::find(this->GetKeywordsMissingValue().begin(), - this->GetKeywordsMissingValue().end(), - "OUTPUT_VARIABLE"_s) != - this->GetKeywordsMissingValue().end()) { - status.SetError("OUTPUT_VARIABLE requires an argument."); - return false; - } - - if (std::find(this->GetParsedKeywords().begin(), - this->GetParsedKeywords().end(), - "OUTPUT_VARIABLE"_s) != this->GetParsedKeywords().end() && - arguments.Output.empty()) { - status.SetError("Invalid name for output variable."); - return false; - } - - return true; + args); } - -private: - mutable std::vector<std::string> KeywordsMissingValue; - mutable std::vector<std::string> ParsedKeywords; }; -struct OutputVariable +struct OutputVariable : public ArgumentParser::ParseResult { - std::string Output; + cm::optional<ArgumentParser::NonEmpty<std::string>> Output; }; // Usable when OUTPUT_VARIABLE is the only option class OutputVariableParser @@ -297,8 +257,8 @@ bool HandleAppendCommand(std::vector<std::string> const& args, const auto arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } cmCMakePath path(status.GetMakefile().GetSafeDefinition(args[1])); @@ -307,7 +267,7 @@ bool HandleAppendCommand(std::vector<std::string> const& args, } status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -319,8 +279,8 @@ bool HandleAppendStringCommand(std::vector<std::string> const& args, const auto arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } std::string inputPath; @@ -334,7 +294,7 @@ bool HandleAppendStringCommand(std::vector<std::string> const& args, } status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -346,8 +306,8 @@ bool HandleRemoveFilenameCommand(std::vector<std::string> const& args, const auto arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!parser.GetInputs().empty()) { @@ -364,7 +324,7 @@ bool HandleRemoveFilenameCommand(std::vector<std::string> const& args, path.RemoveFileName(); status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -376,8 +336,8 @@ bool HandleReplaceFilenameCommand(std::vector<std::string> const& args, const auto arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (parser.GetInputs().size() > 1) { @@ -395,7 +355,7 @@ bool HandleReplaceFilenameCommand(std::vector<std::string> const& args, parser.GetInputs().empty() ? "" : parser.GetInputs().front()); status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -403,9 +363,9 @@ bool HandleReplaceFilenameCommand(std::vector<std::string> const& args, bool HandleRemoveExtensionCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { - std::string Output; + cm::optional<ArgumentParser::NonEmpty<std::string>> Output; bool LastOnly = false; }; @@ -415,8 +375,8 @@ bool HandleRemoveExtensionCommand(std::vector<std::string> const& args, Arguments const arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!parser.GetInputs().empty()) { @@ -438,7 +398,7 @@ bool HandleRemoveExtensionCommand(std::vector<std::string> const& args, } status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -446,9 +406,9 @@ bool HandleRemoveExtensionCommand(std::vector<std::string> const& args, bool HandleReplaceExtensionCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { - std::string Output; + cm::optional<ArgumentParser::NonEmpty<std::string>> Output; bool LastOnly = false; }; @@ -458,8 +418,8 @@ bool HandleReplaceExtensionCommand(std::vector<std::string> const& args, Arguments const arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (parser.GetInputs().size() > 1) { @@ -483,7 +443,7 @@ bool HandleReplaceExtensionCommand(std::vector<std::string> const& args, } status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -495,8 +455,8 @@ bool HandleNormalPathCommand(std::vector<std::string> const& args, const auto arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!parser.GetInputs().empty()) { @@ -512,7 +472,7 @@ bool HandleNormalPathCommand(std::vector<std::string> const& args, auto path = cmCMakePath(inputPath).Normal(); status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -523,10 +483,10 @@ bool HandleTransformPathCommand( const std::string& base)>& transform, bool normalizeOption = false) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { - std::string Output; - std::string BaseDirectory; + cm::optional<ArgumentParser::NonEmpty<std::string>> Output; + cm::optional<std::string> BaseDirectory; bool Normalize = false; }; @@ -538,8 +498,8 @@ bool HandleTransformPathCommand( Arguments arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!parser.GetInputs().empty()) { @@ -547,17 +507,11 @@ bool HandleTransformPathCommand( return false; } - if (std::find(parser.GetKeywordsMissingValue().begin(), - parser.GetKeywordsMissingValue().end(), "BASE_DIRECTORY"_s) != - parser.GetKeywordsMissingValue().end()) { - status.SetError("BASE_DIRECTORY requires an argument."); - return false; - } - - if (std::find(parser.GetParsedKeywords().begin(), - parser.GetParsedKeywords().end(), - "BASE_DIRECTORY"_s) == parser.GetParsedKeywords().end()) { - arguments.BaseDirectory = status.GetMakefile().GetCurrentSourceDirectory(); + std::string baseDirectory; + if (arguments.BaseDirectory) { + baseDirectory = *arguments.BaseDirectory; + } else { + baseDirectory = status.GetMakefile().GetCurrentSourceDirectory(); } std::string inputPath; @@ -565,13 +519,13 @@ bool HandleTransformPathCommand( return false; } - auto path = transform(cmCMakePath(inputPath), arguments.BaseDirectory); + auto path = transform(cmCMakePath(inputPath), baseDirectory); if (arguments.Normalize) { path = path.Normal(); } status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } diff --git a/Source/cmCMakePresetsGraph.cxx b/Source/cmCMakePresetsGraph.cxx index b737c1f..dba79d5 100644 --- a/Source/cmCMakePresetsGraph.cxx +++ b/Source/cmCMakePresetsGraph.cxx @@ -901,8 +901,9 @@ cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles) std::string filename = GetUserFilename(this->SourceDir); std::vector<File*> inProgressFiles; if (cmSystemTools::FileExists(filename)) { - auto result = this->ReadJSONFile(filename, RootType::User, - ReadReason::Root, inProgressFiles, file); + auto result = + this->ReadJSONFile(filename, RootType::User, ReadReason::Root, + inProgressFiles, file, this->errors); if (result != ReadFileResult::READ_OK) { return result; } @@ -910,8 +911,9 @@ cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles) } else { filename = GetFilename(this->SourceDir); if (cmSystemTools::FileExists(filename)) { - auto result = this->ReadJSONFile( - filename, RootType::Project, ReadReason::Root, inProgressFiles, file); + auto result = + this->ReadJSONFile(filename, RootType::Project, ReadReason::Root, + inProgressFiles, file, this->errors); if (result != ReadFileResult::READ_OK) { return result; } diff --git a/Source/cmCMakePresetsGraph.h b/Source/cmCMakePresetsGraph.h index f1f8662..4f3e108 100644 --- a/Source/cmCMakePresetsGraph.h +++ b/Source/cmCMakePresetsGraph.h @@ -52,6 +52,7 @@ public: TEST_OUTPUT_TRUNCATION_UNSUPPORTED, }; + std::string errors; enum class ArchToolsetStrategy { Set, @@ -407,7 +408,7 @@ private: ReadFileResult ReadProjectPresetsInternal(bool allowNoFiles); ReadFileResult ReadJSONFile(const std::string& filename, RootType rootType, ReadReason readReason, - std::vector<File*>& inProgressFiles, - File*& file); + std::vector<File*>& inProgressFiles, File*& file, + std::string& errMsg); void ClearPresets(); }; diff --git a/Source/cmCMakePresetsGraphReadJSON.cxx b/Source/cmCMakePresetsGraphReadJSON.cxx index d11e839..d68af22 100644 --- a/Source/cmCMakePresetsGraphReadJSON.cxx +++ b/Source/cmCMakePresetsGraphReadJSON.cxx @@ -411,7 +411,7 @@ cmCMakePresetsGraph::ReadFileResult EnvironmentMapHelper( cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile( const std::string& filename, RootType rootType, ReadReason readReason, - std::vector<File*>& inProgressFiles, File*& file) + std::vector<File*>& inProgressFiles, File*& file, std::string& errMsg) { ReadFileResult result; @@ -430,6 +430,7 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile( cmsys::ifstream fin(filename.c_str()); if (!fin) { + errMsg = cmStrCat(filename, ": Failed to read file\n", errMsg); return ReadFileResult::FILE_NOT_FOUND; } // If there's a BOM, toss it. @@ -438,7 +439,8 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile( Json::Value root; Json::CharReaderBuilder builder; Json::CharReaderBuilder::strictMode(&builder.settings_); - if (!Json::parseFromStream(builder, fin, &root, nullptr)) { + if (!Json::parseFromStream(builder, fin, &root, &errMsg)) { + errMsg = cmStrCat(filename, ":\n", errMsg); return ReadFileResult::JSON_PARSE_ERROR; } @@ -490,6 +492,8 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile( for (auto& preset : presets.ConfigurePresets) { preset.OriginFile = file; if (preset.Name.empty()) { + errMsg += R"(\n\t)"; + errMsg += filename; return ReadFileResult::INVALID_PRESET; } @@ -523,6 +527,8 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile( for (auto& preset : presets.BuildPresets) { preset.OriginFile = file; if (preset.Name.empty()) { + errMsg += R"(\n\t)"; + errMsg += filename; return ReadFileResult::INVALID_PRESET; } @@ -569,12 +575,13 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile( auto const includeFile = [this, &inProgressFiles, file]( const std::string& include, RootType rootType2, - ReadReason readReason2) -> ReadFileResult { + ReadReason readReason2, + std::string& FailureMessage) -> ReadFileResult { ReadFileResult r; File* includedFile; if ((r = this->ReadJSONFile(include, rootType2, readReason2, - inProgressFiles, includedFile)) != - ReadFileResult::READ_OK) { + inProgressFiles, includedFile, + FailureMessage)) != ReadFileResult::READ_OK) { return r; } @@ -589,8 +596,8 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile( include = cmStrCat(directory, '/', include); } - if ((result = includeFile(include, rootType, ReadReason::Included)) != - ReadFileResult::READ_OK) { + if ((result = includeFile(include, rootType, ReadReason::Included, + errMsg)) != ReadFileResult::READ_OK) { return result; } } @@ -599,7 +606,7 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile( auto cmakePresetsFilename = GetFilename(this->SourceDir); if (cmSystemTools::FileExists(cmakePresetsFilename)) { if ((result = includeFile(cmakePresetsFilename, RootType::Project, - ReadReason::Root)) != + ReadReason::Root, errMsg)) != ReadFileResult::READ_OK) { return result; } diff --git a/Source/cmCommands.cxx b/Source/cmCommands.cxx index c6296f9..3bc4f0e 100644 --- a/Source/cmCommands.cxx +++ b/Source/cmCommands.cxx @@ -14,13 +14,13 @@ #include "cmAddLibraryCommand.h" #include "cmAddSubDirectoryCommand.h" #include "cmAddTestCommand.h" +#include "cmBlockCommand.h" #include "cmBreakCommand.h" #include "cmBuildCommand.h" #include "cmCMakeLanguageCommand.h" #include "cmCMakeMinimumRequired.h" #include "cmCMakePathCommand.h" #include "cmCMakePolicyCommand.h" -#include "cmCommand.h" #include "cmConfigureFileCommand.h" #include "cmContinueCommand.h" #include "cmCreateTestSourceList.h" @@ -127,6 +127,7 @@ void GetScriptingCommands(cmState* state) state->AddFlowControlCommand("macro", cmMacroCommand); state->AddFlowControlCommand("return", cmReturnCommand); state->AddFlowControlCommand("while", cmWhileCommand); + state->AddFlowControlCommand("block", cmBlockCommand); state->AddBuiltinCommand("cmake_language", cmCMakeLanguageCommand); state->AddBuiltinCommand("cmake_minimum_required", cmCMakeMinimumRequired); @@ -199,6 +200,10 @@ void GetScriptingCommands(cmState* state) "An ENDWHILE command was found outside of a proper " "WHILE ENDWHILE structure. Or its arguments did not " "match the opening WHILE command."); + state->AddUnexpectedFlowControlCommand( + "endblock", + "An ENDBLOCK command was found outside of a proper " + "BLOCK ENDBLOCK structure."); #if !defined(CMAKE_BOOTSTRAP) state->AddBuiltinCommand("cmake_host_system_information", @@ -264,9 +269,8 @@ void GetProjectCommands(cmState* state) cmTargetLinkLibrariesCommand); state->AddBuiltinCommand("target_link_options", cmTargetLinkOptionsCommand); state->AddBuiltinCommand("target_sources", cmTargetSourcesCommand); - state->AddBuiltinCommand("try_compile", - cm::make_unique<cmTryCompileCommand>()); - state->AddBuiltinCommand("try_run", cm::make_unique<cmTryRunCommand>()); + state->AddBuiltinCommand("try_compile", cmTryCompileCommand); + state->AddBuiltinCommand("try_run", cmTryRunCommand); state->AddBuiltinCommand("target_precompile_headers", cmTargetPrecompileHeadersCommand); diff --git a/Source/cmCommonTargetGenerator.cxx b/Source/cmCommonTargetGenerator.cxx index ba95168..5fe6756 100644 --- a/Source/cmCommonTargetGenerator.cxx +++ b/Source/cmCommonTargetGenerator.cxx @@ -9,6 +9,7 @@ #include "cmComputeLinkInformation.h" #include "cmGeneratorTarget.h" #include "cmGlobalCommonGenerator.h" +#include "cmGlobalGenerator.h" #include "cmLocalCommonGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" @@ -175,6 +176,9 @@ std::vector<std::string> cmCommonTargetGenerator::GetLinkedTargetDirectories( cmLocalGenerator* lg = linkee->GetLocalGenerator(); std::string di = cmStrCat(lg->GetCurrentBinaryDirectory(), '/', lg->GetTargetDirectory(linkee)); + if (lg->GetGlobalGenerator()->IsMultiConfig()) { + di = cmStrCat(di, '/', config); + } dirs.push_back(std::move(di)); } } diff --git a/Source/cmConfigure.cmake.h.in b/Source/cmConfigure.cmake.h.in index 6a419f6..31d03da 100644 --- a/Source/cmConfigure.cmake.h.in +++ b/Source/cmConfigure.cmake.h.in @@ -14,6 +14,10 @@ #pragma warning(disable : 1572) /* floating-point equality test */ #endif +#if defined(__LCC__) && defined(__EDG__) && (__LCC__ == 123) +#pragma diag_suppress 2910 /* excess -Wunused-function in 1.23.x */ +#endif + #cmakedefine HAVE_ENVIRON_NOT_REQUIRE_PROTOTYPE #cmakedefine HAVE_UNSETENV #cmakedefine CMake_USE_MACH_PARSER diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index 5418e7c..7b9dc2e 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCoreTryCompile.h" +#include <array> #include <cstdio> #include <cstring> #include <set> @@ -13,12 +14,14 @@ #include "cmsys/Directory.hxx" +#include "cmArgumentParser.h" #include "cmExportTryCompileFileGenerator.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmPolicies.h" +#include "cmRange.h" #include "cmState.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -28,142 +31,6 @@ #include "cmake.h" namespace { -class LanguageStandardState -{ -public: - LanguageStandardState(std::string&& lang) - : StandardFlag(lang + "_STANDARD") - , RequiredFlag(lang + "_STANDARD_REQUIRED") - , ExtensionFlag(lang + "_EXTENSIONS") - { - } - - void Enabled(bool isEnabled) { this->IsEnabled = isEnabled; } - - bool UpdateIfMatches(std::vector<std::string> const& argv, size_t& index) - { - bool updated = false; - if (argv[index] == this->StandardFlag) { - this->DidStandard = true; - this->StandardValue = argv[++index]; - updated = true; - } else if (argv[index] == this->RequiredFlag) { - this->DidStandardRequired = true; - this->RequiredValue = argv[++index]; - updated = true; - } else if (argv[index] == this->ExtensionFlag) { - this->DidExtensions = true; - this->ExtensionValue = argv[++index]; - updated = true; - } - return updated; - } - - bool Validate(cmMakefile* const makefile) const - { - if (this->DidStandard) { - makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat(this->StandardFlag, - " allowed only in source file signature.")); - return false; - } - if (this->DidStandardRequired) { - makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat(this->RequiredFlag, - " allowed only in source file signature.")); - return false; - } - if (this->DidExtensions) { - makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat(this->ExtensionFlag, - " allowed only in source file signature.")); - return false; - } - - return true; - } - - bool DidNone() const - { - return !this->DidStandard && !this->DidStandardRequired && - !this->DidExtensions; - } - - void LoadUnsetPropertyValues(cmMakefile* const makefile, bool honorStandard, - bool warnCMP0067, - std::vector<std::string>& warnCMP0067Variables) - { - if (!this->IsEnabled) { - return; - } - - auto lookupStdVar = [&](std::string const& var) -> std::string { - std::string value = makefile->GetSafeDefinition(var); - if (warnCMP0067 && !value.empty()) { - value.clear(); - warnCMP0067Variables.emplace_back(var); - } - return value; - }; - - if (honorStandard || warnCMP0067) { - if (!this->DidStandard) { - this->StandardValue = - lookupStdVar(cmStrCat("CMAKE_", this->StandardFlag)); - } - if (!this->DidStandardRequired) { - this->RequiredValue = - lookupStdVar(cmStrCat("CMAKE_", this->RequiredFlag)); - } - if (!this->DidExtensions) { - this->ExtensionValue = - lookupStdVar(cmStrCat("CMAKE_", this->ExtensionFlag)); - } - } - } - - void WriteProperties(FILE* fout, std::string const& targetName) const - { - if (!this->IsEnabled) { - return; - } - - auto writeProp = [&](std::string const& prop, std::string const& value) { - fprintf(fout, "set_property(TARGET %s PROPERTY %s %s)\n", - targetName.c_str(), - cmOutputConverter::EscapeForCMake(prop).c_str(), - cmOutputConverter::EscapeForCMake(value).c_str()); - }; - - if (!this->StandardValue.empty()) { - writeProp(this->StandardFlag, this->StandardValue); - } - if (!this->RequiredValue.empty()) { - writeProp(this->RequiredFlag, this->RequiredValue); - } - if (!this->ExtensionValue.empty()) { - writeProp(this->ExtensionFlag, this->ExtensionValue); - } - } - -private: - bool IsEnabled = false; - bool DidStandard = false; - bool DidStandardRequired = false; - bool DidExtensions = false; - - std::string StandardFlag; - std::string RequiredFlag; - std::string ExtensionFlag; - - std::string StandardValue; - std::string RequiredValue; - std::string ExtensionValue; -}; - constexpr size_t lang_property_start = 0; constexpr size_t lang_property_size = 4; constexpr size_t pie_property_start = 4; @@ -233,114 +100,160 @@ std::set<std::string> const ghs_platform_vars{ "GHS_OS_ROOT", "GHS_OS_DIR", "GHS_BSP_NAME", "GHS_OS_DIR_OPTION" }; +using Arguments = cmCoreTryCompile::Arguments; + +ArgumentParser::Continue TryCompileLangProp(Arguments& args, + cm::string_view key, + cm::string_view val) +{ + args.LangProps[std::string(key)] = std::string(val); + return ArgumentParser::Continue::No; +} + +ArgumentParser::Continue TryCompileCompileDefs(Arguments& args, + cm::string_view val) +{ + cmExpandList(val, args.CompileDefs); + return ArgumentParser::Continue::Yes; +} + +#define BIND_LANG_PROPS(lang) \ + Bind(#lang "_STANDARD"_s, TryCompileLangProp) \ + .Bind(#lang "_STANDARD_REQUIRED"_s, TryCompileLangProp) \ + .Bind(#lang "_EXTENSIONS"_s, TryCompileLangProp) + +auto const TryCompileArgParser = + cmArgumentParser<Arguments>{} + .Bind(0, &Arguments::CompileResultVariable) + .Bind(1, &Arguments::BinaryDirectory) + .Bind(2, &Arguments::SourceDirectoryOrFile) + .Bind(3, &Arguments::ProjectName) + .Bind(4, &Arguments::TargetName) + .Bind("SOURCES"_s, &Arguments::Sources) + .Bind("CMAKE_FLAGS"_s, &Arguments::CMakeFlags) + .Bind("COMPILE_DEFINITIONS"_s, TryCompileCompileDefs, + ArgumentParser::ExpectAtLeast{ 0 }) + .Bind("LINK_LIBRARIES"_s, &Arguments::LinkLibraries) + .Bind("LINK_OPTIONS"_s, &Arguments::LinkOptions) + .Bind("__CMAKE_INTERNAL"_s, &Arguments::CMakeInternal) + .Bind("OUTPUT_VARIABLE"_s, &Arguments::OutputVariable) + .Bind("COPY_FILE"_s, &Arguments::CopyFileTo) + .Bind("COPY_FILE_ERROR"_s, &Arguments::CopyFileError) + .BIND_LANG_PROPS(C) + .BIND_LANG_PROPS(CUDA) + .BIND_LANG_PROPS(CXX) + .BIND_LANG_PROPS(HIP) + .BIND_LANG_PROPS(OBJC) + .BIND_LANG_PROPS(OBJCXX) + /* keep semicolon on own line */; + +auto const TryRunArgParser = + cmArgumentParser<Arguments>{ TryCompileArgParser } + .Bind("COMPILE_OUTPUT_VARIABLE"_s, &Arguments::CompileOutputVariable) + .Bind("RUN_OUTPUT_VARIABLE"_s, &Arguments::RunOutputVariable) + .Bind("RUN_OUTPUT_STDOUT_VARIABLE"_s, &Arguments::RunOutputStdOutVariable) + .Bind("RUN_OUTPUT_STDERR_VARIABLE"_s, &Arguments::RunOutputStdErrVariable) + .Bind("WORKING_DIRECTORY"_s, &Arguments::RunWorkingDirectory) + .Bind("ARGS"_s, &Arguments::RunArgs) + /* keep semicolon on own line */; + +#undef BIND_LANG_PROPS +} + +Arguments cmCoreTryCompile::ParseArgs( + cmRange<std::vector<std::string>::const_iterator> args, bool isTryRun) +{ + std::vector<std::string> unparsedArguments; + const auto& parser = (isTryRun ? TryRunArgParser : TryCompileArgParser); + auto arguments = parser.Parse(args, &unparsedArguments, 0); + if (!arguments.MaybeReportError(*(this->Makefile)) && + !unparsedArguments.empty()) { + std::string m = "Unknown arguments:"; + for (const auto& i : unparsedArguments) { + m = cmStrCat(m, "\n \"", i, "\""); + } + this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, m); + } + // For historical reasons, treat some empty-valued keyword + // arguments as if they were not specified at all. + if (arguments.OutputVariable && arguments.OutputVariable->empty()) { + arguments.OutputVariable = cm::nullopt; + } + if (isTryRun) { + if (arguments.CompileOutputVariable && + arguments.CompileOutputVariable->empty()) { + arguments.CompileOutputVariable = cm::nullopt; + } + if (arguments.RunOutputVariable && arguments.RunOutputVariable->empty()) { + arguments.RunOutputVariable = cm::nullopt; + } + if (arguments.RunOutputStdOutVariable && + arguments.RunOutputStdOutVariable->empty()) { + arguments.RunOutputStdOutVariable = cm::nullopt; + } + if (arguments.RunOutputStdErrVariable && + arguments.RunOutputStdErrVariable->empty()) { + arguments.RunOutputStdErrVariable = cm::nullopt; + } + if (arguments.RunWorkingDirectory && + arguments.RunWorkingDirectory->empty()) { + arguments.RunWorkingDirectory = cm::nullopt; + } + } + return arguments; } -int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, - bool isTryRun) +bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, + cmStateEnums::TargetType targetType) { - this->BinaryDirectory = argv[1]; this->OutputFile.clear(); // which signature were we called with ? this->SrcFileSignature = true; - cmStateEnums::TargetType targetType = cmStateEnums::EXECUTABLE; - cmValue tt = this->Makefile->GetDefinition("CMAKE_TRY_COMPILE_TARGET_TYPE"); - if (!isTryRun && cmNonempty(tt)) { - if (*tt == cmState::GetTargetTypeName(cmStateEnums::EXECUTABLE)) { - targetType = cmStateEnums::EXECUTABLE; - } else if (*tt == - cmState::GetTargetTypeName(cmStateEnums::STATIC_LIBRARY)) { - targetType = cmStateEnums::STATIC_LIBRARY; - } else { + std::string sourceDirectory; + std::string projectName; + std::string targetName; + if (arguments.SourceDirectoryOrFile && arguments.ProjectName) { + this->SrcFileSignature = false; + sourceDirectory = *arguments.SourceDirectoryOrFile; + projectName = *arguments.ProjectName; + if (arguments.TargetName) { + targetName = *arguments.TargetName; + } + } else { + projectName = "CMAKE_TRY_COMPILE"; + /* Use a random file name to avoid rapid creation and deletion + of the same executable name (some filesystems fail on that). */ + char targetNameBuf[64]; + snprintf(targetNameBuf, sizeof(targetNameBuf), "cmTC_%05x", + cmSystemTools::RandomSeed() & 0xFFFFF); + targetName = targetNameBuf; + } + + if (arguments.BinaryDirectory && !arguments.BinaryDirectory->empty()) { + if (!cmSystemTools::FileIsFullPath(*arguments.BinaryDirectory)) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, - cmStrCat("Invalid value '", *tt, - "' for CMAKE_TRY_COMPILE_TARGET_TYPE. Only '", - cmState::GetTargetTypeName(cmStateEnums::EXECUTABLE), - "' and '", - cmState::GetTargetTypeName(cmStateEnums::STATIC_LIBRARY), - "' are allowed.")); - return -1; + cmStrCat("<bindir> is not an absolute path:\n '", + *arguments.BinaryDirectory, "'")); + return false; + } + this->BinaryDirectory = *arguments.BinaryDirectory; + // compute the binary dir when TRY_COMPILE is called with a src file + // signature + if (this->SrcFileSignature) { + this->BinaryDirectory += "/CMakeFiles/CMakeTmp"; } + } else { + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, + "No <bindir> specified."); + return false; } - std::string sourceDirectory = argv[2]; - std::string projectName; - std::string targetName; - std::vector<std::string> cmakeFlags(1, "CMAKE_FLAGS"); // fake argv[0] - std::vector<std::string> compileDefs; - std::string cmakeInternal; - std::string outputVariable; - std::string copyFile; - std::string copyFileError; - LanguageStandardState cState("C"); - LanguageStandardState cudaState("CUDA"); - LanguageStandardState cxxState("CXX"); - LanguageStandardState hipState("HIP"); - LanguageStandardState objcState("OBJC"); - LanguageStandardState objcxxState("OBJCXX"); std::vector<std::string> targets; - std::vector<std::string> linkOptions; - std::string libsToLink = " "; - bool useOldLinkLibs = true; - char targetNameBuf[64]; - bool didOutputVariable = false; - bool didCopyFile = false; - bool didCopyFileError = false; - bool useSources = argv[2] == "SOURCES"; - std::vector<std::string> sources; - - enum Doing - { - DoingNone, - DoingCMakeFlags, - DoingCompileDefinitions, - DoingLinkOptions, - DoingLinkLibraries, - DoingOutputVariable, - DoingCopyFile, - DoingCopyFileError, - DoingSources, - DoingCMakeInternal - }; - Doing doing = useSources ? DoingSources : DoingNone; - for (size_t i = 3; i < argv.size(); ++i) { - if (argv[i] == "CMAKE_FLAGS") { - doing = DoingCMakeFlags; - } else if (argv[i] == "COMPILE_DEFINITIONS") { - doing = DoingCompileDefinitions; - } else if (argv[i] == "LINK_OPTIONS") { - doing = DoingLinkOptions; - } else if (argv[i] == "LINK_LIBRARIES") { - doing = DoingLinkLibraries; - useOldLinkLibs = false; - } else if (argv[i] == "OUTPUT_VARIABLE") { - doing = DoingOutputVariable; - didOutputVariable = true; - } else if (argv[i] == "COPY_FILE") { - doing = DoingCopyFile; - didCopyFile = true; - } else if (argv[i] == "COPY_FILE_ERROR") { - doing = DoingCopyFileError; - didCopyFileError = true; - } else if (cState.UpdateIfMatches(argv, i) || - cxxState.UpdateIfMatches(argv, i) || - cudaState.UpdateIfMatches(argv, i) || - hipState.UpdateIfMatches(argv, i) || - objcState.UpdateIfMatches(argv, i) || - objcxxState.UpdateIfMatches(argv, i)) { - continue; - } else if (argv[i] == "__CMAKE_INTERNAL") { - doing = DoingCMakeInternal; - } else if (doing == DoingCMakeFlags) { - cmakeFlags.emplace_back(argv[i]); - } else if (doing == DoingCompileDefinitions) { - cmExpandList(argv[i], compileDefs); - } else if (doing == DoingLinkOptions) { - linkOptions.emplace_back(argv[i]); - } else if (doing == DoingLinkLibraries) { - libsToLink += "\"" + cmTrimWhitespace(argv[i]) + "\" "; - if (cmTarget* tgt = this->Makefile->FindTargetToUse(argv[i])) { + if (arguments.LinkLibraries) { + for (std::string const& i : *arguments.LinkLibraries) { + if (cmTarget* tgt = this->Makefile->FindTargetToUse(i)) { switch (tgt->GetType()) { case cmStateEnums::SHARED_LIBRARY: case cmStateEnums::STATIC_LIBRARY: @@ -359,110 +272,62 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, "IMPORTED LINK_LIBRARIES. Got ", tgt->GetName(), " of type ", cmState::GetTargetTypeName(tgt->GetType()), ".")); - return -1; + return false; } if (tgt->IsImported()) { - targets.emplace_back(argv[i]); + targets.emplace_back(i); } } - } else if (doing == DoingOutputVariable) { - outputVariable = argv[i]; - doing = DoingNone; - } else if (doing == DoingCopyFile) { - copyFile = argv[i]; - doing = DoingNone; - } else if (doing == DoingCopyFileError) { - copyFileError = argv[i]; - doing = DoingNone; - } else if (doing == DoingSources) { - sources.emplace_back(argv[i]); - } else if (doing == DoingCMakeInternal) { - cmakeInternal = argv[i]; - doing = DoingNone; - } else if (i == 3) { - this->SrcFileSignature = false; - projectName = argv[i]; - } else if (i == 4 && !this->SrcFileSignature) { - targetName = argv[i]; - } else { - std::ostringstream m; - m << "try_compile given unknown argument \"" << argv[i] << "\"."; - this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, m.str()); } } - if (didCopyFile && copyFile.empty()) { + if (arguments.CopyFileTo && arguments.CopyFileTo->empty()) { this->Makefile->IssueMessage(MessageType::FATAL_ERROR, "COPY_FILE must be followed by a file path"); - return -1; + return false; } - if (didCopyFileError && copyFileError.empty()) { + if (arguments.CopyFileError && arguments.CopyFileError->empty()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, "COPY_FILE_ERROR must be followed by a variable name"); - return -1; + return false; } - if (didCopyFileError && !didCopyFile) { + if (arguments.CopyFileError && !arguments.CopyFileTo) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, "COPY_FILE_ERROR may be used only with COPY_FILE"); - return -1; - } - - if (didOutputVariable && outputVariable.empty()) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "OUTPUT_VARIABLE must be followed by a variable name"); - return -1; + return false; } - if (useSources && sources.empty()) { + if (arguments.Sources && arguments.Sources->empty()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, "SOURCES must be followed by at least one source file"); - return -1; + return false; } + // only valid for srcfile signatures if (!this->SrcFileSignature) { - if (!cState.Validate(this->Makefile)) { - return -1; - } - if (!cudaState.Validate(this->Makefile)) { - return -1; - } - if (!hipState.Validate(this->Makefile)) { - return -1; - } - if (!cxxState.Validate(this->Makefile)) { - return -1; - } - if (!objcState.Validate(this->Makefile)) { - return -1; - } - if (!objcxxState.Validate(this->Makefile)) { - return -1; + if (!arguments.LangProps.empty()) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat(arguments.LangProps.begin()->first, + " allowed only in source file signature.")); + return false; } - } - - // compute the binary dir when TRY_COMPILE is called with a src file - // signature - if (this->SrcFileSignature) { - this->BinaryDirectory += "/CMakeFiles/CMakeTmp"; - } else { - // only valid for srcfile signatures - if (!compileDefs.empty()) { + if (!arguments.CompileDefs.empty()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, "COMPILE_DEFINITIONS specified on a srcdir type TRY_COMPILE"); - return -1; + return false; } - if (!copyFile.empty()) { + if (arguments.CopyFileTo) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, "COPY_FILE specified on a srcdir type TRY_COMPILE"); - return -1; + return false; } } // make sure the binary directory exists @@ -474,7 +339,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, e << "Attempt at a recursive or nested TRY_COMPILE in directory\n" << " " << this->BinaryDirectory << "\n"; this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return -1; + return false; } std::string outFileName = this->BinaryDirectory + "/CMakeLists.txt"; @@ -485,8 +350,12 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, cmSystemTools::RemoveFile(ccFile); // Choose sources. - if (!useSources) { - sources.emplace_back(argv[2]); + std::vector<std::string> sources; + if (arguments.Sources) { + sources = std::move(*arguments.Sources); + } else { + // TODO: ensure SourceDirectoryOrFile has a value + sources.emplace_back(*arguments.SourceDirectoryOrFile); } // Detect languages to enable. @@ -508,7 +377,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, err << cmJoin(langs, " "); err << "\nSee project() command to enable other languages."; this->Makefile->IssueMessage(MessageType::FATAL_ERROR, err.str()); - return -1; + return false; } } @@ -535,7 +404,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, << cmSystemTools::GetLastSystemError(); /* clang-format on */ this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return -1; + return false; } cmValue def = this->Makefile->GetDefinition("CMAKE_MODULE_PATH"); @@ -598,13 +467,22 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, } } fprintf(fout, "project(CMAKE_TRY_COMPILE%s)\n", projectLangs.c_str()); - if (cmakeInternal == "ABI") { + if (arguments.CMakeInternal == "ABI") { // This is the ABI detection step, also used for implicit includes. // Erase any include_directories() calls from the toolchain file so // that we do not see them as implicit. Our ABI detection source // does not include any system headers anyway. fprintf(fout, "set_property(DIRECTORY PROPERTY INCLUDE_DIRECTORIES \"\")\n"); + + // The link and compile lines for ABI detection step need to not use + // response files so we can extract implicit includes given to + // the underlying host compiler + if (testLangs.find("CUDA") != testLangs.end()) { + fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_INCLUDES OFF)\n"); + fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES OFF)\n"); + fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_OBJECTS OFF)\n"); + } } fprintf(fout, "set(CMAKE_VERBOSE_MAKEFILE 1)\n"); for (std::string const& li : testLangs) { @@ -696,18 +574,12 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, fprintf(fout, "set(CMAKE_SUPPRESS_REGENERATION 1)\n"); fprintf(fout, "link_directories(${LINK_DIRECTORIES})\n"); // handle any compile flags we need to pass on - if (!compileDefs.empty()) { + if (!arguments.CompileDefs.empty()) { // Pass using bracket arguments to preserve content. fprintf(fout, "add_definitions([==[%s]==])\n", - cmJoin(compileDefs, "]==] [==[").c_str()); + cmJoin(arguments.CompileDefs, "]==] [==[").c_str()); } - /* Use a random file name to avoid rapid creation and deletion - of the same executable name (some filesystems fail on that). */ - snprintf(targetNameBuf, sizeof(targetNameBuf), "cmTC_%05x", - cmSystemTools::RandomSeed() & 0xFFFFF); - targetName = targetNameBuf; - if (!targets.empty()) { std::string fname = "/" + std::string(targetName) + "Targets.cmake"; cmExportTryCompileFileGenerator tcfg(gg, targets, this->Makefile, @@ -719,7 +591,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, this->Makefile->IssueMessage(MessageType::FATAL_ERROR, "could not write export file."); fclose(fout); - return -1; + return false; } fprintf(fout, "\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/%s\")\n\n", fname.c_str()); @@ -769,18 +641,10 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, } fprintf(fout, ")\n"); - cState.Enabled(testLangs.find("C") != testLangs.end()); - cxxState.Enabled(testLangs.find("CXX") != testLangs.end()); - cudaState.Enabled(testLangs.find("CUDA") != testLangs.end()); - hipState.Enabled(testLangs.find("HIP") != testLangs.end()); - objcState.Enabled(testLangs.find("OBJC") != testLangs.end()); - objcxxState.Enabled(testLangs.find("OBJCXX") != testLangs.end()); - bool warnCMP0067 = false; bool honorStandard = true; - if (cState.DidNone() && cxxState.DidNone() && objcState.DidNone() && - objcxxState.DidNone() && cudaState.DidNone() && hipState.DidNone()) { + if (arguments.LangProps.empty()) { switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0067)) { case cmPolicies::WARN: warnCMP0067 = this->Makefile->PolicyOptionalWarningEnabled( @@ -805,18 +669,33 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, std::vector<std::string> warnCMP0067Variables; - cState.LoadUnsetPropertyValues(this->Makefile, honorStandard, warnCMP0067, - warnCMP0067Variables); - cxxState.LoadUnsetPropertyValues(this->Makefile, honorStandard, - warnCMP0067, warnCMP0067Variables); - cudaState.LoadUnsetPropertyValues(this->Makefile, honorStandard, - warnCMP0067, warnCMP0067Variables); - hipState.LoadUnsetPropertyValues(this->Makefile, honorStandard, - warnCMP0067, warnCMP0067Variables); - objcState.LoadUnsetPropertyValues(this->Makefile, honorStandard, - warnCMP0067, warnCMP0067Variables); - objcxxState.LoadUnsetPropertyValues(this->Makefile, honorStandard, - warnCMP0067, warnCMP0067Variables); + if (honorStandard || warnCMP0067) { + static std::array<std::string, 6> const possibleLangs{ + { "C", "CXX", "CUDA", "HIP", "OBJC", "OBJCXX" } + }; + static std::array<cm::string_view, 3> const langPropSuffixes{ + { "_STANDARD"_s, "_STANDARD_REQUIRED"_s, "_EXTENSIONS"_s } + }; + for (std::string const& lang : possibleLangs) { + if (testLangs.find(lang) == testLangs.end()) { + continue; + } + for (cm::string_view propSuffix : langPropSuffixes) { + std::string langProp = cmStrCat(lang, propSuffix); + if (!arguments.LangProps.count(langProp)) { + std::string langPropVar = cmStrCat("CMAKE_"_s, langProp); + std::string value = this->Makefile->GetSafeDefinition(langPropVar); + if (warnCMP0067 && !value.empty()) { + value.clear(); + warnCMP0067Variables.emplace_back(langPropVar); + } + if (!value.empty()) { + arguments.LangProps[langProp] = value; + } + } + } + } + } if (!warnCMP0067Variables.empty()) { std::ostringstream w; @@ -832,17 +711,20 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); } - cState.WriteProperties(fout, targetName); - cxxState.WriteProperties(fout, targetName); - cudaState.WriteProperties(fout, targetName); - hipState.WriteProperties(fout, targetName); - objcState.WriteProperties(fout, targetName); - objcxxState.WriteProperties(fout, targetName); + for (auto const& p : arguments.LangProps) { + if (p.second.empty()) { + continue; + } + fprintf(fout, "set_property(TARGET %s PROPERTY %s %s)\n", + targetName.c_str(), + cmOutputConverter::EscapeForCMake(p.first).c_str(), + cmOutputConverter::EscapeForCMake(p.second).c_str()); + } - if (!linkOptions.empty()) { + if (!arguments.LinkOptions.empty()) { std::vector<std::string> options; - options.reserve(linkOptions.size()); - for (const auto& option : linkOptions) { + options.reserve(arguments.LinkOptions.size()); + for (const auto& option : arguments.LinkOptions) { options.emplace_back(cmOutputConverter::EscapeForCMake(option)); } @@ -856,15 +738,18 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, } } - if (useOldLinkLibs) { - fprintf(fout, "target_link_libraries(%s ${LINK_LIBRARIES})\n", - targetName.c_str()); - } else { + if (arguments.LinkLibraries) { + std::string libsToLink = " "; + for (std::string const& i : *arguments.LinkLibraries) { + libsToLink += "\"" + cmTrimWhitespace(i) + "\" "; + } fprintf(fout, "target_link_libraries(%s %s)\n", targetName.c_str(), libsToLink.c_str()); + } else { + fprintf(fout, "target_link_libraries(%s ${LINK_LIBRARIES})\n", + targetName.c_str()); } fclose(fout); - projectName = "CMAKE_TRY_COMPILE"; } // Forward a set of variables to the inner project cache. @@ -953,13 +838,13 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, kCMAKE_TRY_COMPILE_OSX_ARCHITECTURES)) { vars.erase(kCMAKE_OSX_ARCHITECTURES); std::string flag = "-DCMAKE_OSX_ARCHITECTURES=" + *tcArchs; - cmakeFlags.emplace_back(std::move(flag)); + arguments.CMakeFlags.emplace_back(std::move(flag)); } for (std::string const& var : vars) { if (cmValue val = this->Makefile->GetDefinition(var)) { std::string flag = "-D" + var + "=" + *val; - cmakeFlags.emplace_back(std::move(flag)); + arguments.CMakeFlags.emplace_back(std::move(flag)); } } } @@ -969,7 +854,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, for (std::string const& var : ghs_platform_vars) { if (cmValue val = this->Makefile->GetDefinition(var)) { std::string flag = "-D" + var + "=" + "'" + *val + "'"; - cmakeFlags.emplace_back(std::move(flag)); + arguments.CMakeFlags.emplace_back(std::move(flag)); } } } @@ -980,26 +865,27 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, // actually do the try compile now that everything is setup int res = this->Makefile->TryCompile( sourceDirectory, this->BinaryDirectory, projectName, targetName, - this->SrcFileSignature, cmake::NO_BUILD_PARALLEL_LEVEL, &cmakeFlags, - output); + this->SrcFileSignature, cmake::NO_BUILD_PARALLEL_LEVEL, + &arguments.CMakeFlags, output); if (erroroc) { cmSystemTools::SetErrorOccurred(); } // set the result var to the return value to indicate success or failure - this->Makefile->AddCacheDefinition(argv[0], (res == 0 ? "TRUE" : "FALSE"), - "Result of TRY_COMPILE", - cmStateEnums::INTERNAL); + this->Makefile->AddCacheDefinition( + *arguments.CompileResultVariable, (res == 0 ? "TRUE" : "FALSE"), + "Result of TRY_COMPILE", cmStateEnums::INTERNAL); - if (!outputVariable.empty()) { - this->Makefile->AddDefinition(outputVariable, output); + if (arguments.OutputVariable) { + this->Makefile->AddDefinition(*arguments.OutputVariable, output); } if (this->SrcFileSignature) { std::string copyFileErrorMessage; this->FindOutputFile(targetName, targetType); - if ((res == 0) && !copyFile.empty()) { + if ((res == 0) && arguments.CopyFileTo) { + std::string const& copyFile = *arguments.CopyFileTo; if (this->OutputFile.empty() || !cmSystemTools::CopyFileAlways(this->OutputFile, copyFile)) { std::ostringstream emsg; @@ -1012,19 +898,20 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, if (!this->FindErrorMessage.empty()) { emsg << this->FindErrorMessage; } - if (copyFileError.empty()) { + if (!arguments.CopyFileError) { this->Makefile->IssueMessage(MessageType::FATAL_ERROR, emsg.str()); - return -1; + return false; } copyFileErrorMessage = emsg.str(); } } - if (!copyFileError.empty()) { + if (arguments.CopyFileError) { + std::string const& copyFileError = *arguments.CopyFileError; this->Makefile->AddDefinition(copyFileError, copyFileErrorMessage); } } - return res; + return res == 0; } void cmCoreTryCompile::CleanupFiles(std::string const& binDir) @@ -1117,7 +1004,8 @@ void cmCoreTryCompile::FindOutputFile(const std::string& targetName, searchDirs.emplace_back(std::move(tmp)); } searchDirs.emplace_back("/Debug"); -#if defined(__APPLE__) + + // handle app-bundles (for targeting apple-platforms) std::string app = "/" + targetName + ".app"; if (cmNonempty(config)) { std::string tmp = cmStrCat('/', *config, app); @@ -1126,7 +1014,7 @@ void cmCoreTryCompile::FindOutputFile(const std::string& targetName, std::string tmp = "/Debug" + app; searchDirs.emplace_back(std::move(tmp)); searchDirs.emplace_back(std::move(app)); -#endif + searchDirs.emplace_back("/Development"); for (std::string const& sdir : searchDirs) { diff --git a/Source/cmCoreTryCompile.h b/Source/cmCoreTryCompile.h index 594fd7f..deefe57 100644 --- a/Source/cmCoreTryCompile.h +++ b/Source/cmCoreTryCompile.h @@ -4,28 +4,75 @@ #include "cmConfigure.h" // IWYU pragma: keep +#include <map> #include <string> #include <vector> -#include "cmCommand.h" +#include <cm/optional> + +#include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmStateTypes.h" +class cmMakefile; +template <typename Iter> +class cmRange; + /** \class cmCoreTryCompile * \brief Base class for cmTryCompileCommand and cmTryRunCommand * * cmCoreTryCompile implements the functionality to build a program. * It is the base class for cmTryCompileCommand and cmTryRunCommand. */ -class cmCoreTryCompile : public cmCommand +class cmCoreTryCompile { public: -protected: + cmCoreTryCompile(cmMakefile* mf) + : Makefile(mf) + { + } + + struct Arguments : public ArgumentParser::ParseResult + { + cm::optional<std::string> CompileResultVariable; + cm::optional<std::string> BinaryDirectory; + cm::optional<std::string> SourceDirectoryOrFile; + cm::optional<std::string> ProjectName; + cm::optional<std::string> TargetName; + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> Sources; + ArgumentParser::MaybeEmpty<std::vector<std::string>> CMakeFlags{ + 1, "CMAKE_FLAGS" + }; // fake argv[0] + std::vector<std::string> CompileDefs; + cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> + LinkLibraries; + ArgumentParser::MaybeEmpty<std::vector<std::string>> LinkOptions; + std::map<std::string, std::string> LangProps; + std::string CMakeInternal; + cm::optional<std::string> OutputVariable; + cm::optional<std::string> CopyFileTo; + cm::optional<std::string> CopyFileError; + + // Argument for try_run only. + // Keep in sync with warnings in cmCoreTryCompile::ParseArgs. + cm::optional<std::string> CompileOutputVariable; + cm::optional<std::string> RunOutputVariable; + cm::optional<std::string> RunOutputStdOutVariable; + cm::optional<std::string> RunOutputStdErrVariable; + cm::optional<std::string> RunWorkingDirectory; + cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> RunArgs; + }; + + Arguments ParseArgs(cmRange<std::vector<std::string>::const_iterator> args, + bool isTryRun); + /** * This is the core code for try compile. It is here so that other * commands, such as TryRun can access the same logic without * duplication. */ - int TryCompileCode(std::vector<std::string> const& argv, bool isTryRun); + bool TryCompileCode(Arguments& arguments, + cmStateEnums::TargetType targetType); /** * This deletes all the files created by TryCompileCode. @@ -46,4 +93,5 @@ protected: std::string OutputFile; std::string FindErrorMessage; bool SrcFileSignature = false; + cmMakefile* Makefile; }; diff --git a/Source/cmCreateTestSourceList.cxx b/Source/cmCreateTestSourceList.cxx index 2a52d1a..75c25e3 100644 --- a/Source/cmCreateTestSourceList.cxx +++ b/Source/cmCreateTestSourceList.cxx @@ -87,9 +87,7 @@ bool cmCreateTestSourceList(std::vector<std::string> const& args, func_name = cmSystemTools::GetFilenameWithoutLastExtension(*i); } cmSystemTools::ConvertToUnixSlashes(func_name); - std::replace(func_name.begin(), func_name.end(), ' ', '_'); - std::replace(func_name.begin(), func_name.end(), '/', '_'); - std::replace(func_name.begin(), func_name.end(), ':', '_'); + func_name = cmSystemTools::MakeCidentifier(func_name); bool already_declared = std::find(tests_func_name.begin(), tests_func_name.end(), func_name) != tests_func_name.end(); diff --git a/Source/cmCxxModuleMapper.cxx b/Source/cmCxxModuleMapper.cxx new file mode 100644 index 0000000..84691c9 --- /dev/null +++ b/Source/cmCxxModuleMapper.cxx @@ -0,0 +1,308 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmCxxModuleMapper.h" + +#include <cassert> +#include <cstddef> +#include <set> +#include <sstream> +#include <string> +#include <utility> +#include <vector> + +#include <cm/string_view> +#include <cmext/string_view> + +#include "cmScanDepFormat.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" + +cm::optional<std::string> CxxModuleLocations::BmiGeneratorPathForModule( + std::string const& logical_name) const +{ + if (auto l = this->BmiLocationForModule(logical_name)) { + return this->PathForGenerator(*l); + } + return {}; +} + +namespace { + +std::string CxxModuleMapContentGcc(CxxModuleLocations const& loc, + cmScanDepInfo const& obj) +{ + std::stringstream mm; + + // Documented in GCC's documentation. The format is a series of + // lines with a module name and the associated filename separated + // by spaces. The first line may use `$root` as the module name + // to specify a "repository root". That is used to anchor any + // relative paths present in the file (CMake should never + // generate any). + + // Write the root directory to use for module paths. + mm << "$root " << loc.RootDirectory << "\n"; + + for (auto const& p : obj.Provides) { + if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) { + mm << p.LogicalName << ' ' << *bmi_loc << '\n'; + } + } + for (auto const& r : obj.Requires) { + if (auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName)) { + mm << r.LogicalName << ' ' << *bmi_loc << '\n'; + } + } + + return mm.str(); +} + +std::string CxxModuleMapContentMsvc(CxxModuleLocations const& loc, + cmScanDepInfo const& obj, + CxxModuleUsage const& usages) +{ + std::stringstream mm; + + // A response file of `-reference NAME=PATH` arguments. + + // MSVC's command line only supports a single output. If more than one is + // expected, we cannot make a useful module map file. + if (obj.Provides.size() > 1) { + return {}; + } + + auto flag_for_method = [](LookupMethod method) -> cm::static_string_view { + switch (method) { + case LookupMethod::ByName: + return "-reference"_s; + case LookupMethod::IncludeAngle: + return "-headerUnit:angle"_s; + case LookupMethod::IncludeQuote: + return "-headerUnit:quote"_s; + } + assert(false && "unsupported lookup method"); + return ""_s; + }; + + for (auto const& p : obj.Provides) { + if (p.IsInterface) { + mm << "-interface\n"; + } else { + mm << "-internalPartition\n"; + } + + if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) { + mm << "-ifcOutput " << *bmi_loc << '\n'; + } + } + + std::set<std::string> transitive_usage_directs; + std::set<std::string> transitive_usage_names; + + for (auto const& r : obj.Requires) { + if (auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName)) { + auto flag = flag_for_method(r.Method); + + mm << flag << ' ' << r.LogicalName << '=' << *bmi_loc << "\n"; + transitive_usage_directs.insert(r.LogicalName); + + // Insert transitive usages. + auto transitive_usages = usages.Usage.find(r.LogicalName); + if (transitive_usages != usages.Usage.end()) { + transitive_usage_names.insert(transitive_usages->second.begin(), + transitive_usages->second.end()); + } + } + } + + for (auto const& transitive_name : transitive_usage_names) { + if (transitive_usage_directs.count(transitive_name)) { + continue; + } + + auto module_ref = usages.Reference.find(transitive_name); + if (module_ref != usages.Reference.end()) { + auto flag = flag_for_method(module_ref->second.Method); + mm << flag << ' ' << transitive_name << '=' << module_ref->second.Path + << "\n"; + } + } + + return mm.str(); +} +} + +bool CxxModuleUsage::AddReference(std::string const& logical, + std::string const& loc, LookupMethod method) +{ + auto r = this->Reference.find(logical); + if (r != this->Reference.end()) { + auto& ref = r->second; + + if (ref.Path == loc && ref.Method == method) { + return true; + } + + auto method_name = [](LookupMethod m) -> cm::static_string_view { + switch (m) { + case LookupMethod::ByName: + return "by-name"_s; + case LookupMethod::IncludeAngle: + return "include-angle"_s; + case LookupMethod::IncludeQuote: + return "include-quote"_s; + } + assert(false && "unsupported lookup method"); + return ""_s; + }; + + cmSystemTools::Error(cmStrCat("Disagreement of the location of the '", + logical, + "' module. " + "Location A: '", + ref.Path, "' via ", method_name(ref.Method), + "; " + "Location B: '", + loc, "' via ", method_name(method), ".")); + return false; + } + + auto& ref = this->Reference[logical]; + ref.Path = loc; + ref.Method = method; + + return true; +} + +cm::static_string_view CxxModuleMapExtension( + cm::optional<CxxModuleMapFormat> format) +{ + if (format) { + switch (*format) { + case CxxModuleMapFormat::Gcc: + return ".gcm"_s; + case CxxModuleMapFormat::Msvc: + return ".ifc"_s; + } + } + + return ".bmi"_s; +} + +std::set<std::string> CxxModuleUsageSeed( + CxxModuleLocations const& loc, std::vector<cmScanDepInfo> const& objects, + CxxModuleUsage& usages) +{ + // Track inner usages to populate usages from internal bits. + // + // This is a map of modules that required some other module that was not + // found to those that were not found. + std::map<std::string, std::set<std::string>> internal_usages; + std::set<std::string> unresolved; + + for (cmScanDepInfo const& object : objects) { + // Add references for each of the provided modules. + for (auto const& p : object.Provides) { + if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) { + // XXX(cxx-modules): How to support header units? + usages.AddReference(p.LogicalName, loc.PathForGenerator(*bmi_loc), + LookupMethod::ByName); + } + } + + // For each requires, pull in what is required. + for (auto const& r : object.Requires) { + // Find transitive usages. + auto transitive_usages = usages.Usage.find(r.LogicalName); + // Find the required name in the current target. + auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName); + + for (auto const& p : object.Provides) { + auto& this_usages = usages.Usage[p.LogicalName]; + + // Add the direct usage. + this_usages.insert(r.LogicalName); + + // Add the transitive usage. + if (transitive_usages != usages.Usage.end()) { + this_usages.insert(transitive_usages->second.begin(), + transitive_usages->second.end()); + } else if (bmi_loc) { + // Mark that we need to update transitive usages later. + internal_usages[p.LogicalName].insert(r.LogicalName); + } + } + + if (bmi_loc) { + usages.AddReference(r.LogicalName, loc.PathForGenerator(*bmi_loc), + r.Method); + } + } + } + + // While we have internal usages to manage. + while (!internal_usages.empty()) { + size_t starting_size = internal_usages.size(); + + // For each internal usage. + for (auto usage = internal_usages.begin(); usage != internal_usages.end(); + /* see end of loop */) { + auto& this_usages = usages.Usage[usage->first]; + + for (auto use = usage->second.begin(); use != usage->second.end(); + /* see end of loop */) { + // Check if this required module uses other internal modules; defer + // if so. + if (internal_usages.count(*use)) { + // Advance the iterator. + ++use; + continue; + } + + auto transitive_usages = usages.Usage.find(*use); + if (transitive_usages != usages.Usage.end()) { + this_usages.insert(transitive_usages->second.begin(), + transitive_usages->second.end()); + } + + // Remove the entry and advance the iterator. + use = usage->second.erase(use); + } + + // Erase the entry if it doesn't have any remaining usages. + if (usage->second.empty()) { + usage = internal_usages.erase(usage); + } else { + ++usage; + } + } + + // Check that at least one usage was resolved. + if (starting_size == internal_usages.size()) { + // Nothing could be resolved this loop; we have a cycle, so record the + // cycle and exit. + for (auto const& usage : internal_usages) { + unresolved.insert(usage.first); + } + break; + } + } + + return unresolved; +} + +std::string CxxModuleMapContent(CxxModuleMapFormat format, + CxxModuleLocations const& loc, + cmScanDepInfo const& obj, + CxxModuleUsage const& usages) +{ + switch (format) { + case CxxModuleMapFormat::Gcc: + return CxxModuleMapContentGcc(loc, obj); + case CxxModuleMapFormat::Msvc: + return CxxModuleMapContentMsvc(loc, obj, usages); + } + + assert(false); + return {}; +} diff --git a/Source/cmCxxModuleMapper.h b/Source/cmCxxModuleMapper.h new file mode 100644 index 0000000..8526a07 --- /dev/null +++ b/Source/cmCxxModuleMapper.h @@ -0,0 +1,85 @@ +/* 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 <map> +#include <set> +#include <string> +#include <vector> + +#include <cm/optional> +#include <cmext/string_view> + +#include "cmScanDepFormat.h" + +enum class CxxModuleMapFormat +{ + Gcc, + Msvc, +}; + +struct CxxModuleLocations +{ + // The path from which all relative paths should be computed. If + // this is relative, it is relative to the compiler's working + // directory. + std::string RootDirectory; + + // A function to convert a full path to a path for the generator. + std::function<std::string(std::string const&)> PathForGenerator; + + // Lookup the BMI location of a logical module name. + std::function<cm::optional<std::string>(std::string const&)> + BmiLocationForModule; + + // Returns the generator path (if known) for the BMI given a + // logical module name. + cm::optional<std::string> BmiGeneratorPathForModule( + std::string const& logical_name) const; +}; + +struct CxxModuleReference +{ + // The path to the module file used. + std::string Path; + // How the module was looked up. + LookupMethod Method; +}; + +struct CxxModuleUsage +{ + // The usage requirements for this object. + std::map<std::string, std::set<std::string>> Usage; + + // The references for this object. + std::map<std::string, CxxModuleReference> Reference; + + // Add a reference to a module. + // + // Returns `true` if it matches how it was found previously, `false` if it + // conflicts. + bool AddReference(std::string const& logical, std::string const& loc, + LookupMethod method); +}; + +// Return the extension to use for a given modulemap format. +cm::static_string_view CxxModuleMapExtension( + cm::optional<CxxModuleMapFormat> format); + +// Fill in module usage information for internal usages. +// +// Returns the set of unresolved module usage requirements (these form an +// import cycle). +std::set<std::string> CxxModuleUsageSeed( + CxxModuleLocations const& loc, std::vector<cmScanDepInfo> const& objects, + CxxModuleUsage& usages); + +// Return the contents of the module map in the given format for the +// object file. +std::string CxxModuleMapContent(CxxModuleMapFormat format, + CxxModuleLocations const& loc, + cmScanDepInfo const& obj, + CxxModuleUsage const& usages); diff --git a/Source/cmDefinePropertyCommand.cxx b/Source/cmDefinePropertyCommand.cxx index faefcb8..31ee665 100644 --- a/Source/cmDefinePropertyCommand.cxx +++ b/Source/cmDefinePropertyCommand.cxx @@ -8,6 +8,7 @@ #include <cmext/string_view> #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmProperty.h" @@ -51,8 +52,8 @@ bool cmDefinePropertyCommand(std::vector<std::string> const& args, // Parse remaining arguments. bool inherited = false; std::string PropertyName; - std::vector<std::string> BriefDocs; - std::vector<std::string> FullDocs; + ArgumentParser::NonEmpty<std::vector<std::string>> BriefDocs; + ArgumentParser::NonEmpty<std::vector<std::string>> FullDocs; std::string initializeFromVariable; cmArgumentParser<void> parser; diff --git a/Source/cmDocumentation.cxx b/Source/cmDocumentation.cxx index 3619ade..d466a12 100644 --- a/Source/cmDocumentation.cxx +++ b/Source/cmDocumentation.cxx @@ -17,30 +17,32 @@ #include "cmVersion.h" static const char* cmDocumentationStandardOptions[][2] = { - { "--help,-help,-usage,-h,-H,/?", "Print usage information and exit." }, - { "--version,-version,/V [<f>]", "Print version number and exit." }, - { "--help-full [<f>]", "Print all help manuals and exit." }, - { "--help-manual <man> [<f>]", "Print one help manual and exit." }, - { "--help-manual-list [<f>]", "List help manuals available and exit." }, - { "--help-command <cmd> [<f>]", "Print help for one command and exit." }, - { "--help-command-list [<f>]", + { "-h,-H,--help,-help,-usage,/?", "Print usage information and exit." }, + { "--version,-version,/V [<file>]", "Print version number and exit." }, + { "--help-full [<file>]", "Print all help manuals and exit." }, + { "--help-manual <man> [<file>]", "Print one help manual and exit." }, + { "--help-manual-list [<file>]", "List help manuals available and exit." }, + { "--help-command <cmd> [<file>]", "Print help for one command and exit." }, + { "--help-command-list [<file>]", "List commands with help available and exit." }, - { "--help-commands [<f>]", "Print cmake-commands manual and exit." }, - { "--help-module <mod> [<f>]", "Print help for one module and exit." }, - { "--help-module-list [<f>]", "List modules with help available and exit." }, - { "--help-modules [<f>]", "Print cmake-modules manual and exit." }, - { "--help-policy <cmp> [<f>]", "Print help for one policy and exit." }, - { "--help-policy-list [<f>]", + { "--help-commands [<file>]", "Print cmake-commands manual and exit." }, + { "--help-module <mod> [<file>]", "Print help for one module and exit." }, + { "--help-module-list [<file>]", + "List modules with help available and exit." }, + { "--help-modules [<file>]", "Print cmake-modules manual and exit." }, + { "--help-policy <cmp> [<file>]", "Print help for one policy and exit." }, + { "--help-policy-list [<file>]", "List policies with help available and exit." }, - { "--help-policies [<f>]", "Print cmake-policies manual and exit." }, - { "--help-property <prop> [<f>]", "Print help for one property and exit." }, - { "--help-property-list [<f>]", + { "--help-policies [<file>]", "Print cmake-policies manual and exit." }, + { "--help-property <prop> [<file>]", + "Print help for one property and exit." }, + { "--help-property-list [<file>]", "List properties with help available and exit." }, - { "--help-properties [<f>]", "Print cmake-properties manual and exit." }, - { "--help-variable var [<f>]", "Print help for one variable and exit." }, - { "--help-variable-list [<f>]", + { "--help-properties [<file>]", "Print cmake-properties manual and exit." }, + { "--help-variable var [<file>]", "Print help for one variable and exit." }, + { "--help-variable-list [<file>]", "List variables with help available and exit." }, - { "--help-variables [<f>]", "Print cmake-variables manual and exit." }, + { "--help-variables [<file>]", "Print cmake-variables manual and exit." }, { nullptr, nullptr } }; diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx index 222ea80..7fbd826 100644 --- a/Source/cmExecuteProcessCommand.cxx +++ b/Source/cmExecuteProcessCommand.cxx @@ -47,7 +47,7 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, return false; } - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { std::vector<std::vector<std::string>> Commands; std::string OutputVariable; @@ -95,14 +95,10 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, .Bind("COMMAND_ERROR_IS_FATAL"_s, &Arguments::CommandErrorIsFatal); std::vector<std::string> unparsedArguments; - std::vector<std::string> keywordsMissingValue; - Arguments const arguments = - parser.Parse(args, &unparsedArguments, &keywordsMissingValue); + Arguments const arguments = parser.Parse(args, &unparsedArguments); - if (!keywordsMissingValue.empty()) { - status.SetError(" called with no value for " + - keywordsMissingValue.front() + "."); - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!unparsedArguments.empty()) { status.SetError(" given unknown argument \"" + unparsedArguments.front() + diff --git a/Source/cmExperimental.cxx b/Source/cmExperimental.cxx new file mode 100644 index 0000000..922b53f --- /dev/null +++ b/Source/cmExperimental.cxx @@ -0,0 +1,63 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmExperimental.h" + +#include <cassert> +#include <cstddef> +#include <string> + +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmValue.h" + +namespace { + +/* + * The `Uuid` fields of these objects should change periodically. + * Search for other instances to keep the documentation and test suite + * up-to-date. + */ + +struct FeatureData +{ + std::string const Uuid; + std::string const Variable; + std::string const Description; + bool Warned; +} LookupTable[] = { + // CxxModuleCMakeApi + { "3c375311-a3c9-4396-a187-3227ef642046", + "CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API", + "CMake's C++ module support is experimental. It is meant only for " + "experimentation and feedback to CMake developers.", + false }, +}; +static_assert(sizeof(LookupTable) / sizeof(LookupTable[0]) == + static_cast<size_t>(cmExperimental::Feature::Sentinel), + "Experimental feature lookup table mismatch"); + +FeatureData& DataForFeature(cmExperimental::Feature f) +{ + assert(f != cmExperimental::Feature::Sentinel); + return LookupTable[static_cast<size_t>(f)]; +} +} + +bool cmExperimental::HasSupportEnabled(cmMakefile const& mf, Feature f) +{ + bool enabled = false; + auto& data = DataForFeature(f); + + auto value = mf.GetDefinition(data.Variable); + if (value == data.Uuid) { + enabled = true; + } + + if (enabled && !data.Warned) { + mf.IssueMessage(MessageType::AUTHOR_WARNING, data.Description); + data.Warned = true; + } + + return enabled; +} diff --git a/Source/cmExperimental.h b/Source/cmExperimental.h new file mode 100644 index 0000000..26e0d17 --- /dev/null +++ b/Source/cmExperimental.h @@ -0,0 +1,21 @@ +/* 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 + +class cmMakefile; + +class cmExperimental +{ +public: + enum class Feature + { + CxxModuleCMakeApi, + + Sentinel, + }; + + static bool HasSupportEnabled(cmMakefile const& mf, Feature f); +}; diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx index 6ce0c98..ed199ea 100644 --- a/Source/cmExportBuildFileGenerator.cxx +++ b/Source/cmExportBuildFileGenerator.cxx @@ -9,10 +9,13 @@ #include <sstream> #include <utility> +#include <cm/string_view> #include <cmext/algorithm> +#include <cmext/string_view> #include "cmExportSet.h" #include "cmFileSet.h" +#include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" @@ -23,6 +26,7 @@ #include "cmPolicies.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" +#include "cmSystemTools.h" #include "cmTarget.h" #include "cmTargetExport.h" #include "cmValue.h" @@ -139,11 +143,18 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os) this->GenerateTargetFileSets(gte, os); } + this->GenerateCxxModuleInformation(os); + // Generate import file content for each configuration. for (std::string const& c : this->Configurations) { this->GenerateImportConfig(os, c); } + // Generate import file content for each configuration. + for (std::string const& c : this->Configurations) { + this->GenerateImportCxxModuleConfigTargetInclusion(c); + } + this->GenerateMissingTargetsCheckCode(os); return true; @@ -382,6 +393,21 @@ std::string cmExportBuildFileGenerator::GetFileSetDirectories( std::any_of(directoryEntries.begin(), directoryEntries.end(), EntryIsContextSensitive); + auto const& type = fileSet->GetType(); + // C++ modules do not support interface file sets which are dependent upon + // the configuration. + if (contextSensitive && + (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s)) { + auto* mf = this->LG->GetMakefile(); + std::ostringstream e; + e << "The \"" << gte->GetName() << "\" target's interface file set \"" + << fileSet->GetName() << "\" of type \"" << type + << "\" contains context-sensitive base directory entries which is not " + "supported."; + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return std::string{}; + } + for (auto const& directory : directories) { auto dest = cmOutputConverter::EscapeForCMake( directory, cmOutputConverter::WrapQuotes::NoWrap); @@ -427,6 +453,21 @@ std::string cmExportBuildFileGenerator::GetFileSetFiles(cmGeneratorTarget* gte, std::any_of(fileEntries.begin(), fileEntries.end(), EntryIsContextSensitive); + auto const& type = fileSet->GetType(); + // C++ modules do not support interface file sets which are dependent upon + // the configuration. + if (contextSensitive && + (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s)) { + auto* mf = this->LG->GetMakefile(); + std::ostringstream e; + e << "The \"" << gte->GetName() << "\" target's interface file set \"" + << fileSet->GetName() << "\" of type \"" << type + << "\" contains context-sensitive file entries which is not " + "supported."; + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return std::string{}; + } + for (auto const& it : files) { for (auto const& filename : it.second) { auto escapedFile = cmOutputConverter::EscapeForCMake( @@ -447,3 +488,60 @@ std::string cmExportBuildFileGenerator::GetFileSetFiles(cmGeneratorTarget* gte, return cmJoin(resultVector, " "); } + +std::string cmExportBuildFileGenerator::GetCxxModulesDirectory() const +{ + return this->CxxModulesDirectory; +} + +void cmExportBuildFileGenerator::GenerateCxxModuleConfigInformation( + std::ostream& os) const +{ + const char* opt = ""; + if (this->Configurations.size() > 1) { + // With more than one configuration, each individual file is optional. + opt = " OPTIONAL"; + } + + // Generate import file content for each configuration. + for (std::string c : this->Configurations) { + if (c.empty()) { + c = "noconfig"; + } + os << "include(\"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-" << c << ".cmake\"" + << opt << ")\n"; + } +} + +bool cmExportBuildFileGenerator::GenerateImportCxxModuleConfigTargetInclusion( + std::string config) const +{ + auto cxx_modules_dirname = this->GetCxxModulesDirectory(); + if (cxx_modules_dirname.empty()) { + return true; + } + + if (config.empty()) { + config = "noconfig"; + } + + std::string fileName = cmStrCat(this->FileDir, '/', cxx_modules_dirname, + "/cxx-modules-", config, ".cmake"); + + cmGeneratedFileStream os(fileName, true); + if (!os) { + std::string se = cmSystemTools::GetLastSystemError(); + std::ostringstream e; + e << "cannot write to file \"" << fileName << "\": " << se; + cmSystemTools::Error(e.str()); + return false; + } + os.SetCopyIfDifferent(true); + + for (auto const* tgt : this->ExportedTargets) { + os << "include(\"${CMAKE_CURRENT_LIST_DIR}/target-" << tgt->GetExportName() + << '-' << config << ".cmake\")\n"; + } + + return true; +} diff --git a/Source/cmExportBuildFileGenerator.h b/Source/cmExportBuildFileGenerator.h index 5681e8f..4636196 100644 --- a/Source/cmExportBuildFileGenerator.h +++ b/Source/cmExportBuildFileGenerator.h @@ -47,6 +47,16 @@ public: } void SetExportSet(cmExportSet*); + /** Set the name of the C++ module directory. */ + void SetCxxModuleDirectory(std::string cxx_module_dir) + { + this->CxxModulesDirectory = std::move(cxx_module_dir); + } + const std::string& GetCxxModuleDirectory() const + { + return this->CxxModulesDirectory; + } + /** Set whether to append generated code to the output file. */ void SetAppendMode(bool append) { this->AppendMode = append; } @@ -81,6 +91,10 @@ protected: std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te) override; + std::string GetCxxModulesDirectory() const override; + void GenerateCxxModuleConfigInformation(std::ostream&) const override; + bool GenerateImportCxxModuleConfigTargetInclusion(std::string) const; + std::pair<std::vector<std::string>, std::string> FindBuildExportInfo( cmGlobalGenerator* gg, const std::string& name); @@ -88,4 +102,6 @@ protected: cmExportSet* ExportSet; std::vector<cmGeneratorTarget*> Exports; cmLocalGenerator* LG; + // The directory for C++ module information. + std::string CxxModulesDirectory; }; diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx index 63440a3..a58f2b7 100644 --- a/Source/cmExportCommand.cxx +++ b/Source/cmExportCommand.cxx @@ -7,13 +7,15 @@ #include <utility> #include <cm/memory> -#include <cmext/algorithm> +#include <cm/optional> #include <cmext/string_view> #include "cmsys/RegularExpression.hxx" #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmExecutionStatus.h" +#include "cmExperimental.h" #include "cmExportBuildAndroidMKGenerator.h" #include "cmExportBuildFileGenerator.h" #include "cmExportSet.h" @@ -57,10 +59,11 @@ bool cmExportCommand(std::vector<std::string> const& args, struct Arguments { std::string ExportSetName; - std::vector<std::string> Targets; + cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Targets; std::string Namespace; std::string Filename; std::string AndroidMKFile; + std::string CxxModulesDirectory; bool Append = false; bool ExportOld = false; }; @@ -69,6 +72,12 @@ bool cmExportCommand(std::vector<std::string> const& args, .Bind("NAMESPACE"_s, &Arguments::Namespace) .Bind("FILE"_s, &Arguments::Filename); + bool const supportCxx20FileSetTypes = cmExperimental::HasSupportEnabled( + status.GetMakefile(), cmExperimental::Feature::CxxModuleCMakeApi); + if (supportCxx20FileSetTypes) { + parser.Bind("CXX_MODULES_DIRECTORY"_s, &Arguments::CxxModulesDirectory); + } + if (args[0] == "EXPORT") { parser.Bind("EXPORT"_s, &Arguments::ExportSetName); } else { @@ -79,9 +88,7 @@ bool cmExportCommand(std::vector<std::string> const& args, } std::vector<std::string> unknownArgs; - std::vector<std::string> keywordsMissingValue; - Arguments const arguments = - parser.Parse(args, &unknownArgs, &keywordsMissingValue); + Arguments const arguments = parser.Parse(args, &unknownArgs); if (!unknownArgs.empty()) { status.SetError("Unknown argument: \"" + unknownArgs.front() + "\"."); @@ -145,9 +152,8 @@ bool cmExportCommand(std::vector<std::string> const& args, return false; } exportSet = &it->second; - } else if (!arguments.Targets.empty() || - cm::contains(keywordsMissingValue, "TARGETS")) { - for (std::string const& currentTarget : arguments.Targets) { + } else if (arguments.Targets) { + for (std::string const& currentTarget : *arguments.Targets) { if (mf.IsAlias(currentTarget)) { std::ostringstream e; e << "given ALIAS target \"" << currentTarget @@ -214,6 +220,7 @@ bool cmExportCommand(std::vector<std::string> const& args, } ebfg->SetExportFile(fname.c_str()); ebfg->SetNamespace(arguments.Namespace); + ebfg->SetCxxModuleDirectory(arguments.CxxModulesDirectory); ebfg->SetAppendMode(arguments.Append); if (exportSet != nullptr) { ebfg->SetExportSet(exportSet); diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index 5a33349..50bc78c 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -939,13 +939,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.22 (this upper limit may be reviewed + // policy settings for up to CMake 3.23 (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.8.3...3.22)\n"; + << "cmake_policy(VERSION 2.8.3...3.23)\n"; /* clang-format on */ } @@ -1095,6 +1095,10 @@ void cmExportFileGenerator::GenerateImportTargetCode( << " PROPERTY IMPORTED_NO_SYSTEM 1)\n"; } + if (target->GetPropertyAsBool("EXPORT_NO_SYSTEM")) { + os << "set_property(TARGET " << targetName << " PROPERTY SYSTEM 0)\n"; + } + os << "\n"; } @@ -1308,3 +1312,28 @@ void cmExportFileGenerator::GenerateTargetFileSets(cmGeneratorTarget* gte, os << " )\nendif()\n\n"; } } + +void cmExportFileGenerator::GenerateCxxModuleInformation(std::ostream& os) +{ + auto const cxx_module_dirname = this->GetCxxModulesDirectory(); + if (cxx_module_dirname.empty()) { + return; + } + + // Write the include. + os << "# Include C++ module properties\n" + << "include(\"${CMAKE_CURRENT_LIST_DIR}/" << cxx_module_dirname + << "/cxx-modules.cmake\")\n\n"; + + // Get the path to the file we're going to write. + std::string path = this->MainImportFile; + path = cmSystemTools::GetFilenamePath(path); + auto trampoline_path = + cmStrCat(path, '/', cxx_module_dirname, "/cxx-modules.cmake"); + + // Include all configuration-specific include files. + cmGeneratedFileStream ap(trampoline_path, true); + ap.SetCopyIfDifferent(true); + + this->GenerateCxxModuleConfigInformation(ap); +} diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h index d27a555..fdda878 100644 --- a/Source/cmExportFileGenerator.h +++ b/Source/cmExportFileGenerator.h @@ -182,6 +182,8 @@ protected: void GenerateTargetFileSets(cmGeneratorTarget* gte, std::ostream& os, cmTargetExport* te = nullptr); + void GenerateCxxModuleInformation(std::ostream& os); + virtual std::string GetFileSetDirectories(cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te) = 0; @@ -226,4 +228,7 @@ private: virtual std::string InstallNameDir(cmGeneratorTarget const* target, const std::string& config) = 0; + + virtual std::string GetCxxModulesDirectory() const = 0; + virtual void GenerateCxxModuleConfigInformation(std::ostream& os) const = 0; }; diff --git a/Source/cmExportInstallAndroidMKGenerator.cxx b/Source/cmExportInstallAndroidMKGenerator.cxx index 4e4f8a1..d53254d 100644 --- a/Source/cmExportInstallAndroidMKGenerator.cxx +++ b/Source/cmExportInstallAndroidMKGenerator.cxx @@ -35,8 +35,7 @@ void cmExportInstallAndroidMKGenerator::GenerateImportHeaderCode( for (size_t n = 0; n < numDotDot; n++) { path += "/.."; } - os << "_IMPORT_PREFIX := " - << "$(LOCAL_PATH)" << path << "\n\n"; + os << "_IMPORT_PREFIX := $(LOCAL_PATH)" << path << "\n\n"; for (std::unique_ptr<cmTargetExport> const& te : this->IEGen->GetExportSet()->GetTargetExports()) { // Collect import properties for this target. diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx index adccdfe..7d8572d 100644 --- a/Source/cmExportInstallFileGenerator.cxx +++ b/Source/cmExportInstallFileGenerator.cxx @@ -7,6 +7,9 @@ #include <sstream> #include <utility> +#include <cm/string_view> +#include <cmext/string_view> + #include "cmExportSet.h" #include "cmFileSet.h" #include "cmGeneratedFileStream.h" @@ -18,6 +21,7 @@ #include "cmInstallTargetGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" +#include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmPolicies.h" #include "cmStateTypes.h" @@ -162,10 +166,20 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) this->LoadConfigFiles(os); + bool result = true; + + this->GenerateCxxModuleInformation(os); + if (requiresConfigFiles) { + for (std::string const& c : this->Configurations) { + if (!this->GenerateImportCxxModuleConfigTargetInclusion(c)) { + result = false; + } + } + } + this->CleanupTemporaryVariables(os); this->GenerateImportedFileCheckLoop(os); - bool result = true; // Generate an import file for each configuration. // Don't do this if we only export INTERFACE_LIBRARY targets. if (requiresConfigFiles) { @@ -562,6 +576,21 @@ std::string cmExportInstallFileGenerator::GetFileSetDirectories( cge->Evaluate(gte->LocalGenerator, config, gte), cmOutputConverter::WrapQuotes::NoWrap)); + auto const& type = fileSet->GetType(); + // C++ modules do not support interface file sets which are dependent upon + // the configuration. + if (cge->GetHadContextSensitiveCondition() && + (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s)) { + auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile(); + std::ostringstream e; + e << "The \"" << gte->GetName() << "\" target's interface file set \"" + << fileSet->GetName() << "\" of type \"" << type + << "\" contains context-sensitive base file entries which is not " + "supported."; + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return std::string{}; + } + if (cge->GetHadContextSensitiveCondition() && configs.size() != 1) { resultVector.push_back( cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\"")); @@ -610,6 +639,21 @@ std::string cmExportInstallFileGenerator::GetFileSetFiles( std::any_of(fileEntries.begin(), fileEntries.end(), EntryIsContextSensitive); + auto const& type = fileSet->GetType(); + // C++ modules do not support interface file sets which are dependent upon + // the configuration. + if (contextSensitive && + (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s)) { + auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile(); + std::ostringstream e; + e << "The \"" << gte->GetName() << "\" target's interface file set \"" + << fileSet->GetName() << "\" of type \"" << type + << "\" contains context-sensitive base file entries which is not " + "supported."; + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return std::string{}; + } + for (auto const& it : files) { auto prefix = it.first.empty() ? "" : cmStrCat(it.first, '/'); for (auto const& filename : it.second) { @@ -635,3 +679,65 @@ std::string cmExportInstallFileGenerator::GetFileSetFiles( return cmJoin(resultVector, " "); } + +std::string cmExportInstallFileGenerator::GetCxxModulesDirectory() const +{ + return IEGen->GetCxxModuleDirectory(); +} + +void cmExportInstallFileGenerator::GenerateCxxModuleConfigInformation( + std::ostream& os) const +{ + // Now load per-configuration properties for them. + /* clang-format off */ + os << "# Load information for each installed configuration.\n" + "file(GLOB _cmake_cxx_module_includes \"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-*.cmake\")\n" + "foreach(_cmake_cxx_module_include IN LISTS _cmake_cxx_module_includes)\n" + " include(\"${_cmake_cxx_module_include}\")\n" + "endforeach()\n" + "unset(_cmake_cxx_module_include)\n" + "unset(_cmake_cxx_module_includes)\n"; + /* clang-format on */ +} + +bool cmExportInstallFileGenerator:: + GenerateImportCxxModuleConfigTargetInclusion(std::string const& config) +{ + auto cxx_modules_dirname = this->GetCxxModulesDirectory(); + if (cxx_modules_dirname.empty()) { + return true; + } + + std::string filename_config = config; + if (filename_config.empty()) { + filename_config = "noconfig"; + } + + std::string const dest = + cmStrCat(this->FileDir, '/', cxx_modules_dirname, '/'); + std::string fileName = + cmStrCat(dest, "cxx-modules-", filename_config, ".cmake"); + + cmGeneratedFileStream os(fileName, true); + if (!os) { + std::string se = cmSystemTools::GetLastSystemError(); + std::ostringstream e; + e << "cannot write to file \"" << fileName << "\": " << se; + cmSystemTools::Error(e.str()); + return false; + } + os.SetCopyIfDifferent(true); + + // Record this per-config import file. + this->ConfigCxxModuleFiles[config] = fileName; + + auto& prop_files = this->ConfigCxxModuleTargetFiles[config]; + for (auto const* tgt : this->ExportedTargets) { + auto prop_filename = cmStrCat("target-", tgt->GetExportName(), '-', + filename_config, ".cmake"); + prop_files.emplace_back(cmStrCat(dest, prop_filename)); + os << "include(\"${CMAKE_CURRENT_LIST_DIR}/" << prop_filename << "\")\n"; + } + + return true; +} diff --git a/Source/cmExportInstallFileGenerator.h b/Source/cmExportInstallFileGenerator.h index 86fb505..e073a31 100644 --- a/Source/cmExportInstallFileGenerator.h +++ b/Source/cmExportInstallFileGenerator.h @@ -50,6 +50,23 @@ public: return this->ConfigImportFiles; } + /** Get the per-config C++ module file generated for each configuration. + This maps from the configuration name to the file temporary location + for installation. */ + std::map<std::string, std::string> const& GetConfigCxxModuleFiles() + { + return this->ConfigCxxModuleFiles; + } + + /** Get the per-config C++ module file generated for each configuration. + This maps from the configuration name to the file temporary location + for installation for each target in the export set. */ + std::map<std::string, std::vector<std::string>> const& + GetConfigCxxModuleTargetFiles() + { + return this->ConfigCxxModuleTargetFiles; + } + /** Compute the globbing expression used to load per-config import files from the main file. */ std::string GetConfigImportFileGlob(); @@ -100,8 +117,16 @@ protected: std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te) override; + std::string GetCxxModulesDirectory() const override; + void GenerateCxxModuleConfigInformation(std::ostream&) const override; + bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&); + cmInstallExportGenerator* IEGen; // The import file generated for each configuration. std::map<std::string, std::string> ConfigImportFiles; + // The C++ module property file generated for each configuration. + std::map<std::string, std::string> ConfigCxxModuleFiles; + // The C++ module property target files generated for each configuration. + std::map<std::string, std::vector<std::string>> ConfigCxxModuleTargetFiles; }; diff --git a/Source/cmExportTryCompileFileGenerator.h b/Source/cmExportTryCompileFileGenerator.h index 1dd8a20..5c34fad 100644 --- a/Source/cmExportTryCompileFileGenerator.h +++ b/Source/cmExportTryCompileFileGenerator.h @@ -55,6 +55,9 @@ protected: std::string GetFileSetFiles(cmGeneratorTarget* target, cmFileSet* fileSet, cmTargetExport* te) override; + std::string GetCxxModulesDirectory() const override { return {}; } + void GenerateCxxModuleConfigInformation(std::ostream&) const override {} + private: std::string FindTargets(const std::string& prop, const cmGeneratorTarget* tgt, diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx index dd0540c..0581802 100644 --- a/Source/cmFileAPICodemodel.cxx +++ b/Source/cmFileAPICodemodel.cxx @@ -27,6 +27,7 @@ #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" +#include "cmInstallCxxModuleBmiGenerator.h" #include "cmInstallDirectoryGenerator.h" #include "cmInstallExportGenerator.h" #include "cmInstallFileSetGenerator.h" @@ -1092,6 +1093,21 @@ Json::Value DirectoryObject::DumpInstaller(cmInstallGenerator* gen) if (installFileSet->GetOptional()) { installer["isOptional"] = true; } + } else if (auto* cxxModuleBmi = + dynamic_cast<cmInstallCxxModuleBmiGenerator*>(gen)) { + installer["type"] = "cxxModuleBmi"; + installer["destination"] = cxxModuleBmi->GetDestination(this->Config); + + auto const* target = cxxModuleBmi->GetTarget(); + installer["cxxModuleBmiTarget"] = Json::objectValue; + installer["cxxModuleBmiTarget"]["id"] = TargetId(target, this->TopBuild); + installer["cxxModuleBmiTarget"]["index"] = this->TargetIndexMap[target]; + + // FIXME: Parse FilePermissions. + // FIXME: Parse MessageLevel. + if (cxxModuleBmi->GetOptional()) { + installer["isOptional"] = true; + } } // Add fields common to all install generators. diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index 7d05347..7810040 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -28,8 +28,8 @@ #include "cm_sys_stat.h" -#include "cmAlgorithms.h" #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmCMakePath.h" #include "cmCryptoHash.h" #include "cmELF.h" @@ -172,7 +172,8 @@ bool HandleReadCommand(std::vector<std::string> const& args, .Bind("LIMIT"_s, &Arguments::Limit) .Bind("HEX"_s, &Arguments::Hex); - Arguments const arguments = parser.Parse(cmMakeRange(args).advance(3)); + Arguments const arguments = parser.Parse(cmMakeRange(args).advance(3), + /*unparsedArguments=*/nullptr); std::string fileName = fileNameArg; if (!cmsys::SystemTools::FileIsFullPath(fileName)) { @@ -953,42 +954,34 @@ bool HandleRPathChangeCommand(std::vector<std::string> const& args, { // Evaluate arguments. std::string file; - std::string oldRPath; - std::string newRPath; + cm::optional<std::string> oldRPath; + cm::optional<std::string> newRPath; bool removeEnvironmentRPath = false; cmArgumentParser<void> parser; std::vector<std::string> unknownArgs; - std::vector<std::string> missingArgs; - std::vector<std::string> parsedArgs; parser.Bind("FILE"_s, file) .Bind("OLD_RPATH"_s, oldRPath) .Bind("NEW_RPATH"_s, newRPath) .Bind("INSTALL_REMOVE_ENVIRONMENT_RPATH"_s, removeEnvironmentRPath); - parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs, - &parsedArgs); + ArgumentParser::ParseResult parseResult = + parser.Parse(cmMakeRange(args).advance(1), &unknownArgs); if (!unknownArgs.empty()) { status.SetError( cmStrCat("RPATH_CHANGE given unknown argument ", unknownArgs.front())); return false; } - if (!missingArgs.empty()) { - status.SetError(cmStrCat("RPATH_CHANGE \"", missingArgs.front(), - "\" argument not given value.")); - return false; + if (parseResult.MaybeReportError(status.GetMakefile())) { + return true; } if (file.empty()) { status.SetError("RPATH_CHANGE not given FILE option."); return false; } - if (oldRPath.empty() && - std::find(parsedArgs.begin(), parsedArgs.end(), "OLD_RPATH") == - parsedArgs.end()) { + if (!oldRPath) { status.SetError("RPATH_CHANGE not given OLD_RPATH option."); return false; } - if (newRPath.empty() && - std::find(parsedArgs.begin(), parsedArgs.end(), "NEW_RPATH") == - parsedArgs.end()) { + if (!newRPath) { status.SetError("RPATH_CHANGE not given NEW_RPATH option."); return false; } @@ -1002,17 +995,17 @@ bool HandleRPathChangeCommand(std::vector<std::string> const& args, std::string emsg; bool changed; - if (!cmSystemTools::ChangeRPath(file, oldRPath, newRPath, + if (!cmSystemTools::ChangeRPath(file, *oldRPath, *newRPath, removeEnvironmentRPath, &emsg, &changed)) { status.SetError(cmStrCat("RPATH_CHANGE could not write new RPATH:\n ", - newRPath, "\nto the file:\n ", file, "\n", + *newRPath, "\nto the file:\n ", file, "\n", emsg)); success = false; } if (success) { if (changed) { std::string message = - cmStrCat("Set runtime path of \"", file, "\" to \"", newRPath, '"'); + cmStrCat("Set runtime path of \"", file, "\" to \"", *newRPath, '"'); status.GetMakefile().DisplayStatus(message, -1); } ft.Store(file); @@ -1025,31 +1018,25 @@ bool HandleRPathSetCommand(std::vector<std::string> const& args, { // Evaluate arguments. std::string file; - std::string newRPath; + cm::optional<std::string> newRPath; cmArgumentParser<void> parser; std::vector<std::string> unknownArgs; - std::vector<std::string> missingArgs; - std::vector<std::string> parsedArgs; parser.Bind("FILE"_s, file).Bind("NEW_RPATH"_s, newRPath); - parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs, - &parsedArgs); + ArgumentParser::ParseResult parseResult = + parser.Parse(cmMakeRange(args).advance(1), &unknownArgs); if (!unknownArgs.empty()) { status.SetError(cmStrCat("RPATH_SET given unrecognized argument \"", unknownArgs.front(), "\".")); return false; } - if (!missingArgs.empty()) { - status.SetError(cmStrCat("RPATH_SET \"", missingArgs.front(), - "\" argument not given value.")); - return false; + if (parseResult.MaybeReportError(status.GetMakefile())) { + return true; } if (file.empty()) { status.SetError("RPATH_SET not given FILE option."); return false; } - if (newRPath.empty() && - std::find(parsedArgs.begin(), parsedArgs.end(), "NEW_RPATH") == - parsedArgs.end()) { + if (!newRPath) { status.SetError("RPATH_SET not given NEW_RPATH option."); return false; } @@ -1063,16 +1050,16 @@ bool HandleRPathSetCommand(std::vector<std::string> const& args, std::string emsg; bool changed; - if (!cmSystemTools::SetRPath(file, newRPath, &emsg, &changed)) { + if (!cmSystemTools::SetRPath(file, *newRPath, &emsg, &changed)) { status.SetError(cmStrCat("RPATH_SET could not write new RPATH:\n ", - newRPath, "\nto the file:\n ", file, "\n", + *newRPath, "\nto the file:\n ", file, "\n", emsg)); success = false; } if (success) { if (changed) { std::string message = - cmStrCat("Set runtime path of \"", file, "\" to \"", newRPath, '"'); + cmStrCat("Set runtime path of \"", file, "\" to \"", *newRPath, '"'); status.GetMakefile().DisplayStatus(message, -1); } ft.Store(file); @@ -1087,18 +1074,16 @@ bool HandleRPathRemoveCommand(std::vector<std::string> const& args, std::string file; cmArgumentParser<void> parser; std::vector<std::string> unknownArgs; - std::vector<std::string> missingArgs; parser.Bind("FILE"_s, file); - parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs); + ArgumentParser::ParseResult parseResult = + parser.Parse(cmMakeRange(args).advance(1), &unknownArgs); if (!unknownArgs.empty()) { status.SetError( cmStrCat("RPATH_REMOVE given unknown argument ", unknownArgs.front())); return false; } - if (!missingArgs.empty()) { - status.SetError(cmStrCat("RPATH_REMOVE \"", missingArgs.front(), - "\" argument not given value.")); - return false; + if (parseResult.MaybeReportError(status.GetMakefile())) { + return true; } if (file.empty()) { status.SetError("RPATH_REMOVE not given FILE option."); @@ -1135,31 +1120,25 @@ bool HandleRPathCheckCommand(std::vector<std::string> const& args, { // Evaluate arguments. std::string file; - std::string rpath; + cm::optional<std::string> rpath; cmArgumentParser<void> parser; std::vector<std::string> unknownArgs; - std::vector<std::string> missingArgs; - std::vector<std::string> parsedArgs; parser.Bind("FILE"_s, file).Bind("RPATH"_s, rpath); - parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs, - &parsedArgs); + ArgumentParser::ParseResult parseResult = + parser.Parse(cmMakeRange(args).advance(1), &unknownArgs); if (!unknownArgs.empty()) { status.SetError( cmStrCat("RPATH_CHECK given unknown argument ", unknownArgs.front())); return false; } - if (!missingArgs.empty()) { - status.SetError(cmStrCat("RPATH_CHECK \"", missingArgs.front(), - "\" argument not given value.")); - return false; + if (parseResult.MaybeReportError(status.GetMakefile())) { + return true; } if (file.empty()) { status.SetError("RPATH_CHECK not given FILE option."); return false; } - if (rpath.empty() && - std::find(parsedArgs.begin(), parsedArgs.end(), "RPATH") == - parsedArgs.end()) { + if (!rpath) { status.SetError("RPATH_CHECK not given RPATH option."); return false; } @@ -1168,7 +1147,7 @@ bool HandleRPathCheckCommand(std::vector<std::string> const& args, // delete it. This is used during installation to re-install a file // if its RPath will change. if (cmSystemTools::FileExists(file, true) && - !cmSystemTools::CheckRPath(file, rpath)) { + !cmSystemTools::CheckRPath(file, *rpath)) { cmSystemTools::RemoveFile(file); } @@ -1197,7 +1176,8 @@ bool HandleReadElfCommand(std::vector<std::string> const& args, .Bind("RPATH"_s, &Arguments::RPath) .Bind("RUNPATH"_s, &Arguments::RunPath) .Bind("CAPTURE_ERROR"_s, &Arguments::Error); - Arguments const arguments = parser.Parse(cmMakeRange(args).advance(2)); + Arguments const arguments = parser.Parse(cmMakeRange(args).advance(2), + /*unparsedArguments=*/nullptr); if (!cmSystemTools::FileExists(fileNameArg, true)) { status.SetError(cmStrCat("READ_ELF given FILE \"", fileNameArg, @@ -1250,9 +1230,9 @@ bool HandleRealPathCommand(std::vector<std::string> const& args, return false; } - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { - std::string BaseDirectory; + cm::optional<std::string> BaseDirectory; bool ExpandTilde = false; }; static auto const parser = @@ -1261,22 +1241,18 @@ bool HandleRealPathCommand(std::vector<std::string> const& args, .Bind("EXPAND_TILDE"_s, &Arguments::ExpandTilde); std::vector<std::string> unparsedArguments; - std::vector<std::string> keywordsMissingValue; - std::vector<std::string> parsedKeywords; auto arguments = - parser.Parse(cmMakeRange(args).advance(3), &unparsedArguments, - &keywordsMissingValue, &parsedKeywords); + parser.Parse(cmMakeRange(args).advance(3), &unparsedArguments); if (!unparsedArguments.empty()) { status.SetError("REAL_PATH called with unexpected arguments"); return false; } - if (!keywordsMissingValue.empty()) { - status.SetError("BASE_DIRECTORY requires a value"); - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } - if (parsedKeywords.empty()) { + if (!arguments.BaseDirectory) { arguments.BaseDirectory = status.GetMakefile().GetCurrentSourceDirectory(); } @@ -1295,7 +1271,7 @@ bool HandleRealPathCommand(std::vector<std::string> const& args, } cmCMakePath path(input, cmCMakePath::auto_format); - path = path.Absolute(arguments.BaseDirectory).Normal(); + path = path.Absolute(*arguments.BaseDirectory).Normal(); auto realPath = cmSystemTools::GetRealPath(path.GenericString()); status.GetMakefile().AddDefinition(args[2], realPath); @@ -1938,7 +1914,7 @@ bool HandleDownloadCommand(std::vector<std::string> const& args, std::string msg; std::string actualHash = hash->HashFile(file); if (actualHash == expectedHash) { - msg = cmStrCat("returning early; file already exists with expected ", + msg = cmStrCat("skipping download as file already exists with expected ", hashMatchMSG, '"'); if (!statusVar.empty()) { status.GetMakefile().AddDefinition(statusVar, cmStrCat(0, ";\"", msg)); @@ -2495,17 +2471,18 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, return false; } - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { - std::string Output; - std::string Input; - std::string Content; - std::string Condition; - std::string Target; - std::string NewLineStyle; + cm::optional<std::string> Output; + cm::optional<std::string> Input; + cm::optional<std::string> Content; + cm::optional<std::string> Condition; + cm::optional<std::string> Target; + cm::optional<std::string> NewLineStyle; bool NoSourcePermissions = false; bool UseSourcePermissions = false; - std::vector<std::string> FilePermissions; + ArgumentParser::NonEmpty<std::vector<std::string>> FilePermissions; + std::vector<cm::string_view> ParsedKeywords; }; static auto const parser = @@ -2518,18 +2495,15 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, .Bind("NO_SOURCE_PERMISSIONS"_s, &Arguments::NoSourcePermissions) .Bind("USE_SOURCE_PERMISSIONS"_s, &Arguments::UseSourcePermissions) .Bind("FILE_PERMISSIONS"_s, &Arguments::FilePermissions) - .Bind("NEWLINE_STYLE"_s, &Arguments::NewLineStyle); + .Bind("NEWLINE_STYLE"_s, &Arguments::NewLineStyle) + .BindParsedKeywords(&Arguments::ParsedKeywords); 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); + parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments); - if (!keywordsMissingValues.empty()) { - status.SetError("Incorrect arguments to GENERATE subcommand."); - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!unparsedArguments.empty()) { @@ -2537,56 +2511,41 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, 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 (!arguments.Output || arguments.ParsedKeywords[0] != "OUTPUT"_s) { + status.SetError("GENERATE requires OUTPUT as first option."); + return false; } - if (!mandatoryOptionsSpecified) { - status.SetError("Incorrect arguments to GENERATE subcommand."); + std::string const& output = *arguments.Output; + + if (!arguments.Input && !arguments.Content) { + status.SetError("GENERATE requires INPUT or CONTENT option."); return false; } + const bool inputIsContent = arguments.ParsedKeywords[1] == "CONTENT"_s; + if (!inputIsContent && arguments.ParsedKeywords[1] == "INPUT") { + status.SetError("Unknown argument to GENERATE subcommand."); + } + std::string const& input = + inputIsContent ? *arguments.Content : *arguments.Input; - const bool conditionOptionSpecified = - std::find(parsedKeywords.begin(), parsedKeywords.end(), "CONDITION"_s) != - parsedKeywords.end(); - if (conditionOptionSpecified && arguments.Condition.empty()) { + if (arguments.Condition && arguments.Condition->empty()) { status.SetError("CONDITION of sub-command GENERATE must not be empty " "if specified."); return false; } + std::string const& condition = + arguments.Condition ? *arguments.Condition : std::string(); - const bool targetOptionSpecified = - std::find(parsedKeywords.begin(), parsedKeywords.end(), "TARGET"_s) != - parsedKeywords.end(); - if (targetOptionSpecified && arguments.Target.empty()) { + if (arguments.Target && arguments.Target->empty()) { status.SetError("TARGET of sub-command GENERATE must not be empty " "if specified."); return false; } + std::string const& target = + arguments.Target ? *arguments.Target : std::string(); - 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; - } - - const bool inputIsContent = parsedKeywords[1] != "INPUT"_s; - if (inputIsContent && parsedKeywords[1] != "CONTENT") { - status.SetError("Unknown argument to GENERATE subcommand."); - } - - const bool newLineStyleSpecified = - std::find(parsedKeywords.begin(), parsedKeywords.end(), - "NEWLINE_STYLE"_s) != parsedKeywords.end(); cmNewLineStyle newLineStyle; - if (newLineStyleSpecified) { + if (arguments.NewLineStyle) { std::string errorMessage; if (!newLineStyle.ReadFromArguments(args, errorMessage)) { status.SetError(cmStrCat("GENERATE ", errorMessage)); @@ -2594,11 +2553,6 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, } } - std::string input = arguments.Input; - if (inputIsContent) { - input = arguments.Content; - } - if (arguments.NoSourcePermissions && arguments.UseSourcePermissions) { status.SetError("given both NO_SOURCE_PERMISSIONS and " "USE_SOURCE_PERMISSIONS. Only one option allowed."); @@ -2656,8 +2610,7 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, } } - AddEvaluationFile(input, arguments.Target, arguments.Output, - arguments.Condition, inputIsContent, + AddEvaluationFile(input, target, output, condition, inputIsContent, newLineStyle.GetCharacters(), permissions, status); return true; } @@ -2836,7 +2789,11 @@ bool HandleTimestampCommand(std::vector<std::string> const& args, unsigned int argsIndex = 1; - const std::string& filename = args[argsIndex++]; + std::string filename = args[argsIndex++]; + if (!cmsys::SystemTools::FileIsFullPath(filename)) { + filename = cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', + filename); + } const std::string& outputVariable = args[argsIndex++]; @@ -3060,24 +3017,25 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, "\n ]])"); } - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { std::string ResolvedDependenciesVar; std::string UnresolvedDependenciesVar; std::string ConflictingDependenciesPrefix; std::string RPathPrefix; std::string BundleExecutable; - std::vector<std::string> Executables; - std::vector<std::string> Libraries; - std::vector<std::string> Directories; - std::vector<std::string> Modules; - std::vector<std::string> PreIncludeRegexes; - std::vector<std::string> PreExcludeRegexes; - std::vector<std::string> PostIncludeRegexes; - std::vector<std::string> PostExcludeRegexes; - std::vector<std::string> PostIncludeFiles; - std::vector<std::string> PostExcludeFiles; - std::vector<std::string> PostExcludeFilesStrict; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Executables; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Libraries; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Directories; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Modules; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PreIncludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PreExcludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeFiles; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeFiles; + ArgumentParser::MaybeEmpty<std::vector<std::string>> + PostExcludeFilesStrict; }; static auto const parser = @@ -3102,10 +3060,8 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, .Bind("POST_EXCLUDE_FILES_STRICT"_s, &Arguments::PostExcludeFilesStrict); std::vector<std::string> unrecognizedArguments; - std::vector<std::string> keywordsMissingValues; auto parsedArgs = - parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, - &keywordsMissingValues); + parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments); auto argIt = unrecognizedArguments.begin(); if (argIt != unrecognizedArguments.end()) { status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, "\"")); @@ -3113,26 +3069,9 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, return false; } - const std::vector<std::string> LIST_ARGS = { - "DIRECTORIES", - "EXECUTABLES", - "LIBRARIES", - "MODULES", - "POST_EXCLUDE_FILES", - "POST_EXCLUDE_FILES_STRICT", - "POST_EXCLUDE_REGEXES", - "POST_INCLUDE_FILES", - "POST_INCLUDE_REGEXES", - "PRE_EXCLUDE_REGEXES", - "PRE_INCLUDE_REGEXES", - }; - auto kwbegin = keywordsMissingValues.cbegin(); - auto kwend = cmRemoveMatching(keywordsMissingValues, LIST_ARGS); - if (kwend != kwbegin) { - status.SetError(cmStrCat("Keywords missing values:\n ", - cmJoin(cmMakeRange(kwbegin, kwend), "\n "))); + if (parsedArgs.MaybeReportError(status.GetMakefile())) { cmSystemTools::SetFatalErrorOccurred(); - return false; + return true; } cmRuntimeDependencyArchive archive( @@ -3232,13 +3171,14 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, bool HandleConfigureCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { - std::string Output; - std::string Content; + cm::optional<std::string> Output; + cm::optional<std::string> Content; bool EscapeQuotes = false; bool AtOnly = false; - std::string NewlineStyle; + // "NEWLINE_STYLE" requires one value, but we use a custom check below. + ArgumentParser::Maybe<std::string> NewlineStyle; }; static auto const parser = @@ -3250,11 +3190,8 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, .Bind("NEWLINE_STYLE"_s, &Arguments::NewlineStyle); std::vector<std::string> unrecognizedArguments; - std::vector<std::string> keywordsMissingArguments; - std::vector<std::string> parsedKeywords; auto parsedArgs = - parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, - &keywordsMissingArguments, &parsedKeywords); + parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments); auto argIt = unrecognizedArguments.begin(); if (argIt != unrecognizedArguments.end()) { @@ -3264,28 +3201,20 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, return false; } - std::vector<std::string> mandatoryOptions{ "OUTPUT", "CONTENT" }; - for (auto const& e : mandatoryOptions) { - const bool optionHasNoValue = - std::find(keywordsMissingArguments.begin(), - keywordsMissingArguments.end(), - e) != keywordsMissingArguments.end(); - if (optionHasNoValue) { - status.SetError(cmStrCat("CONFIGURE ", e, " option needs a value.")); - cmSystemTools::SetFatalErrorOccurred(); - return false; - } + if (parsedArgs.MaybeReportError(status.GetMakefile())) { + cmSystemTools::SetFatalErrorOccurred(); + return true; } - for (auto const& e : mandatoryOptions) { - const bool optionGiven = - std::find(parsedKeywords.begin(), parsedKeywords.end(), e) != - parsedKeywords.end(); - if (!optionGiven) { - status.SetError(cmStrCat("CONFIGURE ", e, " option is mandatory.")); - cmSystemTools::SetFatalErrorOccurred(); - return false; - } + if (!parsedArgs.Output) { + status.SetError("CONFIGURE OUTPUT option is mandatory."); + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + if (!parsedArgs.Content) { + status.SetError("CONFIGURE CONTENT option is mandatory."); + cmSystemTools::SetFatalErrorOccurred(); + return false; } std::string errorMessage; @@ -3297,7 +3226,7 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, // Check for generator expressions std::string outputFile = cmSystemTools::CollapseFullPath( - parsedArgs.Output, status.GetMakefile().GetCurrentBinaryDirectory()); + *parsedArgs.Output, status.GetMakefile().GetCurrentBinaryDirectory()); std::string::size_type pos = outputFile.find_first_of("<>"); if (pos != std::string::npos) { @@ -3346,7 +3275,7 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, fout.SetCopyIfDifferent(true); // copy input to output and expand variables from input at the same time - std::stringstream sin(parsedArgs.Content, std::ios::in); + std::stringstream sin(*parsedArgs.Content, std::ios::in); std::string inLine; std::string outLine; bool hasNewLine = false; @@ -3369,15 +3298,19 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, bool HandleArchiveCreateCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { std::string Output; std::string Format; std::string Compression; std::string CompressionLevel; - std::string MTime; + // "MTIME" should require one value, but it has long been accidentally + // accepted without one and treated as if an empty value were given. + // Fixing this would require a policy. + ArgumentParser::Maybe<std::string> MTime; bool Verbose = false; - std::vector<std::string> Paths; + // "PATHS" requires at least one value, but use a custom check below. + ArgumentParser::MaybeEmpty<std::vector<std::string>> Paths; }; static auto const parser = @@ -3391,10 +3324,8 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args, .Bind("PATHS"_s, &Arguments::Paths); std::vector<std::string> unrecognizedArguments; - std::vector<std::string> keywordsMissingValues; auto parsedArgs = - parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, - &keywordsMissingValues); + parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments); auto argIt = unrecognizedArguments.begin(); if (argIt != unrecognizedArguments.end()) { status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, "\"")); @@ -3402,16 +3333,9 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args, return false; } - const std::vector<std::string> LIST_ARGS = { - "OUTPUT", "FORMAT", "COMPRESSION", "COMPRESSION_LEVEL", "MTIME", "PATHS" - }; - auto kwbegin = keywordsMissingValues.cbegin(); - auto kwend = cmRemoveMatching(keywordsMissingValues, LIST_ARGS); - if (kwend != kwbegin) { - status.SetError(cmStrCat("Keywords missing values:\n ", - cmJoin(cmMakeRange(kwbegin, kwend), "\n "))); + if (parsedArgs.MaybeReportError(status.GetMakefile())) { cmSystemTools::SetFatalErrorOccurred(); - return false; + return true; } const char* knownFormats[] = { @@ -3500,13 +3424,13 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args, bool HandleArchiveExtractCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { std::string Input; bool Verbose = false; bool ListOnly = false; std::string Destination; - std::vector<std::string> Patterns; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Patterns; bool Touch = false; }; @@ -3519,10 +3443,8 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args, .Bind("TOUCH"_s, &Arguments::Touch); std::vector<std::string> unrecognizedArguments; - std::vector<std::string> keywordsMissingValues; auto parsedArgs = - parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, - &keywordsMissingValues); + parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments); auto argIt = unrecognizedArguments.begin(); if (argIt != unrecognizedArguments.end()) { status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, "\"")); @@ -3530,15 +3452,9 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args, return false; } - const std::vector<std::string> LIST_ARGS = { "INPUT", "DESTINATION", - "PATTERNS" }; - auto kwbegin = keywordsMissingValues.cbegin(); - auto kwend = cmRemoveMatching(keywordsMissingValues, LIST_ARGS); - if (kwend != kwbegin) { - status.SetError(cmStrCat("Keywords missing values:\n ", - cmJoin(cmMakeRange(kwbegin, kwend), "\n "))); + if (parsedArgs.MaybeReportError(status.GetMakefile())) { cmSystemTools::SetFatalErrorOccurred(); - return false; + return true; } std::string inFile = parsedArgs.Input; @@ -3593,10 +3509,15 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args, return true; } -bool ValidateAndConvertPermissions(const std::vector<std::string>& permissions, - mode_t& perms, cmExecutionStatus& status) +bool ValidateAndConvertPermissions( + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> const& + permissions, + mode_t& perms, cmExecutionStatus& status) { - for (const auto& i : permissions) { + if (!permissions) { + return true; + } + for (const auto& i : *permissions) { if (!cmFSPermissions::stringToModeT(i, perms)) { status.SetError(i + " is an invalid permission specifier"); cmSystemTools::SetFatalErrorOccurred(); @@ -3628,11 +3549,14 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, globber.SetRecurse(recurse); globber.SetRecurseListDirs(recurse); - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { - std::vector<std::string> Permissions; - std::vector<std::string> FilePermissions; - std::vector<std::string> DirectoryPermissions; + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> + Permissions; + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> + FilePermissions; + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> + DirectoryPermissions; }; static auto const parser = @@ -3642,21 +3566,20 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, .Bind("DIRECTORY_PERMISSIONS"_s, &Arguments::DirectoryPermissions); std::vector<std::string> pathEntries; - std::vector<std::string> keywordsMissingValues; - Arguments parsedArgs = parser.Parse(cmMakeRange(args).advance(1), - &pathEntries, &keywordsMissingValues); + Arguments parsedArgs = + parser.Parse(cmMakeRange(args).advance(1), &pathEntries); // check validity of arguments - if (parsedArgs.Permissions.empty() && parsedArgs.FilePermissions.empty() && - parsedArgs.DirectoryPermissions.empty()) // no permissions given + if (!parsedArgs.Permissions && !parsedArgs.FilePermissions && + !parsedArgs.DirectoryPermissions) // no permissions given { status.SetError("No permissions given"); cmSystemTools::SetFatalErrorOccurred(); return false; } - if (!parsedArgs.Permissions.empty() && !parsedArgs.FilePermissions.empty() && - !parsedArgs.DirectoryPermissions.empty()) // all keywords are used + if (parsedArgs.Permissions && parsedArgs.FilePermissions && + parsedArgs.DirectoryPermissions) // all keywords are used { status.SetError("Remove either PERMISSIONS or FILE_PERMISSIONS or " "DIRECTORY_PERMISSIONS from the invocation"); @@ -3664,12 +3587,9 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, return false; } - if (!keywordsMissingValues.empty()) { - for (const auto& i : keywordsMissingValues) { - status.SetError(i + " is not given any arguments"); - cmSystemTools::SetFatalErrorOccurred(); - } - return false; + if (parsedArgs.MaybeReportError(status.GetMakefile())) { + cmSystemTools::SetFatalErrorOccurred(); + return true; } // validate permissions @@ -3713,7 +3633,7 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, if (cmSystemTools::FileExists(i, true)) { bool success = true; const mode_t& filePermissions = - parsedArgs.FilePermissions.empty() ? perms : fperms; + parsedArgs.FilePermissions ? fperms : perms; if (filePermissions) { success = SetPermissions(i, filePermissions, status); } @@ -3725,7 +3645,7 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, else if (cmSystemTools::FileIsDirectory(i)) { bool success = true; const mode_t& directoryPermissions = - parsedArgs.DirectoryPermissions.empty() ? perms : dperms; + parsedArgs.DirectoryPermissions ? dperms : perms; if (directoryPermissions) { success = SetPermissions(i, directoryPermissions, status); } diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx index 1da995a..b8d345f 100644 --- a/Source/cmFindBase.cxx +++ b/Source/cmFindBase.cxx @@ -2,15 +2,20 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmFindBase.h" +#include <algorithm> #include <cstddef> #include <deque> +#include <functional> #include <map> #include <utility> #include <cm/optional> #include <cmext/algorithm> +#include <cmext/string_view> #include "cmCMakePath.h" +#include "cmExecutionStatus.h" +#include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" @@ -24,8 +29,6 @@ #include "cmWindowsRegistry.h" #include "cmake.h" -class cmExecutionStatus; - cmFindBase::cmFindBase(std::string findCommandName, cmExecutionStatus& status) : cmFindCommon(status) , FindCommandName(std::move(findCommandName)) @@ -138,6 +141,31 @@ bool cmFindBase::ParseArguments(std::vector<std::string> const& argsIn) cmStrCat("given invalid value for \"REGISTRY_VIEW\": ", args[j])); return false; } + } else if (args[j] == "VALIDATOR") { + if (++j == args.size()) { + this->SetError("missing required argument for \"VALIDATOR\""); + return false; + } + auto command = this->Makefile->GetState()->GetCommand(args[j]); + if (command == nullptr) { + this->SetError(cmStrCat( + "command specified for \"VALIDATOR\" is undefined: ", args[j], '.')); + return false; + } + // ensure a macro is not specified as validator + const auto& validatorName = args[j]; + auto macros = cmExpandedList(this->Makefile->GetProperty("MACROS")); + if (std::find_if(macros.begin(), macros.end(), + [&validatorName](const std::string& item) { + return cmSystemTools::Strucmp(validatorName.c_str(), + item.c_str()) == 0; + }) != macros.end()) { + this->SetError(cmStrCat( + "command specified for \"VALIDATOR\" is not a function: ", args[j], + '.')); + return false; + } + this->ValidatorName = args[j]; } else if (this->CheckCommonArgument(args[j])) { doing = DoingNone; } else { @@ -188,6 +216,36 @@ bool cmFindBase::ParseArguments(std::vector<std::string> const& argsIn) return true; } +bool cmFindBase::Validate(const std::string& path) const +{ + if (this->ValidatorName.empty()) { + return true; + } + + // The validator command will be executed in an isolated scope. + cmMakefile::ScopePushPop varScope(this->Makefile); + cmMakefile::PolicyPushPop polScope(this->Makefile); + static_cast<void>(varScope); + static_cast<void>(polScope); + + auto resultName = + cmStrCat("CMAKE_"_s, cmSystemTools::UpperCase(this->FindCommandName), + "_VALIDATOR_STATUS"_s); + + this->Makefile->AddDefinitionBool(resultName, true); + + cmListFileFunction validator( + this->ValidatorName, 0, 0, + { cmListFileArgument(resultName, cmListFileArgument::Unquoted, 0), + cmListFileArgument(path, cmListFileArgument::Quoted, 0) }); + cmExecutionStatus status(*this->Makefile); + + if (this->Makefile->ExecuteCommand(validator, status)) { + return this->Makefile->GetDefinition(resultName).IsOn(); + } + return false; +} + void cmFindBase::ExpandPaths() { if (!this->NoDefaultPath) { diff --git a/Source/cmFindBase.h b/Source/cmFindBase.h index d197424..75d9a6d 100644 --- a/Source/cmFindBase.h +++ b/Source/cmFindBase.h @@ -31,6 +31,11 @@ public: */ virtual bool ParseArguments(std::vector<std::string> const& args); + /** + * To check validity of a found path using user's validator, if any + */ + bool Validate(const std::string& path) const; + protected: friend class cmFindBaseDebugState; void ExpandPaths(); @@ -63,6 +68,8 @@ protected: bool Required = false; + std::string ValidatorName; + private: // Add pieces of the search. void FillPackageRootPath(); diff --git a/Source/cmFindLibraryCommand.cxx b/Source/cmFindLibraryCommand.cxx index 1c4039b..6296a60 100644 --- a/Source/cmFindLibraryCommand.cxx +++ b/Source/cmFindLibraryCommand.cxx @@ -192,6 +192,7 @@ struct cmFindLibraryHelper // Context information. cmMakefile* Makefile; + cmFindBase const* FindBase; cmGlobalGenerator* GG; // List of valid prefixes and suffixes. @@ -239,6 +240,11 @@ struct cmFindLibraryHelper bool CheckDirectory(std::string const& path); bool CheckDirectoryForName(std::string const& path, Name& name); + bool Validate(const std::string& path) const + { + return this->FindBase->Validate(path); + } + cmFindBaseDebugState DebugSearches; void DebugLibraryFailed(std::string const& name, std::string const& path) @@ -291,6 +297,7 @@ std::string const& get_suffixes(cmMakefile* mf) cmFindLibraryHelper::cmFindLibraryHelper(std::string debugName, cmMakefile* mf, cmFindBase const* base) : Makefile(mf) + , FindBase(base) , DebugMode(base->DebugModeEnabled()) , DebugSearches(std::move(debugName), base) { @@ -416,10 +423,13 @@ bool cmFindLibraryHelper::CheckDirectoryForName(std::string const& path, if (!exists) { this->DebugLibraryFailed(name.Raw, path); } else { - this->DebugLibraryFound(name.Raw, path); - this->BestPath = cmSystemTools::CollapseFullPath(this->TestPath); - cmSystemTools::ConvertToUnixSlashes(this->BestPath); - return true; + auto testPath = cmSystemTools::CollapseFullPath(this->TestPath); + if (this->Validate(testPath)) { + this->DebugLibraryFound(name.Raw, path); + this->BestPath = testPath; + return true; + } + this->DebugLibraryFailed(name.Raw, path); } } @@ -443,8 +453,11 @@ bool cmFindLibraryHelper::CheckDirectoryForName(std::string const& path, this->TestPath = cmStrCat(path, origName); // Make sure the path is readable and is not a directory. if (cmSystemTools::FileExists(this->TestPath, true)) { - this->DebugLibraryFound(name.Raw, dir); + if (!this->Validate(cmSystemTools::CollapseFullPath(this->TestPath))) { + continue; + } + this->DebugLibraryFound(name.Raw, dir); // This is a matching file. Check if it is better than the // best name found so far. Earlier prefixes are preferred, // followed by earlier suffixes. For OpenBSD, shared library @@ -541,7 +554,10 @@ std::string cmFindLibraryCommand::FindFrameworkLibraryNamesPerDir() for (std::string const& n : this->Names) { fwPath = cmStrCat(d, n, ".framework"); if (cmSystemTools::FileIsDirectory(fwPath)) { - return cmSystemTools::CollapseFullPath(fwPath); + auto finalPath = cmSystemTools::CollapseFullPath(fwPath); + if (this->Validate(finalPath)) { + return finalPath; + } } } } @@ -558,7 +574,10 @@ std::string cmFindLibraryCommand::FindFrameworkLibraryDirsPerName() for (std::string const& d : this->SearchPaths) { fwPath = cmStrCat(d, n, ".framework"); if (cmSystemTools::FileIsDirectory(fwPath)) { - return cmSystemTools::CollapseFullPath(fwPath); + auto finalPath = cmSystemTools::CollapseFullPath(fwPath); + if (this->Validate(finalPath)) { + return finalPath; + } } } } diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index 8c6a0aa..3f8378b 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx @@ -5,7 +5,6 @@ #include <algorithm> #include <cassert> #include <cstdio> -#include <cstring> #include <deque> #include <functional> #include <iterator> @@ -43,8 +42,405 @@ # include <StorageDefs.h> #endif +#if defined(_WIN32) && !defined(__CYGWIN__) +# include <windows.h> +// http://msdn.microsoft.com/en-us/library/aa384253%28v=vs.85%29.aspx +# if !defined(KEY_WOW64_32KEY) +# define KEY_WOW64_32KEY 0x0200 +# endif +# if !defined(KEY_WOW64_64KEY) +# define KEY_WOW64_64KEY 0x0100 +# endif +#endif + class cmExecutionStatus; -class cmFileList; + +namespace { + +template <template <typename> class Op> +struct StrverscmpOp +{ + bool operator()(const std::string& lhs, const std::string& rhs) const + { + return Op<int>()(cmSystemTools::strverscmp(lhs, rhs), 0); + } +}; + +std::size_t collectPathsForDebug(std::string& buffer, + cmSearchPath const& searchPath, + std::size_t const startIndex = 0) +{ + const auto& paths = searchPath.GetPaths(); + if (paths.empty()) { + buffer += " none\n"; + return 0; + } + for (auto i = startIndex; i < paths.size(); i++) { + buffer += " " + paths[i].Path + "\n"; + } + return paths.size(); +} + +#if !(defined(_WIN32) && !defined(__CYGWIN__)) +class cmFindPackageCommandHoldFile +{ + const char* File; + +public: + cmFindPackageCommandHoldFile(const char* const f) + : File(f) + { + } + ~cmFindPackageCommandHoldFile() + { + if (this->File) { + cmSystemTools::RemoveFile(this->File); + } + } + cmFindPackageCommandHoldFile(const cmFindPackageCommandHoldFile&) = delete; + cmFindPackageCommandHoldFile& operator=( + const cmFindPackageCommandHoldFile&) = delete; + void Release() { this->File = nullptr; } +}; +#endif + +bool isDirentryToIgnore(const char* const fname) +{ + assert(fname != nullptr); + assert(fname[0] != 0); + return fname[0] == '.' && + (fname[1] == 0 || (fname[1] == '.' && fname[2] == 0)); +} + +class cmAppendPathSegmentGenerator +{ +public: + cmAppendPathSegmentGenerator(cm::string_view dirName) + : DirName{ dirName } + { + } + + std::string GetNextCandidate(const std::string& parent) + { + if (this->NeedReset) { + return {}; + } + this->NeedReset = true; + return cmStrCat(parent, '/', this->DirName); + } + + void Reset() { this->NeedReset = false; } + +private: + const cm::string_view DirName; + bool NeedReset = false; +}; + +class cmEnumPathSegmentsGenerator +{ +public: + cmEnumPathSegmentsGenerator(const std::vector<cm::string_view>& init) + : Names{ init } + , Current{ this->Names.get().cbegin() } + { + } + + std::string GetNextCandidate(const std::string& parent) + { + if (this->Current != this->Names.get().cend()) { + return cmStrCat(parent, '/', *this->Current++); + } + return {}; + } + + void Reset() { this->Current = this->Names.get().cbegin(); } + +private: + std::reference_wrapper<const std::vector<cm::string_view>> Names; + std::vector<cm::string_view>::const_iterator Current; +}; + +class cmCaseInsensitiveDirectoryListGenerator +{ +public: + cmCaseInsensitiveDirectoryListGenerator(cm::string_view name) + : DirectoryLister{} + , DirName{ name } + { + } + + std::string GetNextCandidate(const std::string& parent) + { + if (!this->Loaded) { + this->CurrentIdx = 0ul; + this->Loaded = true; + if (!this->DirectoryLister.Load(parent)) { + return {}; + } + } + + while (this->CurrentIdx < this->DirectoryLister.GetNumberOfFiles()) { + const char* const fname = + this->DirectoryLister.GetFile(this->CurrentIdx++); + if (isDirentryToIgnore(fname)) { + continue; + } + if (cmsysString_strcasecmp(fname, this->DirName.data()) == 0) { + auto candidate = cmStrCat(parent, '/', fname); + if (cmSystemTools::FileIsDirectory(candidate)) { + return candidate; + } + } + } + return {}; + } + + void Reset() { this->Loaded = false; } + +private: + cmsys::Directory DirectoryLister; + const cm::string_view DirName; + unsigned long CurrentIdx = 0ul; + bool Loaded = false; +}; + +class cmDirectoryListGenerator +{ +public: + cmDirectoryListGenerator(std::vector<std::string> const& names) + : Names{ names } + , Matches{} + , Current{ this->Matches.cbegin() } + { + } + virtual ~cmDirectoryListGenerator() = default; + + std::string GetNextCandidate(const std::string& parent) + { + // Construct a list of matches if not yet + if (this->Matches.empty()) { + cmsys::Directory directoryLister; + // ALERT `Directory::Load()` keeps only names + // internally and LOST entry type from `dirent`. + // So, `Directory::FileIsDirectory` gonna use + // `SystemTools::FileIsDirectory()` and waste a syscall. + // TODO Need to enhance the `Directory` class. + directoryLister.Load(parent); + + // ATTENTION Is it guaranteed that first two entries are + // `.` and `..`? + // TODO If so, just start with index 2 and drop the + // `isDirentryToIgnore(i)` condition to check. + for (auto i = 0ul; i < directoryLister.GetNumberOfFiles(); ++i) { + const char* const fname = directoryLister.GetFile(i); + if (isDirentryToIgnore(fname)) { + continue; + } + + for (const auto& n : this->Names.get()) { + // NOTE Customization point for `cmMacProjectDirectoryListGenerator` + const auto name = this->TransformNameBeforeCmp(n); + // Skip entries that don't match and non-directories. + // ATTENTION BTW, original code also didn't check if it's a symlink + // to a directory! + const auto equal = + (cmsysString_strncasecmp(fname, name.c_str(), name.length()) == 0); + if (equal && directoryLister.FileIsDirectory(i)) { + this->Matches.emplace_back(fname); + } + } + } + // NOTE Customization point for `cmProjectDirectoryListGenerator` + this->OnMatchesLoaded(); + + this->Current = this->Matches.cbegin(); + } + + if (this->Current != this->Matches.cend()) { + auto candidate = cmStrCat(parent, '/', *this->Current++); + return candidate; + } + + return {}; + } + + void Reset() + { + this->Matches.clear(); + this->Current = this->Matches.cbegin(); + } + +protected: + virtual void OnMatchesLoaded() {} + virtual std::string TransformNameBeforeCmp(std::string same) { return same; } + + std::reference_wrapper<const std::vector<std::string>> Names; + std::vector<std::string> Matches; + std::vector<std::string>::const_iterator Current; +}; + +class cmProjectDirectoryListGenerator : public cmDirectoryListGenerator +{ +public: + cmProjectDirectoryListGenerator(std::vector<std::string> const& names, + cmFindPackageCommand::SortOrderType so, + cmFindPackageCommand::SortDirectionType sd) + : cmDirectoryListGenerator{ names } + , SortOrder{ so } + , SortDirection{ sd } + { + } + +protected: + void OnMatchesLoaded() override + { + // check if there is a specific sorting order to perform + if (this->SortOrder != cmFindPackageCommand::None) { + cmFindPackageCommand::Sort(this->Matches.begin(), this->Matches.end(), + this->SortOrder, this->SortDirection); + } + } + +private: + // sort parameters + const cmFindPackageCommand::SortOrderType SortOrder; + const cmFindPackageCommand::SortDirectionType SortDirection; +}; + +class cmMacProjectDirectoryListGenerator : public cmDirectoryListGenerator +{ +public: + cmMacProjectDirectoryListGenerator(const std::vector<std::string>& names, + cm::string_view ext) + : cmDirectoryListGenerator{ names } + , Extension{ ext } + { + } + +protected: + std::string TransformNameBeforeCmp(std::string name) override + { + return cmStrCat(name, this->Extension); + } + +private: + const cm::string_view Extension; +}; + +class cmFileListGeneratorGlob +{ +public: + cmFileListGeneratorGlob(cm::string_view pattern) + : Pattern(pattern) + , Files{} + , Current{} + { + } + + std::string GetNextCandidate(const std::string& parent) + { + if (this->Files.empty()) { + // Glob the set of matching files. + std::string expr = cmStrCat(parent, this->Pattern); + cmsys::Glob g; + if (!g.FindFiles(expr)) { + return {}; + } + this->Files = g.GetFiles(); + this->Current = this->Files.cbegin(); + } + + // Skip non-directories + for (; this->Current != this->Files.cend() && + !cmSystemTools::FileIsDirectory(*this->Current); + ++this->Current) { + } + + return (this->Current != this->Files.cend()) ? *this->Current++ + : std::string{}; + } + + void Reset() + { + this->Files.clear(); + this->Current = this->Files.cbegin(); + } + +private: + cm::string_view Pattern; + std::vector<std::string> Files; + std::vector<std::string>::const_iterator Current; +}; + +#if defined(__LCC__) +# define CM_LCC_DIAG_SUPPRESS_1222 +# pragma diag_suppress 1222 // invalid error number (3288, but works anyway) +# define CM_LCC_DIAG_SUPPRESS_3288 +# pragma diag_suppress 3288 // parameter was declared but never referenced +#endif + +void ResetGenerator() +{ +} + +template <typename Generator> +void ResetGenerator(Generator&& generator) +{ + std::forward<Generator&&>(generator).Reset(); +} + +template <typename Generator, typename... Generators> +void ResetGenerator(Generator&& generator, Generators&&... generators) +{ + ResetGenerator(std::forward<Generator&&>(generator)); + ResetGenerator(std::forward<Generators&&>(generators)...); +} + +template <typename CallbackFn> +bool TryGeneratedPaths(CallbackFn&& filesCollector, + const std::string& fullPath) +{ + assert(!fullPath.empty() && fullPath.back() != '/'); + return std::forward<CallbackFn&&>(filesCollector)(fullPath + '/'); +} + +template <typename CallbackFn, typename Generator, typename... Rest> +bool TryGeneratedPaths(CallbackFn&& filesCollector, + const std::string& startPath, Generator&& gen, + Rest&&... tail) +{ + ResetGenerator(std::forward<Generator&&>(gen)); + for (auto path = gen.GetNextCandidate(startPath); !path.empty(); + path = gen.GetNextCandidate(startPath)) { + ResetGenerator(std::forward<Rest&&>(tail)...); + if (TryGeneratedPaths(std::forward<CallbackFn&&>(filesCollector), path, + std::forward<Rest&&>(tail)...)) { + return true; + } + } + return false; +} + +#ifdef CM_LCC_DIAG_SUPPRESS_3288 +# undef CM_LCC_DIAG_SUPPRESS_3288 +# pragma diag_default 3288 +#endif + +#ifdef CM_LCC_DIAG_SUPPRESS_1222 +# undef CM_LCC_DIAG_SUPPRESS_1222 +# pragma diag_default 1222 +#endif + +// Parse the version number and store the results that were +// successfully parsed. +int parseVersion(const std::string& version, unsigned int& major, + unsigned int& minor, unsigned int& patch, unsigned int& tweak) +{ + return std::sscanf(version.c_str(), "%u.%u.%u.%u", &major, &minor, &patch, + &tweak); +} + +} // anonymous namespace cmFindPackageCommand::PathLabel cmFindPackageCommand::PathLabel::PackageRedirect("PACKAGE_REDIRECT"); @@ -60,25 +456,10 @@ const cm::string_view cmFindPackageCommand::VERSION_ENDPOINT_INCLUDED( const cm::string_view cmFindPackageCommand::VERSION_ENDPOINT_EXCLUDED( "EXCLUDE"); -struct StrverscmpGreater -{ - bool operator()(const std::string& lhs, const std::string& rhs) const - { - return cmSystemTools::strverscmp(lhs, rhs) > 0; - } -}; - -struct StrverscmpLesser -{ - bool operator()(const std::string& lhs, const std::string& rhs) const - { - return cmSystemTools::strverscmp(lhs, rhs) < 0; - } -}; - void cmFindPackageCommand::Sort(std::vector<std::string>::iterator begin, std::vector<std::string>::iterator end, - SortOrderType order, SortDirectionType dir) + SortOrderType const order, + SortDirectionType const dir) { if (order == Name_order) { if (dir == Dec) { @@ -86,14 +467,13 @@ void cmFindPackageCommand::Sort(std::vector<std::string>::iterator begin, } else { std::sort(begin, end); } - } else if (order == Natural) - // natural order uses letters and numbers (contiguous numbers digit are - // compared such that e.g. 000 00 < 01 < 010 < 09 < 0 < 1 < 9 < 10 - { + } else if (order == Natural) { + // natural order uses letters and numbers (contiguous numbers digit are + // compared such that e.g. 000 00 < 01 < 010 < 09 < 0 < 1 < 9 < 10 if (dir == Dec) { - std::sort(begin, end, StrverscmpGreater()); + std::sort(begin, end, StrverscmpOp<std::greater>()); } else { - std::sort(begin, end, StrverscmpLesser()); + std::sort(begin, end, StrverscmpOp<std::less>()); } } // else do not sort @@ -113,11 +493,10 @@ cmFindPackageCommand::cmFindPackageCommand(cmExecutionStatus& status) void cmFindPackageCommand::AppendSearchPathGroups() { - std::vector<cmFindCommon::PathLabel>* labels; - // Update the All group with new paths. Note that package redirection must // take precedence over everything else, so it has to be first in the array. - labels = &this->PathGroupLabelMap[PathGroup::All]; + std::vector<cmFindCommon::PathLabel>* const labels = + &this->PathGroupLabelMap[PathGroup::All]; labels->insert(labels->begin(), PathLabel::PackageRedirect); labels->insert( std::find(labels->begin(), labels->end(), PathLabel::CMakeSystem), @@ -147,15 +526,15 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } // Lookup required version of CMake. - if (cmValue rv = + if (cmValue const rv = this->Makefile->GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION")) { unsigned int v[3] = { 0, 0, 0 }; - sscanf(rv->c_str(), "%u.%u.%u", &v[0], &v[1], &v[2]); + std::sscanf(rv->c_str(), "%u.%u.%u", &v[0], &v[1], &v[2]); this->RequiredCMakeVersion = CMake_VERSION_ENCODE(v[0], v[1], v[2]); } // Lookup target architecture, if any. - if (cmValue arch = + if (cmValue const arch = this->Makefile->GetDefinition("CMAKE_LIBRARY_ARCHITECTURE")) { this->LibraryArchitecture = *arch; } @@ -184,7 +563,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) // Check if User Package Registry should be disabled // The `CMAKE_FIND_USE_PACKAGE_REGISTRY` has // priority over the deprecated CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY - if (cmValue def = + if (cmValue const def = this->Makefile->GetDefinition("CMAKE_FIND_USE_PACKAGE_REGISTRY")) { this->NoUserRegistry = !cmIsOn(*def); } else if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY")) { @@ -194,7 +573,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) // Check if System Package Registry should be disabled // The `CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY` has // priority over the deprecated CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY - if (cmValue def = this->Makefile->GetDefinition( + if (cmValue const def = this->Makefile->GetDefinition( "CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY")) { this->NoSystemRegistry = !cmIsOn(*def); } else if (this->Makefile->IsOn( @@ -208,7 +587,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } // Check if Sorting should be enabled - if (cmValue so = + if (cmValue const so = this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_SORT_ORDER")) { if (*so == "NAME") { @@ -219,7 +598,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) this->SortOrder = None; } } - if (cmValue sd = + if (cmValue const sd = this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_SORT_DIRECTION")) { this->SortDirection = (*sd == "ASC") ? Asc : Dec; } @@ -265,9 +644,9 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) cmsys::RegularExpression versionRegex( R"V(^([0-9]+(\.[0-9]+)*)(\.\.\.(<?)([0-9]+(\.[0-9]+)*))?$)V"); bool haveVersion = false; - std::set<unsigned int> configArgs; - std::set<unsigned int> moduleArgs; - for (unsigned int i = 1; i < args.size(); ++i) { + std::vector<std::size_t> configArgs; + std::vector<std::size_t> moduleArgs; + for (std::size_t i = 1u; i < args.size(); ++i) { if (args[i] == "QUIET") { this->Quiet = true; doing = DoingNone; @@ -281,17 +660,17 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) this->GlobalScope = true; doing = DoingNone; } else if (args[i] == "MODULE") { - moduleArgs.insert(i); + moduleArgs.push_back(i); doing = DoingNone; // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 // NOLINTNEXTLINE(bugprone-branch-clone) } else if (args[i] == "CONFIG") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 // NOLINTNEXTLINE(bugprone-branch-clone) } else if (args[i] == "NO_MODULE") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; } else if (args[i] == "REQUIRED") { this->Required = true; @@ -301,36 +680,36 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } else if (args[i] == "OPTIONAL_COMPONENTS") { doing = DoingOptionalComponents; } else if (args[i] == "NAMES") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNames; } else if (args[i] == "PATHS") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingPaths; } else if (args[i] == "HINTS") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingHints; } else if (args[i] == "PATH_SUFFIXES") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingPathSuffixes; } else if (args[i] == "CONFIGS") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingConfigs; } else if (args[i] == "NO_POLICY_SCOPE") { this->PolicyScope = false; doing = DoingNone; } else if (args[i] == "NO_CMAKE_PACKAGE_REGISTRY") { this->NoUserRegistry = true; - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; } else if (args[i] == "NO_CMAKE_SYSTEM_PACKAGE_REGISTRY") { this->NoSystemRegistry = true; - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 // NOLINTNEXTLINE(bugprone-branch-clone) } else if (args[i] == "NO_CMAKE_BUILDS_PATH") { // Ignore legacy option. - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; } else if (args[i] == "REGISTRY_VIEW") { if (++i == args.size()) { @@ -347,7 +726,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) return false; } } else if (this->CheckCommonArgument(args[i])) { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; } else if ((doing == DoingComponents) || (doing == DoingOptionalComponents)) { @@ -361,8 +740,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) requiredComponents.insert(args[i]); } - std::string req_var = this->Name + "_FIND_REQUIRED_" + args[i]; - componentVarDefs.emplace_back(req_var, isRequired); + componentVarDefs.emplace_back(this->Name + "_FIND_REQUIRED_" + args[i], + isRequired); // Append to the list of required components. components += components_sep; @@ -420,11 +799,11 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) if (!this->UseFindModules && !this->UseConfigFiles) { std::ostringstream e; e << "given options exclusive to Module mode:\n"; - for (unsigned int si : moduleArgs) { + for (auto si : moduleArgs) { e << " " << args[si] << "\n"; } e << "and options exclusive to Config mode:\n"; - for (unsigned int si : configArgs) { + for (auto si : configArgs) { e << " " << args[si] << "\n"; } e << "The options are incompatible."; @@ -443,20 +822,20 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) if (this->VersionComplete.empty() || components.empty()) { // Check whether we are recursing inside "Find<name>.cmake" within // another find_package(<name>) call. - std::string mod = cmStrCat(this->Name, "_FIND_MODULE"); + std::string const mod = cmStrCat(this->Name, "_FIND_MODULE"); if (this->Makefile->IsOn(mod)) { if (this->VersionComplete.empty()) { // Get version information from the outer call if necessary. // Requested version string. - std::string ver = cmStrCat(this->Name, "_FIND_VERSION_COMPLETE"); + std::string const ver = cmStrCat(this->Name, "_FIND_VERSION_COMPLETE"); this->VersionComplete = this->Makefile->GetSafeDefinition(ver); // Whether an exact version is required. - std::string exact = cmStrCat(this->Name, "_FIND_VERSION_EXACT"); + std::string const exact = cmStrCat(this->Name, "_FIND_VERSION_EXACT"); this->VersionExact = this->Makefile->IsOn(exact); } if (components.empty()) { - std::string components_var = this->Name + "_FIND_COMPONENTS"; + std::string const components_var = this->Name + "_FIND_COMPONENTS"; components = this->Makefile->GetSafeDefinition(components_var); } } @@ -497,15 +876,6 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) return false; } - // Parse the version number and store the results that were - // successfully parsed. - auto parseVersion = [](const std::string& version, unsigned int& major, - unsigned int& minor, unsigned int& patch, - unsigned int& tweak) -> unsigned int { - return sscanf(version.c_str(), "%u.%u.%u.%u", &major, &minor, &patch, - &tweak); - }; - if (!this->Version.empty()) { this->VersionCount = parseVersion(this->Version, this->VersionMajor, this->VersionMinor, @@ -533,7 +903,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } } - std::string disableFindPackageVar = + std::string const disableFindPackageVar = cmStrCat("CMAKE_DISABLE_FIND_PACKAGE_", this->Name); if (this->Makefile->IsOn(disableFindPackageVar)) { if (this->Required) { @@ -557,8 +927,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) // A dependency provider (if set) gets first look before other methods. // We do this before modifying the package root path stack because a // provider might use methods that ignore that. - cmState* state = this->Makefile->GetState(); - cmState::Command providerCommand = state->GetDependencyProviderCommand( + cmState* const state = this->Makefile->GetState(); + cmState::Command const providerCommand = state->GetDependencyProviderCommand( cmDependencyProvider::Method::FindPackage); if (bypassProvider) { if (this->DebugMode && providerCommand) { @@ -725,11 +1095,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) "package configuration file provided by " << this->Name << " (" << this->Name << "Config.cmake or " << cmSystemTools::LowerCase(this->Name) - << "-config.cmake). " - "Otherwise make Find" - << this->Name - << ".cmake available in " - "CMAKE_MODULE_PATH."; + << "-config.cmake). Otherwise make Find" << this->Name + << ".cmake available in CMAKE_MODULE_PATH."; } aw << "\n" "(Variable CMAKE_FIND_PACKAGE_WARN_NO_MODULE enabled this " @@ -813,22 +1180,22 @@ bool cmFindPackageCommand::FindPackageUsingConfigMode() void cmFindPackageCommand::SetVersionVariables( const std::function<void(const std::string&, cm::string_view)>& addDefinition, - const std::string& prefix, const std::string& version, unsigned int count, - unsigned int major, unsigned int minor, unsigned int patch, - unsigned int tweak) + const std::string& prefix, const std::string& version, + const unsigned int count, const unsigned int major, const unsigned int minor, + const unsigned int patch, const unsigned int tweak) { addDefinition(prefix, version); char buf[64]; snprintf(buf, sizeof(buf), "%u", major); addDefinition(prefix + "_MAJOR", buf); - sprintf(buf, "%u", minor); + snprintf(buf, sizeof(buf), "%u", minor); addDefinition(prefix + "_MINOR", buf); - sprintf(buf, "%u", patch); + snprintf(buf, sizeof(buf), "%u", patch); addDefinition(prefix + "_PATCH", buf); - sprintf(buf, "%u", tweak); + snprintf(buf, sizeof(buf), "%u", tweak); addDefinition(prefix + "_TWEAK", buf); - sprintf(buf, "%u", count); + snprintf(buf, sizeof(buf), "%u", count); addDefinition(prefix + "_COUNT", buf); } @@ -910,7 +1277,7 @@ void cmFindPackageCommand::SetModuleVariables( } void cmFindPackageCommand::AddFindDefinition(const std::string& var, - cm::string_view value) + const cm::string_view value) { if (cmValue old = this->Makefile->GetDefinition(var)) { this->OriginalDefs[var].exists = true; @@ -954,7 +1321,7 @@ bool cmFindPackageCommand::FindModule(bool& found) if (!mfile.empty()) { if (system) { - auto it = this->DeprecatedFindModules.find(this->Name); + auto const it = this->DeprecatedFindModules.find(this->Name); if (it != this->DeprecatedFindModules.end()) { cmPolicies::PolicyStatus status = this->Makefile->GetPolicyStatus(it->second); @@ -978,13 +1345,13 @@ bool cmFindPackageCommand::FindModule(bool& found) // Load the module we found, and set "<name>_FIND_MODULE" to true // while inside it. found = true; - std::string var = cmStrCat(this->Name, "_FIND_MODULE"); + std::string const var = cmStrCat(this->Name, "_FIND_MODULE"); this->Makefile->AddDefinition(var, "1"); bool result = this->ReadListFile(mfile, DoPolicyScope); this->Makefile->RemoveDefinition(var); if (this->DebugMode) { - std::string foundVar = cmStrCat(this->Name, "_FOUND"); + std::string const foundVar = cmStrCat(this->Name, "_FOUND"); if (this->Makefile->IsDefinitionSet(foundVar) && !this->Makefile->IsOn(foundVar)) { @@ -999,7 +1366,7 @@ bool cmFindPackageCommand::FindModule(bool& found) } bool cmFindPackageCommand::HandlePackageMode( - HandlePackageModeType handlePackageModeType) + const HandlePackageModeType handlePackageModeType) { this->ConsideredConfigs.clear(); @@ -1042,8 +1409,9 @@ bool cmFindPackageCommand::HandlePackageMode( } } - std::string foundVar = cmStrCat(this->Name, "_FOUND"); - std::string notFoundMessageVar = cmStrCat(this->Name, "_NOT_FOUND_MESSAGE"); + std::string const foundVar = cmStrCat(this->Name, "_FOUND"); + std::string const notFoundMessageVar = + cmStrCat(this->Name, "_NOT_FOUND_MESSAGE"); std::string notFoundMessage; // If the directory for the config file was found, try to read the file. @@ -1123,8 +1491,9 @@ bool cmFindPackageCommand::HandlePackageMode( << (this->VersionExact ? "exactly matches" : "is compatible with") << " requested version " << (this->VersionRange.empty() ? "" : "range ") << "\"" - << this->VersionComplete << "\".\n" - << "The following configuration files were considered but not " + << this->VersionComplete + << "\".\n" + "The following configuration files were considered but not " "accepted:\n"; for (ConfigFileInfo const& info : @@ -1172,8 +1541,9 @@ bool cmFindPackageCommand::HandlePackageMode( "package or SDK, be sure it has been installed."; } else // if(!this->UseFindModules && !this->UseConfigFiles) { - e << "No \"Find" << this->Name << ".cmake\" found in " - << "CMAKE_MODULE_PATH."; + e << "No \"Find" << this->Name + << ".cmake\" found in " + "CMAKE_MODULE_PATH."; aw << "Find" << this->Name @@ -1217,16 +1587,16 @@ bool cmFindPackageCommand::HandlePackageMode( this->Makefile->AddDefinition(foundVar, found ? "1" : "0"); // Set a variable naming the configuration file that was found. - std::string fileVar = cmStrCat(this->Name, "_CONFIG"); + std::string const fileVar = cmStrCat(this->Name, "_CONFIG"); if (found) { this->Makefile->AddDefinition(fileVar, this->FileFound); } else { this->Makefile->RemoveDefinition(fileVar); } - std::string consideredConfigsVar = + std::string const consideredConfigsVar = cmStrCat(this->Name, "_CONSIDERED_CONFIGS"); - std::string consideredVersionsVar = + std::string const consideredVersionsVar = cmStrCat(this->Name, "_CONSIDERED_VERSIONS"); std::string consideredConfigFiles; @@ -1312,7 +1682,7 @@ bool cmFindPackageCommand::FindConfig() void cmFindPackageCommand::SetConfigDirCacheVariable(const std::string& value) { - std::string help = + std::string const help = cmStrCat("The directory containing a CMake configuration file for ", this->Name, '.'); this->Makefile->AddCacheDefinition(this->Variable, value, help.c_str(), @@ -1351,7 +1721,7 @@ bool cmFindPackageCommand::FindAppBundleConfig() } bool cmFindPackageCommand::ReadListFile(const std::string& f, - PolicyScopeRule psr) + const PolicyScopeRule psr) { const bool noPolicyScope = !this->PolicyScope || psr == NoPolicyScope; @@ -1362,12 +1732,12 @@ bool cmFindPackageCommand::ReadListFile(const std::string& f, if (this->Makefile->ReadDependentFile(f, noPolicyScope)) { return true; } - std::string e = cmStrCat("Error reading CMake code from \"", f, "\"."); + std::string const e = cmStrCat("Error reading CMake code from \"", f, "\"."); this->SetError(e); return false; } -void cmFindPackageCommand::AppendToFoundProperty(bool found) +void cmFindPackageCommand::AppendToFoundProperty(const bool found) { std::vector<std::string> foundContents; cmValue foundProp = @@ -1410,27 +1780,28 @@ void cmFindPackageCommand::AppendToFoundProperty(bool found) void cmFindPackageCommand::AppendSuccessInformation() { { - std::string transitivePropName = + std::string const transitivePropName = cmStrCat("_CMAKE_", this->Name, "_TRANSITIVE_DEPENDENCY"); this->Makefile->GetState()->SetGlobalProperty(transitivePropName, "False"); } - std::string found = cmStrCat(this->Name, "_FOUND"); - std::string upperFound = cmSystemTools::UpperCase(found); + std::string const found = cmStrCat(this->Name, "_FOUND"); + std::string const upperFound = cmSystemTools::UpperCase(found); - bool upperResult = this->Makefile->IsOn(upperFound); - bool result = this->Makefile->IsOn(found); - bool packageFound = (result || upperResult); + bool const upperResult = this->Makefile->IsOn(upperFound); + bool const result = this->Makefile->IsOn(found); + bool const packageFound = (result || upperResult); this->AppendToFoundProperty(packageFound); // Record whether the find was quiet or not, so this can be used // e.g. in FeatureSummary.cmake - std::string quietInfoPropName = cmStrCat("_CMAKE_", this->Name, "_QUIET"); + std::string const quietInfoPropName = + cmStrCat("_CMAKE_", this->Name, "_QUIET"); this->Makefile->GetState()->SetGlobalProperty( quietInfoPropName, this->Quiet ? "TRUE" : "FALSE"); // set a global property to record the required version of this package - std::string versionInfoPropName = + std::string const versionInfoPropName = cmStrCat("_CMAKE_", this->Name, "_REQUIRED_VERSION"); std::string versionInfo; if (!this->VersionRange.empty()) { @@ -1442,28 +1813,13 @@ void cmFindPackageCommand::AppendSuccessInformation() this->Makefile->GetState()->SetGlobalProperty(versionInfoPropName, versionInfo.c_str()); if (this->Required) { - std::string requiredInfoPropName = + std::string const requiredInfoPropName = cmStrCat("_CMAKE_", this->Name, "_TYPE"); this->Makefile->GetState()->SetGlobalProperty(requiredInfoPropName, "REQUIRED"); } } -inline std::size_t collectPathsForDebug(std::string& buffer, - cmSearchPath const& searchPath, - std::size_t startIndex = 0) -{ - const auto& paths = searchPath.GetPaths(); - if (paths.empty()) { - buffer += " none\n"; - return 0; - } - for (std::size_t i = startIndex; i < paths.size(); i++) { - buffer += " " + paths[i].Path + "\n"; - } - return paths.size(); -} - void cmFindPackageCommand::ComputePrefixes() { this->FillPrefixesPackageRedirect(); @@ -1674,14 +2030,6 @@ void cmFindPackageCommand::FillPrefixesSystemRegistry() } #if defined(_WIN32) && !defined(__CYGWIN__) -# include <windows.h> -// http://msdn.microsoft.com/en-us/library/aa384253%28v=vs.85%29.aspx -# if !defined(KEY_WOW64_32KEY) -# define KEY_WOW64_32KEY 0x0200 -# endif -# if !defined(KEY_WOW64_64KEY) -# define KEY_WOW64_64KEY 0x0100 -# endif void cmFindPackageCommand::LoadPackageRegistryWinUser() { // HKEY_CURRENT_USER\\Software shares 32-bit and 64-bit views. @@ -1704,7 +2052,8 @@ void cmFindPackageCommand::LoadPackageRegistryWinSystem() } } -void cmFindPackageCommand::LoadPackageRegistryWin(bool user, unsigned int view, +void cmFindPackageCommand::LoadPackageRegistryWin(const bool user, + const unsigned int view, cmSearchPath& outPaths) { std::wstring key = L"Software\\Kitware\\CMake\\Packages\\"; @@ -1756,28 +2105,8 @@ void cmFindPackageCommand::LoadPackageRegistryWin(bool user, unsigned int view, RegCloseKey(hKey); } } -#else -class cmFindPackageCommandHoldFile -{ - const char* File; - -public: - cmFindPackageCommandHoldFile(const char* f) - : File(f) - { - } - ~cmFindPackageCommandHoldFile() - { - if (this->File) { - cmSystemTools::RemoveFile(this->File); - } - } - cmFindPackageCommandHoldFile(const cmFindPackageCommandHoldFile&) = delete; - cmFindPackageCommandHoldFile& operator=( - const cmFindPackageCommandHoldFile&) = delete; - void Release() { this->File = nullptr; } -}; +#else void cmFindPackageCommand::LoadPackageRegistryDir(std::string const& dir, cmSearchPath& outPaths) { @@ -1877,7 +2206,7 @@ void cmFindPackageCommand::FillPrefixesCMakeSystemVariable() std::vector<std::string> expanded = cmExpandedList(*prefix_paths); long count = 0; for (const auto& path : expanded) { - bool to_add = + bool const to_add = !(path == install_path_to_remove && ++count == install_prefix_count); if (to_add) { paths.AddPath(path); @@ -1941,7 +2270,7 @@ bool cmFindPackageCommand::SearchDirectory(std::string const& dir) std::string d = dir; if (!s.empty()) { d += s; - d += "/"; + d += '/'; } if (this->CheckDirectory(d)) { return true; @@ -1955,7 +2284,7 @@ bool cmFindPackageCommand::CheckDirectory(std::string const& dir) assert(!dir.empty() && dir.back() == '/'); // Look for the file in this directory. - std::string d = dir.substr(0, dir.size() - 1); + std::string const d = dir.substr(0, dir.size() - 1); if (this->FindConfigFile(d, this->FileFound)) { // Remove duplicate slashes. cmSystemTools::ConvertToUnixSlashes(this->FileFound); @@ -2028,8 +2357,8 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file, std::string& result_version) { // The version file will be loaded in an isolated scope. - cmMakefile::ScopePushPop varScope(this->Makefile); - cmMakefile::PolicyPushPop polScope(this->Makefile); + cmMakefile::ScopePushPop const varScope(this->Makefile); + cmMakefile::PolicyPushPop const polScope(this->Makefile); static_cast<void>(varScope); static_cast<void>(polScope); @@ -2076,7 +2405,7 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file, if (this->ReadListFile(version_file, NoPolicyScope)) { // Check the output variables. bool okay = this->Makefile->IsOn("PACKAGE_VERSION_EXACT"); - bool unsuitable = this->Makefile->IsOn("PACKAGE_VERSION_UNSUITABLE"); + bool const unsuitable = this->Makefile->IsOn("PACKAGE_VERSION_UNSUITABLE"); if (!okay && !this->VersionExact) { okay = this->Makefile->IsOn("PACKAGE_VERSION_COMPATIBLE"); } @@ -2096,8 +2425,8 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file, unsigned int parsed_patch; unsigned int parsed_tweak; this->VersionFoundCount = - sscanf(this->VersionFound.c_str(), "%u.%u.%u.%u", &parsed_major, - &parsed_minor, &parsed_patch, &parsed_tweak); + parseVersion(this->VersionFound, parsed_major, parsed_minor, + parsed_patch, parsed_tweak); switch (this->VersionFoundCount) { case 4: this->VersionFoundTweak = parsed_tweak; @@ -2129,7 +2458,7 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file, void cmFindPackageCommand::StoreVersionFound() { // Store the whole version string. - std::string ver = cmStrCat(this->Name, "_VERSION"); + std::string const ver = cmStrCat(this->Name, "_VERSION"); auto addDefinition = [this](const std::string& variable, cm::string_view value) { this->Makefile->AddDefinition(variable, value); @@ -2145,357 +2474,6 @@ void cmFindPackageCommand::StoreVersionFound() } } -class cmFileListGeneratorBase -{ -public: - virtual ~cmFileListGeneratorBase() = default; - -protected: - bool Consider(std::string const& fullPath, cmFileList& listing); - -private: - bool Search(cmFileList&); - virtual bool Search(std::string const& parent, cmFileList&) = 0; - virtual std::unique_ptr<cmFileListGeneratorBase> Clone() const = 0; - friend class cmFileList; - cmFileListGeneratorBase* SetNext(cmFileListGeneratorBase const& next); - std::unique_ptr<cmFileListGeneratorBase> Next; -}; - -class cmFileList -{ -public: - virtual ~cmFileList() = default; - cmFileList& operator/(cmFileListGeneratorBase const& rhs) - { - if (this->Last) { - this->Last = this->Last->SetNext(rhs); - } else { - this->First = rhs.Clone(); - this->Last = this->First.get(); - } - return *this; - } - bool Search() - { - if (this->First) { - return this->First->Search(*this); - } - return false; - } - -private: - virtual bool Visit(std::string const& fullPath) = 0; - friend class cmFileListGeneratorBase; - std::unique_ptr<cmFileListGeneratorBase> First; - cmFileListGeneratorBase* Last = nullptr; -}; - -class cmFindPackageFileList : public cmFileList -{ -public: - cmFindPackageFileList(cmFindPackageCommand* fpc, bool use_suffixes = true) - : FPC(fpc) - , UseSuffixes(use_suffixes) - { - } - -private: - bool Visit(std::string const& fullPath) override - { - if (this->UseSuffixes) { - return this->FPC->SearchDirectory(fullPath); - } - return this->FPC->CheckDirectory(fullPath); - } - cmFindPackageCommand* FPC; - bool UseSuffixes; -}; - -bool cmFileListGeneratorBase::Search(cmFileList& listing) -{ - return this->Search("", listing); -} - -cmFileListGeneratorBase* cmFileListGeneratorBase::SetNext( - cmFileListGeneratorBase const& next) -{ - this->Next = next.Clone(); - return this->Next.get(); -} - -bool cmFileListGeneratorBase::Consider(std::string const& fullPath, - cmFileList& listing) -{ - if (!fullPath.empty() && !cmSystemTools::FileIsDirectory(fullPath)) { - return false; - } - if (this->Next) { - return this->Next->Search(fullPath + "/", listing); - } - return listing.Visit(fullPath + "/"); -} - -class cmFileListGeneratorFixed : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorFixed(std::string str) - : String(std::move(str)) - { - } - cmFileListGeneratorFixed(cmFileListGeneratorFixed const& r) - : String(r.String) - { - } - -private: - std::string String; - bool Search(std::string const& parent, cmFileList& lister) override - { - std::string fullPath = parent + this->String; - return this->Consider(fullPath, lister); - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - std::unique_ptr<cmFileListGeneratorBase> g( - new cmFileListGeneratorFixed(*this)); - return g; - } -}; - -class cmFileListGeneratorEnumerate : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorEnumerate(std::vector<std::string> const& v) - : Vector(v) - { - } - cmFileListGeneratorEnumerate(cmFileListGeneratorEnumerate const& r) - : Vector(r.Vector) - { - } - -private: - std::vector<std::string> const& Vector; - bool Search(std::string const& parent, cmFileList& lister) override - { - for (std::string const& i : this->Vector) { - if (this->Consider(parent + i, lister)) { - return true; - } - } - return false; - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - std::unique_ptr<cmFileListGeneratorBase> g( - new cmFileListGeneratorEnumerate(*this)); - return g; - } -}; - -class cmFileListGeneratorProject : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorProject(std::vector<std::string> const& names, - cmFindPackageCommand::SortOrderType so, - cmFindPackageCommand::SortDirectionType sd) - : Names(names) - { - this->SetSort(so, sd); - } - cmFileListGeneratorProject(cmFileListGeneratorProject const& r) - : Names(r.Names) - { - this->SetSort(r.SortOrder, r.SortDirection); - } - - void SetSort(cmFindPackageCommand::SortOrderType o, - cmFindPackageCommand::SortDirectionType d) - { - this->SortOrder = o; - this->SortDirection = d; - } - -protected: - // sort parameters - cmFindPackageCommand::SortOrderType SortOrder; - cmFindPackageCommand::SortDirectionType SortDirection; - -private: - std::vector<std::string> const& Names; - bool Search(std::string const& parent, cmFileList& lister) override - { - // Construct a list of matches. - std::vector<std::string> matches; - cmsys::Directory d; - d.Load(parent); - for (unsigned long i = 0; i < d.GetNumberOfFiles(); ++i) { - const char* fname = d.GetFile(i); - if (strcmp(fname, ".") == 0 || strcmp(fname, "..") == 0) { - continue; - } - for (std::string const& n : this->Names) { - if (cmsysString_strncasecmp(fname, n.c_str(), n.length()) == 0) { - matches.emplace_back(fname); - } - } - } - - // before testing the matches check if there is a specific sorting order to - // perform - if (this->SortOrder != cmFindPackageCommand::None) { - cmFindPackageCommand::Sort(matches.begin(), matches.end(), - this->SortOrder, this->SortDirection); - } - - for (std::string const& i : matches) { - if (this->Consider(parent + i, lister)) { - return true; - } - } - return false; - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - std::unique_ptr<cmFileListGeneratorBase> g( - new cmFileListGeneratorProject(*this)); - return g; - } -}; - -class cmFileListGeneratorMacProject : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorMacProject(std::vector<std::string> const& names, - const char* ext) - : Names(names) - , Extension(ext) - { - } - cmFileListGeneratorMacProject(cmFileListGeneratorMacProject const& r) - : Names(r.Names) - , Extension(r.Extension) - { - } - -private: - std::vector<std::string> const& Names; - std::string Extension; - bool Search(std::string const& parent, cmFileList& lister) override - { - // Construct a list of matches. - std::vector<std::string> matches; - cmsys::Directory d; - d.Load(parent); - for (unsigned long i = 0; i < d.GetNumberOfFiles(); ++i) { - const char* fname = d.GetFile(i); - if (strcmp(fname, ".") == 0 || strcmp(fname, "..") == 0) { - continue; - } - for (std::string name : this->Names) { - name += this->Extension; - if (cmsysString_strcasecmp(fname, name.c_str()) == 0) { - matches.emplace_back(fname); - } - } - } - - for (std::string const& i : matches) { - if (this->Consider(parent + i, lister)) { - return true; - } - } - return false; - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - std::unique_ptr<cmFileListGeneratorBase> g( - new cmFileListGeneratorMacProject(*this)); - return g; - } -}; - -class cmFileListGeneratorCaseInsensitive : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorCaseInsensitive(std::string str) - : String(std::move(str)) - { - } - cmFileListGeneratorCaseInsensitive( - cmFileListGeneratorCaseInsensitive const& r) - : String(r.String) - { - } - -private: - std::string String; - bool Search(std::string const& parent, cmFileList& lister) override - { - // Look for matching files. - std::vector<std::string> matches; - cmsys::Directory d; - d.Load(parent); - for (unsigned long i = 0; i < d.GetNumberOfFiles(); ++i) { - const char* fname = d.GetFile(i); - if (strcmp(fname, ".") == 0 || strcmp(fname, "..") == 0) { - continue; - } - if (cmsysString_strcasecmp(fname, this->String.c_str()) == 0) { - if (this->Consider(parent + fname, lister)) { - return true; - } - } - } - return false; - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - std::unique_ptr<cmFileListGeneratorBase> g( - new cmFileListGeneratorCaseInsensitive(*this)); - return g; - } -}; - -class cmFileListGeneratorGlob : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorGlob(std::string str) - : Pattern(std::move(str)) - { - } - cmFileListGeneratorGlob(cmFileListGeneratorGlob const& r) - : Pattern(r.Pattern) - { - } - -private: - std::string Pattern; - bool Search(std::string const& parent, cmFileList& lister) override - { - // Glob the set of matching files. - std::string expr = cmStrCat(parent, this->Pattern); - cmsys::Glob g; - if (!g.FindFiles(expr)) { - return false; - } - std::vector<std::string> const& files = g.GetFiles(); - - // Look for directories among the matches. - for (std::string const& f : files) { - if (this->Consider(f, lister)) { - return true; - } - } - return false; - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - return cm::make_unique<cmFileListGeneratorGlob>(*this); - } -}; - bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in) { assert(!prefix_in.empty() && prefix_in.back() == '/'); @@ -2515,148 +2493,101 @@ bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in) return false; } - // PREFIX/ (useful on windows or in build trees) + // PREFIX/ (useful on windows or in build trees) if (this->SearchDirectory(prefix_in)) { return true; } // Strip the trailing slash because the path generator is about to // add one. - std::string prefix = prefix_in.substr(0, prefix_in.size() - 1); + std::string const prefix = prefix_in.substr(0, prefix_in.size() - 1); - // PREFIX/(cmake|CMake)/ (useful on windows or in build trees) - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } + auto searchFn = [this](const std::string& fullPath) -> bool { + return this->SearchDirectory(fullPath); + }; + + auto iCMakeGen = cmCaseInsensitiveDirectoryListGenerator{ "cmake"_s }; + auto firstPkgDirGen = + cmProjectDirectoryListGenerator{ this->Names, this->SortOrder, + this->SortDirection }; + + // PREFIX/(cmake|CMake)/ (useful on windows or in build trees) + if (TryGeneratedPaths(searchFn, prefix, iCMakeGen)) { + return true; } - // PREFIX/(Foo|foo|FOO).*/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection); - if (lister.Search()) { - return true; - } + // PREFIX/(Foo|foo|FOO).*/ + if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen)) { + return true; } - // PREFIX/(Foo|foo|FOO).*/(cmake|CMake)/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } + // PREFIX/(Foo|foo|FOO).*/(cmake|CMake)/ + if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, iCMakeGen)) { + return true; + } + + auto secondPkgDirGen = + cmProjectDirectoryListGenerator{ this->Names, this->SortOrder, + this->SortDirection }; + + // PREFIX/(Foo|foo|FOO).*/(cmake|CMake)/(Foo|foo|FOO).*/ + if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, iCMakeGen, + secondPkgDirGen)) { + return true; } // Construct list of common install locations (lib and share). - std::vector<std::string> common; + std::vector<cm::string_view> common; + std::string libArch; if (!this->LibraryArchitecture.empty()) { - common.push_back("lib/" + this->LibraryArchitecture); + libArch = "lib/" + this->LibraryArchitecture; + common.emplace_back(libArch); } if (this->UseLib32Paths) { - common.emplace_back("lib32"); + common.emplace_back("lib32"_s); } if (this->UseLib64Paths) { - common.emplace_back("lib64"); + common.emplace_back("lib64"_s); } if (this->UseLibx32Paths) { - common.emplace_back("libx32"); + common.emplace_back("libx32"_s); } - common.emplace_back("lib"); - common.emplace_back("share"); + common.emplace_back("lib"_s); + common.emplace_back("share"_s); - // PREFIX/(lib/ARCH|lib*|share)/cmake/(Foo|foo|FOO).*/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorFixed("cmake") / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection); - if (lister.Search()) { - return true; - } + auto cmnGen = cmEnumPathSegmentsGenerator{ common }; + auto cmakeGen = cmAppendPathSegmentGenerator{ "cmake"_s }; + + // PREFIX/(lib/ARCH|lib*|share)/cmake/(Foo|foo|FOO).*/ + if (TryGeneratedPaths(searchFn, prefix, cmnGen, cmakeGen, firstPkgDirGen)) { + return true; } - // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection); - if (lister.Search()) { - return true; - } + // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/ + if (TryGeneratedPaths(searchFn, prefix, cmnGen, firstPkgDirGen)) { + return true; } - // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/(cmake|CMake)/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } + // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/(cmake|CMake)/ + if (TryGeneratedPaths(searchFn, prefix, cmnGen, firstPkgDirGen, iCMakeGen)) { + return true; } // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/cmake/(Foo|foo|FOO).*/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorFixed("cmake") / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, cmnGen, cmakeGen, + secondPkgDirGen)) { + return true; } // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, cmnGen, + secondPkgDirGen)) { + return true; } // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/(cmake|CMake)/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } - } - - return false; + return TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, cmnGen, + secondPkgDirGen, iCMakeGen); } bool cmFindPackageCommand::SearchFrameworkPrefix(std::string const& prefix_in) @@ -2665,56 +2596,36 @@ bool cmFindPackageCommand::SearchFrameworkPrefix(std::string const& prefix_in) // Strip the trailing slash because the path generator is about to // add one. - std::string prefix = prefix_in.substr(0, prefix_in.size() - 1); + std::string const prefix = prefix_in.substr(0, prefix_in.size() - 1); + + auto searchFn = [this](const std::string& fullPath) -> bool { + return this->SearchDirectory(fullPath); + }; + + auto iCMakeGen = cmCaseInsensitiveDirectoryListGenerator{ "cmake"_s }; + auto fwGen = + cmMacProjectDirectoryListGenerator{ this->Names, ".framework"_s }; + auto rGen = cmAppendPathSegmentGenerator{ "Resources"_s }; + auto vGen = cmAppendPathSegmentGenerator{ "Versions"_s }; + auto grGen = cmFileListGeneratorGlob{ "/*/Resources"_s }; // <prefix>/Foo.framework/Resources/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".framework") / - cmFileListGeneratorFixed("Resources"); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, fwGen, rGen)) { + return true; } + // <prefix>/Foo.framework/Resources/CMake/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".framework") / - cmFileListGeneratorFixed("Resources") / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, fwGen, rGen, iCMakeGen)) { + return true; } // <prefix>/Foo.framework/Versions/*/Resources/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".framework") / - cmFileListGeneratorFixed("Versions") / - cmFileListGeneratorGlob("*/Resources"); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, fwGen, vGen, grGen)) { + return true; } // <prefix>/Foo.framework/Versions/*/Resources/CMake/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".framework") / - cmFileListGeneratorFixed("Versions") / - cmFileListGeneratorGlob("*/Resources") / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } - } - - return false; + return TryGeneratedPaths(searchFn, prefix, fwGen, vGen, grGen, iCMakeGen); } bool cmFindPackageCommand::SearchAppBundlePrefix(std::string const& prefix_in) @@ -2723,32 +2634,24 @@ bool cmFindPackageCommand::SearchAppBundlePrefix(std::string const& prefix_in) // Strip the trailing slash because the path generator is about to // add one. - std::string prefix = prefix_in.substr(0, prefix_in.size() - 1); + std::string const prefix = prefix_in.substr(0, prefix_in.size() - 1); + + auto searchFn = [this](const std::string& fullPath) -> bool { + return this->SearchDirectory(fullPath); + }; + + auto appGen = cmMacProjectDirectoryListGenerator{ this->Names, ".app"_s }; + auto crGen = cmAppendPathSegmentGenerator{ "Contents/Resources"_s }; // <prefix>/Foo.app/Contents/Resources - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".app") / - cmFileListGeneratorFixed("Contents/Resources"); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, appGen, crGen)) { + return true; } // <prefix>/Foo.app/Contents/Resources/CMake - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".app") / - cmFileListGeneratorFixed("Contents/Resources") / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } - } - - return false; + return TryGeneratedPaths( + searchFn, prefix, appGen, crGen, + cmCaseInsensitiveDirectoryListGenerator{ "cmake"_s }); } // TODO: Debug cmsys::Glob double slash problem. diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h index 80fd8f8..28e00a1 100644 --- a/Source/cmFindPackageCommand.h +++ b/Source/cmFindPackageCommand.h @@ -153,8 +153,6 @@ private: bool SearchFrameworkPrefix(std::string const& prefix_in); bool SearchAppBundlePrefix(std::string const& prefix_in); - friend class cmFindPackageFileList; - struct OriginalDef { bool exists; diff --git a/Source/cmFindPathCommand.cxx b/Source/cmFindPathCommand.cxx index 27074ff..74a69d8 100644 --- a/Source/cmFindPathCommand.cxx +++ b/Source/cmFindPathCommand.cxx @@ -88,7 +88,8 @@ std::string cmFindPathCommand::FindHeaderInFramework( if (!frameWorkName.empty()) { std::string fpath = cmStrCat(dir, frameWorkName, ".framework"); std::string intPath = cmStrCat(fpath, "/Headers/", fileName); - if (cmSystemTools::FileExists(intPath)) { + if (cmSystemTools::FileExists(intPath) && + this->Validate(this->IncludeFileInPath ? intPath : fpath)) { debug.FoundAt(intPath); if (this->IncludeFileInPath) { return intPath; @@ -124,7 +125,8 @@ std::string cmFindPathCommand::FindNormalHeader(cmFindBaseDebugState& debug) for (std::string const& n : this->Names) { for (std::string const& sp : this->SearchPaths) { tryPath = cmStrCat(sp, n); - if (cmSystemTools::FileExists(tryPath)) { + if (cmSystemTools::FileExists(tryPath) && + this->Validate(this->IncludeFileInPath ? tryPath : sp)) { debug.FoundAt(tryPath); if (this->IncludeFileInPath) { return tryPath; diff --git a/Source/cmFindProgramCommand.cxx b/Source/cmFindProgramCommand.cxx index a64e0e4..8a2a69e 100644 --- a/Source/cmFindProgramCommand.cxx +++ b/Source/cmFindProgramCommand.cxx @@ -27,6 +27,7 @@ struct cmFindProgramHelper cmFindBase const* base) : DebugSearches(std::move(debugName), base) , Makefile(makefile) + , FindBase(base) , PolicyCMP0109(makefile->GetPolicyStatus(cmPolicies::CMP0109)) { #if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) @@ -56,6 +57,7 @@ struct cmFindProgramHelper // Debug state cmFindBaseDebugState DebugSearches; cmMakefile* Makefile; + cmFindBase const* FindBase; cmPolicies::PolicyStatus PolicyCMP0109; @@ -94,7 +96,7 @@ struct cmFindProgramHelper this->TestNameExt = cmStrCat(name, ext); this->TestPath = cmSystemTools::CollapseFullPath( this->TestNameExt, path); - bool exists = this->FileIsExecutable(this->TestPath); + bool exists = this->FileIsValid(this->TestPath); exists ? this->DebugSearches.FoundAt(this->TestPath) : this->DebugSearches.FailedAt(this->TestPath); if (exists) { @@ -104,12 +106,12 @@ struct cmFindProgramHelper return false; }); } - bool FileIsExecutable(std::string const& file) const + bool FileIsValid(std::string const& file) const { -#ifdef _WIN32 if (!this->FileIsExecutableCMP0109(file)) { return false; } +#ifdef _WIN32 // Pretend the Windows "python" app installer alias does not exist. if (cmSystemTools::LowerCase(file).find("/windowsapps/python") != std::string::npos) { @@ -119,10 +121,8 @@ struct cmFindProgramHelper return false; } } - return true; -#else - return this->FileIsExecutableCMP0109(file); #endif + return this->FindBase->Validate(file); } bool FileIsExecutableCMP0109(std::string const& file) const { diff --git a/Source/cmFunctionBlocker.cxx b/Source/cmFunctionBlocker.cxx index 40e692d..523482a 100644 --- a/Source/cmFunctionBlocker.cxx +++ b/Source/cmFunctionBlocker.cxx @@ -24,10 +24,11 @@ bool cmFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff, auto self = mf.RemoveFunctionBlocker(); assert(self.get() == this); - if (!this->ArgumentsMatch(lff, mf)) { - cmListFileContext const& lfc = this->GetStartingContext(); - cmListFileContext closingContext = - cmListFileContext::FromListFileFunction(lff, lfc.FilePath); + cmListFileContext const& lfc = this->GetStartingContext(); + cmListFileContext closingContext = + cmListFileContext::FromListFileFunction(lff, lfc.FilePath); + if (this->EndCommandSupportsArguments() && + !this->ArgumentsMatch(lff, mf)) { std::ostringstream e; /* clang-format off */ e << "A logical block opening on the line\n" @@ -37,6 +38,15 @@ bool cmFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff, << "with mis-matching arguments."; /* clang-format on */ mf.IssueMessage(MessageType::AUTHOR_WARNING, e.str()); + } else if (!this->EndCommandSupportsArguments() && + !lff.Arguments().empty()) { + std::ostringstream e; + /* clang-format off */ + e << "A logical block closing on the line\n" + " " << closingContext << "\n" + "has unexpected arguments."; + /* clang-format on */ + mf.IssueMessage(MessageType::AUTHOR_WARNING, e.str()); } return this->Replay(std::move(this->Functions), status); diff --git a/Source/cmFunctionBlocker.h b/Source/cmFunctionBlocker.h index 38abeba..3e096f2 100644 --- a/Source/cmFunctionBlocker.h +++ b/Source/cmFunctionBlocker.h @@ -38,6 +38,8 @@ private: virtual cm::string_view StartCommandName() const = 0; virtual cm::string_view EndCommandName() const = 0; + virtual bool EndCommandSupportsArguments() const { return true; } + virtual bool ArgumentsMatch(cmListFileFunction const& lff, cmMakefile& mf) const = 0; diff --git a/Source/cmGeneratedFileStream.cxx b/Source/cmGeneratedFileStream.cxx index b529b8f..6212bbd 100644 --- a/Source/cmGeneratedFileStream.cxx +++ b/Source/cmGeneratedFileStream.cxx @@ -124,10 +124,10 @@ cmGeneratedFileStreamBase::~cmGeneratedFileStreamBase() void cmGeneratedFileStreamBase::Open(std::string const& name) { // Save the original name of the file. - this->Name = name; + this->Name = cmSystemTools::CollapseFullPath(name); // Create the name of the temporary file. - this->TempName = name; + this->TempName = this->Name; #if defined(__VMS) this->TempName += "_"; #else @@ -231,7 +231,7 @@ int cmGeneratedFileStreamBase::RenameFile(std::string const& oldname, void cmGeneratedFileStream::SetName(const std::string& fname) { - this->Name = fname; + this->Name = cmSystemTools::CollapseFullPath(fname); } void cmGeneratedFileStream::SetTempExt(std::string const& ext) diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 0c351ad..54c900f 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -808,11 +808,16 @@ void handleSystemIncludesDep(cmLocalGenerator* lg, dagChecker, depTgt, language), result); } - if (!depTgt->IsImported() || excludeImported) { + if (!depTgt->GetPropertyAsBool("SYSTEM")) { return; } - if (depTgt->GetPropertyAsBool("IMPORTED_NO_SYSTEM")) { - return; + if (depTgt->IsImported()) { + if (excludeImported) { + return; + } + if (depTgt->GetPropertyAsBool("IMPORTED_NO_SYSTEM")) { + return; + } } if (cmValue dirs = depTgt->GetProperty("INTERFACE_INCLUDE_DIRECTORIES")) { @@ -912,11 +917,19 @@ bool cmGeneratorTarget::IsIPOEnabled(std::string const& lang, return false; } - if (lang != "C" && lang != "CXX" && lang != "Fortran") { + if (lang != "C" && lang != "CXX" && lang != "CUDA" && lang != "Fortran") { // We do not define IPO behavior for other languages. return false; } + if (lang == "CUDA") { + // CUDA IPO requires both CUDA_ARCHITECTURES and CUDA_SEPARABLE_COMPILATION + if (cmIsOff(this->GetSafeProperty("CUDA_ARCHITECTURES")) || + cmIsOff(this->GetSafeProperty("CUDA_SEPARABLE_COMPILATION"))) { + return false; + } + } + cmPolicies::PolicyStatus cmp0069 = this->GetPolicyStatusCMP0069(); if (cmp0069 == cmPolicies::OLD || cmp0069 == cmPolicies::WARN) { @@ -1702,13 +1715,14 @@ void addFileSetEntry(cmGeneratorTarget const* headTarget, } bool found = false; for (auto const& sg : headTarget->Makefile->GetSourceGroups()) { - if (sg.MatchesFiles(path)) { + if (sg.MatchChildrenFiles(path)) { found = true; break; } } if (!found) { - if (fileSet->GetType() == "HEADERS"_s) { + if (fileSet->GetType() == "HEADERS"_s || + fileSet->GetType() == "CXX_MODULE_HEADER_UNITS"_s) { headTarget->Makefile->GetOrCreateSourceGroup("Header Files") ->AddGroupFile(path); } @@ -1729,6 +1743,20 @@ void AddFileSetEntries(cmGeneratorTarget const* headTarget, addFileSetEntry(headTarget, config, dagChecker, headerSet, entries); } } + for (auto const& entry : headTarget->Target->GetCxxModuleSetsEntries()) { + for (auto const& name : cmExpandedList(entry.Value)) { + auto const* cxxModuleSet = headTarget->Target->GetFileSet(name); + addFileSetEntry(headTarget, config, dagChecker, cxxModuleSet, entries); + } + } + for (auto const& entry : + headTarget->Target->GetCxxModuleHeaderSetsEntries()) { + for (auto const& name : cmExpandedList(entry.Value)) { + auto const* cxxModuleHeaderSet = headTarget->Target->GetFileSet(name); + addFileSetEntry(headTarget, config, dagChecker, cxxModuleHeaderSet, + entries); + } + } } bool processSources(cmGeneratorTarget const* tgt, @@ -3409,7 +3437,9 @@ void cmGeneratorTarget::AddExplicitLanguageFlags(std::string& flags, "EXPLICIT_LANGUAGE"); } -void cmGeneratorTarget::AddCUDAArchitectureFlags(std::string& flags) const +void cmGeneratorTarget::AddCUDAArchitectureFlags(cmBuildStep compileOrLink, + const std::string& config, + std::string& flags) const { std::string property = this->GetSafeProperty("CUDA_ARCHITECTURES"); @@ -3441,6 +3471,7 @@ void cmGeneratorTarget::AddCUDAArchitectureFlags(std::string& flags) const std::string const& compiler = this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID"); + const bool ipoEnabled = this->IsIPOEnabled("CUDA", config); // Check for special modes: `all`, `all-major`. if (property == "all" || property == "all-major") { @@ -3520,6 +3551,13 @@ void cmGeneratorTarget::AddCUDAArchitectureFlags(std::string& flags) const } if (compiler == "NVIDIA") { + if (ipoEnabled && compileOrLink == cmBuildStep::Link) { + if (cmValue cudaIPOFlags = + this->Makefile->GetDefinition("CMAKE_CUDA_LINK_OPTIONS_IPO")) { + flags += cudaIPOFlags; + } + } + for (CudaArchitecture& architecture : architectures) { flags += " --generate-code=arch=compute_" + architecture.name + ",code=["; @@ -3532,7 +3570,13 @@ void cmGeneratorTarget::AddCUDAArchitectureFlags(std::string& flags) const } } - if (architecture.real) { + if (ipoEnabled) { + if (compileOrLink == cmBuildStep::Compile) { + flags += "lto_" + architecture.name; + } else if (compileOrLink == cmBuildStep::Link) { + flags += "sm_" + architecture.name; + } + } else if (architecture.real) { flags += "sm_" + architecture.name; } @@ -8724,3 +8768,76 @@ std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile( return filename; } + +bool cmGeneratorTarget::HaveCxx20ModuleSources() const +{ + auto const& fs_names = this->Target->GetAllFileSetNames(); + return std::any_of(fs_names.begin(), fs_names.end(), + [this](std::string const& name) -> bool { + auto const* file_set = this->Target->GetFileSet(name); + if (!file_set) { + this->Makefile->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", this->Target->GetName(), + "\" is tracked to have file set \"", name, + "\", but it was not found.")); + return false; + } + + auto const& fs_type = file_set->GetType(); + return fs_type == "CXX_MODULES"_s || + fs_type == "CXX_MODULE_HEADER_UNITS"_s; + }); +} + +cmGeneratorTarget::Cxx20SupportLevel cmGeneratorTarget::HaveCxxModuleSupport( + std::string const& config) const +{ + auto const* state = this->Makefile->GetState(); + if (!state->GetLanguageEnabled("CXX")) { + return Cxx20SupportLevel::MissingCxx; + } + cmStandardLevelResolver standardResolver(this->Makefile); + if (!standardResolver.HaveStandardAvailable(this, "CXX", config, + "cxx_std_20")) { + return Cxx20SupportLevel::NoCxx20; + } + if (!this->Makefile->IsOn("CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP")) { + return Cxx20SupportLevel::MissingExperimentalFlag; + } + return Cxx20SupportLevel::Supported; +} + +void cmGeneratorTarget::CheckCxxModuleStatus(std::string const& config) const +{ + // Check for `CXX_MODULE*` file sets and a lack of support. + if (this->HaveCxx20ModuleSources()) { + switch (this->HaveCxxModuleSupport(config)) { + case cmGeneratorTarget::Cxx20SupportLevel::MissingCxx: + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("The \"", this->GetName(), + "\" target has C++ module sources but the \"CXX\" language " + "has not been enabled")); + break; + case cmGeneratorTarget::Cxx20SupportLevel::MissingExperimentalFlag: + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("The \"", this->GetName(), + "\" target has C++ module sources but its experimental " + "support has not been requested")); + break; + case cmGeneratorTarget::Cxx20SupportLevel::NoCxx20: + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat( + "The \"", this->GetName(), + "\" target has C++ module sources but is not using at least " + "\"cxx_std_20\"")); + break; + case cmGeneratorTarget::Cxx20SupportLevel::Supported: + // All is well. + break; + } + } +} diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 6bce7d2..25e6a81 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -23,6 +23,7 @@ #include "cmStateTypes.h" #include "cmValue.h" +enum class cmBuildStep; class cmComputeLinkInformation; class cmCustomCommand; class cmGlobalGenerator; @@ -471,7 +472,9 @@ public: void AddExplicitLanguageFlags(std::string& flags, cmSourceFile const& sf) const; - void AddCUDAArchitectureFlags(std::string& flags) const; + void AddCUDAArchitectureFlags(cmBuildStep compileOrLink, + const std::string& config, + std::string& flags) const; void AddCUDAToolkitFlags(std::string& flags) const; void AddHIPArchitectureFlags(std::string& flags) const; @@ -1196,4 +1199,34 @@ public: bool operator()(cmGeneratorTarget const* t1, cmGeneratorTarget const* t2) const; }; + + // C++20 module support queries. + + /** + * Query whether the target expects C++20 module support. + * + * This will inspect the target itself to see if C++20 module + * support is expected to work based on its sources. + */ + bool HaveCxx20ModuleSources() const; + + enum class Cxx20SupportLevel + { + // C++ is not available. + MissingCxx, + // The experimental feature is not available. + MissingExperimentalFlag, + // The target does not require at least C++20. + NoCxx20, + // C++20 modules are available and working. + Supported, + }; + /** + * Query whether the target has C++20 module support available (regardless of + * whether it is required or not). + */ + Cxx20SupportLevel HaveCxxModuleSupport(std::string const& config) const; + + // Check C++ module status for the target. + void CheckCxxModuleStatus(std::string const& config) const; }; diff --git a/Source/cmGhsMultiTargetGenerator.cxx b/Source/cmGhsMultiTargetGenerator.cxx index bf019c3..138d3f1 100644 --- a/Source/cmGhsMultiTargetGenerator.cxx +++ b/Source/cmGhsMultiTargetGenerator.cxx @@ -183,8 +183,8 @@ void cmGhsMultiTargetGenerator::SetCompilerFlags(std::string const& config, auto i = this->FlagsByLanguage.find(language); if (i == this->FlagsByLanguage.end()) { std::string flags; - this->LocalGenerator->AddLanguageFlags(flags, this->GeneratorTarget, - language, config); + this->LocalGenerator->AddLanguageFlags( + flags, this->GeneratorTarget, cmBuildStep::Compile, language, config); this->LocalGenerator->AddCMP0018Flags(flags, this->GeneratorTarget, language, config); this->LocalGenerator->AddVisibilityPresetFlags( diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 4feae6d..07ad1e8 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -1854,6 +1854,15 @@ void cmGlobalGenerator::FinalizeTargetCompileInfo() cmExpandedList(standardIncludesStr); standardIncludesSet.insert(standardIncludesVec.begin(), standardIncludesVec.end()); + if (li == "CUDA") { + std::string const& cudaSystemIncludeVar = + mf->GetSafeDefinition("CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES"); + std::vector<std::string> cudaToolkitIncludeVec = + cmExpandedList(cudaSystemIncludeVar); + standardIncludesSet.insert(cudaToolkitIncludeVec.begin(), + cudaToolkitIncludeVec.end()); + mf->AddIncludeDirectories(cudaToolkitIncludeVec); + } } mf->AddSystemIncludeDirectories(standardIncludesSet); } diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index acaed36..c326ca6 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -3,8 +3,10 @@ #include "cmGlobalNinjaGenerator.h" #include <algorithm> +#include <cassert> #include <cctype> #include <cstdio> +#include <functional> #include <sstream> #include <utility> @@ -14,6 +16,7 @@ #include <cm/string_view> #include <cmext/algorithm> #include <cmext/memory> +#include <cmext/string_view> #include <cm3p/json/reader.h> #include <cm3p/json/value.h> @@ -21,7 +24,9 @@ #include "cmsys/FStream.hxx" +#include "cmCxxModuleMapper.h" #include "cmDocumentationEntry.h" +#include "cmFileSet.h" #include "cmFortranParser.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpressionEvaluationFile.h" @@ -2480,13 +2485,53 @@ cm::optional<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran( } } +struct CxxModuleFileSet +{ + std::string Name; + std::string RelativeDirectory; + std::string SourcePath; + std::string Type; + cmFileSetVisibility Visibility; + cm::optional<std::string> Destination; +}; + +struct CxxModuleBmiInstall +{ + std::string Component; + std::string Destination; + bool ExcludeFromAll; + bool Optional; + std::string Permissions; + std::string MessageLevel; + std::string ScriptLocation; +}; + +struct CxxModuleExport +{ + std::string Name; + std::string Destination; + std::string Prefix; + std::string CxxModuleInfoDir; + std::string Namespace; + bool Install; +}; + +struct cmGlobalNinjaGenerator::CxxModuleExportInfo +{ + std::map<std::string, CxxModuleFileSet> ObjectToFileSet; + cm::optional<CxxModuleBmiInstall> BmiInstallation; + std::vector<CxxModuleExport> Exports; + std::string Config; +}; + bool cmGlobalNinjaGenerator::WriteDyndepFile( std::string const& dir_top_src, std::string const& dir_top_bld, std::string const& dir_cur_src, std::string const& dir_cur_bld, std::string const& arg_dd, std::vector<std::string> const& arg_ddis, std::string const& module_dir, std::vector<std::string> const& linked_target_dirs, - std::string const& arg_lang, std::string const& arg_modmapfmt) + std::string const& arg_lang, std::string const& arg_modmapfmt, + CxxModuleExportInfo const& export_info) { // Setup path conversions. { @@ -2511,13 +2556,15 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( objects.push_back(std::move(info)); } + CxxModuleUsage usages; + // Map from module name to module file path, if known. std::map<std::string, std::string> mod_files; // Populate the module map with those provided by linked targets first. for (std::string const& linked_target_dir : linked_target_dirs) { std::string const ltmn = - cmStrCat(linked_target_dir, "/", arg_lang, "Modules.json"); + cmStrCat(linked_target_dir, '/', arg_lang, "Modules.json"); Json::Value ltm; cmsys::ifstream ltmf(ltmn.c_str(), std::ios::in | std::ios::binary); Json::Reader reader; @@ -2528,21 +2575,71 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( return false; } if (ltm.isObject()) { - for (Json::Value::iterator i = ltm.begin(); i != ltm.end(); ++i) { - mod_files[i.key().asString()] = i->asString(); + Json::Value const& target_modules = ltm["modules"]; + if (target_modules.isObject()) { + for (auto i = target_modules.begin(); i != target_modules.end(); ++i) { + mod_files[i.key().asString()] = i->asString(); + } + } + Json::Value const& target_modules_references = ltm["references"]; + if (target_modules_references.isObject()) { + for (auto i = target_modules_references.begin(); + i != target_modules_references.end(); ++i) { + if (i->isObject()) { + Json::Value const& reference_path = (*i)["path"]; + CxxModuleReference module_reference; + if (reference_path.isString()) { + module_reference.Path = reference_path.asString(); + } + Json::Value const& reference_method = (*i)["lookup-method"]; + if (reference_method.isString()) { + std::string reference = reference_method.asString(); + if (reference == "by-name") { + module_reference.Method = LookupMethod::ByName; + } else if (reference == "include-angle") { + module_reference.Method = LookupMethod::IncludeAngle; + } else if (reference == "include-quote") { + module_reference.Method = LookupMethod::IncludeQuote; + } + } + usages.Reference[i.key().asString()] = module_reference; + } + } + } + Json::Value const& target_modules_usage = ltm["usages"]; + if (target_modules_usage.isObject()) { + for (auto i = target_modules_usage.begin(); + i != target_modules_usage.end(); ++i) { + if (i->isArray()) { + for (auto j = i->begin(); j != i->end(); ++j) { + usages.Usage[i.key().asString()].insert(j->asString()); + } + } + } } } } - const char* module_ext = ""; - if (arg_modmapfmt == "gcc") { - module_ext = ".gcm"; + cm::optional<CxxModuleMapFormat> modmap_fmt; + if (arg_modmapfmt.empty()) { + // nothing to do. + } else if (arg_modmapfmt == "gcc") { + modmap_fmt = CxxModuleMapFormat::Gcc; + } else if (arg_modmapfmt == "msvc") { + modmap_fmt = CxxModuleMapFormat::Msvc; + } else { + cmSystemTools::Error( + cmStrCat("-E cmake_ninja_dyndep does not understand the ", arg_modmapfmt, + " module map format")); + return false; } + auto module_ext = CxxModuleMapExtension(modmap_fmt); + // Extend the module map with those provided by this target. // We do this after loading the modules provided by linked targets // in case we have one of the same name that must be preferred. - Json::Value tm = Json::objectValue; + Json::Value target_modules = Json::objectValue; for (cmScanDepInfo const& object : objects) { for (auto const& p : object.Provides) { std::string mod; @@ -2555,12 +2652,13 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( } } else { // Assume the module file path matches the logical module name. - std::string safe_logical_name = p.LogicalName; + std::string safe_logical_name = + p.LogicalName; // TODO: needs fixing for header units cmSystemTools::ReplaceString(safe_logical_name, ":", "-"); mod = cmStrCat(module_dir, safe_logical_name, module_ext); } mod_files[p.LogicalName] = mod; - tm[p.LogicalName] = mod; + target_modules[p.LogicalName] = mod; } } @@ -2568,6 +2666,32 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( ddf << "ninja_dyndep_version = 1.0\n"; { + CxxModuleLocations locs; + locs.RootDirectory = "."; + locs.PathForGenerator = [this](std::string const& path) -> std::string { + return this->ConvertToNinjaPath(path); + }; + locs.BmiLocationForModule = + [&mod_files](std::string const& logical) -> cm::optional<std::string> { + auto m = mod_files.find(logical); + if (m != mod_files.end()) { + return m->second; + } + return {}; + }; + + // Insert information about the current target's modules. + if (modmap_fmt) { + auto cycle_modules = CxxModuleUsageSeed(locs, objects, usages); + if (!cycle_modules.empty()) { + cmSystemTools::Error( + cmStrCat("Circular dependency detected in the C++ module import " + "graph. See modules named: \"", + cmJoin(cycle_modules, R"(", ")"_s), '"')); + return false; + } + } + cmNinjaBuild build("dyndep"); build.Outputs.emplace_back(""); for (cmScanDepInfo const& object : objects) { @@ -2589,60 +2713,332 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( build.Variables.emplace("restat", "1"); } - if (arg_modmapfmt.empty()) { - // nothing to do. - } else { - std::stringstream mm; - if (arg_modmapfmt == "gcc") { - // Documented in GCC's documentation. The format is a series of lines - // with a module name and the associated filename separated by - // spaces. The first line may use `$root` as the module name to - // specify a "repository root". That is used to anchor any relative - // paths present in the file (CMake should never generate any). - - // Write the root directory to use for module paths. - mm << "$root .\n"; - - for (auto const& l : object.Provides) { - auto m = mod_files.find(l.LogicalName); - if (m != mod_files.end()) { - mm << l.LogicalName << " " << this->ConvertToNinjaPath(m->second) - << "\n"; - } - } - for (auto const& r : object.Requires) { - auto m = mod_files.find(r.LogicalName); - if (m != mod_files.end()) { - mm << r.LogicalName << " " << this->ConvertToNinjaPath(m->second) - << "\n"; - } - } - } else { - cmSystemTools::Error( - cmStrCat("-E cmake_ninja_dyndep does not understand the ", - arg_modmapfmt, " module map format")); - return false; - } + if (modmap_fmt) { + auto mm = CxxModuleMapContent(*modmap_fmt, locs, object, usages); // XXX(modmap): If changing this path construction, change // `cmNinjaTargetGenerator::WriteObjectBuildStatements` to generate the // corresponding file path. cmGeneratedFileStream mmf(cmStrCat(object.PrimaryOutput, ".modmap")); - mmf << mm.str(); + mmf << mm; } this->WriteBuild(ddf, build); } } + Json::Value target_module_info = Json::objectValue; + target_module_info["modules"] = target_modules; + + auto& target_usages = target_module_info["usages"] = Json::objectValue; + for (auto const& u : usages.Usage) { + auto& mod_usage = target_usages[u.first] = Json::arrayValue; + for (auto const& v : u.second) { + mod_usage.append(v); + } + } + + auto name_for_method = [](LookupMethod method) -> cm::static_string_view { + switch (method) { + case LookupMethod::ByName: + return "by-name"_s; + case LookupMethod::IncludeAngle: + return "include-angle"_s; + case LookupMethod::IncludeQuote: + return "include-quote"_s; + } + assert(false && "unsupported lookup method"); + return ""_s; + }; + + auto& target_references = target_module_info["references"] = + Json::objectValue; + for (auto const& r : usages.Reference) { + auto& mod_ref = target_references[r.first] = Json::objectValue; + mod_ref["path"] = r.second.Path; + mod_ref["lookup-method"] = std::string(name_for_method(r.second.Method)); + } + // Store the map of modules provided by this target in a file for // use by dependents that reference this target in linked-target-dirs. std::string const target_mods_file = cmStrCat( cmSystemTools::GetFilenamePath(arg_dd), '/', arg_lang, "Modules.json"); cmGeneratedFileStream tmf(target_mods_file); - tmf << tm; + tmf << target_module_info; + + bool result = true; + + // Fortran doesn't support any of the file-set or BMI installation considered + // below. + if (arg_lang != "Fortran"_s) { + // Prepare the export information blocks. + std::string const config_upper = + cmSystemTools::UpperCase(export_info.Config); + std::vector<std::pair<std::unique_ptr<cmGeneratedFileStream>, + CxxModuleExport const*>> + exports; + for (auto const& exp : export_info.Exports) { + std::unique_ptr<cmGeneratedFileStream> properties; + + std::string const export_dir = + cmStrCat(exp.Prefix, '/', exp.CxxModuleInfoDir, '/'); + std::string const property_file_path = cmStrCat( + export_dir, "target-", exp.Name, '-', export_info.Config, ".cmake"); + properties = cm::make_unique<cmGeneratedFileStream>(property_file_path); + + // Set up the preamble. + *properties << "set_property(TARGET \"" << exp.Namespace << exp.Name + << "\"\n" + << " PROPERTY IMPORTED_CXX_MODULES_" << config_upper + << '\n'; + + exports.emplace_back(std::move(properties), &exp); + } + + std::unique_ptr<cmGeneratedFileStream> bmi_install_script; + if (export_info.BmiInstallation) { + bmi_install_script = cm::make_unique<cmGeneratedFileStream>( + export_info.BmiInstallation->ScriptLocation); + } + + auto cmEscape = [](cm::string_view str) { + return cmOutputConverter::EscapeForCMake( + str, cmOutputConverter::WrapQuotes::NoWrap); + }; + auto install_destination = + [&cmEscape](std::string const& dest) -> std::pair<bool, std::string> { + if (cmSystemTools::FileIsFullPath(dest)) { + return std::make_pair(true, cmEscape(dest)); + } + return std::make_pair(false, + cmStrCat("${_IMPORT_PREFIX}/", cmEscape(dest))); + }; - return true; + // public/private requirement tracking. + std::set<std::string> private_modules; + std::map<std::string, std::set<std::string>> public_source_requires; + + for (cmScanDepInfo const& object : objects) { + // Convert to forward slashes. + auto output_path = object.PrimaryOutput; +# ifdef _WIN32 + cmSystemTools::ConvertToUnixSlashes(output_path); +# endif + // Find the fileset for this object. + auto fileset_info_itr = export_info.ObjectToFileSet.find(output_path); + bool const has_provides = !object.Provides.empty(); + if (fileset_info_itr == export_info.ObjectToFileSet.end()) { + // If it provides anything, it should have a `CXX_MODULES` or + // `CXX_MODULE_INTERNAL_PARTITIONS` type and be present. + if (has_provides) { + // Take the first module provided to provide context. + auto const& provides = object.Provides[0]; + char const* ok_types = "`CXX_MODULES`"; + if (provides.LogicalName.find(':') != std::string::npos) { + ok_types = "`CXX_MODULES` (or `CXX_MODULE_INTERNAL_PARTITIONS` if " + "it is not `export`ed)"; + } + cmSystemTools::Error( + cmStrCat("Output ", object.PrimaryOutput, " provides the `", + provides.LogicalName, + "` module but it is not found in a `FILE_SET` of type ", + ok_types)); + result = false; + } + + // This object file does not provide anything, so nothing more needs to + // be done. + continue; + } + + auto const& file_set = fileset_info_itr->second; + + // Verify the fileset type for the object. + if (file_set.Type == "CXX_MODULES"_s) { + if (!has_provides) { + cmSystemTools::Error(cmStrCat( + "Output ", object.PrimaryOutput, + " is of type `CXX_MODULES` but does not provide a module")); + result = false; + continue; + } + } else if (file_set.Type == "CXX_MODULE_INTERNAL_PARTITIONS"_s) { + if (!has_provides) { + cmSystemTools::Error(cmStrCat( + "Source ", file_set.SourcePath, + " is of type `CXX_MODULE_INTERNAL_PARTITIONS` but does not " + "provide a module")); + result = false; + continue; + } + auto const& provides = object.Provides[0]; + if (provides.LogicalName.find(':') == std::string::npos) { + cmSystemTools::Error(cmStrCat( + "Source ", file_set.SourcePath, + " is of type `CXX_MODULE_INTERNAL_PARTITIONS` but does not " + "provide a module partition")); + result = false; + continue; + } + } else if (file_set.Type == "CXX_MODULE_HEADERS"_s) { + // TODO. + } else { + if (has_provides) { + auto const& provides = object.Provides[0]; + char const* ok_types = "`CXX_MODULES`"; + if (provides.LogicalName.find(':') != std::string::npos) { + ok_types = "`CXX_MODULES` (or `CXX_MODULE_INTERNAL_PARTITIONS` if " + "it is not `export`ed)"; + } + cmSystemTools::Error(cmStrCat( + "Source ", file_set.SourcePath, " provides the `", + provides.LogicalName, "` C++ module but is of type `", + file_set.Type, "` module but must be of type ", ok_types)); + result = false; + } + + // Not a C++ module; ignore. + continue; + } + + if (!cmFileSetVisibilityIsForInterface(file_set.Visibility)) { + // Nothing needs to be conveyed about non-`PUBLIC` modules. + for (auto const& p : object.Provides) { + private_modules.insert(p.LogicalName); + } + continue; + } + + // The module is public. Record what it directly requires. + { + auto& reqs = public_source_requires[file_set.SourcePath]; + for (auto const& r : object.Requires) { + reqs.insert(r.LogicalName); + } + } + + // Write out properties and install rules for any exports. + for (auto const& p : object.Provides) { + bool bmi_dest_is_abs = false; + std::string bmi_destination; + if (export_info.BmiInstallation) { + auto dest = + install_destination(export_info.BmiInstallation->Destination); + bmi_dest_is_abs = dest.first; + bmi_destination = cmStrCat(dest.second, '/'); + } + + std::string install_bmi_path; + std::string build_bmi_path; + auto m = mod_files.find(p.LogicalName); + if (m != mod_files.end()) { + install_bmi_path = + cmStrCat(bmi_destination, + cmEscape(cmSystemTools::GetFilenameName(m->second))); + build_bmi_path = cmEscape(m->second); + } + + for (auto const& exp : exports) { + std::string iface_source; + if (exp.second->Install && file_set.Destination) { + auto dest = install_destination(*file_set.Destination); + iface_source = cmStrCat( + dest.second, '/', cmEscape(file_set.RelativeDirectory), + cmEscape(cmSystemTools::GetFilenameName(file_set.SourcePath))); + } else { + iface_source = cmEscape(file_set.SourcePath); + } + + std::string bmi_path; + if (exp.second->Install && export_info.BmiInstallation) { + bmi_path = install_bmi_path; + } else if (!exp.second->Install) { + bmi_path = build_bmi_path; + } + + if (iface_source.empty()) { + // No destination for the C++ module source; ignore this property + // value. + continue; + } + + *exp.first << " \"" << cmEscape(p.LogicalName) << '=' + << iface_source; + if (!bmi_path.empty()) { + *exp.first << ',' << bmi_path; + } + *exp.first << "\"\n"; + } + + if (bmi_install_script) { + auto const& bmi_install = *export_info.BmiInstallation; + + *bmi_install_script << "if (CMAKE_INSTALL_COMPONENT STREQUAL \"" + << cmEscape(bmi_install.Component) << '\"'; + if (!bmi_install.ExcludeFromAll) { + *bmi_install_script << " OR NOT CMAKE_INSTALL_COMPONENT"; + } + *bmi_install_script << ")\n"; + *bmi_install_script << " file(INSTALL\n" + " DESTINATION \""; + if (!bmi_dest_is_abs) { + *bmi_install_script << "${CMAKE_INSTALL_PREFIX}/"; + } + *bmi_install_script << cmEscape(bmi_install.Destination) + << "\"\n" + " TYPE FILE\n"; + if (bmi_install.Optional) { + *bmi_install_script << " OPTIONAL\n"; + } + if (!bmi_install.MessageLevel.empty()) { + *bmi_install_script << " " << bmi_install.MessageLevel << "\n"; + } + if (!bmi_install.Permissions.empty()) { + *bmi_install_script << " PERMISSIONS" << bmi_install.Permissions + << "\n"; + } + *bmi_install_script << " FILES \"" << m->second << "\")\n"; + if (bmi_dest_is_abs) { + *bmi_install_script + << " list(APPEND CMAKE_ABSOLUTE_DESTINATION_FILES\n" + " \"" + << cmEscape(cmSystemTools::GetFilenameName(m->second)) + << "\")\n" + " if (CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION)\n" + " message(WARNING\n" + " \"ABSOLUTE path INSTALL DESTINATION : " + "${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n" + " endif ()\n" + " if (CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION)\n" + " message(FATAL_ERROR\n" + " \"ABSOLUTE path INSTALL DESTINATION forbidden (by " + "caller): ${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n" + " endif ()\n"; + } + *bmi_install_script << "endif ()\n"; + } + } + } + + // Add trailing parenthesis for the `set_property` call. + for (auto const& exp : exports) { + *exp.first << ")\n"; + } + + // Check that public sources only require public modules. + for (auto const& pub_reqs : public_source_requires) { + for (auto const& req : pub_reqs.second) { + if (private_modules.count(req)) { + cmSystemTools::Error(cmStrCat( + "Public C++ module source `", pub_reqs.first, "` requires the `", + req, "` C++ module which is provided by a private source")); + result = false; + } + } + } + } + + return result; } int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, @@ -2716,6 +3112,59 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, } } + cmGlobalNinjaGenerator::CxxModuleExportInfo export_info; + export_info.Config = tdi["config"].asString(); + if (export_info.Config.empty()) { + export_info.Config = "noconfig"; + } + Json::Value const& tdi_exports = tdi["exports"]; + if (tdi_exports.isArray()) { + for (auto const& tdi_export : tdi_exports) { + CxxModuleExport exp; + exp.Install = tdi_export["install"].asBool(); + exp.Name = tdi_export["export-name"].asString(); + exp.Destination = tdi_export["destination"].asString(); + exp.Prefix = tdi_export["export-prefix"].asString(); + exp.CxxModuleInfoDir = tdi_export["cxx-module-info-dir"].asString(); + exp.Namespace = tdi_export["namespace"].asString(); + + export_info.Exports.push_back(exp); + } + } + auto const& bmi_installation = tdi["bmi-installation"]; + if (bmi_installation.isObject()) { + CxxModuleBmiInstall bmi_install; + + bmi_install.Component = bmi_installation["component"].asString(); + bmi_install.Destination = bmi_installation["destination"].asString(); + bmi_install.ExcludeFromAll = bmi_installation["exclude-from-all"].asBool(); + bmi_install.Optional = bmi_installation["optional"].asBool(); + bmi_install.Permissions = bmi_installation["permissions"].asString(); + bmi_install.MessageLevel = bmi_installation["message-level"].asString(); + bmi_install.ScriptLocation = + bmi_installation["script-location"].asString(); + + export_info.BmiInstallation = bmi_install; + } + Json::Value const& tdi_cxx_modules = tdi["cxx-modules"]; + if (tdi_cxx_modules.isObject()) { + for (auto i = tdi_cxx_modules.begin(); i != tdi_cxx_modules.end(); ++i) { + CxxModuleFileSet& fsi = export_info.ObjectToFileSet[i.key().asString()]; + auto const& tdi_cxx_module_info = *i; + fsi.Name = tdi_cxx_module_info["name"].asString(); + fsi.RelativeDirectory = + tdi_cxx_module_info["relative-directory"].asString(); + fsi.SourcePath = tdi_cxx_module_info["source"].asString(); + fsi.Type = tdi_cxx_module_info["type"].asString(); + fsi.Visibility = cmFileSetVisibilityFromName( + tdi_cxx_module_info["visibility"].asString(), nullptr); + auto const& tdi_fs_dest = tdi_cxx_module_info["destination"]; + if (tdi_fs_dest.isString()) { + fsi.Destination = tdi_fs_dest.asString(); + } + } + } + cmake cm(cmake::RoleInternal, cmState::Unknown); cm.SetHomeDirectory(dir_top_src); cm.SetHomeOutputDirectory(dir_top_bld); @@ -2723,7 +3172,8 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, if (!ggd || !cm::static_reference_cast<cmGlobalNinjaGenerator>(ggd).WriteDyndepFile( dir_top_src, dir_top_bld, dir_cur_src, dir_cur_bld, arg_dd, arg_ddis, - module_dir, linked_target_dirs, arg_lang, arg_modmapfmt)) { + module_dir, linked_target_dirs, arg_lang, arg_modmapfmt, + export_info)) { return 1; } return 0; diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index aa2df4d..dc4f444 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -417,13 +417,15 @@ public: bool HasOutputPathPrefix() const { return !this->OutputPathPrefix.empty(); } void StripNinjaOutputPathPrefixAsSuffix(std::string& path); + struct CxxModuleExportInfo; bool WriteDyndepFile( std::string const& dir_top_src, std::string const& dir_top_bld, std::string const& dir_cur_src, std::string const& dir_cur_bld, std::string const& arg_dd, std::vector<std::string> const& arg_ddis, std::string const& module_dir, std::vector<std::string> const& linked_target_dirs, - std::string const& arg_lang, std::string const& arg_modmapfmt); + std::string const& arg_lang, std::string const& arg_modmapfmt, + CxxModuleExportInfo const& export_info); virtual std::string BuildAlias(const std::string& alias, const std::string& /*config*/) const diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index f7f7317..e53ae8e 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx @@ -395,12 +395,27 @@ void cmGlobalVisualStudio7Generator::WriteTargetsToSolution( { VisualStudioFolders.clear(); + std::vector<std::string> configs = + root->GetMakefile()->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); + for (cmGeneratorTarget const* target : projectTargets) { if (!this->IsInSolution(target)) { continue; } bool written = false; + for (auto const& c : configs) { + target->CheckCxxModuleStatus(c); + } + + if (target->HaveCxx20ModuleSources() && !this->SupportsCxxModuleDyndep()) { + root->GetMakefile()->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("The \"", target->GetName(), + "\" target contains C++ module sources which are not " + "supported by the generator")); + } + // handle external vc project files cmValue expath = target->GetProperty("EXTERNAL_MSPROJECT"); if (expath) { diff --git a/Source/cmGlobalVisualStudio7Generator.h b/Source/cmGlobalVisualStudio7Generator.h index a55cf45..288069c 100644 --- a/Source/cmGlobalVisualStudio7Generator.h +++ b/Source/cmGlobalVisualStudio7Generator.h @@ -157,6 +157,8 @@ protected: cmValue typeGuid, const std::set<BT<std::pair<std::string, bool>>>& dependencies) = 0; + virtual bool SupportsCxxModuleDyndep() const { return false; } + std::string ConvertToSolutionPath(const std::string& path); std::set<std::string> IsPartOfDefaultBuild( diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index d5783ef..7523662 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -468,6 +468,10 @@ bool cmGlobalXCodeGenerator::Open(const std::string& bindir, } CFRelease(cfStr); } +#else + (void)bindir; + (void)projectName; + (void)dryRun; #endif return ret; @@ -1372,6 +1376,18 @@ bool cmGlobalXCodeGenerator::CreateXCodeTarget( return true; } + for (std::string const& configName : this->CurrentConfigurationTypes) { + gtgt->CheckCxxModuleStatus(configName); + } + + if (gtgt->HaveCxx20ModuleSources()) { + gtgt->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("The \"", gtgt->GetName(), + "\" target contains C++ module sources which are not " + "supported by the generator")); + } + auto& gtgt_visited = this->CommandsVisited[gtgt]; auto& deps = this->GetTargetDirectDepends(gtgt); for (auto& d : deps) { @@ -2352,8 +2368,8 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, std::string& flags = cflags[lang]; // Add language-specific flags. - this->CurrentLocalGenerator->AddLanguageFlags(flags, gtgt, lang, - configName); + this->CurrentLocalGenerator->AddLanguageFlags( + flags, gtgt, cmBuildStep::Compile, lang, configName); if (gtgt->IsIPOEnabled(lang, configName)) { this->CurrentLocalGenerator->AppendFeatureOptions(flags, lang, "IPO"); @@ -2407,6 +2423,18 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, this->AppendDefines(ppDefs, targetDefines); buildSettings->AddAttribute("GCC_PREPROCESSOR_DEFINITIONS", ppDefs.CreateList()); + if (languages.count("Swift")) { + if (this->XcodeVersion < 80) { + std::string defineString; + std::set<std::string> defines(targetDefines.begin(), + targetDefines.end()); + this->CurrentLocalGenerator->JoinDefines(defines, defineString, "Swift"); + cflags["Swift"] += " " + defineString; + } else { + buildSettings->AddAttribute("SWIFT_ACTIVE_COMPILATION_CONDITIONS", + ppDefs.CreateList()); + } + } std::string extraLinkOptionsVar; std::string extraLinkOptions; @@ -2866,8 +2894,8 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, } // Create the INSTALL_PATH attribute. - std::string install_name_dir; if (gtgt->GetType() == cmStateEnums::SHARED_LIBRARY) { + std::string install_name_dir; // Get the install_name directory for the build tree. install_name_dir = gtgt->GetInstallNameDirForBuildTree(configName); // Xcode doesn't create the correct install_name in some cases. @@ -2889,9 +2917,9 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, extraLinkOptions += " -install_name "; extraLinkOptions += XCodeEscapePath(install_name); } + buildSettings->AddAttribute("INSTALL_PATH", + this->CreateString(install_name_dir)); } - buildSettings->AddAttribute("INSTALL_PATH", - this->CreateString(install_name_dir)); // Create the LD_RUNPATH_SEARCH_PATHS cmComputeLinkInformation* pcli = gtgt->GetLinkInformation(configName); diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 7ca5b23..82adca8 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -12,18 +12,22 @@ #include <utility> #include <cm/memory> +#include <cm/optional> #include <cm/string_view> #include <cmext/string_view> #include "cmsys/Glob.hxx" #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmExecutionStatus.h" +#include "cmExperimental.h" #include "cmExportSet.h" #include "cmFileSet.h" #include "cmGeneratorExpression.h" #include "cmGlobalGenerator.h" #include "cmInstallCommandArguments.h" +#include "cmInstallCxxModuleBmiGenerator.h" #include "cmInstallDirectoryGenerator.h" #include "cmInstallExportGenerator.h" #include "cmInstallFileSetGenerator.h" @@ -54,13 +58,13 @@ namespace { struct RuntimeDependenciesArgs { - std::vector<std::string> Directories; - std::vector<std::string> PreIncludeRegexes; - std::vector<std::string> PreExcludeRegexes; - std::vector<std::string> PostIncludeRegexes; - std::vector<std::string> PostExcludeRegexes; - std::vector<std::string> PostIncludeFiles; - std::vector<std::string> PostExcludeFiles; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Directories; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PreIncludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PreExcludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeFiles; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeFiles; }; auto const RuntimeDependenciesArgHelper = @@ -109,6 +113,8 @@ public: const cmInstallCommandArguments* args) const; std::string GetLibraryDestination( const cmInstallCommandArguments* args) const; + std::string GetCxxModulesBmiDestination( + const cmInstallCommandArguments* args) const; std::string GetIncludeDestination( const cmInstallCommandArguments* args) const; std::string GetSysconfDestination( @@ -401,16 +407,17 @@ bool HandleTargetsMode(std::vector<std::string> const& args, struct ArgVectors { - std::vector<std::string> Archive; - std::vector<std::string> Library; - std::vector<std::string> Runtime; - std::vector<std::string> Object; - std::vector<std::string> Framework; - std::vector<std::string> Bundle; - std::vector<std::string> Includes; - std::vector<std::string> PrivateHeader; - std::vector<std::string> PublicHeader; - std::vector<std::string> Resource; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Archive; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Library; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Runtime; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Object; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Framework; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Bundle; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Includes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PrivateHeader; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PublicHeader; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Resource; + ArgumentParser::MaybeEmpty<std::vector<std::string>> CxxModulesBmi; std::vector<std::vector<std::string>> FileSets; }; @@ -426,7 +433,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args, .Bind("PRIVATE_HEADER"_s, &ArgVectors::PrivateHeader) .Bind("PUBLIC_HEADER"_s, &ArgVectors::PublicHeader) .Bind("RESOURCE"_s, &ArgVectors::Resource) - .Bind("FILE_SET"_s, &ArgVectors::FileSets); + .Bind("FILE_SET"_s, &ArgVectors::FileSets) + .Bind("CXX_MODULES_BMI"_s, &ArgVectors::CxxModulesBmi); std::vector<std::string> genericArgVector; ArgVectors const argVectors = argHelper.Parse(args, &genericArgVector); @@ -434,26 +442,25 @@ bool HandleTargetsMode(std::vector<std::string> const& args, // now parse the generic args (i.e. the ones not specialized on LIBRARY/ // ARCHIVE, RUNTIME etc. (see above) // These generic args also contain the targets and the export stuff - std::vector<std::string> targetList; + ArgumentParser::MaybeEmpty<std::vector<std::string>> targetList; std::string exports; - std::vector<std::string> runtimeDependenciesArgVector; + cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> + runtimeDependenciesArgVector; std::string runtimeDependencySetArg; std::vector<std::string> unknownArgs; - std::vector<std::string> parsedArgs; cmInstallCommandArguments genericArgs(helper.DefaultComponentName); genericArgs.Bind("TARGETS"_s, targetList); genericArgs.Bind("EXPORT"_s, exports); genericArgs.Bind("RUNTIME_DEPENDENCIES"_s, runtimeDependenciesArgVector); genericArgs.Bind("RUNTIME_DEPENDENCY_SET"_s, runtimeDependencySetArg); - genericArgs.Parse(genericArgVector, &unknownArgs, nullptr, &parsedArgs); + genericArgs.Parse(genericArgVector, &unknownArgs); bool success = genericArgs.Finalize(); - bool withRuntimeDependencies = - std::find(parsedArgs.begin(), parsedArgs.end(), "RUNTIME_DEPENDENCIES") != - parsedArgs.end(); RuntimeDependenciesArgs runtimeDependenciesArgs = - RuntimeDependenciesArgHelper.Parse(runtimeDependenciesArgVector, - &unknownArgs); + runtimeDependenciesArgVector + ? RuntimeDependenciesArgHelper.Parse(*runtimeDependenciesArgVector, + &unknownArgs) + : RuntimeDependenciesArgs(); cmInstallCommandArguments archiveArgs(helper.DefaultComponentName); cmInstallCommandArguments libraryArgs(helper.DefaultComponentName); @@ -467,6 +474,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, cmInstallCommandIncludesArgument includesArgs; std::vector<cmInstallCommandFileSetArguments> fileSetArgs( argVectors.FileSets.size(), { helper.DefaultComponentName }); + cmInstallCommandArguments cxxModuleBmiArgs(helper.DefaultComponentName); // now parse the args for specific parts of the target (e.g. LIBRARY, // RUNTIME, ARCHIVE etc. @@ -490,6 +498,15 @@ bool HandleTargetsMode(std::vector<std::string> const& args, fileSetArgs[i] = std::move(fileSetArg); } + bool const supportCxx20FileSetTypes = cmExperimental::HasSupportEnabled( + *helper.Makefile, cmExperimental::Feature::CxxModuleCMakeApi); + if (!supportCxx20FileSetTypes) { + std::copy(argVectors.CxxModulesBmi.begin(), argVectors.CxxModulesBmi.end(), + std::back_inserter(unknownArgs)); + } else { + cxxModuleBmiArgs.Parse(argVectors.CxxModulesBmi, &unknownArgs); + } + if (!unknownArgs.empty()) { // Unknown argument. status.SetError( @@ -510,6 +527,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, for (auto& fileSetArg : fileSetArgs) { fileSetArg.SetGenericArguments(&genericArgs); } + cxxModuleBmiArgs.SetGenericArguments(&genericArgs); success = success && archiveArgs.Finalize(); success = success && libraryArgs.Finalize(); @@ -523,6 +541,9 @@ bool HandleTargetsMode(std::vector<std::string> const& args, for (auto& fileSetArg : fileSetArgs) { success = success && fileSetArg.Finalize(); } + if (supportCxx20FileSetTypes) { + success = success && cxxModuleBmiArgs.Finalize(); + } if (!success) { return false; @@ -536,7 +557,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args, publicHeaderArgs.GetNamelinkOnly() || resourceArgs.GetNamelinkOnly() || std::any_of(fileSetArgs.begin(), fileSetArgs.end(), [](const cmInstallCommandFileSetArguments& fileSetArg) - -> bool { return fileSetArg.GetNamelinkOnly(); })) { + -> bool { return fileSetArg.GetNamelinkOnly(); }) || + cxxModuleBmiArgs.GetNamelinkOnly()) { status.SetError( "TARGETS given NAMELINK_ONLY option not in LIBRARY group. " "The NAMELINK_ONLY option may be specified only following LIBRARY."); @@ -548,7 +570,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args, publicHeaderArgs.GetNamelinkSkip() || resourceArgs.GetNamelinkSkip() || std::any_of(fileSetArgs.begin(), fileSetArgs.end(), [](const cmInstallCommandFileSetArguments& fileSetArg) - -> bool { return fileSetArg.GetNamelinkSkip(); })) { + -> bool { return fileSetArg.GetNamelinkSkip(); }) || + cxxModuleBmiArgs.GetNamelinkSkip()) { status.SetError( "TARGETS given NAMELINK_SKIP option not in LIBRARY group. " "The NAMELINK_SKIP option may be specified only following LIBRARY."); @@ -564,7 +587,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args, resourceArgs.HasNamelinkComponent() || std::any_of(fileSetArgs.begin(), fileSetArgs.end(), [](const cmInstallCommandFileSetArguments& fileSetArg) - -> bool { return fileSetArg.HasNamelinkComponent(); })) { + -> bool { return fileSetArg.HasNamelinkComponent(); }) || + cxxModuleBmiArgs.HasNamelinkComponent()) { status.SetError( "TARGETS given NAMELINK_COMPONENT option not in LIBRARY group. " "The NAMELINK_COMPONENT option may be specified only following " @@ -583,7 +607,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args, !publicHeaderArgs.GetType().empty() || !resourceArgs.GetType().empty() || std::any_of(fileSetArgs.begin(), fileSetArgs.end(), [](const cmInstallCommandFileSetArguments& fileSetArg) - -> bool { return !fileSetArg.GetType().empty(); })) { + -> bool { return !fileSetArg.GetType().empty(); }) || + !cxxModuleBmiArgs.GetType().empty()) { status.SetError( "TARGETS given TYPE option. The TYPE option may only be specified in " " install(FILES) and install(DIRECTORIES)."); @@ -597,7 +622,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, } cmInstallRuntimeDependencySet* runtimeDependencySet = nullptr; - if (withRuntimeDependencies) { + if (runtimeDependenciesArgVector) { if (!runtimeDependencySetArg.empty()) { status.SetError("TARGETS cannot have both RUNTIME_DEPENDENCIES and " "RUNTIME_DEPENDENCY_SET."); @@ -706,6 +731,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, bool installsPublicHeader = false; bool installsResource = false; std::vector<bool> installsFileSet(fileSetArgs.size(), false); + bool installsCxxModuleBmi = false; // Generate install script code to install the given targets. for (cmTarget* ti : targets) { @@ -722,6 +748,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, std::unique_ptr<cmInstallFilesGenerator> publicHeaderGenerator; std::unique_ptr<cmInstallFilesGenerator> resourceGenerator; std::vector<std::unique_ptr<cmInstallFileSetGenerator>> fileSetGenerators; + std::unique_ptr<cmInstallCxxModuleBmiGenerator> cxxModuleBmiGenerator; // Avoid selecting default destinations for PUBLIC_HEADER and // PRIVATE_HEADER if any artifacts are specified. @@ -760,6 +787,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, for (auto const& gen : fileSetGenerators) { te->FileSetGenerators[gen->GetFileSet()] = gen.get(); } + te->CxxModuleBmiGenerator = cxxModuleBmiGenerator.get(); target.AddInstallIncludeDirectories( *te, cmMakeRange(includesArgs.GetIncludeDirs())); te->NamelinkOnly = namelinkOnly; @@ -1105,6 +1133,19 @@ bool HandleTargetsMode(std::vector<std::string> const& args, } } + if (supportCxx20FileSetTypes && + !cxxModuleBmiArgs.GetDestination().empty()) { + cxxModuleBmiGenerator = cm::make_unique<cmInstallCxxModuleBmiGenerator>( + target.GetName(), + helper.GetCxxModulesBmiDestination(&cxxModuleBmiArgs), + cxxModuleBmiArgs.GetPermissions(), + cxxModuleBmiArgs.GetConfigurations(), cxxModuleBmiArgs.GetComponent(), + cmInstallGenerator::SelectMessageLevel(target.GetMakefile()), + cxxModuleBmiArgs.GetExcludeFromAll(), cxxModuleBmiArgs.GetOptional(), + helper.Makefile->GetBacktrace()); + target.SetHaveInstallRule(true); + } + // Add this install rule to an export if one was specified. if (!addTargetExport()) { return false; @@ -1121,6 +1162,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, installsPrivateHeader = installsPrivateHeader || privateHeaderGenerator; installsPublicHeader = installsPublicHeader || publicHeaderGenerator; installsResource = installsResource || resourceGenerator; + installsCxxModuleBmi = installsCxxModuleBmi || cxxModuleBmiGenerator; helper.Makefile->AddInstallGenerator(std::move(archiveGenerator)); helper.Makefile->AddInstallGenerator(std::move(libraryGenerator)); @@ -1135,9 +1177,10 @@ bool HandleTargetsMode(std::vector<std::string> const& args, for (auto& gen : fileSetGenerators) { helper.Makefile->AddInstallGenerator(std::move(gen)); } + helper.Makefile->AddInstallGenerator(std::move(cxxModuleBmiGenerator)); } - if (withRuntimeDependencies && !runtimeDependencySet->Empty()) { + if (runtimeDependenciesArgVector && !runtimeDependencySet->Empty()) { AddInstallRuntimeDependenciesGenerator( helper, runtimeDependencySet, runtimeArgs, libraryArgs, frameworkArgs, std::move(runtimeDependenciesArgs), installsRuntime, installsLibrary, @@ -1192,6 +1235,10 @@ bool HandleTargetsMode(std::vector<std::string> const& args, fileSetArgs[i].GetComponent()); } } + if (installsCxxModuleBmi) { + helper.Makefile->GetGlobalGenerator()->AddInstallComponent( + cxxModuleBmiArgs.GetComponent()); + } return true; } @@ -1206,10 +1253,10 @@ bool HandleImportedRuntimeArtifactsMode(std::vector<std::string> const& args, struct ArgVectors { - std::vector<std::string> Library; - std::vector<std::string> Runtime; - std::vector<std::string> Framework; - std::vector<std::string> Bundle; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Library; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Runtime; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Framework; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Bundle; }; static auto const argHelper = cmArgumentParser<ArgVectors>{} @@ -1223,7 +1270,7 @@ bool HandleImportedRuntimeArtifactsMode(std::vector<std::string> const& args, // now parse the generic args (i.e. the ones not specialized on LIBRARY, // RUNTIME etc. (see above) - std::vector<std::string> targetList; + ArgumentParser::MaybeEmpty<std::vector<std::string>> targetList; std::string runtimeDependencySetArg; std::vector<std::string> unknownArgs; cmInstallCommandArguments genericArgs(helper.DefaultComponentName); @@ -1464,7 +1511,7 @@ bool HandleFilesMode(std::vector<std::string> const& args, // This is the FILES mode. bool programs = (args[0] == "PROGRAMS"); cmInstallCommandArguments ica(helper.DefaultComponentName); - std::vector<std::string> files; + ArgumentParser::MaybeEmpty<std::vector<std::string>> files; ica.Bind(programs ? "PROGRAMS"_s : "FILES"_s, files); std::vector<std::string> unknownArgs; ica.Parse(args, &unknownArgs); @@ -1950,7 +1997,7 @@ bool HandleExportAndroidMKMode(std::vector<std::string> const& args, cm::make_unique<cmInstallExportGenerator>( &exportSet, ica.GetDestination(), ica.GetPermissions(), ica.GetConfigurations(), ica.GetComponent(), message, - ica.GetExcludeFromAll(), fname, name_space, exportOld, true, + ica.GetExcludeFromAll(), fname, name_space, "", exportOld, true, helper.Makefile->GetBacktrace())); return true; @@ -1973,12 +2020,19 @@ bool HandleExportMode(std::vector<std::string> const& args, std::string name_space; bool exportOld = false; std::string filename; + std::string cxx_modules_directory; ica.Bind("EXPORT"_s, exp); ica.Bind("NAMESPACE"_s, name_space); ica.Bind("EXPORT_LINK_INTERFACE_LIBRARIES"_s, exportOld); ica.Bind("FILE"_s, filename); + bool const supportCxx20FileSetTypes = cmExperimental::HasSupportEnabled( + *helper.Makefile, cmExperimental::Feature::CxxModuleCMakeApi); + if (supportCxx20FileSetTypes) { + ica.Bind("CXX_MODULES_DIRECTORY"_s, cxx_modules_directory); + } + std::vector<std::string> unknownArgs; ica.Parse(args, &unknownArgs); @@ -2064,8 +2118,8 @@ bool HandleExportMode(std::vector<std::string> const& args, cm::make_unique<cmInstallExportGenerator>( &exportSet, ica.GetDestination(), ica.GetPermissions(), ica.GetConfigurations(), ica.GetComponent(), message, - ica.GetExcludeFromAll(), fname, name_space, exportOld, false, - helper.Makefile->GetBacktrace())); + ica.GetExcludeFromAll(), fname, name_space, cxx_modules_directory, + exportOld, false, helper.Makefile->GetBacktrace())); return true; } @@ -2088,9 +2142,9 @@ bool HandleRuntimeDependencySetMode(std::vector<std::string> const& args, struct ArgVectors { - std::vector<std::string> Library; - std::vector<std::string> Runtime; - std::vector<std::string> Framework; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Library; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Runtime; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Framework; }; static auto const argHelper = cmArgumentParser<ArgVectors>{} @@ -2106,11 +2160,9 @@ bool HandleRuntimeDependencySetMode(std::vector<std::string> const& args, // These generic args also contain the runtime dependency set std::string runtimeDependencySetArg; std::vector<std::string> runtimeDependencyArgVector; - std::vector<std::string> parsedArgs; cmInstallCommandArguments genericArgs(helper.DefaultComponentName); genericArgs.Bind("RUNTIME_DEPENDENCY_SET"_s, runtimeDependencySetArg); - genericArgs.Parse(genericArgVector, &runtimeDependencyArgVector, nullptr, - &parsedArgs); + genericArgs.Parse(genericArgVector, &runtimeDependencyArgVector); bool success = genericArgs.Finalize(); cmInstallCommandArguments libraryArgs(helper.DefaultComponentName); @@ -2280,6 +2332,15 @@ std::string Helper::GetLibraryDestination( return this->GetDestination(args, "CMAKE_INSTALL_LIBDIR", "lib"); } +std::string Helper::GetCxxModulesBmiDestination( + const cmInstallCommandArguments* args) const +{ + if (args) { + return args->GetDestination(); + } + return {}; +} + std::string Helper::GetIncludeDestination( const cmInstallCommandArguments* args) const { diff --git a/Source/cmInstallCommandArguments.h b/Source/cmInstallCommandArguments.h index 79bd945..6e46aac 100644 --- a/Source/cmInstallCommandArguments.h +++ b/Source/cmInstallCommandArguments.h @@ -8,6 +8,7 @@ #include <vector> #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" class cmInstallCommandArguments : public cmArgumentParser<void> { @@ -44,8 +45,8 @@ private: std::string NamelinkComponent; bool ExcludeFromAll = false; std::string Rename; - std::vector<std::string> Permissions; - std::vector<std::string> Configurations; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Permissions; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Configurations; bool Optional = false; bool NamelinkOnly = false; bool NamelinkSkip = false; diff --git a/Source/cmInstallCxxModuleBmiGenerator.cxx b/Source/cmInstallCxxModuleBmiGenerator.cxx new file mode 100644 index 0000000..1ef1eaa --- /dev/null +++ b/Source/cmInstallCxxModuleBmiGenerator.cxx @@ -0,0 +1,75 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmInstallCxxModuleBmiGenerator.h" + +#include <ostream> +#include <utility> + +#include "cmGeneratorExpression.h" +#include "cmGeneratorTarget.h" +#include "cmGlobalGenerator.h" +#include "cmListFileCache.h" +#include "cmLocalGenerator.h" +#include "cmOutputConverter.h" +#include "cmStringAlgorithms.h" + +cmInstallCxxModuleBmiGenerator::cmInstallCxxModuleBmiGenerator( + std::string target, std::string const& dest, std::string file_permissions, + std::vector<std::string> const& configurations, std::string const& component, + MessageLevel message, bool exclude_from_all, bool optional, + cmListFileBacktrace backtrace) + : cmInstallGenerator(dest, configurations, component, message, + exclude_from_all, false, std::move(backtrace)) + , TargetName(std::move(target)) + , FilePermissions(std::move(file_permissions)) + , Optional(optional) +{ + this->ActionsPerConfig = true; +} + +cmInstallCxxModuleBmiGenerator::~cmInstallCxxModuleBmiGenerator() = default; + +bool cmInstallCxxModuleBmiGenerator::Compute(cmLocalGenerator* lg) +{ + this->LocalGenerator = lg; + + this->Target = lg->FindLocalNonAliasGeneratorTarget(this->TargetName); + if (!this->Target) { + // If no local target has been found, find it in the global scope. + this->Target = + lg->GetGlobalGenerator()->FindGeneratorTarget(this->TargetName); + } + + return true; +} + +std::string cmInstallCxxModuleBmiGenerator::GetScriptLocation( + std::string const& config) const +{ + char const* config_name = config.c_str(); + if (config.empty()) { + config_name = "noconfig"; + } + return cmStrCat(this->Target->GetSupportDirectory(), + "/install-cxx-module-bmi-", config_name, ".cmake"); +} + +std::string cmInstallCxxModuleBmiGenerator::GetDestination( + std::string const& config) const +{ + return cmGeneratorExpression::Evaluate(this->Destination, + this->LocalGenerator, config); +} + +void cmInstallCxxModuleBmiGenerator::GenerateScriptForConfig( + std::ostream& os, const std::string& config, Indent indent) +{ + auto const& loc = this->GetScriptLocation(config); + if (loc.empty()) { + return; + } + os << indent << "include(\"" + << cmOutputConverter::EscapeForCMake( + loc, cmOutputConverter::WrapQuotes::NoWrap) + << "\" OPTIONAL)\n"; +} diff --git a/Source/cmInstallCxxModuleBmiGenerator.h b/Source/cmInstallCxxModuleBmiGenerator.h new file mode 100644 index 0000000..21edb2e --- /dev/null +++ b/Source/cmInstallCxxModuleBmiGenerator.h @@ -0,0 +1,52 @@ +/* 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 <iosfwd> +#include <string> +#include <vector> + +#include "cmInstallGenerator.h" +#include "cmScriptGenerator.h" + +class cmGeneratorTarget; +class cmListFileBacktrace; +class cmLocalGenerator; + +/** \class cmInstallCxxModuleBmiGenerator + * \brief Generate C++ module BMI installation rules. + */ +class cmInstallCxxModuleBmiGenerator : public cmInstallGenerator +{ +public: + cmInstallCxxModuleBmiGenerator( + std::string target, std::string const& dest, std::string file_permissions, + std::vector<std::string> const& configurations, + std::string const& component, MessageLevel message, bool exclude_from_all, + bool optional, cmListFileBacktrace backtrace); + ~cmInstallCxxModuleBmiGenerator() override; + + bool Compute(cmLocalGenerator* lg) override; + + std::string const& GetFilePermissions() const + { + return this->FilePermissions; + } + std::string GetDestination(std::string const& config) const; + std::string GetScriptLocation(std::string const& config) const; + cmGeneratorTarget const* GetTarget() const { return this->Target; } + bool GetOptional() const { return this->Optional; } + MessageLevel GetMessageLevel() const { return this->Message; } + +protected: + void GenerateScriptForConfig(std::ostream& os, const std::string& config, + Indent indent) override; + + std::string const TargetName; + cmGeneratorTarget const* Target = nullptr; + cmLocalGenerator* LocalGenerator = nullptr; + std::string const FilePermissions; + bool const Optional; +}; diff --git a/Source/cmInstallExportGenerator.cxx b/Source/cmInstallExportGenerator.cxx index b80437d..1d81b0b 100644 --- a/Source/cmInstallExportGenerator.cxx +++ b/Source/cmInstallExportGenerator.cxx @@ -23,7 +23,8 @@ cmInstallExportGenerator::cmInstallExportGenerator( cmExportSet* exportSet, std::string const& destination, std::string file_permissions, std::vector<std::string> const& configurations, std::string const& component, MessageLevel message, bool exclude_from_all, - std::string filename, std::string name_space, bool exportOld, bool android, + std::string filename, std::string name_space, + std::string cxx_modules_directory, bool exportOld, bool android, cmListFileBacktrace backtrace) : cmInstallGenerator(destination, configurations, component, message, exclude_from_all, false, std::move(backtrace)) @@ -31,6 +32,7 @@ cmInstallExportGenerator::cmInstallExportGenerator( , FilePermissions(std::move(file_permissions)) , FileName(std::move(filename)) , Namespace(std::move(name_space)) + , CxxModulesDirectory(std::move(cxx_modules_directory)) , ExportOld(exportOld) { if (android) { @@ -141,6 +143,75 @@ void cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os, os << indent << "endif()\n"; files.clear(); } + + // Now create a configuration-specific install rule for the C++ module import + // property file of each configuration. + auto cxx_module_dest = + cmStrCat(this->Destination, '/', this->CxxModulesDirectory); + std::string config_file_example; + for (auto const& i : this->EFGen->GetConfigCxxModuleFiles()) { + config_file_example = i.second; + break; + } + if (!config_file_example.empty()) { + // Remove old per-configuration export files if the main changes. + std::string installedDir = cmStrCat( + "$ENV{DESTDIR}", ConvertToAbsoluteDestination(cxx_module_dest), '/'); + std::string installedFile = cmStrCat(installedDir, "/cxx-modules.cmake"); + std::string toInstallFile = + cmStrCat(cmSystemTools::GetFilenamePath(config_file_example), + "/cxx-modules.cmake"); + os << indent << "if(EXISTS \"" << installedFile << "\")\n"; + Indent indentN = indent.Next(); + Indent indentNN = indentN.Next(); + Indent indentNNN = indentNN.Next(); + /* clang-format off */ + os << indentN << "file(DIFFERENT _cmake_export_file_changed FILES\n" + << indentN << " \"" << installedFile << "\"\n" + << indentN << " \"" << toInstallFile << "\")\n"; + os << indentN << "if(_cmake_export_file_changed)\n"; + os << indentNN << "file(GLOB _cmake_old_config_files \"" << installedDir + << this->EFGen->GetConfigImportFileGlob() << "\")\n"; + os << indentNN << "if(_cmake_old_config_files)\n"; + os << indentNNN << "string(REPLACE \";\" \", \" _cmake_old_config_files_text \"${_cmake_old_config_files}\")\n"; + os << indentNNN << R"(message(STATUS "Old C++ module export file \")" << installedFile + << "\\\" will be replaced. Removing files [${_cmake_old_config_files_text}].\")\n"; + os << indentNNN << "unset(_cmake_old_config_files_text)\n"; + os << indentNNN << "file(REMOVE ${_cmake_old_config_files})\n"; + os << indentNN << "endif()\n"; + os << indentNN << "unset(_cmake_old_config_files)\n"; + os << indentN << "endif()\n"; + os << indentN << "unset(_cmake_export_file_changed)\n"; + os << indent << "endif()\n"; + /* clang-format on */ + + // All of these files are siblings; get its location to know where the + // "anchor" file is. + files.push_back(toInstallFile); + this->AddInstallRule(os, cxx_module_dest, cmInstallType_FILES, files, + false, this->FilePermissions.c_str(), nullptr, + nullptr, nullptr, indent); + files.clear(); + } + for (auto const& i : this->EFGen->GetConfigCxxModuleFiles()) { + files.push_back(i.second); + std::string config_test = this->CreateConfigTest(i.first); + os << indent << "if(" << config_test << ")\n"; + this->AddInstallRule(os, cxx_module_dest, cmInstallType_FILES, files, + false, this->FilePermissions.c_str(), nullptr, + nullptr, nullptr, indent.Next()); + os << indent << "endif()\n"; + files.clear(); + } + for (auto const& i : this->EFGen->GetConfigCxxModuleTargetFiles()) { + std::string config_test = this->CreateConfigTest(i.first); + os << indent << "if(" << config_test << ")\n"; + this->AddInstallRule(os, cxx_module_dest, cmInstallType_FILES, i.second, + false, this->FilePermissions.c_str(), nullptr, + nullptr, nullptr, indent.Next()); + os << indent << "endif()\n"; + files.clear(); + } } void cmInstallExportGenerator::GenerateScriptActions(std::ostream& os, diff --git a/Source/cmInstallExportGenerator.h b/Source/cmInstallExportGenerator.h index 02fe1fa..346ca67 100644 --- a/Source/cmInstallExportGenerator.h +++ b/Source/cmInstallExportGenerator.h @@ -28,7 +28,8 @@ public: const std::vector<std::string>& configurations, std::string const& component, MessageLevel message, bool exclude_from_all, std::string filename, - std::string name_space, bool exportOld, + std::string name_space, + std::string cxx_modules_directory, bool exportOld, bool android, cmListFileBacktrace backtrace); cmInstallExportGenerator(const cmInstallExportGenerator&) = delete; ~cmInstallExportGenerator() override; @@ -50,6 +51,10 @@ public: std::string GetDestinationFile() const; std::string GetFileName() const { return this->FileName; } std::string GetTempDir() const; + std::string GetCxxModuleDirectory() const + { + return this->CxxModulesDirectory; + } protected: void GenerateScript(std::ostream& os) override; @@ -64,6 +69,7 @@ protected: std::string const FilePermissions; std::string const FileName; std::string const Namespace; + std::string const CxxModulesDirectory; bool const ExportOld; cmLocalGenerator* LocalGenerator = nullptr; diff --git a/Source/cmLinkLineDeviceComputer.cxx b/Source/cmLinkLineDeviceComputer.cxx index 43f1b8e..b06dc3d 100644 --- a/Source/cmLinkLineDeviceComputer.cxx +++ b/Source/cmLinkLineDeviceComputer.cxx @@ -57,7 +57,6 @@ bool cmLinkLineDeviceComputer::ComputeRequiresDeviceLinking( // For this we only consider targets using ItemVector = cmComputeLinkInformation::ItemVector; ItemVector const& items = cli.GetItems(); - std::string config = cli.GetConfig(); return std::any_of( items.begin(), items.end(), [](cmComputeLinkInformation::Item const& item) -> bool { @@ -69,6 +68,26 @@ bool cmLinkLineDeviceComputer::ComputeRequiresDeviceLinking( }); } +bool cmLinkLineDeviceComputer::ComputeRequiresDeviceLinkingIPOFlag( + cmComputeLinkInformation& cli) +{ + // Determine if this item might requires device linking. + // For this we only consider targets + using ItemVector = cmComputeLinkInformation::ItemVector; + ItemVector const& items = cli.GetItems(); + std::string config = cli.GetConfig(); + return std::any_of( + items.begin(), items.end(), + [config](cmComputeLinkInformation::Item const& item) -> bool { + return item.Target && + item.Target->GetType() == cmStateEnums::STATIC_LIBRARY && + // this dependency requires us to device link it + !item.Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS") && + item.Target->GetPropertyAsBool("CUDA_SEPARABLE_COMPILATION") && + item.Target->IsIPOEnabled("CUDA", config); + }); +} + void cmLinkLineDeviceComputer::ComputeLinkLibraries( cmComputeLinkInformation& cli, std::string const& stdLibString, std::vector<BT<std::string>>& linkLibraries) diff --git a/Source/cmLinkLineDeviceComputer.h b/Source/cmLinkLineDeviceComputer.h index dee625b..0916307 100644 --- a/Source/cmLinkLineDeviceComputer.h +++ b/Source/cmLinkLineDeviceComputer.h @@ -30,6 +30,7 @@ public: delete; bool ComputeRequiresDeviceLinking(cmComputeLinkInformation& cli); + bool ComputeRequiresDeviceLinkingIPOFlag(cmComputeLinkInformation& cli); void ComputeLinkLibraries( cmComputeLinkInformation& cli, std::string const& stdLibString, diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx index 91157cb..6270c82 100644 --- a/Source/cmListFileCache.cxx +++ b/Source/cmListFileCache.cxx @@ -347,6 +347,7 @@ enum class NestingStateEnum Foreach, Function, Macro, + Block }; struct NestingState @@ -434,6 +435,16 @@ cm::optional<cmListFileContext> cmListFileParser::CheckNesting() const return cmListFileContext::FromListFileFunction(func, this->FileName); } stack.pop_back(); + } else if (name == "block") { + stack.push_back({ + NestingStateEnum::Block, + cmListFileContext::FromListFileFunction(func, this->FileName), + }); + } else if (name == "endblock") { + if (!TopIs(stack, NestingStateEnum::Block)) { + return cmListFileContext::FromListFileFunction(func, this->FileName); + } + stack.pop_back(); } } diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 67c8bf2..b44d2a0 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -15,6 +15,7 @@ #include <vector> #include <cm/memory> +#include <cm/optional> #include <cm/string_view> #include <cmext/algorithm> #include <cmext/string_view> @@ -36,6 +37,7 @@ #include "cmInstallScriptGenerator.h" #include "cmInstallTargetGenerator.h" #include "cmLinkLineComputer.h" +#include "cmLinkLineDeviceComputer.h" #include "cmMakefile.h" #include "cmRange.h" #include "cmRulePlaceholderExpander.h" @@ -1381,7 +1383,7 @@ std::vector<BT<std::string>> cmLocalGenerator::GetStaticLibraryFlags( } void cmLocalGenerator::GetDeviceLinkFlags( - cmLinkLineComputer& linkLineComputer, const std::string& config, + cmLinkLineDeviceComputer& linkLineComputer, const std::string& config, std::string& linkLibs, std::string& linkFlags, std::string& frameworkPath, std::string& linkPath, cmGeneratorTarget* target) { @@ -1389,6 +1391,18 @@ void cmLocalGenerator::GetDeviceLinkFlags( cmComputeLinkInformation* pcli = target->GetLinkInformation(config); + auto linklang = linkLineComputer.GetLinkerLanguage(target, config); + auto ipoEnabled = target->IsIPOEnabled(linklang, config); + if (!ipoEnabled) { + ipoEnabled = linkLineComputer.ComputeRequiresDeviceLinkingIPOFlag(*pcli); + } + if (ipoEnabled) { + if (cmValue cudaIPOFlags = this->Makefile->GetDefinition( + "CMAKE_CUDA_DEVICE_LINK_OPTIONS_IPO")) { + linkFlags += cudaIPOFlags; + } + } + if (pcli) { // Compute the required device link libraries when // resolving gpu lang device symbols @@ -1396,6 +1410,8 @@ void cmLocalGenerator::GetDeviceLinkFlags( linkPath); } + // iterate link deps and see if any of them need IPO + std::vector<std::string> linkOpts; target->GetLinkOptions(linkOpts, config, "CUDA"); // LINK_OPTIONS are escaped. @@ -1590,7 +1606,8 @@ std::vector<BT<std::string>> cmLocalGenerator::GetTargetCompileFlags( cmMakefile* mf = this->GetMakefile(); // Add language-specific flags. - this->AddLanguageFlags(compileFlags, target, lang, config); + this->AddLanguageFlags(compileFlags, target, cmBuildStep::Compile, lang, + config); if (target->IsIPOEnabled(lang, config)) { this->AppendFeatureOptions(compileFlags, lang, "IPO"); @@ -1903,6 +1920,7 @@ void cmLocalGenerator::AddArchitectureFlags(std::string& flags, void cmLocalGenerator::AddLanguageFlags(std::string& flags, cmGeneratorTarget const* target, + cmBuildStep compileOrLink, const std::string& lang, const std::string& config) { @@ -1926,7 +1944,7 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags, } } } else if (lang == "CUDA") { - target->AddCUDAArchitectureFlags(flags); + target->AddCUDAArchitectureFlags(compileOrLink, config, flags); target->AddCUDAToolkitFlags(flags); } else if (lang == "ISPC") { target->AddISPCTargetFlags(flags); @@ -2038,7 +2056,7 @@ void cmLocalGenerator::AddLanguageFlagsForLinking( this->AddCompilerRequirementFlag(flags, target, lang, config); } - this->AddLanguageFlags(flags, target, lang, config); + this->AddLanguageFlags(flags, target, cmBuildStep::Link, lang, config); if (target->IsIPOEnabled(lang, config)) { this->AppendFeatureOptions(flags, lang, "IPO"); @@ -2574,7 +2592,9 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target) if (pchSource.empty() || pchHeader.empty()) { if (this->GetGlobalGenerator()->IsXcode() && !pchLangSet.empty()) { for (auto* sf : sources) { - if (pchLangSet.find(sf->GetLanguage()) == pchLangSet.end()) { + const auto sourceLanguage = sf->GetLanguage(); + if (!sourceLanguage.empty() && + pchLangSet.find(sourceLanguage) == pchLangSet.end()) { sf->SetProperty("SKIP_PRECOMPILE_HEADERS", "ON"); } } @@ -2667,7 +2687,7 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target) cmStrCat(linkerProperty, configUpper), cmStrCat(" ", this->ConvertToOutputFormat(pchSourceObj, SHELL)), - true); + cm::nullopt, true); } else if (reuseTarget->GetType() == cmStateEnums::OBJECT_LIBRARY) { // FIXME: This can propagate more than one level, unlike diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index 7cae1fc..0529431 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -35,6 +35,7 @@ class cmGeneratorTarget; class cmGlobalGenerator; class cmImplicitDependsList; class cmLinkLineComputer; +class cmLinkLineDeviceComputer; class cmMakefile; class cmRulePlaceholderExpander; class cmSourceFile; @@ -59,6 +60,13 @@ enum class cmDependencyScannerKind Compiler }; +/** What to compute language flags for */ +enum class cmBuildStep +{ + Compile, + Link +}; + /** Target and source file which have a specific output. */ struct cmSourcesWithOutput { @@ -143,7 +151,8 @@ public: const std::string& filterArch = std::string()); void AddLanguageFlags(std::string& flags, cmGeneratorTarget const* target, - const std::string& lang, const std::string& config); + cmBuildStep compileOrLink, const std::string& lang, + const std::string& config); void AddLanguageFlagsForLinking(std::string& flags, cmGeneratorTarget const* target, const std::string& lang, @@ -476,7 +485,7 @@ public: /** Fill out these strings for the given target. Libraries to link, * flags, and linkflags. */ - void GetDeviceLinkFlags(cmLinkLineComputer& linkLineComputer, + void GetDeviceLinkFlags(cmLinkLineDeviceComputer& linkLineComputer, const std::string& config, std::string& linkLibs, std::string& linkFlags, std::string& frameworkPath, std::string& linkPath, cmGeneratorTarget* target); diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index e125470..de1d3cd 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -1412,13 +1412,16 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies( if (needRescanDependInfo || needRescanDirInfo || needRescanDependencies) { // The dependencies must be regenerated. - std::string targetName = cmSystemTools::GetFilenameName(targetDir); - targetName = targetName.substr(0, targetName.length() - 4); - std::string message = - cmStrCat("Scanning dependencies of target ", targetName); - cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundMagenta | - cmsysTerminal_Color_ForegroundBold, - message.c_str(), true, color); + if (verbose) { + std::string targetName = cmSystemTools::GetFilenameName(targetDir); + targetName = targetName.substr(0, targetName.length() - 4); + std::string message = + cmStrCat("Scanning dependencies of target ", targetName); + cmSystemTools::MakefileColorEcho( + cmsysTerminal_Color_ForegroundMagenta | + cmsysTerminal_Color_ForegroundBold, + message.c_str(), true, color); + } status = this->ScanDependencies(targetDir, dependFile, internalDependFile, validDependencies); @@ -1447,13 +1450,19 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies( this->GetBinaryDirectory()) : std::function<bool(const std::string&)>())) { // regenerate dependencies files - std::string targetName = - cmCMakePath(targetDir).GetFileName().RemoveExtension().GenericString(); - auto message = cmStrCat( - "Consolidate compiler generated dependencies of target ", targetName); - cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundMagenta | - cmsysTerminal_Color_ForegroundBold, - message.c_str(), true, color); + if (verbose) { + std::string targetName = cmCMakePath(targetDir) + .GetFileName() + .RemoveExtension() + .GenericString(); + auto message = + cmStrCat("Consolidate compiler generated dependencies of target ", + targetName); + cmSystemTools::MakefileColorEcho( + cmsysTerminal_Color_ForegroundMagenta | + cmsysTerminal_Color_ForegroundBold, + message.c_str(), true, color); + } // Open the make depends file. This should be copy-if-different // because the make tool may try to reload it needlessly otherwise. diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index f65add1..0451d96 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -680,7 +680,8 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( langForClCompile = linkLanguage; if (langForClCompile == "C" || langForClCompile == "CXX" || langForClCompile == "Fortran") { - this->AddLanguageFlags(flags, target, langForClCompile, configName); + this->AddLanguageFlags(flags, target, cmBuildStep::Compile, + langForClCompile, configName); } // set the correct language if (linkLanguage == "C") { diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 628eb1d..b228e2b 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -149,6 +149,29 @@ void cmMakefile::IssueMessage(MessageType t, std::string const& text) const this->GetCMakeInstance()->IssueMessage(t, text, this->Backtrace); } +Message::LogLevel cmMakefile::GetCurrentLogLevel() const +{ + const cmake* cmakeInstance = this->GetCMakeInstance(); + + const Message::LogLevel logLevelCliOrDefault = cmakeInstance->GetLogLevel(); + assert("Expected a valid log level here" && + logLevelCliOrDefault != Message::LogLevel::LOG_UNDEFINED); + + Message::LogLevel result = logLevelCliOrDefault; + + // If the log-level was set via the command line option, it takes precedence + // over the CMAKE_MESSAGE_LOG_LEVEL variable. + if (!cmakeInstance->WasLogLevelSetViaCLI()) { + const Message::LogLevel logLevelFromVar = cmake::StringToLogLevel( + this->GetSafeDefinition("CMAKE_MESSAGE_LOG_LEVEL")); + if (logLevelFromVar != Message::LogLevel::LOG_UNDEFINED) { + result = logLevelFromVar; + } + } + + return result; +} + bool cmMakefile::CheckCMP0037(std::string const& targetName, cmStateEnums::TargetType targetType) const { @@ -3456,7 +3479,7 @@ void cmMakefile::AddTargetObject(std::string const& tgtName, #endif } -void cmMakefile::EnableLanguage(std::vector<std::string> const& lang, +void cmMakefile::EnableLanguage(std::vector<std::string> const& languages, bool optional) { if (this->DeferRunning) { @@ -3468,24 +3491,48 @@ void cmMakefile::EnableLanguage(std::vector<std::string> const& lang, if (const char* def = this->GetGlobalGenerator()->GetCMakeCFGIntDir()) { this->AddDefinition("CMAKE_CFG_INTDIR", def); } + + std::vector<std::string> unique_languages; + { + std::vector<std::string> duplicate_languages; + for (std::string const& language : languages) { + if (!cm::contains(unique_languages, language)) { + unique_languages.push_back(language); + } else if (!cm::contains(duplicate_languages, language)) { + duplicate_languages.push_back(language); + } + } + if (!duplicate_languages.empty()) { + auto quantity = duplicate_languages.size() == 1 ? std::string(" has") + : std::string("s have"); + this->IssueMessage(MessageType::AUTHOR_WARNING, + "Languages to be enabled may not be specified more " + "than once at the same time. The following language" + + quantity + " been specified multiple times: " + + cmJoin(duplicate_languages, ", ")); + } + } + // If RC is explicitly listed we need to do it after other languages. // On some platforms we enable RC implicitly while enabling others. // Do not let that look like recursive enable_language(RC). - std::vector<std::string> langs; - std::vector<std::string> langsRC; - langs.reserve(lang.size()); - for (std::string const& i : lang) { - if (i == "RC") { - langsRC.push_back(i); + std::vector<std::string> languages_without_RC; + std::vector<std::string> languages_for_RC; + languages_without_RC.reserve(unique_languages.size()); + for (std::string const& language : unique_languages) { + if (language == "RC") { + languages_for_RC.push_back(language); } else { - langs.push_back(i); + languages_without_RC.push_back(language); } } - if (!langs.empty()) { - this->GetGlobalGenerator()->EnableLanguage(langs, this, optional); + if (!languages_without_RC.empty()) { + this->GetGlobalGenerator()->EnableLanguage(languages_without_RC, this, + optional); } - if (!langsRC.empty()) { - this->GetGlobalGenerator()->EnableLanguage(langsRC, this, optional); + if (!languages_for_RC.empty()) { + this->GetGlobalGenerator()->EnableLanguage(languages_for_RC, this, + optional); } } @@ -4431,7 +4478,7 @@ bool cmMakefile::SetPolicy(cmPolicies::PolicyID id, } // Deprecate old policies. - if (status == cmPolicies::OLD && id <= cmPolicies::CMP0097 && + if (status == cmPolicies::OLD && id <= cmPolicies::CMP0102 && !(this->GetCMakeInstance()->GetIsInTryCompile() && ( // Policies set by cmCoreTryCompile::TryCompileCode. @@ -4496,6 +4543,19 @@ bool cmMakefile::SetPolicyVersion(std::string const& version_min, cmPolicies::WarnCompat::On); } +cmMakefile::VariablePushPop::VariablePushPop(cmMakefile* m) + : Makefile(m) +{ + this->Makefile->StateSnapshot = + this->Makefile->GetState()->CreateVariableScopeSnapshot( + this->Makefile->StateSnapshot); +} + +cmMakefile::VariablePushPop::~VariablePushPop() +{ + this->Makefile->PopSnapshot(); +} + bool cmMakefile::HasCMP0054AlreadyBeenReported( cmListFileContext const& context) const { diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index e7b9716..10432a8 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -376,6 +376,20 @@ public: }; friend class PolicyPushPop; + /** Helper class to push and pop variables scopes automatically. */ + class VariablePushPop + { + public: + VariablePushPop(cmMakefile* m); + ~VariablePushPop(); + + VariablePushPop(VariablePushPop const&) = delete; + VariablePushPop& operator=(VariablePushPop const&) = delete; + + private: + cmMakefile* Makefile; + }; + /** * Determine if the given context, name pair has already been reported * in context of CMP0054. @@ -862,6 +876,10 @@ public: void PushScope(); void PopScope(); void RaiseScope(const std::string& var, const char* value); + void RaiseScope(const std::string& var, cmValue value) + { + this->RaiseScope(var, value.GetCStr()); + } // push and pop loop scopes void PushLoopBlockBarrier(); @@ -925,6 +943,7 @@ public: }; void IssueMessage(MessageType t, std::string const& text) const; + Message::LogLevel GetCurrentLogLevel() const; /** Set whether or not to report a CMP0000 violation. */ void SetCheckCMP0000(bool b) { this->CheckCMP0000 = b; } diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx index 3849c6f..54f03b9 100644 --- a/Source/cmMakefileExecutableTargetGenerator.cxx +++ b/Source/cmMakefileExecutableTargetGenerator.cxx @@ -136,17 +136,11 @@ void cmMakefileExecutableTargetGenerator::WriteNvidiaDeviceExecutableRule( std::vector<std::string> depends; this->AppendLinkDepends(depends, linkLanguage); - // Build a list of compiler flags and linker flags. - std::string langFlags; - std::string linkFlags; - // Add language feature flags. + std::string langFlags; this->LocalGenerator->AddLanguageFlagsForLinking( langFlags, this->GeneratorTarget, linkLanguage, this->GetConfigName()); - // Add device-specific linker flags. - this->GetDeviceLinkFlags(linkFlags, linkLanguage); - // Construct a list of files associated with this executable that // may need to be cleaned. std::vector<std::string> exeCleanFiles; @@ -173,23 +167,32 @@ void cmMakefileExecutableTargetGenerator::WriteNvidiaDeviceExecutableRule( // Set path conversion for link script shells. this->LocalGenerator->SetLinkScriptShell(useLinkScript); - std::unique_ptr<cmLinkLineComputer> linkLineComputer( + std::unique_ptr<cmLinkLineDeviceComputer> linkLineComputer( new cmLinkLineDeviceComputer( this->LocalGenerator, this->LocalGenerator->GetStateSnapshot().GetDirectory())); linkLineComputer->SetForResponse(useResponseFileForLibs); linkLineComputer->SetRelink(relink); + // Create set of linking flags. + std::string linkFlags; + std::string ignored_; + this->LocalGenerator->GetDeviceLinkFlags( + *linkLineComputer, this->GetConfigName(), ignored_, linkFlags, ignored_, + ignored_, this->GeneratorTarget); + // Collect up flags to link in needed libraries. std::string linkLibs; - this->CreateLinkLibs(linkLineComputer.get(), linkLibs, - useResponseFileForLibs, depends); + this->CreateLinkLibs( + linkLineComputer.get(), linkLibs, useResponseFileForLibs, depends, + cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink); // Construct object file lists that may be needed to expand the // rule. std::string buildObjs; - this->CreateObjectLists(useLinkScript, false, useResponseFileForObjects, - buildObjs, depends, false); + this->CreateObjectLists( + useLinkScript, false, useResponseFileForObjects, buildObjs, depends, + false, cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink); cmRulePlaceholderExpander::RuleVariables vars; std::string objectDir = this->GeneratorTarget->GetSupportDirectory(); diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx index f30ec27..45ef8c8 100644 --- a/Source/cmMakefileLibraryTargetGenerator.cxx +++ b/Source/cmMakefileLibraryTargetGenerator.cxx @@ -287,10 +287,6 @@ void cmMakefileLibraryTargetGenerator::WriteNvidiaDeviceLibraryRules( this->LocalGenerator->AddLanguageFlagsForLinking( langFlags, this->GeneratorTarget, linkLanguage, this->GetConfigName()); - // Create set of linking flags. - std::string linkFlags; - this->GetDeviceLinkFlags(linkFlags, linkLanguage); - // Clean files associated with this library. std::set<std::string> libCleanFiles; libCleanFiles.insert( @@ -315,22 +311,31 @@ void cmMakefileLibraryTargetGenerator::WriteNvidiaDeviceLibraryRules( // Collect up flags to link in needed libraries. std::string linkLibs; - std::unique_ptr<cmLinkLineComputer> linkLineComputer( + std::unique_ptr<cmLinkLineDeviceComputer> linkLineComputer( new cmLinkLineDeviceComputer( this->LocalGenerator, this->LocalGenerator->GetStateSnapshot().GetDirectory())); linkLineComputer->SetForResponse(useResponseFileForLibs); linkLineComputer->SetRelink(relink); - this->CreateLinkLibs(linkLineComputer.get(), linkLibs, - useResponseFileForLibs, depends); + // Create set of linking flags. + std::string linkFlags; + std::string ignored_; + this->LocalGenerator->GetDeviceLinkFlags( + *linkLineComputer, this->GetConfigName(), ignored_, linkFlags, ignored_, + ignored_, this->GeneratorTarget); + + this->CreateLinkLibs( + linkLineComputer.get(), linkLibs, useResponseFileForLibs, depends, + cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink); // Construct object file lists that may be needed to expand the // rule. std::string buildObjs; - this->CreateObjectLists(useLinkScript, false, // useArchiveRules - useResponseFileForObjects, buildObjs, depends, - false); + this->CreateObjectLists( + useLinkScript, false, // useArchiveRules + useResponseFileForObjects, buildObjs, depends, false, + cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink); std::string objectDir = this->GeneratorTarget->GetSupportDirectory(); objectDir = this->LocalGenerator->ConvertToOutputFormat( diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index aec6577..1e1df79 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -21,6 +21,7 @@ #include "cmComputeLinkInformation.h" #include "cmCustomCommand.h" #include "cmCustomCommandGenerator.h" +#include "cmFileSet.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" @@ -46,6 +47,7 @@ #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmTarget.h" #include "cmValue.h" #include "cmake.h" @@ -107,7 +109,7 @@ std::unique_ptr<cmMakefileTargetGenerator> cmMakefileTargetGenerator::New( return result; } -std::string cmMakefileTargetGenerator::GetConfigName() +std::string cmMakefileTargetGenerator::GetConfigName() const { auto const& configNames = this->LocalGenerator->GetConfigNames(); assert(configNames.size() == 1); @@ -190,6 +192,16 @@ void cmMakefileTargetGenerator::CreateRuleFile() void cmMakefileTargetGenerator::WriteTargetBuildRules() { + this->GeneratorTarget->CheckCxxModuleStatus(this->GetConfigName()); + + if (this->GeneratorTarget->HaveCxx20ModuleSources()) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("The \"", this->GeneratorTarget->GetName(), + "\" target contains C++ module sources which are not supported " + "by the generator")); + } + // -- Write the custom commands for this target // Evaluates generator expressions and expands prop_value @@ -302,6 +314,40 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules() } } + std::map<std::string, std::string> file_set_map; + + auto const* tgt = this->GeneratorTarget->Target; + for (auto const& name : tgt->GetAllFileSetNames()) { + auto const* file_set = tgt->GetFileSet(name); + if (!file_set) { + this->Makefile->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", tgt->GetName(), + "\" is tracked to have file set \"", name, + "\", but it was not found.")); + continue; + } + + auto fileEntries = file_set->CompileFileEntries(); + auto directoryEntries = file_set->CompileDirectoryEntries(); + auto directories = file_set->EvaluateDirectoryEntries( + directoryEntries, this->LocalGenerator, this->GetConfigName(), + this->GeneratorTarget); + + std::map<std::string, std::vector<std::string>> files; + for (auto const& entry : fileEntries) { + file_set->EvaluateFileEntry(directories, files, entry, + this->LocalGenerator, this->GetConfigName(), + this->GeneratorTarget); + } + + for (auto const& it : files) { + for (auto const& filename : it.second) { + file_set_map[filename] = file_set->GetType(); + } + } + } + std::vector<cmSourceFile const*> objectSources; this->GeneratorTarget->GetObjectSources(objectSources, this->GetConfigName()); @@ -314,6 +360,25 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules() this->WriteObjectRuleFiles(*sf); } } + + for (cmSourceFile const* sf : objectSources) { + auto const& path = sf->GetFullPath(); + auto const it = file_set_map.find(path); + if (it != file_set_map.end()) { + auto const& file_set_type = it->second; + if (file_set_type == "CXX_MODULES"_s || + file_set_type == "CXX_MODULE_HEADER_UNITS"_s) { + if (sf->GetLanguage() != "CXX"_s) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat( + "Target \"", tgt->GetName(), "\" contains the source\n ", path, + "\nin a file set of type \"", file_set_type, + R"(" but the source is not classified as a "CXX" source.)")); + } + } + } + } } void cmMakefileTargetGenerator::WriteCommonCodeRules() @@ -2080,7 +2145,7 @@ bool cmMakefileTargetGenerator::CheckUseResponseFileForLibraries( } std::string cmMakefileTargetGenerator::CreateResponseFile( - const char* name, std::string const& options, + const std::string& name, std::string const& options, std::vector<std::string>& makefile_depends) { // FIXME: Find a better way to determine the response file encoding, @@ -2126,7 +2191,8 @@ cmMakefileTargetGenerator::CreateLinkLineComputer( void cmMakefileTargetGenerator::CreateLinkLibs( cmLinkLineComputer* linkLineComputer, std::string& linkLibs, - bool useResponseFile, std::vector<std::string>& makefile_depends) + bool useResponseFile, std::vector<std::string>& makefile_depends, + ResponseFlagFor responseMode) { std::string frameworkPath; std::string linkPath; @@ -2139,20 +2205,13 @@ void cmMakefileTargetGenerator::CreateLinkLibs( if (useResponseFile && linkLibs.find_first_not_of(' ') != std::string::npos) { // Lookup the response file reference flag. - std::string responseFlagVar = - cmStrCat("CMAKE_", - this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()), - "_RESPONSE_FILE_LINK_FLAG"); - std::string responseFlag; - if (cmValue p = this->Makefile->GetDefinition(responseFlagVar)) { - responseFlag = *p; - } else { - responseFlag = "@"; - } + std::string responseFlag = this->GetResponseFlag(responseMode); // Create this response file. + std::string responseFileName = + (responseMode == Link) ? "linkLibs.rsp" : "deviceLinkLibs.rsp"; std::string link_rsp = - this->CreateResponseFile("linklibs.rsp", linkLibs, makefile_depends); + this->CreateResponseFile(responseFileName, linkLibs, makefile_depends); // Reference the response file. linkLibs = cmStrCat(responseFlag, @@ -2164,7 +2223,7 @@ void cmMakefileTargetGenerator::CreateLinkLibs( void cmMakefileTargetGenerator::CreateObjectLists( bool useLinkScript, bool useArchiveRules, bool useResponseFile, std::string& buildObjs, std::vector<std::string>& makefile_depends, - bool useWatcomQuote) + bool useWatcomQuote, ResponseFlagFor responseMode) { std::string variableName; std::string variableNameExternal; @@ -2179,27 +2238,19 @@ void cmMakefileTargetGenerator::CreateObjectLists( this->WriteObjectsStrings(object_strings, responseFileLimit); // Lookup the response file reference flag. - std::string responseFlagVar = - cmStrCat("CMAKE_", - this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()), - "_RESPONSE_FILE_LINK_FLAG"); - std::string responseFlag; - if (cmValue p = this->Makefile->GetDefinition(responseFlagVar)) { - responseFlag = *p; - } else { - responseFlag = "@"; - } + std::string responseFlag = this->GetResponseFlag(responseMode); // Write a response file for each string. const char* sep = ""; for (unsigned int i = 0; i < object_strings.size(); ++i) { // Number the response files. - char rsp[32]; - snprintf(rsp, sizeof(rsp), "objects%u.rsp", i + 1); + std::string responseFileName = + (responseMode == Link) ? "objects" : "deviceObjects"; + responseFileName += std::to_string(i + 1); // Create this response file. - std::string objects_rsp = - this->CreateResponseFile(rsp, object_strings[i], makefile_depends); + std::string objects_rsp = this->CreateResponseFile( + responseFileName, object_strings[i], makefile_depends); // Separate from previous response file references. buildObjs += sep; @@ -2251,7 +2302,7 @@ void cmMakefileTargetGenerator::AddIncludeFlags(std::string& flags, } std::string name = cmStrCat("includes_", lang, ".rsp"); std::string arg = std::move(responseFlag) + - this->CreateResponseFile(name.c_str(), includeFlags, + this->CreateResponseFile(name, includeFlags, this->FlagFileDepends[lang]); this->LocalGenerator->AppendFlags(flags, arg); } else { @@ -2304,3 +2355,22 @@ void cmMakefileTargetGenerator::GenDefFile( fout << src->GetFullPath() << "\n"; } } + +std::string cmMakefileTargetGenerator::GetResponseFlag( + ResponseFlagFor mode) const +{ + std::string responseFlag = "@"; + std::string responseFlagVar; + + auto lang = this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()); + if (mode == cmMakefileTargetGenerator::ResponseFlagFor::Link) { + responseFlagVar = cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_LINK_FLAG"); + } else if (mode == cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink) { + responseFlagVar = "CMAKE_CUDA_RESPONSE_FILE_DEVICE_LINK_FLAG"; + } + + if (cmValue p = this->Makefile->GetDefinition(responseFlagVar)) { + responseFlag = *p; + } + return responseFlag; +} diff --git a/Source/cmMakefileTargetGenerator.h b/Source/cmMakefileTargetGenerator.h index cb804e0..dafa650 100644 --- a/Source/cmMakefileTargetGenerator.h +++ b/Source/cmMakefileTargetGenerator.h @@ -56,7 +56,7 @@ public: cmGeneratorTarget* GetGeneratorTarget() { return this->GeneratorTarget; } - std::string GetConfigName(); + std::string GetConfigName() const; protected: void GetDeviceLinkFlags(std::string& linkFlags, @@ -157,22 +157,31 @@ protected: /** Create a response file with the given set of options. Returns the relative path from the target build working directory to the response file name. */ - std::string CreateResponseFile(const char* name, std::string const& options, + std::string CreateResponseFile(const std::string& name, + std::string const& options, std::vector<std::string>& makefile_depends); bool CheckUseResponseFileForObjects(std::string const& l) const; bool CheckUseResponseFileForLibraries(std::string const& l) const; + enum ResponseFlagFor + { + Link, + DeviceLink + }; + /** Create list of flags for link libraries. */ void CreateLinkLibs(cmLinkLineComputer* linkLineComputer, std::string& linkLibs, bool useResponseFile, - std::vector<std::string>& makefile_depends); + std::vector<std::string>& makefile_depends, + ResponseFlagFor responseMode = ResponseFlagFor::Link); /** Create lists of object files for linking and cleaning. */ void CreateObjectLists(bool useLinkScript, bool useArchiveRules, bool useResponseFile, std::string& buildObjs, std::vector<std::string>& makefile_depends, - bool useWatcomQuote); + bool useWatcomQuote, + ResponseFlagFor responseMode = ResponseFlagFor::Link); /** Add commands for generate def files */ void GenDefFile(std::vector<std::string>& real_link_commands); @@ -180,6 +189,9 @@ protected: void AddIncludeFlags(std::string& flags, const std::string& lang, const std::string& config) override; + /** Return the response flag for the given configuration */ + std::string GetResponseFlag(ResponseFlagFor mode) const; + virtual void CloseFileStreams(); cmLocalUnixMakefileGenerator3* LocalGenerator; cmGlobalUnixMakefileGenerator3* GlobalGenerator; diff --git a/Source/cmMessageCommand.cxx b/Source/cmMessageCommand.cxx index cd57600..fa29ec9 100644 --- a/Source/cmMessageCommand.cxx +++ b/Source/cmMessageCommand.cxx @@ -81,94 +81,83 @@ bool cmMessageCommand(std::vector<std::string> const& args, auto type = MessageType::MESSAGE; auto fatal = false; - auto level = cmake::LogLevel::LOG_UNDEFINED; + auto level = Message::LogLevel::LOG_UNDEFINED; auto checkingType = CheckingType::UNDEFINED; if (*i == "SEND_ERROR") { type = MessageType::FATAL_ERROR; - level = cmake::LogLevel::LOG_ERROR; + level = Message::LogLevel::LOG_ERROR; ++i; } else if (*i == "FATAL_ERROR") { fatal = true; type = MessageType::FATAL_ERROR; - level = cmake::LogLevel::LOG_ERROR; + level = Message::LogLevel::LOG_ERROR; ++i; } else if (*i == "WARNING") { type = MessageType::WARNING; - level = cmake::LogLevel::LOG_WARNING; + level = Message::LogLevel::LOG_WARNING; ++i; } else if (*i == "AUTHOR_WARNING") { if (mf.IsSet("CMAKE_SUPPRESS_DEVELOPER_ERRORS") && !mf.IsOn("CMAKE_SUPPRESS_DEVELOPER_ERRORS")) { fatal = true; type = MessageType::AUTHOR_ERROR; - level = cmake::LogLevel::LOG_ERROR; + level = Message::LogLevel::LOG_ERROR; } else if (!mf.IsOn("CMAKE_SUPPRESS_DEVELOPER_WARNINGS")) { type = MessageType::AUTHOR_WARNING; - level = cmake::LogLevel::LOG_WARNING; + level = Message::LogLevel::LOG_WARNING; } else { return true; } ++i; } else if (*i == "CHECK_START") { - level = cmake::LogLevel::LOG_STATUS; + level = Message::LogLevel::LOG_STATUS; checkingType = CheckingType::CHECK_START; ++i; } else if (*i == "CHECK_PASS") { - level = cmake::LogLevel::LOG_STATUS; + level = Message::LogLevel::LOG_STATUS; checkingType = CheckingType::CHECK_PASS; ++i; } else if (*i == "CHECK_FAIL") { - level = cmake::LogLevel::LOG_STATUS; + level = Message::LogLevel::LOG_STATUS; checkingType = CheckingType::CHECK_FAIL; ++i; } else if (*i == "STATUS") { - level = cmake::LogLevel::LOG_STATUS; + level = Message::LogLevel::LOG_STATUS; ++i; } else if (*i == "VERBOSE") { - level = cmake::LogLevel::LOG_VERBOSE; + level = Message::LogLevel::LOG_VERBOSE; ++i; } else if (*i == "DEBUG") { - level = cmake::LogLevel::LOG_DEBUG; + level = Message::LogLevel::LOG_DEBUG; ++i; } else if (*i == "TRACE") { - level = cmake::LogLevel::LOG_TRACE; + level = Message::LogLevel::LOG_TRACE; ++i; } else if (*i == "DEPRECATION") { if (mf.IsOn("CMAKE_ERROR_DEPRECATED")) { fatal = true; type = MessageType::DEPRECATION_ERROR; - level = cmake::LogLevel::LOG_ERROR; + level = Message::LogLevel::LOG_ERROR; } else if (!mf.IsSet("CMAKE_WARN_DEPRECATED") || mf.IsOn("CMAKE_WARN_DEPRECATED")) { type = MessageType::DEPRECATION_WARNING; - level = cmake::LogLevel::LOG_WARNING; + level = Message::LogLevel::LOG_WARNING; } else { return true; } ++i; } else if (*i == "NOTICE") { // `NOTICE` message type is going to be output to stderr - level = cmake::LogLevel::LOG_NOTICE; + level = Message::LogLevel::LOG_NOTICE; ++i; } else { // Messages w/o any type are `NOTICE`s - level = cmake::LogLevel::LOG_NOTICE; + level = Message::LogLevel::LOG_NOTICE; } assert("Message log level expected to be set" && - level != cmake::LogLevel::LOG_UNDEFINED); - - auto desiredLevel = mf.GetCMakeInstance()->GetLogLevel(); - assert("Expected a valid log level here" && - desiredLevel != cmake::LogLevel::LOG_UNDEFINED); - - // Command line option takes precedence over the cache variable - if (!mf.GetCMakeInstance()->WasLogLevelSetViaCLI()) { - const auto desiredLevelFromCache = - cmake::StringToLogLevel(mf.GetSafeDefinition("CMAKE_MESSAGE_LOG_LEVEL")); - if (desiredLevelFromCache != cmake::LogLevel::LOG_UNDEFINED) { - desiredLevel = desiredLevelFromCache; - } - } + level != Message::LogLevel::LOG_UNDEFINED); + + Message::LogLevel desiredLevel = mf.GetCurrentLogLevel(); if (desiredLevel < level) { // Suppress the message @@ -178,17 +167,17 @@ bool cmMessageCommand(std::vector<std::string> const& args, auto message = cmJoin(cmMakeRange(i, args.cend()), ""); switch (level) { - case cmake::LogLevel::LOG_ERROR: - case cmake::LogLevel::LOG_WARNING: + case Message::LogLevel::LOG_ERROR: + case Message::LogLevel::LOG_WARNING: // we've overridden the message type, above, so display it directly mf.GetMessenger()->DisplayMessage(type, message, mf.GetBacktrace()); break; - case cmake::LogLevel::LOG_NOTICE: + case Message::LogLevel::LOG_NOTICE: cmSystemTools::Message(IndentText(message, mf)); break; - case cmake::LogLevel::LOG_STATUS: + case Message::LogLevel::LOG_STATUS: switch (checkingType) { case CheckingType::CHECK_START: mf.DisplayStatus(IndentText(message, mf), -1); @@ -209,9 +198,9 @@ bool cmMessageCommand(std::vector<std::string> const& args, } break; - case cmake::LogLevel::LOG_VERBOSE: - case cmake::LogLevel::LOG_DEBUG: - case cmake::LogLevel::LOG_TRACE: + case Message::LogLevel::LOG_VERBOSE: + case Message::LogLevel::LOG_DEBUG: + case Message::LogLevel::LOG_TRACE: mf.DisplayStatus(IndentText(message, mf), -1); break; diff --git a/Source/cmMessageType.h b/Source/cmMessageType.h index 44de429..decb4b3 100644 --- a/Source/cmMessageType.h +++ b/Source/cmMessageType.h @@ -16,3 +16,19 @@ enum class MessageType DEPRECATION_ERROR, DEPRECATION_WARNING }; + +namespace Message { + +/** \brief Define log level constants. */ +enum class LogLevel +{ + LOG_UNDEFINED, + LOG_ERROR, + LOG_WARNING, + LOG_NOTICE, + LOG_STATUS, + LOG_VERBOSE, + LOG_DEBUG, + LOG_TRACE +}; +} diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index d4f1608..42f0329 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -537,7 +537,6 @@ std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeDeviceLinkCmd() // this target requires separable cuda compilation // now build the correct command depending on if the target is // an executable or a dynamic library. - std::string linkCmd; switch (this->GetGeneratorTarget()->GetType()) { case cmStateEnums::STATIC_LIBRARY: case cmStateEnums::SHARED_LIBRARY: diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 3fac7f5..a4080d8 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -21,11 +21,17 @@ #include "cmComputeLinkInformation.h" #include "cmCustomCommandGenerator.h" +#include "cmExportBuildFileGenerator.h" +#include "cmExportSet.h" #include "cmFileSet.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalNinjaGenerator.h" +#include "cmInstallCxxModuleBmiGenerator.h" +#include "cmInstallExportGenerator.h" +#include "cmInstallFileSetGenerator.h" +#include "cmInstallGenerator.h" #include "cmLocalGenerator.h" #include "cmLocalNinjaGenerator.h" #include "cmMakefile.h" @@ -36,12 +42,12 @@ #include "cmRange.h" #include "cmRulePlaceholderExpander.h" #include "cmSourceFile.h" -#include "cmStandardLevelResolver.h" #include "cmState.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" +#include "cmTargetExport.h" #include "cmValue.h" #include "cmake.h" @@ -153,17 +159,12 @@ std::string cmNinjaTargetGenerator::LanguageDyndepRule( bool cmNinjaTargetGenerator::NeedCxxModuleSupport( std::string const& lang, std::string const& config) const { - if (lang != "CXX") { + if (lang != "CXX"_s) { return false; } - if (!this->Makefile->IsOn("CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP")) { - return false; - } - cmGeneratorTarget const* tgt = this->GetGeneratorTarget(); - cmStandardLevelResolver standardResolver(this->Makefile); - bool const uses_cxx20 = - standardResolver.HaveStandardAvailable(tgt, "CXX", config, "cxx_std_20"); - return uses_cxx20 && this->GetGlobalGenerator()->CheckCxxModuleSupport(); + return this->GetGeneratorTarget()->HaveCxxModuleSupport(config) == + cmGeneratorTarget::Cxx20SupportLevel::Supported && + this->GetGlobalGenerator()->CheckCxxModuleSupport(); } bool cmNinjaTargetGenerator::NeedDyndep(std::string const& lang, @@ -255,51 +256,53 @@ std::string cmNinjaTargetGenerator::ComputeFlagsForObject( flags, genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS)); } - if (this->NeedCxxModuleSupport(language, config)) { - auto const& path = source->GetFullPath(); - auto const* tgt = this->GeneratorTarget->Target; + auto const& path = source->GetFullPath(); + auto const* tgt = this->GeneratorTarget->Target; - std::string file_set_type; + std::string file_set_type; - for (auto const& name : tgt->GetAllFileSetNames()) { - auto const* file_set = tgt->GetFileSet(name); - if (!file_set) { - this->GetMakefile()->IssueMessage( - MessageType::INTERNAL_ERROR, - cmStrCat("Target `", tgt->GetName(), - "` is tracked to have file set `", name, - "`, but it was not found.")); - continue; - } + for (auto const& name : tgt->GetAllFileSetNames()) { + auto const* file_set = tgt->GetFileSet(name); + if (!file_set) { + this->GetMakefile()->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", tgt->GetName(), + "\" is tracked to have file set \"", name, + "\", but it was not found.")); + continue; + } - auto fileEntries = file_set->CompileFileEntries(); - auto directoryEntries = file_set->CompileDirectoryEntries(); - auto directories = file_set->EvaluateDirectoryEntries( - directoryEntries, this->LocalGenerator, config, this->GeneratorTarget); + auto fileEntries = file_set->CompileFileEntries(); + auto directoryEntries = file_set->CompileDirectoryEntries(); + auto directories = file_set->EvaluateDirectoryEntries( + directoryEntries, this->LocalGenerator, config, this->GeneratorTarget); - std::map<std::string, std::vector<std::string>> files; - for (auto const& entry : fileEntries) { - file_set->EvaluateFileEntry(directories, files, entry, - this->LocalGenerator, config, - this->GeneratorTarget); - } + std::map<std::string, std::vector<std::string>> files; + for (auto const& entry : fileEntries) { + file_set->EvaluateFileEntry(directories, files, entry, + this->LocalGenerator, config, + this->GeneratorTarget); + } - for (auto const& it : files) { - for (auto const& filename : it.second) { - if (filename == path) { - file_set_type = file_set->GetType(); - break; - } + for (auto const& it : files) { + for (auto const& filename : it.second) { + if (filename == path) { + file_set_type = file_set->GetType(); + break; } } + } - if (!file_set_type.empty()) { - std::string source_type_var = cmStrCat( - "CMAKE_EXPERIMENTAL_CXX_MODULE_SOURCE_TYPE_FLAG_", file_set_type); - cmMakefile* mf = this->GetMakefile(); - if (cmValue source_type_flag = mf->GetDefinition(source_type_var)) { - this->LocalGenerator->AppendFlags(flags, *source_type_flag); - } + if (file_set_type == "CXX_MODULES"_s || + file_set_type == "CXX_MODULE_HEADER_UNITS"_s) { + if (source->GetLanguage() != "CXX"_s) { + this->GetMakefile()->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat( + "Target \"", tgt->GetName(), "\" contains the source\n ", path, + "\nin a file set of type \"", file_set_type, + R"(" but the source is not classified as a "CXX" source.)")); + continue; } } } @@ -1038,6 +1041,8 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements( const std::string& config, const std::string& fileConfig, bool firstForConfig) { + this->GeneratorTarget->CheckCxxModuleStatus(config); + // Write comments. cmGlobalNinjaGenerator::WriteDivider(this->GetImplFileStream(fileConfig)); this->GetImplFileStream(fileConfig) @@ -1338,9 +1343,11 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( } } - this->ExportObjectCompileCommand( - language, sourceFilePath, objectDir, objectFileName, objectFileDir, - vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], config); + if (firstForConfig) { + this->ExportObjectCompileCommand( + language, sourceFilePath, objectDir, objectFileName, objectFileDir, + vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], config); + } objBuild.Outputs.push_back(objectFileName); if (firstForConfig) { @@ -1616,8 +1623,9 @@ void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang, mod_dir = this->GeneratorTarget->GetFortranModuleDirectory( this->Makefile->GetHomeOutputDirectory()); } else if (lang == "CXX") { - mod_dir = - cmSystemTools::CollapseFullPath(this->GeneratorTarget->ObjectDirectory); + mod_dir = this->GetGlobalGenerator()->ExpandCFGIntDir( + cmSystemTools::CollapseFullPath(this->GeneratorTarget->ObjectDirectory), + config); } if (mod_dir.empty()) { mod_dir = this->Makefile->GetCurrentBinaryDirectory(); @@ -1654,6 +1662,215 @@ void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang, tdi_linked_target_dirs.append(l); } + cmTarget* tgt = this->GeneratorTarget->Target; + auto all_file_sets = tgt->GetAllFileSetNames(); + Json::Value& tdi_cxx_module_info = tdi["cxx-modules"] = Json::objectValue; + for (auto const& file_set_name : all_file_sets) { + auto* file_set = tgt->GetFileSet(file_set_name); + if (!file_set) { + this->GetMakefile()->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", tgt->GetName(), + "\" is tracked to have file set \"", file_set_name, + "\", but it was not found.")); + continue; + } + auto fs_type = file_set->GetType(); + // We only care about C++ module sources here. + if (fs_type != "CXX_MODULES"_s) { + continue; + } + + auto fileEntries = file_set->CompileFileEntries(); + auto directoryEntries = file_set->CompileDirectoryEntries(); + + auto directories = file_set->EvaluateDirectoryEntries( + directoryEntries, this->GeneratorTarget->LocalGenerator, config, + this->GeneratorTarget); + std::map<std::string, std::vector<std::string>> files_per_dirs; + for (auto const& entry : fileEntries) { + file_set->EvaluateFileEntry(directories, files_per_dirs, entry, + this->GeneratorTarget->LocalGenerator, + config, this->GeneratorTarget); + } + + std::map<std::string, cmSourceFile const*> sf_map; + { + std::vector<cmSourceFile const*> objectSources; + this->GeneratorTarget->GetObjectSources(objectSources, config); + for (auto const* sf : objectSources) { + auto full_path = sf->GetFullPath(); + if (full_path.empty()) { + this->GetMakefile()->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", tgt->GetName(), + "\" has a full path-less source file.")); + continue; + } + sf_map[full_path] = sf; + } + } + + Json::Value fs_dest = Json::nullValue; + for (auto const& ig : this->GetMakefile()->GetInstallGenerators()) { + if (auto const* fsg = + dynamic_cast<cmInstallFileSetGenerator const*>(ig.get())) { + if (fsg->GetTarget() == this->GeneratorTarget && + fsg->GetFileSet() == file_set) { + fs_dest = fsg->GetDestination(config); + continue; + } + } + } + + for (auto const& files_per_dir : files_per_dirs) { + for (auto const& file : files_per_dir.second) { + auto lookup = sf_map.find(file); + if (lookup == sf_map.end()) { + this->GetMakefile()->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", tgt->GetName(), "\" has source file \"", + file, + R"(" which is not in any of its "FILE_SET BASE_DIRS".)")); + continue; + } + + auto const* sf = lookup->second; + + if (!sf) { + this->GetMakefile()->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", tgt->GetName(), "\" has source file \"", + file, "\" which has not been tracked properly.")); + continue; + } + + auto obj_path = this->GetObjectFilePath(sf, config); + Json::Value& tdi_module_info = tdi_cxx_module_info[obj_path] = + Json::objectValue; + + tdi_module_info["source"] = file; + tdi_module_info["relative-directory"] = files_per_dir.first; + tdi_module_info["name"] = file_set->GetName(); + tdi_module_info["type"] = file_set->GetType(); + tdi_module_info["visibility"] = + std::string(cmFileSetVisibilityToName(file_set->GetVisibility())); + tdi_module_info["destination"] = fs_dest; + } + } + } + + tdi["config"] = config; + + // Add information about the export sets that this target is a member of. + Json::Value& tdi_exports = tdi["exports"] = Json::arrayValue; + std::string export_name = this->GeneratorTarget->GetExportName(); + + cmInstallCxxModuleBmiGenerator const* bmi_gen = nullptr; + for (auto const& ig : this->GetMakefile()->GetInstallGenerators()) { + if (auto const* bmig = + dynamic_cast<cmInstallCxxModuleBmiGenerator const*>(ig.get())) { + if (bmig->GetTarget() == this->GeneratorTarget) { + bmi_gen = bmig; + continue; + } + } + } + if (bmi_gen) { + Json::Value tdi_bmi_info = Json::objectValue; + + tdi_bmi_info["permissions"] = bmi_gen->GetFilePermissions(); + tdi_bmi_info["destination"] = bmi_gen->GetDestination(config); + const char* msg_level = ""; + switch (bmi_gen->GetMessageLevel()) { + case cmInstallGenerator::MessageDefault: + break; + case cmInstallGenerator::MessageAlways: + msg_level = "MESSAGE_ALWAYS"; + break; + case cmInstallGenerator::MessageLazy: + msg_level = "MESSAGE_LAZY"; + break; + case cmInstallGenerator::MessageNever: + msg_level = "MESSAGE_NEVER"; + break; + } + tdi_bmi_info["message-level"] = msg_level; + tdi_bmi_info["script-location"] = bmi_gen->GetScriptLocation(config); + + tdi["bmi-installation"] = tdi_bmi_info; + } else { + tdi["bmi-installation"] = Json::nullValue; + } + + auto const& all_install_exports = + this->GetGlobalGenerator()->GetExportSets(); + for (auto const& exp : all_install_exports) { + // Ignore exports sets which are not for this target. + auto const& targets = exp.second.GetTargetExports(); + auto tgt_export = + std::find_if(targets.begin(), targets.end(), + [this](std::unique_ptr<cmTargetExport> const& te) { + return te->Target == this->GeneratorTarget; + }); + if (tgt_export == targets.end()) { + continue; + } + + auto const* installs = exp.second.GetInstallations(); + for (auto const* install : *installs) { + Json::Value tdi_export_info = Json::objectValue; + + auto const& ns = install->GetNamespace(); + auto const& dest = install->GetDestination(); + auto const& cxxm_dir = install->GetCxxModuleDirectory(); + auto const& export_prefix = install->GetTempDir(); + + tdi_export_info["namespace"] = ns; + tdi_export_info["export-name"] = export_name; + tdi_export_info["destination"] = dest; + tdi_export_info["cxx-module-info-dir"] = cxxm_dir; + tdi_export_info["export-prefix"] = export_prefix; + tdi_export_info["install"] = true; + + tdi_exports.append(tdi_export_info); + } + } + + auto const& all_build_exports = + this->GetMakefile()->GetExportBuildFileGenerators(); + for (auto const& exp : all_build_exports) { + std::vector<std::string> targets; + exp->GetTargets(targets); + + // Ignore exports sets which are not for this target. + auto const& name = this->GeneratorTarget->GetName(); + bool has_current_target = + std::any_of(targets.begin(), targets.end(), + [name](std::string const& tname) { return tname == name; }); + if (!has_current_target) { + continue; + } + + Json::Value tdi_export_info = Json::objectValue; + + auto const& ns = exp->GetNamespace(); + auto const& main_fn = exp->GetMainExportFileName(); + auto const& cxxm_dir = exp->GetCxxModuleDirectory(); + auto dest = cmsys::SystemTools::GetParentDirectory(main_fn); + auto const& export_prefix = + cmSystemTools::GetFilenamePath(exp->GetMainExportFileName()); + + tdi_export_info["namespace"] = ns; + tdi_export_info["export-name"] = export_name; + tdi_export_info["destination"] = dest; + tdi_export_info["cxx-module-info-dir"] = cxxm_dir; + tdi_export_info["export-prefix"] = export_prefix; + tdi_export_info["install"] = false; + + tdi_exports.append(tdi_export_info); + } + std::string const tdin = this->GetTargetDependInfoPath(lang, config); cmGeneratedFileStream tdif(tdin); tdif << tdi; diff --git a/Source/cmParseArgumentsCommand.cxx b/Source/cmParseArgumentsCommand.cxx index 95f3e7e..7e19566 100644 --- a/Source/cmParseArgumentsCommand.cxx +++ b/Source/cmParseArgumentsCommand.cxx @@ -10,6 +10,7 @@ #include <cm/string_view> #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmMessageType.h" @@ -41,11 +42,18 @@ namespace { using options_map = std::map<std::string, bool>; using single_map = std::map<std::string, std::string>; -using multi_map = std::map<std::string, std::vector<std::string>>; -using options_set = std::set<std::string>; +using multi_map = + std::map<std::string, ArgumentParser::NonEmpty<std::vector<std::string>>>; +using options_set = std::set<cm::string_view>; struct UserArgumentParser : public cmArgumentParser<void> { + void BindKeywordsMissingValue(std::vector<cm::string_view>& ref) + { + this->cmArgumentParser<void>::BindKeywordMissingValue( + [&ref](Instance&, cm::string_view arg) { ref.emplace_back(arg); }); + } + template <typename T, typename H> void Bind(std::vector<std::string> const& names, std::map<std::string, T>& ref, H duplicateKey) @@ -208,9 +216,10 @@ bool cmParseArgumentsCommand(std::vector<std::string> const& args, } } - std::vector<std::string> keywordsMissingValues; + std::vector<cm::string_view> keywordsMissingValues; + parser.BindKeywordsMissingValue(keywordsMissingValues); - parser.Parse(list, &unparsed, &keywordsMissingValues); + parser.Parse(list, &unparsed); PassParsedArguments( prefix, status.GetMakefile(), options, singleValArgs, multiValArgs, diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index 40f3ab5..6c500b0 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -1847,8 +1847,7 @@ void cmQtAutoGenInitializer::AddToSourceGroup(std::string const& fileName, void cmQtAutoGenInitializer::AddCleanFile(std::string const& fileName) { - this->GenTarget->Target->AppendProperty("ADDITIONAL_CLEAN_FILES", fileName, - false); + this->GenTarget->Target->AppendProperty("ADDITIONAL_CLEAN_FILES", fileName); } void cmQtAutoGenInitializer::ConfigFileNames(ConfigString& configString, diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx index 8c09b56..8adaa6c 100644 --- a/Source/cmQtAutoMocUic.cxx +++ b/Source/cmQtAutoMocUic.cxx @@ -2183,6 +2183,9 @@ void cmQtAutoMocUicT::JobMocsCompilationT::Process() content += "// No files found that require moc or the moc files are " "included\n" "void " + + functionName + + "();\n" + "void " + functionName + "() {}\n"; } else { // Valid content diff --git a/Source/cmScanDepFormat.cxx b/Source/cmScanDepFormat.cxx index 82a374a..ec53af5 100644 --- a/Source/cmScanDepFormat.cxx +++ b/Source/cmScanDepFormat.cxx @@ -5,6 +5,7 @@ #include <cctype> #include <cstdio> +#include <iostream> #include <utility> #include <cm/optional> @@ -188,6 +189,19 @@ bool cmScanDepFormat_P1689_Parse(std::string const& arg_pp, return false; } + if (provide.isMember("is-interface")) { + Json::Value const& is_interface = provide["is-interface"]; + if (!is_interface.isBool()) { + cmSystemTools::Error( + cmStrCat("-E cmake_ninja_dyndep failed to parse ", arg_pp, + ": is-interface is not a boolean")); + return false; + } + provide_info.IsInterface = is_interface.asBool(); + } else { + provide_info.IsInterface = true; + } + info->Provides.push_back(provide_info); } } @@ -267,6 +281,27 @@ bool cmScanDepFormat_P1689_Parse(std::string const& arg_pp, info->Requires.push_back(require_info); } } + + // MSVC 17.3 toolchain bug. Remove when 17.4 is available. + if (rule.isMember("is-interface")) { + std::cerr + << "warning: acknowledging an VS 17.3 toolchain bug; accepting " + "until a new release which fixes it is available" + << std::endl; + + Json::Value const& is_interface_json = rule["is-interface"]; + if (!is_interface_json.isBool()) { + cmSystemTools::Error( + cmStrCat("-E cmake_ninja_dyndep failed to parse ", arg_pp, + ": is-interface is not a boolean")); + return false; + } + bool is_interface = is_interface_json.asBool(); + + for (auto& provide : info->Provides) { + provide.IsInterface = is_interface; + } + } } } @@ -308,6 +343,8 @@ bool cmScanDepFormat_P1689_Write(std::string const& path, provide_obj["source-path"] = EncodeFilename(provide.SourcePath); } + provide_obj["is-interface"] = provide.IsInterface; + provides.append(provide_obj); } diff --git a/Source/cmScanDepFormat.h b/Source/cmScanDepFormat.h index dae28d9..dc55bf1 100644 --- a/Source/cmScanDepFormat.h +++ b/Source/cmScanDepFormat.h @@ -18,6 +18,11 @@ struct cmSourceReqInfo std::string SourcePath; std::string CompiledModulePath; bool UseSourcePath = false; + + // Provides-only fields. + bool IsInterface = true; + + // Requires-only fields. LookupMethod Method = LookupMethod::ByName; }; diff --git a/Source/cmScriptGenerator.cxx b/Source/cmScriptGenerator.cxx index 166ee56..32f9bec 100644 --- a/Source/cmScriptGenerator.cxx +++ b/Source/cmScriptGenerator.cxx @@ -133,7 +133,7 @@ void cmScriptGenerator::GenerateScriptActionsOnce(std::ostream& os, std::string config_test = this->CreateConfigTest(this->Configurations); os << indent << "if(" << config_test << ")\n"; this->GenerateScriptActions(os, indent.Next()); - os << indent << "endif(" << config_test << ")\n"; + os << indent << "endif()\n"; } } diff --git a/Source/cmSetPropertyCommand.cxx b/Source/cmSetPropertyCommand.cxx index db10cd4..521cf63 100644 --- a/Source/cmSetPropertyCommand.cxx +++ b/Source/cmSetPropertyCommand.cxx @@ -9,6 +9,7 @@ #include "cmExecutionStatus.h" #include "cmGlobalGenerator.h" #include "cmInstalledFile.h" +#include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" @@ -561,7 +562,8 @@ bool HandleTarget(cmTarget* target, cmMakefile& makefile, { // Set or append the property. if (appendMode) { - target->AppendProperty(propertyName, propertyValue, appendAsString); + target->AppendProperty(propertyName, propertyValue, + makefile.GetBacktrace(), appendAsString); } else { if (remove) { target->SetProperty(propertyName, nullptr); diff --git a/Source/cmSourceGroup.cxx b/Source/cmSourceGroup.cxx index 155068cb..6019de1 100644 --- a/Source/cmSourceGroup.cxx +++ b/Source/cmSourceGroup.cxx @@ -124,6 +124,21 @@ cmSourceGroup* cmSourceGroup::MatchChildrenFiles(const std::string& name) return nullptr; } +const cmSourceGroup* cmSourceGroup::MatchChildrenFiles( + const std::string& name) const +{ + if (this->MatchesFiles(name)) { + return this; + } + for (const cmSourceGroup& group : this->Internal->GroupChildren) { + const cmSourceGroup* result = group.MatchChildrenFiles(name); + if (result) { + return result; + } + } + return nullptr; +} + cmSourceGroup* cmSourceGroup::MatchChildrenRegex(const std::string& name) { for (cmSourceGroup& group : this->Internal->GroupChildren) { diff --git a/Source/cmSourceGroup.h b/Source/cmSourceGroup.h index 295240d..9ce71c7 100644 --- a/Source/cmSourceGroup.h +++ b/Source/cmSourceGroup.h @@ -80,6 +80,12 @@ public: cmSourceGroup* MatchChildrenFiles(const std::string& name); /** + * Check if the given name matches this group's explicit file list + * in children. + */ + const cmSourceGroup* MatchChildrenFiles(const std::string& name) const; + + /** * Check if the given name matches this group's regex in children. */ cmSourceGroup* MatchChildrenRegex(const std::string& name); diff --git a/Source/cmStandardLevelResolver.cxx b/Source/cmStandardLevelResolver.cxx index 785f356..be15288 100644 --- a/Source/cmStandardLevelResolver.cxx +++ b/Source/cmStandardLevelResolver.cxx @@ -18,6 +18,7 @@ #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" +#include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" @@ -416,7 +417,8 @@ bool cmStandardLevelResolver::AddRequiredTargetFeature( cmTarget* target, const std::string& feature, std::string* error) const { if (cmGeneratorExpression::Find(feature) != std::string::npos) { - target->AppendProperty("COMPILE_FEATURES", feature); + target->AppendProperty("COMPILE_FEATURES", feature, + this->Makefile->GetBacktrace()); return true; } @@ -426,7 +428,8 @@ bool cmStandardLevelResolver::AddRequiredTargetFeature( return false; } - target->AppendProperty("COMPILE_FEATURES", feature); + target->AppendProperty("COMPILE_FEATURES", feature, + this->Makefile->GetBacktrace()); // FIXME: Add a policy to avoid updating the <LANG>_STANDARD target // property due to COMPILE_FEATURES. The language standard selection diff --git a/Source/cmStringCommand.cxx b/Source/cmStringCommand.cxx index c3ee695..c12d1fe 100644 --- a/Source/cmStringCommand.cxx +++ b/Source/cmStringCommand.cxx @@ -143,7 +143,8 @@ bool HandleHexCommand(std::vector<std::string> const& args, std::string::size_type hexIndex = 0; for (auto const& c : instr) { - sprintf(&output[hexIndex], "%.2x", static_cast<unsigned char>(c) & 0xFF); + snprintf(&output[hexIndex], 3, "%.2x", + static_cast<unsigned char>(c) & 0xFF); hexIndex += 2; } @@ -1013,7 +1014,7 @@ int ParseIndex( Json::ArrayIndex index = static_cast<Json::ArrayIndex>(lindex); if (index >= max) { cmAlphaNum sizeStr{ max }; - throw json_error({ "expected an index less then "_s, sizeStr.View(), + throw json_error({ "expected an index less than "_s, sizeStr.View(), " got '"_s, str, "'"_s }, progress); } diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index f077801..3c4e709 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -16,6 +16,7 @@ #include <cm/optional> #include <cmext/algorithm> +#include <cmext/string_view> #include <cm3p/uv.h> @@ -1256,7 +1257,7 @@ std::string cmSystemTools::ComputeCertificateThumbprint( certContext, CERT_HASH_PROP_ID, hashData, &hashLength)) { for (DWORD i = 0; i < hashLength; i++) { // Convert each byte to hexadecimal - sprintf(pHashPrint, "%02X", hashData[i]); + snprintf(pHashPrint, 3, "%02X", hashData[i]); pHashPrint += 2; } *pHashPrint = '\0'; @@ -1539,8 +1540,7 @@ std::string cmSystemTools::RelativeIfUnder(std::string const& top, bool cmSystemTools::UnsetEnv(const char* value) { # if !defined(HAVE_UNSETENV) - std::string var = cmStrCat(value, '='); - return cmSystemTools::PutEnv(var); + return cmSystemTools::UnPutEnv(value); # else unsetenv(value); return true; @@ -1564,6 +1564,144 @@ void cmSystemTools::AppendEnv(std::vector<std::string> const& env) } } +void cmSystemTools::EnvDiff::AppendEnv(std::vector<std::string> const& env) +{ + for (std::string const& eit : env) { + this->PutEnv(eit); + } +} + +void cmSystemTools::EnvDiff::PutEnv(const std::string& env) +{ + auto const eq_loc = env.find('='); + if (eq_loc != std::string::npos) { + std::string name = env.substr(0, eq_loc); + diff[name] = env.substr(eq_loc + 1); + } else { + this->UnPutEnv(env); + } +} + +void cmSystemTools::EnvDiff::UnPutEnv(const std::string& env) +{ + diff[env] = {}; +} + +bool cmSystemTools::EnvDiff::ParseOperation(const std::string& envmod) +{ + char path_sep = GetSystemPathlistSeparator(); + + auto apply_diff = [this](const std::string& name, + std::function<void(std::string&)> const& apply) { + cm::optional<std::string> old_value = diff[name]; + std::string output; + if (old_value) { + output = *old_value; + } else { + const char* curval = cmSystemTools::GetEnv(name); + if (curval) { + output = curval; + } + } + apply(output); + diff[name] = output; + }; + + // Split on `=` + auto const eq_loc = envmod.find_first_of('='); + if (eq_loc == std::string::npos) { + cmSystemTools::Error(cmStrCat( + "Error: Missing `=` after the variable name in: ", envmod, '\n')); + return false; + } + + auto const name = envmod.substr(0, eq_loc); + + // Split value on `:` + auto const op_value_start = eq_loc + 1; + auto const colon_loc = envmod.find_first_of(':', op_value_start); + if (colon_loc == std::string::npos) { + cmSystemTools::Error( + cmStrCat("Error: Missing `:` after the operation in: ", envmod, '\n')); + return false; + } + auto const op = envmod.substr(op_value_start, colon_loc - op_value_start); + + auto const value_start = colon_loc + 1; + auto const value = envmod.substr(value_start); + + // Determine what to do with the operation. + if (op == "reset"_s) { + auto entry = diff.find(name); + if (entry != diff.end()) { + diff.erase(entry); + } + } else if (op == "set"_s) { + diff[name] = value; + } else if (op == "unset"_s) { + diff[name] = {}; + } else if (op == "string_append"_s) { + apply_diff(name, [&value](std::string& output) { output += value; }); + } else if (op == "string_prepend"_s) { + apply_diff(name, + [&value](std::string& output) { output.insert(0, value); }); + } else if (op == "path_list_append"_s) { + apply_diff(name, [&value, path_sep](std::string& output) { + if (!output.empty()) { + output += path_sep; + } + output += value; + }); + } else if (op == "path_list_prepend"_s) { + apply_diff(name, [&value, path_sep](std::string& output) { + if (!output.empty()) { + output.insert(output.begin(), path_sep); + } + output.insert(0, value); + }); + } else if (op == "cmake_list_append"_s) { + apply_diff(name, [&value](std::string& output) { + if (!output.empty()) { + output += ';'; + } + output += value; + }); + } else if (op == "cmake_list_prepend"_s) { + apply_diff(name, [&value](std::string& output) { + if (!output.empty()) { + output.insert(output.begin(), ';'); + } + output.insert(0, value); + }); + } else { + cmSystemTools::Error(cmStrCat( + "Error: Unrecognized environment manipulation argument: ", op, '\n')); + return false; + } + + return true; +} + +void cmSystemTools::EnvDiff::ApplyToCurrentEnv(std::ostringstream* measurement) +{ + for (auto const& env_apply : diff) { + if (env_apply.second) { + auto const env_update = + cmStrCat(env_apply.first, '=', *env_apply.second); + cmSystemTools::PutEnv(env_update); + if (measurement) { + *measurement << env_update << std::endl; + } + } else { + cmSystemTools::UnsetEnv(env_apply.first.c_str()); + if (measurement) { + // Signify that this variable is being actively unset + *measurement << '#' << env_apply.first << "=\n"; + } + } + } +} + cmSystemTools::SaveRestoreEnvironment::SaveRestoreEnvironment() { this->Env = cmSystemTools::GetEnvironmentVariables(); diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index ec650f7..48bbe23 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -6,9 +6,12 @@ #include <cstddef> #include <functional> +#include <map> +#include <sstream> #include <string> #include <vector> +#include <cm/optional> #include <cm/string_view> #include "cmsys/Process.h" @@ -377,6 +380,42 @@ public: /** Append multiple variables to the current environment. */ static void AppendEnv(std::vector<std::string> const& env); + /** + * Helper class to represent an environment diff directly. This is to avoid + * repeated in-place environment modification (i.e. via setenv/putenv), which + * could be slow. + */ + class EnvDiff + { + public: + /** Append multiple variables to the current environment diff */ + void AppendEnv(std::vector<std::string> const& env); + + /** + * Add a single variable (or remove if no = sign) to the current + * environment diff. + */ + void PutEnv(const std::string& env); + + /** Remove a single variable from the current environment diff. */ + void UnPutEnv(const std::string& env); + + /** + * Apply an ENVIRONMENT_MODIFICATION operation to this diff. Returns + * false and issues an error on parse failure. + */ + bool ParseOperation(const std::string& envmod); + + /** + * Apply this diff to the actual environment, optionally writing out the + * modifications to a CTest-compatible measurement stream. + */ + void ApplyToCurrentEnv(std::ostringstream* measurement = nullptr); + + private: + std::map<std::string, cm::optional<std::string>> diff; + }; + /** Helper class to save and restore the environment. Instantiate this class as an automatic variable on the stack. Its constructor saves a copy of the current diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 62ec344..050206a 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -272,6 +272,8 @@ public: cmListFileBacktrace Backtrace; FileSetType HeadersFileSets; + FileSetType CxxModulesFileSets; + FileSetType CxxModuleHeadersFileSets; cmTargetInternals(); @@ -293,6 +295,12 @@ public: cm::string_view fileSetType) const; cmValue GetFileSetPaths(cmTarget const* self, std::string const& fileSetName, cm::string_view fileSetType) const; + + cmListFileBacktrace GetBacktrace( + cm::optional<cmListFileBacktrace> const& bt) const + { + return bt ? *bt : this->Makefile->GetBacktrace(); + } }; cmTargetInternals::cmTargetInternals() @@ -301,6 +309,19 @@ cmTargetInternals::cmTargetInternals() "The default header set"_s, "Header set"_s, FileSetEntries("HEADER_SETS"_s), FileSetEntries("INTERFACE_HEADER_SETS"_s)) + , CxxModulesFileSets("CXX_MODULES"_s, "CXX_MODULE_DIRS"_s, + "CXX_MODULE_SET"_s, "CXX_MODULE_DIRS_"_s, + "CXX_MODULE_SET_"_s, "C++ module"_s, + "The default C++ module set"_s, "C++ module set"_s, + FileSetEntries("CXX_MODULE_SETS"_s), + FileSetEntries("INTERFACE_CXX_MODULE_SETS"_s)) + , CxxModuleHeadersFileSets( + "CXX_MODULE_HEADER_UNITS"_s, "CXX_MODULE_HEADER_UNIT_DIRS"_s, + "CXX_MODULE_HEADER_UNIT_SET"_s, "CXX_MODULE_HEADER_UNIT_DIRS_"_s, + "CXX_MODULE_HEADER_UNIT_SET_"_s, "C++ module header"_s, + "The default C++ module header set"_s, "C++ module header set"_s, + FileSetEntries("CXX_MODULE_HEADER_UNIT_SETS"_s), + FileSetEntries("INTERFACE_CXX_MODULE_HEADER_UNIT_SETS"_s)) { } @@ -611,6 +632,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, initProp("XCODE_SCHEME_MALLOC_SCRIBBLE"); initProp("XCODE_SCHEME_MALLOC_GUARD_EDGES"); initProp("XCODE_SCHEME_GUARD_MALLOC"); + initProp("XCODE_SCHEME_LAUNCH_MODE"); initProp("XCODE_SCHEME_ZOMBIE_OBJECTS"); initProp("XCODE_SCHEME_MALLOC_STACK"); initProp("XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE"); @@ -760,6 +782,10 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, } } + if (this->IsImported()) { + this->SetProperty("SYSTEM", "ON"); + } + for (auto const& prop : mf->GetState()->GetPropertyDefinitions().GetMap()) { if (prop.first.second == cmProperty::TARGET && !prop.second.GetInitializeFromVariable().empty()) { @@ -1223,7 +1249,8 @@ void cmTarget::AddLinkLibrary(cmMakefile& mf, std::string const& lib, ? targetNameGenex(lib) : lib; this->AppendProperty("LINK_LIBRARIES", - this->GetDebugGeneratorExpressions(libName, llt)); + this->GetDebugGeneratorExpressions(libName, llt), + mf.GetBacktrace()); } if (cmGeneratorExpression::Find(lib) != std::string::npos || @@ -1367,11 +1394,32 @@ cmBTStringRange cmTarget::GetHeaderSetsEntries() const return cmMakeRange(this->impl->HeadersFileSets.SelfEntries.Entries); } +cmBTStringRange cmTarget::GetCxxModuleSetsEntries() const +{ + return cmMakeRange(this->impl->CxxModulesFileSets.SelfEntries.Entries); +} + +cmBTStringRange cmTarget::GetCxxModuleHeaderSetsEntries() const +{ + return cmMakeRange(this->impl->CxxModuleHeadersFileSets.SelfEntries.Entries); +} + cmBTStringRange cmTarget::GetInterfaceHeaderSetsEntries() const { return cmMakeRange(this->impl->HeadersFileSets.InterfaceEntries.Entries); } +cmBTStringRange cmTarget::GetInterfaceCxxModuleSetsEntries() const +{ + return cmMakeRange(this->impl->CxxModulesFileSets.InterfaceEntries.Entries); +} + +cmBTStringRange cmTarget::GetInterfaceCxxModuleHeaderSetsEntries() const +{ + return cmMakeRange( + this->impl->CxxModuleHeadersFileSets.InterfaceEntries.Entries); +} + namespace { #define MAKE_PROP(PROP) const std::string prop##PROP = #PROP MAKE_PROP(C_STANDARD); @@ -1631,13 +1679,21 @@ void cmTarget::StoreProperty(const std::string& prop, ValueType value) } else if (this->impl->HeadersFileSets.WriteProperties( this, this->impl.get(), prop, value, true)) { /* Handled in the `if` condition. */ + } else if (this->impl->CxxModulesFileSets.WriteProperties( + this, this->impl.get(), prop, value, true)) { + /* Handled in the `if` condition. */ + } else if (this->impl->CxxModuleHeadersFileSets.WriteProperties( + this, this->impl.get(), prop, value, true)) { + /* Handled in the `if` condition. */ } else { this->impl->Properties.SetProperty(prop, value); } } void cmTarget::AppendProperty(const std::string& prop, - const std::string& value, bool asString) + const std::string& value, + cm::optional<cmListFileBacktrace> const& bt, + bool asString) { if (prop == "NAME") { this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, @@ -1668,32 +1724,32 @@ void cmTarget::AppendProperty(const std::string& prop, } if (prop == "INCLUDE_DIRECTORIES") { if (!value.empty()) { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->IncludeDirectoriesEntries.emplace_back(value, lfbt); } } else if (prop == "COMPILE_OPTIONS") { if (!value.empty()) { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->CompileOptionsEntries.emplace_back(value, lfbt); } } else if (prop == "COMPILE_FEATURES") { if (!value.empty()) { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->CompileFeaturesEntries.emplace_back(value, lfbt); } } else if (prop == "COMPILE_DEFINITIONS") { if (!value.empty()) { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->CompileDefinitionsEntries.emplace_back(value, lfbt); } } else if (prop == "LINK_OPTIONS") { if (!value.empty()) { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->LinkOptionsEntries.emplace_back(value, lfbt); } } else if (prop == "LINK_DIRECTORIES") { if (!value.empty()) { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->LinkDirectoriesEntries.emplace_back(value, lfbt); } } else if (prop == "PRECOMPILE_HEADERS") { @@ -1706,32 +1762,32 @@ void cmTarget::AppendProperty(const std::string& prop, return; } if (!value.empty()) { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->PrecompileHeadersEntries.emplace_back(value, lfbt); } } else if (prop == "LINK_LIBRARIES") { if (!value.empty()) { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->LinkImplementationPropertyEntries.emplace_back(value, lfbt); } } else if (prop == propINTERFACE_LINK_LIBRARIES) { if (!value.empty()) { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->LinkInterfacePropertyEntries.emplace_back(value, lfbt); } } else if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT) { if (!value.empty()) { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->LinkInterfaceDirectPropertyEntries.emplace_back(value, lfbt); } } else if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE) { if (!value.empty()) { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->LinkInterfaceDirectExcludePropertyEntries.emplace_back(value, lfbt); } } else if (prop == "SOURCES") { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->SourceEntries.emplace_back(value, lfbt); } else if (cmHasLiteralPrefix(prop, "IMPORTED_LIBNAME")) { this->impl->Makefile->IssueMessage( @@ -1742,6 +1798,13 @@ void cmTarget::AppendProperty(const std::string& prop, this->impl->Makefile->IssueMessage( MessageType::FATAL_ERROR, prop + " property may not be appended."); } else if (this->impl->HeadersFileSets.WriteProperties( + this, this->impl.get(), prop, value, + false)) { // NOLINT(bugprone-branch-clone) + /* Handled in the `if` condition. */ + } else if (this->impl->CxxModulesFileSets.WriteProperties( + this, this->impl.get(), prop, value, false)) { + /* Handled in the `if` condition. */ + } else if (this->impl->CxxModuleHeadersFileSets.WriteProperties( this, this->impl.get(), prop, value, false)) { /* Handled in the `if` condition. */ } else { @@ -2295,6 +2358,17 @@ cmValue cmTarget::GetProperty(const std::string& prop) const if (headers.first) { return headers.second; } + auto cxx_modules = this->impl->CxxModulesFileSets.ReadProperties( + this, this->impl.get(), prop); + if (cxx_modules.first) { + return cxx_modules.second; + } + auto cxx_module_headers = + this->impl->CxxModuleHeadersFileSets.ReadProperties( + this, this->impl.get(), prop); + if (cxx_module_headers.first) { + return cxx_module_headers.second; + } } cmValue retVal = this->impl->Properties.GetPropertyValue(prop); @@ -2572,6 +2646,11 @@ std::pair<cmFileSet*, bool> cmTarget::GetOrCreateFileSet( auto bt = this->impl->Makefile->GetBacktrace(); if (type == this->impl->HeadersFileSets.TypeName) { this->impl->HeadersFileSets.AddFileSet(name, vis, std::move(bt)); + } else if (type == this->impl->CxxModulesFileSets.TypeName) { + this->impl->CxxModulesFileSets.AddFileSet(name, vis, std::move(bt)); + } else if (type == this->impl->CxxModuleHeadersFileSets.TypeName) { + this->impl->CxxModuleHeadersFileSets.AddFileSet(name, vis, + std::move(bt)); } } return std::make_pair(&result.first->second, result.second); @@ -2582,6 +2661,12 @@ std::string cmTarget::GetFileSetsPropertyName(const std::string& type) if (type == "HEADERS") { return "HEADER_SETS"; } + if (type == "CXX_MODULES") { + return "CXX_MODULE_SETS"; + } + if (type == "CXX_MODULE_HEADER_UNITS") { + return "CXX_MODULE_HEADER_UNIT_SETS"; + } return ""; } @@ -2590,6 +2675,12 @@ std::string cmTarget::GetInterfaceFileSetsPropertyName(const std::string& type) if (type == "HEADERS") { return "INTERFACE_HEADER_SETS"; } + if (type == "CXX_MODULES") { + return "INTERFACE_CXX_MODULE_SETS"; + } + if (type == "CXX_MODULE_HEADER_UNITS") { + return "INTERFACE_CXX_MODULE_HEADER_UNIT_SETS"; + } return ""; } @@ -2617,6 +2708,8 @@ std::vector<std::string> cmTarget::GetAllInterfaceFileSets() const }; appendEntries(this->impl->HeadersFileSets.InterfaceEntries.Entries); + appendEntries(this->impl->CxxModulesFileSets.InterfaceEntries.Entries); + appendEntries(this->impl->CxxModuleHeadersFileSets.InterfaceEntries.Entries); return result; } diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 37701f0..1550f5b 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -185,8 +185,10 @@ public: { this->SetProperty(prop, cmValue(value)); } - void AppendProperty(const std::string& prop, const std::string& value, - bool asString = false); + void AppendProperty( + const std::string& prop, const std::string& value, + cm::optional<cmListFileBacktrace> const& bt = cm::nullopt, + bool asString = false); //! Might return a nullptr if the property is not set or invalid cmValue GetProperty(const std::string& prop) const; //! Always returns a valid pointer @@ -281,8 +283,12 @@ public: cmBTStringRange GetLinkInterfaceDirectExcludeEntries() const; cmBTStringRange GetHeaderSetsEntries() const; + cmBTStringRange GetCxxModuleSetsEntries() const; + cmBTStringRange GetCxxModuleHeaderSetsEntries() const; cmBTStringRange GetInterfaceHeaderSetsEntries() const; + cmBTStringRange GetInterfaceCxxModuleSetsEntries() const; + cmBTStringRange GetInterfaceCxxModuleHeaderSetsEntries() const; std::string ImportedGetFullPath(const std::string& config, cmStateEnums::ArtifactType artifact) const; diff --git a/Source/cmTargetCompileDefinitionsCommand.cxx b/Source/cmTargetCompileDefinitionsCommand.cxx index b56b245..268bfac 100644 --- a/Source/cmTargetCompileDefinitionsCommand.cxx +++ b/Source/cmTargetCompileDefinitionsCommand.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmTargetCompileDefinitionsCommand.h" +#include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmStringAlgorithms.h" @@ -28,7 +29,8 @@ private: const std::vector<std::string>& content, bool /*prepend*/, bool /*system*/) override { - tgt->AppendProperty("COMPILE_DEFINITIONS", this->Join(content)); + tgt->AppendProperty("COMPILE_DEFINITIONS", this->Join(content), + this->Makefile->GetBacktrace()); return true; // Successfully handled. } diff --git a/Source/cmTargetExport.h b/Source/cmTargetExport.h index 885ac74..1cef888 100644 --- a/Source/cmTargetExport.h +++ b/Source/cmTargetExport.h @@ -8,6 +8,7 @@ class cmFileSet; class cmGeneratorTarget; +class cmInstallCxxModuleBmiGenerator; class cmInstallFileSetGenerator; class cmInstallFilesGenerator; class cmInstallTargetGenerator; @@ -32,6 +33,7 @@ public: cmInstallTargetGenerator* BundleGenerator; cmInstallFilesGenerator* HeaderGenerator; std::map<cmFileSet*, cmInstallFileSetGenerator*> FileSetGenerators; + cmInstallCxxModuleBmiGenerator* CxxModuleBmiGenerator; ///@} bool NamelinkOnly = false; diff --git a/Source/cmTargetIncludeDirectoriesCommand.cxx b/Source/cmTargetIncludeDirectoriesCommand.cxx index b4b4319..cb83873 100644 --- a/Source/cmTargetIncludeDirectoriesCommand.cxx +++ b/Source/cmTargetIncludeDirectoriesCommand.cxx @@ -88,7 +88,8 @@ void TargetIncludeDirectoriesImpl::HandleInterfaceContent( system); if (system) { std::string joined = this->Join(content); - tgt->AppendProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", joined); + tgt->AppendProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", joined, + this->Makefile->GetBacktrace()); } } diff --git a/Source/cmTargetLinkLibrariesCommand.cxx b/Source/cmTargetLinkLibrariesCommand.cxx index ba901d0..fb03b62 100644 --- a/Source/cmTargetLinkLibrariesCommand.cxx +++ b/Source/cmTargetLinkLibrariesCommand.cxx @@ -625,7 +625,7 @@ bool TLL::HandleLibrary(ProcessingState currentProcessingState, void TLL::AppendProperty(std::string const& prop, std::string const& value) { this->AffectsProperty(prop); - this->Target->AppendProperty(prop, value); + this->Target->AppendProperty(prop, value, this->Makefile.GetBacktrace()); } void TLL::AffectsProperty(std::string const& prop) @@ -636,14 +636,16 @@ void TLL::AffectsProperty(std::string const& prop) // Add a wrapper to the expression to tell LookupLinkItem to look up // names in the caller's directory. if (this->Props.insert(prop).second) { - this->Target->AppendProperty(prop, this->DirectoryId); + this->Target->AppendProperty(prop, this->DirectoryId, + this->Makefile.GetBacktrace()); } } TLL::~TLL() { for (std::string const& prop : this->Props) { - this->Target->AppendProperty(prop, CMAKE_DIRECTORY_ID_SEP); + this->Target->AppendProperty(prop, CMAKE_DIRECTORY_ID_SEP, + this->Makefile.GetBacktrace()); } } diff --git a/Source/cmTargetPrecompileHeadersCommand.cxx b/Source/cmTargetPrecompileHeadersCommand.cxx index a5066cc..4dd158d 100644 --- a/Source/cmTargetPrecompileHeadersCommand.cxx +++ b/Source/cmTargetPrecompileHeadersCommand.cxx @@ -5,6 +5,7 @@ #include <utility> #include "cmGeneratorExpression.h" +#include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmStringAlgorithms.h" @@ -48,7 +49,8 @@ private: { std::string const& base = this->Makefile->GetCurrentSourceDirectory(); tgt->AppendProperty("PRECOMPILE_HEADERS", - this->Join(ConvertToAbsoluteContent(content, base))); + this->Join(ConvertToAbsoluteContent(content, base)), + this->Makefile->GetBacktrace()); return true; } diff --git a/Source/cmTargetSourcesCommand.cxx b/Source/cmTargetSourcesCommand.cxx index b1367e1..53e25b5 100644 --- a/Source/cmTargetSourcesCommand.cxx +++ b/Source/cmTargetSourcesCommand.cxx @@ -9,6 +9,8 @@ #include <cmext/string_view> #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" +#include "cmExperimental.h" #include "cmFileSet.h" #include "cmGeneratorExpression.h" #include "cmListFileCache.h" @@ -27,8 +29,8 @@ struct FileSetArgs { std::string Type; std::string FileSet; - std::vector<std::string> BaseDirs; - std::vector<std::string> Files; + ArgumentParser::MaybeEmpty<std::vector<std::string>> BaseDirs; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Files; }; auto const FileSetArgsParser = cmArgumentParser<FileSetArgs>() @@ -77,7 +79,8 @@ private: { tgt->AppendProperty("SOURCES", this->Join(this->ConvertToAbsoluteContent( - tgt, content, IsInterface::No, CheckCMP0076::Yes))); + tgt, content, IsInterface::No, CheckCMP0076::Yes)), + this->Makefile->GetBacktrace()); return true; // Successfully handled. } @@ -196,7 +199,7 @@ std::vector<std::string> TargetSourcesImpl::ConvertToAbsoluteContent( bool TargetSourcesImpl::HandleFileSetMode( const std::string& scope, const std::vector<std::string>& content) { - auto args = FileSetsArgsParser.Parse(content); + auto args = FileSetsArgsParser.Parse(content, /*unparsedArguments=*/nullptr); for (auto& argList : args.FileSets) { argList.emplace(argList.begin(), "FILE_SET"_s); @@ -256,9 +259,31 @@ bool TargetSourcesImpl::HandleOneFileSet( this->SetError("Must specify a TYPE when creating file set"); return false; } - if (type != "HEADERS"_s) { - this->SetError("File set TYPE may only be \"HEADERS\""); - return false; + bool const supportCxx20FileSetTypes = cmExperimental::HasSupportEnabled( + *this->Makefile, cmExperimental::Feature::CxxModuleCMakeApi); + + if (supportCxx20FileSetTypes) { + if (type != "HEADERS"_s && type != "CXX_MODULES"_s && + type != "CXX_MODULE_HEADER_UNITS"_s) { + this->SetError( + R"(File set TYPE may only be "HEADERS", "CXX_MODULES", or "CXX_MODULE_HEADER_UNITS")"); + return false; + } + + if (cmFileSetVisibilityIsForInterface(visibility) && + !cmFileSetVisibilityIsForSelf(visibility) && + !this->Target->IsImported()) { + if (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s) { + this->SetError( + R"(File set TYPEs "CXX_MODULES" and "CXX_MODULE_HEADER_UNITS" may not have "INTERFACE" visibility)"); + return false; + } + } + } else { + if (type != "HEADERS"_s) { + this->SetError("File set TYPE may only be \"HEADERS\""); + return false; + } } if (args.BaseDirs.empty()) { @@ -294,17 +319,19 @@ bool TargetSourcesImpl::HandleOneFileSet( if (!baseDirectories.empty()) { fileSet.first->AddDirectoryEntry( BT<std::string>(baseDirectories, this->Makefile->GetBacktrace())); - if (type == "HEADERS"_s) { + if (type == "HEADERS"_s || type == "CXX_MODULE_HEADER_UNITS"_s) { for (auto const& dir : cmExpandedList(baseDirectories)) { auto interfaceDirectoriesGenex = cmStrCat("$<BUILD_INTERFACE:", dir, ">"); if (cmFileSetVisibilityIsForSelf(visibility)) { this->Target->AppendProperty("INCLUDE_DIRECTORIES", - interfaceDirectoriesGenex); + interfaceDirectoriesGenex, + this->Makefile->GetBacktrace()); } if (cmFileSetVisibilityIsForInterface(visibility)) { this->Target->AppendProperty("INTERFACE_INCLUDE_DIRECTORIES", - interfaceDirectoriesGenex); + interfaceDirectoriesGenex, + this->Makefile->GetBacktrace()); } } } diff --git a/Source/cmTryCompileCommand.cxx b/Source/cmTryCompileCommand.cxx index 130c228..b59c225 100644 --- a/Source/cmTryCompileCommand.cxx +++ b/Source/cmTryCompileCommand.cxx @@ -2,34 +2,66 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmTryCompileCommand.h" +#include "cmCoreTryCompile.h" +#include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmRange.h" +#include "cmState.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmValue.h" #include "cmake.h" -class cmExecutionStatus; - -// cmTryCompileCommand -bool cmTryCompileCommand::InitialPass(std::vector<std::string> const& argv, - cmExecutionStatus&) +bool cmTryCompileCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) { - if (argv.size() < 3) { + if (args.size() < 3) { return false; } - if (this->Makefile->GetCMakeInstance()->GetWorkingMode() == - cmake::FIND_PACKAGE_MODE) { - this->Makefile->IssueMessage( + cmMakefile& mf = status.GetMakefile(); + + if (mf.GetCMakeInstance()->GetWorkingMode() == cmake::FIND_PACKAGE_MODE) { + mf.IssueMessage( MessageType::FATAL_ERROR, "The try_compile() command is not supported in --find-package mode."); return false; } - this->TryCompileCode(argv, false); + cmStateEnums::TargetType targetType = cmStateEnums::EXECUTABLE; + cmValue tt = mf.GetDefinition("CMAKE_TRY_COMPILE_TARGET_TYPE"); + if (cmNonempty(tt)) { + if (*tt == cmState::GetTargetTypeName(cmStateEnums::EXECUTABLE)) { + targetType = cmStateEnums::EXECUTABLE; + } else if (*tt == + cmState::GetTargetTypeName(cmStateEnums::STATIC_LIBRARY)) { + targetType = cmStateEnums::STATIC_LIBRARY; + } else { + mf.IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Invalid value '", *tt, + "' for CMAKE_TRY_COMPILE_TARGET_TYPE. Only '", + cmState::GetTargetTypeName(cmStateEnums::EXECUTABLE), + "' and '", + cmState::GetTargetTypeName(cmStateEnums::STATIC_LIBRARY), + "' are allowed.")); + return false; + } + } + + cmCoreTryCompile tc(&mf); + cmCoreTryCompile::Arguments arguments = + tc.ParseArgs(cmMakeRange(args), false); + if (!arguments) { + return true; + } + tc.TryCompileCode(arguments, targetType); // if They specified clean then we clean up what we can - if (this->SrcFileSignature) { - if (!this->Makefile->GetCMakeInstance()->GetDebugTryCompile()) { - this->CleanupFiles(this->BinaryDirectory); + if (tc.SrcFileSignature) { + if (!mf.GetCMakeInstance()->GetDebugTryCompile()) { + tc.CleanupFiles(tc.BinaryDirectory); } } return true; diff --git a/Source/cmTryCompileCommand.h b/Source/cmTryCompileCommand.h index d8cc16e..6a3430b 100644 --- a/Source/cmTryCompileCommand.h +++ b/Source/cmTryCompileCommand.h @@ -7,33 +7,7 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" -#include "cmCoreTryCompile.h" - class cmExecutionStatus; -/** \class cmTryCompileCommand - * \brief Specifies where to install some files - * - * cmTryCompileCommand is used to test if source code can be compiled - */ -class cmTryCompileCommand : public cmCoreTryCompile -{ -public: - /** - * This is a virtual constructor for the command. - */ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmTryCompileCommand>(); - } - - /** - * This is called when the command is first encountered in - * the CMakeLists.txt file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; -}; +bool cmTryCompileCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); diff --git a/Source/cmTryRunCommand.cxx b/Source/cmTryRunCommand.cxx index c82ac64..7a29521 100644 --- a/Source/cmTryRunCommand.cxx +++ b/Source/cmTryRunCommand.cxx @@ -4,9 +4,14 @@ #include <cstdio> +#include <cm/optional> + #include "cmsys/FStream.hxx" +#include "cmArgumentParserTypes.h" +#include "cmCoreTryCompile.h" #include "cmDuration.h" +#include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmRange.h" @@ -17,157 +22,155 @@ #include "cmValue.h" #include "cmake.h" -class cmExecutionStatus; +namespace { -// cmTryRunCommand -bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, - cmExecutionStatus&) +class TryRunCommandImpl : public cmCoreTryCompile { - if (argv.size() < 4) { - return false; +public: + TryRunCommandImpl(cmMakefile* mf) + : cmCoreTryCompile(mf) + { } - if (this->Makefile->GetCMakeInstance()->GetWorkingMode() == - cmake::FIND_PACKAGE_MODE) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "The try_run() command is not supported in --find-package mode."); - return false; - } + bool TryRunCode(std::vector<std::string> const& args); - // build an arg list for TryCompile and extract the runArgs, - std::vector<std::string> tryCompile; - - this->CompileResultVariable.clear(); - this->RunResultVariable.clear(); - this->OutputVariable.clear(); - this->RunOutputVariable.clear(); - this->CompileOutputVariable.clear(); - - std::string runArgs; - unsigned int i; - for (i = 1; i < argv.size(); ++i) { - if (argv[i] == "ARGS") { - ++i; - while (i < argv.size() && argv[i] != "COMPILE_DEFINITIONS" && - argv[i] != "CMAKE_FLAGS" && argv[i] != "LINK_OPTIONS" && - argv[i] != "LINK_LIBRARIES") { - runArgs += " "; - runArgs += argv[i]; - ++i; - } - if (i < argv.size()) { - tryCompile.push_back(argv[i]); - } - } else { - if (argv[i] == "OUTPUT_VARIABLE") { - if (argv.size() <= (i + 1)) { - cmSystemTools::Error( - "OUTPUT_VARIABLE specified but there is no variable"); - return false; - } - i++; - this->OutputVariable = argv[i]; - } else if (argv[i] == "RUN_OUTPUT_VARIABLE") { - if (argv.size() <= (i + 1)) { - cmSystemTools::Error( - "RUN_OUTPUT_VARIABLE specified but there is no variable"); - return false; - } - i++; - this->RunOutputVariable = argv[i]; - } else if (argv[i] == "COMPILE_OUTPUT_VARIABLE") { - if (argv.size() <= (i + 1)) { - cmSystemTools::Error( - "COMPILE_OUTPUT_VARIABLE specified but there is no variable"); - return false; - } - i++; - this->CompileOutputVariable = argv[i]; - } else if (argv[i] == "WORKING_DIRECTORY") { - if (argv.size() <= (i + 1)) { - cmSystemTools::Error( - "WORKING_DIRECTORY specified but there is no variable"); - return false; - } - i++; - this->WorkingDirectory = argv[i]; - } else { - tryCompile.push_back(argv[i]); - } - } + void RunExecutable(const std::string& runArgs, + cm::optional<std::string> const& workDir, + std::string* runOutputContents, + std::string* runOutputStdOutContents, + std::string* runOutputStdErrContents); + void DoNotRunExecutable(const std::string& runArgs, + const std::string& srcFile, + std::string const& compileResultVariable, + std::string* runOutputContents, + std::string* runOutputStdOutContents, + std::string* runOutputStdErrContents); + + std::string RunResultVariable; +}; + +bool TryRunCommandImpl::TryRunCode(std::vector<std::string> const& argv) +{ + this->RunResultVariable = argv[0]; + cmCoreTryCompile::Arguments arguments = + this->ParseArgs(cmMakeRange(argv).advance(1), true); + if (!arguments) { + return true; } // although they could be used together, don't allow it, because // using OUTPUT_VARIABLE makes crosscompiling harder - if (!this->OutputVariable.empty() && - (!this->RunOutputVariable.empty() || - !this->CompileOutputVariable.empty())) { + if (arguments.OutputVariable && + (arguments.CompileOutputVariable || arguments.RunOutputVariable || + arguments.RunOutputStdOutVariable || + arguments.RunOutputStdErrVariable)) { cmSystemTools::Error( "You cannot use OUTPUT_VARIABLE together with COMPILE_OUTPUT_VARIABLE " - "or RUN_OUTPUT_VARIABLE. Please use only COMPILE_OUTPUT_VARIABLE and/or " - "RUN_OUTPUT_VARIABLE."); + ", RUN_OUTPUT_VARIABLE, RUN_OUTPUT_STDOUT_VARIABLE or " + "RUN_OUTPUT_STDERR_VARIABLE. " + "Please use only COMPILE_OUTPUT_VARIABLE, RUN_OUTPUT_VARIABLE, " + "RUN_OUTPUT_STDOUT_VARIABLE " + "and/or RUN_OUTPUT_STDERR_VARIABLE."); return false; } - if (!this->WorkingDirectory.empty()) { - if (!cmSystemTools::MakeDirectory(this->WorkingDirectory)) { + if ((arguments.RunOutputStdOutVariable || + arguments.RunOutputStdErrVariable) && + arguments.RunOutputVariable) { + cmSystemTools::Error( + "You cannot use RUN_OUTPUT_STDOUT_VARIABLE or " + "RUN_OUTPUT_STDERR_VARIABLE together " + "with RUN_OUTPUT_VARIABLE. Please use only COMPILE_OUTPUT_VARIABLE or " + "RUN_OUTPUT_STDOUT_VARIABLE and/or RUN_OUTPUT_STDERR_VARIABLE."); + return false; + } + + if (arguments.RunWorkingDirectory) { + if (!cmSystemTools::MakeDirectory(*arguments.RunWorkingDirectory)) { cmSystemTools::Error(cmStrCat("Error creating working directory \"", - this->WorkingDirectory, "\".")); + *arguments.RunWorkingDirectory, "\".")); return false; } } bool captureRunOutput = false; - if (!this->OutputVariable.empty()) { + bool captureRunOutputStdOutErr = false; + if (arguments.OutputVariable) { captureRunOutput = true; - tryCompile.emplace_back("OUTPUT_VARIABLE"); - tryCompile.push_back(this->OutputVariable); - } - if (!this->CompileOutputVariable.empty()) { - tryCompile.emplace_back("OUTPUT_VARIABLE"); - tryCompile.push_back(this->CompileOutputVariable); + } else if (arguments.CompileOutputVariable) { + arguments.OutputVariable = arguments.CompileOutputVariable; } - if (!this->RunOutputVariable.empty()) { + if (arguments.RunOutputStdOutVariable || arguments.RunOutputStdErrVariable) { + captureRunOutputStdOutErr = true; + } else if (arguments.RunOutputVariable) { captureRunOutput = true; } - this->RunResultVariable = argv[0]; - this->CompileResultVariable = argv[1]; - // do the try compile - int res = this->TryCompileCode(tryCompile, true); + bool compiled = this->TryCompileCode(arguments, cmStateEnums::EXECUTABLE); // now try running the command if it compiled - if (!res) { + if (compiled) { if (this->OutputFile.empty()) { cmSystemTools::Error(this->FindErrorMessage); } else { + std::string runArgs; + if (arguments.RunArgs) { + runArgs = cmStrCat(" ", cmJoin(*arguments.RunArgs, " ")); + } + // "run" it and capture the output std::string runOutputContents; + std::string runOutputStdOutContents; + std::string runOutputStdErrContents; if (this->Makefile->IsOn("CMAKE_CROSSCOMPILING") && !this->Makefile->IsDefinitionSet("CMAKE_CROSSCOMPILING_EMULATOR")) { this->DoNotRunExecutable( - runArgs, argv[3], captureRunOutput ? &runOutputContents : nullptr); + runArgs, *arguments.SourceDirectoryOrFile, + *arguments.CompileResultVariable, + captureRunOutput ? &runOutputContents : nullptr, + captureRunOutputStdOutErr && arguments.RunOutputStdOutVariable + ? &runOutputStdOutContents + : nullptr, + captureRunOutputStdOutErr && arguments.RunOutputStdErrVariable + ? &runOutputStdErrContents + : nullptr); } else { - this->RunExecutable(runArgs, &runOutputContents); + this->RunExecutable( + runArgs, arguments.RunWorkingDirectory, + captureRunOutput ? &runOutputContents : nullptr, + captureRunOutputStdOutErr && arguments.RunOutputStdOutVariable + ? &runOutputStdOutContents + : nullptr, + captureRunOutputStdOutErr && arguments.RunOutputStdErrVariable + ? &runOutputStdErrContents + : nullptr); } // now put the output into the variables - if (!this->RunOutputVariable.empty()) { - this->Makefile->AddDefinition(this->RunOutputVariable, + if (arguments.RunOutputVariable) { + this->Makefile->AddDefinition(*arguments.RunOutputVariable, runOutputContents); } + if (arguments.RunOutputStdOutVariable) { + this->Makefile->AddDefinition(*arguments.RunOutputStdOutVariable, + runOutputStdOutContents); + } + if (arguments.RunOutputStdErrVariable) { + this->Makefile->AddDefinition(*arguments.RunOutputStdErrVariable, + runOutputStdErrContents); + } - if (!this->OutputVariable.empty()) { + if (arguments.OutputVariable && !arguments.CompileOutputVariable) { // if the TryCompileCore saved output in this outputVariable then // prepend that output to this output cmValue compileOutput = - this->Makefile->GetDefinition(this->OutputVariable); + this->Makefile->GetDefinition(*arguments.OutputVariable); if (compileOutput) { runOutputContents = *compileOutput + runOutputContents; } - this->Makefile->AddDefinition(this->OutputVariable, runOutputContents); + this->Makefile->AddDefinition(*arguments.OutputVariable, + runOutputContents); } } } @@ -179,8 +182,10 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, return true; } -void cmTryRunCommand::RunExecutable(const std::string& runArgs, - std::string* out) +void TryRunCommandImpl::RunExecutable(const std::string& runArgs, + cm::optional<std::string> const& workDir, + std::string* out, std::string* stdOut, + std::string* stdErr) { int retVal = -1; @@ -204,9 +209,10 @@ void cmTryRunCommand::RunExecutable(const std::string& runArgs, finalCommand += runArgs; } bool worked = cmSystemTools::RunSingleCommand( - finalCommand, out, out, &retVal, - this->WorkingDirectory.empty() ? nullptr : this->WorkingDirectory.c_str(), - cmSystemTools::OUTPUT_NONE, cmDuration::zero()); + finalCommand, stdOut || stdErr ? stdOut : out, + stdOut || stdErr ? stdErr : out, &retVal, + workDir ? workDir->c_str() : nullptr, cmSystemTools::OUTPUT_NONE, + cmDuration::zero()); // set the run var char retChar[16]; const char* retStr; @@ -225,9 +231,10 @@ void cmTryRunCommand::RunExecutable(const std::string& runArgs, executable, two cache variables are created which will hold the results the executable would have produced. */ -void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, - const std::string& srcFile, - std::string* out) +void TryRunCommandImpl::DoNotRunExecutable( + const std::string& runArgs, const std::string& srcFile, + std::string const& compileResultVariable, std::string* out, + std::string* stdOut, std::string* stdErr) { // copy the executable out of the CMakeFiles/ directory, so it is not // removed at the end of try_run() and the user can run it manually @@ -246,6 +253,10 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, std::string internalRunOutputName = this->RunResultVariable + "__TRYRUN_OUTPUT"; + std::string internalRunOutputStdOutName = + this->RunResultVariable + "__TRYRUN_OUTPUT_STDOUT"; + std::string internalRunOutputStdErrName = + this->RunResultVariable + "__TRYRUN_OUTPUT_STDERR"; bool error = false; if (!this->Makefile->GetDefinition(this->RunResultVariable)) { @@ -269,7 +280,51 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, } // is the output from the executable used ? - if (out) { + if (stdOut || stdErr) { + if (!this->Makefile->GetDefinition(internalRunOutputStdOutName)) { + // if the variables doesn't exist, create it with a helpful error text + // and mark it as advanced + std::string comment = cmStrCat( + "Output of try_run(), contains the text, which the executable " + "would have printed on stdout on its target platform.\n", + detailsString); + + this->Makefile->AddCacheDefinition( + internalRunOutputStdOutName, "PLEASE_FILL_OUT-NOTFOUND", + comment.c_str(), cmStateEnums::STRING); + cmState* state = this->Makefile->GetState(); + cmValue existing = + state->GetCacheEntryValue(internalRunOutputStdOutName); + if (existing) { + state->SetCacheEntryProperty(internalRunOutputStdOutName, "ADVANCED", + "1"); + } + + error = true; + } + + if (!this->Makefile->GetDefinition(internalRunOutputStdErrName)) { + // if the variables doesn't exist, create it with a helpful error text + // and mark it as advanced + std::string comment = cmStrCat( + "Output of try_run(), contains the text, which the executable " + "would have printed on stderr on its target platform.\n", + detailsString); + + this->Makefile->AddCacheDefinition( + internalRunOutputStdErrName, "PLEASE_FILL_OUT-NOTFOUND", + comment.c_str(), cmStateEnums::STRING); + cmState* state = this->Makefile->GetState(); + cmValue existing = + state->GetCacheEntryValue(internalRunOutputStdErrName); + if (existing) { + state->SetCacheEntryProperty(internalRunOutputStdErrName, "ADVANCED", + "1"); + } + + error = true; + } + } else if (out) { if (!this->Makefile->GetDefinition(internalRunOutputName)) { // if the variables doesn't exist, create it with a helpful error text // and mark it as advanced @@ -317,7 +372,34 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, " to\n" " the exit code (in many cases 0 for success), otherwise " "enter \"FAILED_TO_RUN\".\n"); - if (out) { + if (stdOut || stdErr) { + if (stdOut) { + comment += internalRunOutputStdOutName; + comment += + "\n contains the text the executable " + "would have printed on stdout.\n" + " If the executable would not have been able to run, set "; + comment += internalRunOutputStdOutName; + comment += " empty.\n" + " Otherwise check if the output is evaluated by the " + "calling CMake code. If so,\n" + " check what the source file would have printed when " + "called with the given arguments.\n"; + } + if (stdErr) { + comment += internalRunOutputStdErrName; + comment += + "\n contains the text the executable " + "would have printed on stderr.\n" + " If the executable would not have been able to run, set "; + comment += internalRunOutputStdErrName; + comment += " empty.\n" + " Otherwise check if the output is evaluated by the " + "calling CMake code. If so,\n" + " check what the source file would have printed when " + "called with the given arguments.\n"; + } + } else if (out) { comment += internalRunOutputName; comment += "\n contains the text the executable " @@ -330,8 +412,9 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, " check what the source file would have printed when " "called with the given arguments.\n"; } + comment += "The "; - comment += this->CompileResultVariable; + comment += compileResultVariable; comment += " variable holds the build result for this try_run().\n\n" "Source file : "; comment += srcFile + "\n"; @@ -370,7 +453,35 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, return; } - if (out) { + if (stdOut || stdErr) { + if (stdOut) { + (*stdOut) = *this->Makefile->GetDefinition(internalRunOutputStdOutName); + } + if (stdErr) { + (*stdErr) = *this->Makefile->GetDefinition(internalRunOutputStdErrName); + } + } else if (out) { (*out) = *this->Makefile->GetDefinition(internalRunOutputName); } } +} + +bool cmTryRunCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + if (args.size() < 4) { + return false; + } + + cmMakefile& mf = status.GetMakefile(); + + if (mf.GetCMakeInstance()->GetWorkingMode() == cmake::FIND_PACKAGE_MODE) { + mf.IssueMessage( + MessageType::FATAL_ERROR, + "The try_run() command is not supported in --find-package mode."); + return false; + } + + TryRunCommandImpl tr(&mf); + return tr.TryRunCode(args); +} diff --git a/Source/cmTryRunCommand.h b/Source/cmTryRunCommand.h index d45acd8..38e3638 100644 --- a/Source/cmTryRunCommand.h +++ b/Source/cmTryRunCommand.h @@ -7,47 +7,7 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" -#include "cmCoreTryCompile.h" - class cmExecutionStatus; -/** \class cmTryRunCommand - * \brief Specifies where to install some files - * - * cmTryRunCommand is used to test if source code can be compiled - */ -class cmTryRunCommand : public cmCoreTryCompile -{ -public: - /** - * This is a virtual constructor for the command. - */ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmTryRunCommand>(); - } - - /** - * This is called when the command is first encountered in - * the CMakeLists.txt file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; - -private: - void RunExecutable(const std::string& runArgs, - std::string* runOutputContents); - void DoNotRunExecutable(const std::string& runArgs, - const std::string& srcFile, - std::string* runOutputContents); - - std::string CompileResultVariable; - std::string RunResultVariable; - std::string OutputVariable; - std::string RunOutputVariable; - std::string CompileOutputVariable; - std::string WorkingDirectory; -}; +bool cmTryRunCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 9f3d620..4c1fa01 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -354,6 +354,18 @@ std::ostream& cmVisualStudio10TargetGenerator::Elem::WriteString( void cmVisualStudio10TargetGenerator::Generate() { + for (std::string const& config : this->Configurations) { + this->GeneratorTarget->CheckCxxModuleStatus(config); + } + + if (this->GeneratorTarget->HaveCxx20ModuleSources()) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("The \"", this->GeneratorTarget->GetName(), + "\" target contains C++ module sources which are not supported " + "by the generator")); + } + this->ProjectType = computeProjectType(this->GeneratorTarget); this->Managed = this->ProjectType == VsProjectType::csproj; const std::string ProjectFileExtension = @@ -957,6 +969,10 @@ void cmVisualStudio10TargetGenerator::WriteSdkStyleProjectFile( std::string outDir = this->GeneratorTarget->GetDirectory(config) + "/"; ConvertToWindowsSlash(outDir); e1.Element("OutputPath", outDir); + + Options& o = *(this->ClOptions[config]); + OptionsHelper oh(o, e1); + oh.OutputFlagMap(); } this->WriteDotNetDocumentationFile(e0); @@ -1507,6 +1523,10 @@ void cmVisualStudio10TargetGenerator::WriteMSToolConfigurationValues( this->ASanEnabledConfigurations.end()) { e1.Element("EnableAsan", "true"); } + if (this->FuzzerEnabledConfigurations.find(config) != + this->FuzzerEnabledConfigurations.end()) { + e1.Element("EnableFuzzer", "true"); + } { auto s = this->SpectreMitigation.find(config); if (s != this->SpectreMitigation.end()) { @@ -2875,7 +2895,7 @@ void cmVisualStudio10TargetGenerator::WritePathAndIncrementalLinkOptions( Elem& e0) { cmStateEnums::TargetType ttype = this->GeneratorTarget->GetType(); - if (ttype > cmStateEnums::GLOBAL_TARGET) { + if (ttype > cmStateEnums::INTERFACE_LIBRARY) { return; } if (this->ProjectType == VsProjectType::csproj) { @@ -3117,6 +3137,7 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( this->LangForClCompile = langForClCompile; if (!langForClCompile.empty()) { this->LocalGenerator->AddLanguageFlags(flags, this->GeneratorTarget, + cmBuildStep::Compile, langForClCompile, configName); this->LocalGenerator->AddCompileOptions(flags, this->GeneratorTarget, langForClCompile, configName); @@ -3128,10 +3149,17 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( } // Check if ASan is enabled. - if (flags.find("/fsanitize=address") != std::string::npos) { + if (flags.find("/fsanitize=address") != std::string::npos || + flags.find("-fsanitize=address") != std::string::npos) { this->ASanEnabledConfigurations.insert(configName); } + // Check if (lib)Fuzzer is enabled. + if (flags.find("/fsanitize=fuzzer") != std::string::npos || + flags.find("-fsanitize=fuzzer") != std::string::npos) { + this->FuzzerEnabledConfigurations.insert(configName); + } + // Precompile Headers std::string pchHeader = this->GeneratorTarget->GetPchHeader(configName, linkLanguage); @@ -3173,7 +3201,9 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( // anymore, because cmGeneratorTarget may not be aware that the // target uses C++/CLI. if (flags.find("/clr") != std::string::npos || - defineFlags.find("/clr") != std::string::npos) { + flags.find("-clr") != std::string::npos || + defineFlags.find("/clr") != std::string::npos || + defineFlags.find("-clr") != std::string::npos) { if (configName == this->Configurations[0]) { std::string message = "For the target \"" + this->GeneratorTarget->GetName() + @@ -3492,8 +3522,8 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaOptions( // Get compile flags for CUDA in this directory. std::string flags; - this->LocalGenerator->AddLanguageFlags(flags, this->GeneratorTarget, "CUDA", - configName); + this->LocalGenerator->AddLanguageFlags( + flags, this->GeneratorTarget, cmBuildStep::Compile, "CUDA", configName); this->LocalGenerator->AddCompileOptions(flags, this->GeneratorTarget, "CUDA", configName); @@ -3685,21 +3715,28 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions( this->GeneratorTarget->GetLinkOptions(linkOpts, configName, "CUDA"); // LINK_OPTIONS are escaped. this->LocalGenerator->AppendCompileOptions(linkFlags, linkOpts); + + cmComputeLinkInformation* pcli = + this->GeneratorTarget->GetLinkInformation(configName); + if (doDeviceLinking && pcli) { + + cmLinkLineDeviceComputer computer( + this->LocalGenerator, + this->LocalGenerator->GetStateSnapshot().GetDirectory()); + std::string ignored_; + this->LocalGenerator->GetDeviceLinkFlags(computer, configName, ignored_, + linkFlags, ignored_, ignored_, + this->GeneratorTarget); + + this->LocalGenerator->AddLanguageFlagsForLinking( + linkFlags, this->GeneratorTarget, "CUDA", configName); + } cudaLinkOptions.AppendFlagString("AdditionalOptions", linkFlags); // For static libraries that have device linking enabled compute // the libraries if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY && doDeviceLinking) { - cmComputeLinkInformation* pcli = - this->GeneratorTarget->GetLinkInformation(configName); - if (!pcli) { - cmSystemTools::Error( - "CMake can not compute cmComputeLinkInformation for target: " + - this->Name); - return false; - } - cmComputeLinkInformation& cli = *pcli; cmLinkLineDeviceComputer computer( this->LocalGenerator, @@ -3757,7 +3794,8 @@ bool cmVisualStudio10TargetGenerator::ComputeMasmOptions( std::string flags; this->LocalGenerator->AddLanguageFlags(flags, this->GeneratorTarget, - "ASM_MASM", configName); + cmBuildStep::Compile, "ASM_MASM", + configName); masmOptions.Parse(flags); @@ -3809,7 +3847,8 @@ bool cmVisualStudio10TargetGenerator::ComputeNasmOptions( std::string flags; this->LocalGenerator->AddLanguageFlags(flags, this->GeneratorTarget, - "ASM_NASM", configName); + cmBuildStep::Compile, "ASM_NASM", + configName); flags += " -f"; flags += this->Makefile->GetSafeDefinition("CMAKE_ASM_NASM_OBJECT_FORMAT"); nasmOptions.Parse(flags); diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index 8d777a3..17dcecd 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -231,6 +231,7 @@ private: bool TargetCompileAsWinRT; std::set<std::string> IPOEnabledConfigurations; std::set<std::string> ASanEnabledConfigurations; + std::set<std::string> FuzzerEnabledConfigurations; std::map<std::string, std::string> SpectreMitigation; cmGlobalVisualStudio10Generator* const GlobalGenerator; cmLocalVisualStudio10Generator* const LocalGenerator; diff --git a/Source/cmVisualStudioGeneratorOptions.cxx b/Source/cmVisualStudioGeneratorOptions.cxx index 00c65ed..16584f5 100644 --- a/Source/cmVisualStudioGeneratorOptions.cxx +++ b/Source/cmVisualStudioGeneratorOptions.cxx @@ -161,71 +161,12 @@ bool cmVisualStudioGeneratorOptions::UsingSBCS() const void cmVisualStudioGeneratorOptions::FixCudaCodeGeneration() { - // Extract temporary values stored by our flag table. - FlagValue arch = this->TakeFlag("cmake-temp-arch"); - FlagValue code = this->TakeFlag("cmake-temp-code"); - FlagValue gencode = this->TakeFlag("cmake-temp-gencode"); - - // No -code allowed without -arch. - if (arch.empty()) { - code.clear(); - } - - // Create a CodeGeneration field with [arch],[code] syntax in each entry. - // CUDA will convert it to `-gencode=arch=[arch],code="[code],[arch]"`. - FlagValue& result = this->FlagMap["CodeGeneration"]; - - // If there are no flags, leave the CodeGeneration field empty. - if (arch.empty() && gencode.empty()) { - return; - } - - // First entries for the -arch=<arch> [-code=<code>,...] pair. - if (!arch.empty()) { - std::string arch_name = arch[0]; - if (arch_name == "all" || arch_name == "all-major" || - arch_name == "native") { - AppendFlagString("AdditionalOptions", "-arch=" + arch_name); - return; - } - std::vector<std::string> codes; - if (!code.empty()) { - codes = cmTokenize(code[0], ","); - } - if (codes.empty()) { - codes.push_back(arch_name); - // nvcc -arch=<arch> has a special case that allows a real - // architecture to be specified instead of a virtual arch. - // It translates to -arch=<virtual> -code=<real>. - cmSystemTools::ReplaceString(arch_name, "sm_", "compute_"); - } - for (std::string const& c : codes) { - std::string entry = arch_name + "," + c; - result.push_back(entry); - } - } - - // Now add entries for the following signatures: - // -gencode=<arch>,<code> - // -gencode=<arch>,[<code1>,<code2>] - // -gencode=<arch>,"<code1>,<code2>" - for (std::string const& e : gencode) { - std::string entry = e; - cmSystemTools::ReplaceString(entry, "arch=", ""); - cmSystemTools::ReplaceString(entry, "code=", ""); - cmSystemTools::ReplaceString(entry, "[", ""); - cmSystemTools::ReplaceString(entry, "]", ""); - cmSystemTools::ReplaceString(entry, "\"", ""); - - std::vector<std::string> codes = cmTokenize(entry, ","); - if (codes.size() >= 2) { - auto gencode_arch = cm::cbegin(codes); - for (auto ci = gencode_arch + 1; ci != cm::cend(codes); ++ci) { - std::string code_entry = *gencode_arch + "," + *ci; - result.push_back(code_entry); - } - } - } + // Create an empty CodeGeneration field, and pass the the actual + // compile flags via additional options so that we have consistent + // behavior and avoid issues with MSBuild extensions injecting + // virtual code when we request real only. + FlagValue& code_gen_flag = this->FlagMap["CodeGeneration"]; + code_gen_flag = ""; } void cmVisualStudioGeneratorOptions::FixManifestUACFlags() diff --git a/Source/cmXCodeScheme.cxx b/Source/cmXCodeScheme.cxx index adc500a..a62015f 100644 --- a/Source/cmXCodeScheme.cxx +++ b/Source/cmXCodeScheme.cxx @@ -147,7 +147,15 @@ void cmXCodeScheme::WriteLaunchAction(cmXMLWriter& xout, "Xcode.DebuggerFoundation.Debugger.LLDB"); xout.Attribute("selectedLauncherIdentifier", "Xcode.DebuggerFoundation.Launcher.LLDB"); - xout.Attribute("launchStyle", "0"); + { + cmValue launchMode = + this->Target->GetTarget()->GetProperty("XCODE_SCHEME_LAUNCH_MODE"); + std::string value = "0"; // == 'AUTO' + if (launchMode && *launchMode == "WAIT") { + value = "1"; + } + xout.Attribute("launchStyle", value); + } WriteCustomWorkingDirectory(xout, configuration); xout.Attribute("ignoresPersistentStateOnLaunch", "NO"); diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 1c1cab3..9b2b119 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -3,6 +3,7 @@ #include "cmake.h" #include <algorithm> +#include <array> #include <cstdio> #include <cstdlib> #include <cstring> @@ -951,7 +952,7 @@ void cmake::SetArgs(const std::vector<std::string>& args) CommandArgument::Values::One, [](std::string const& value, cmake* state) -> bool { const auto logLevel = StringToLogLevel(value); - if (logLevel == LogLevel::LOG_UNDEFINED) { + if (logLevel == Message::LogLevel::LOG_UNDEFINED) { cmSystemTools::Error( "Invalid level specified for --log-level"); return false; @@ -967,7 +968,7 @@ void cmake::SetArgs(const std::vector<std::string>& args) CommandArgument::Values::One, [](std::string const& value, cmake* state) -> bool { const auto logLevel = StringToLogLevel(value); - if (logLevel == LogLevel::LOG_UNDEFINED) { + if (logLevel == Message::LogLevel::LOG_UNDEFINED) { cmSystemTools::Error( "Invalid level specified for --loglevel"); return false; @@ -1282,9 +1283,13 @@ void cmake::SetArgs(const std::vector<std::string>& args) cmCMakePresetsGraph presetsGraph; auto result = presetsGraph.ReadProjectPresets(this->GetHomeDirectory()); if (result != cmCMakePresetsGraph::ReadFileResult::READ_OK) { - cmSystemTools::Error( + std::string errorMsg = cmStrCat("Could not read presets from ", this->GetHomeDirectory(), - ": ", cmCMakePresetsGraph::ResultToString(result))); + ": ", cmCMakePresetsGraph::ResultToString(result)); + if (!presetsGraph.errors.empty()) { + errorMsg = cmStrCat(errorMsg, "\nErrors:\n", presetsGraph.errors); + } + cmSystemTools::Error(errorMsg); return; } @@ -1398,23 +1403,52 @@ void cmake::SetArgs(const std::vector<std::string>& args) #endif } -cmake::LogLevel cmake::StringToLogLevel(const std::string& levelStr) -{ - using LevelsPair = std::pair<std::string, LogLevel>; - static const std::vector<LevelsPair> levels = { - { "error", LogLevel::LOG_ERROR }, { "warning", LogLevel::LOG_WARNING }, - { "notice", LogLevel::LOG_NOTICE }, { "status", LogLevel::LOG_STATUS }, - { "verbose", LogLevel::LOG_VERBOSE }, { "debug", LogLevel::LOG_DEBUG }, - { "trace", LogLevel::LOG_TRACE } +namespace { +using LevelsPair = std::pair<cm::string_view, Message::LogLevel>; +using LevelsPairArray = std::array<LevelsPair, 7>; +const LevelsPairArray& getStringToLogLevelPairs() +{ + static const LevelsPairArray levels = { + { { "error", Message::LogLevel::LOG_ERROR }, + { "warning", Message::LogLevel::LOG_WARNING }, + { "notice", Message::LogLevel::LOG_NOTICE }, + { "status", Message::LogLevel::LOG_STATUS }, + { "verbose", Message::LogLevel::LOG_VERBOSE }, + { "debug", Message::LogLevel::LOG_DEBUG }, + { "trace", Message::LogLevel::LOG_TRACE } } }; + return levels; +} +} // namespace - const auto levelStrLowCase = cmSystemTools::LowerCase(levelStr); +Message::LogLevel cmake::StringToLogLevel(cm::string_view levelStr) +{ + const LevelsPairArray& levels = getStringToLogLevelPairs(); + + const auto levelStrLowCase = + cmSystemTools::LowerCase(std::string{ levelStr }); + // NOLINTNEXTLINE(readability-qualified-auto) const auto it = std::find_if(levels.cbegin(), levels.cend(), [&levelStrLowCase](const LevelsPair& p) { return p.first == levelStrLowCase; }); - return (it != levels.cend()) ? it->second : LogLevel::LOG_UNDEFINED; + return (it != levels.cend()) ? it->second : Message::LogLevel::LOG_UNDEFINED; +} + +std::string cmake::LogLevelToString(Message::LogLevel level) +{ + const LevelsPairArray& levels = getStringToLogLevelPairs(); + + // NOLINTNEXTLINE(readability-qualified-auto) + const auto it = + std::find_if(levels.cbegin(), levels.cend(), + [&level](const LevelsPair& p) { return p.second == level; }); + const cm::string_view levelStrLowerCase = + (it != levels.cend()) ? it->first : "undefined"; + std::string levelStrUpperCase = + cmSystemTools::UpperCase(std::string{ levelStrLowerCase }); + return levelStrUpperCase; } cmake::TraceFormat cmake::StringToTraceFormat(const std::string& traceStr) diff --git a/Source/cmake.h b/Source/cmake.h index 3c6af17..a631647 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -119,19 +119,6 @@ public: FIND_PACKAGE_MODE }; - /** \brief Define log level constants. */ - enum LogLevel - { - LOG_UNDEFINED, - LOG_ERROR, - LOG_WARNING, - LOG_NOTICE, - LOG_STATUS, - LOG_VERBOSE, - LOG_DEBUG, - LOG_TRACE - }; - /** \brief Define supported trace formats **/ enum TraceFormat { @@ -469,9 +456,10 @@ public: bool WasLogLevelSetViaCLI() const { return this->LogLevelWasSetViaCLI; } //! Get the selected log level for `message()` commands during the cmake run. - LogLevel GetLogLevel() const { return this->MessageLogLevel; } - void SetLogLevel(LogLevel level) { this->MessageLogLevel = level; } - static LogLevel StringToLogLevel(const std::string& levelStr); + Message::LogLevel GetLogLevel() const { return this->MessageLogLevel; } + void SetLogLevel(Message::LogLevel level) { this->MessageLogLevel = level; } + static Message::LogLevel StringToLogLevel(cm::string_view levelStr); + static std::string LogLevelToString(Message::LogLevel level); static TraceFormat StringToTraceFormat(const std::string& levelStr); bool HasCheckInProgress() const @@ -732,7 +720,7 @@ private: std::set<std::string> DebugFindPkgs; std::set<std::string> DebugFindVars; - LogLevel MessageLogLevel = LogLevel::LOG_STATUS; + Message::LogLevel MessageLogLevel = Message::LogLevel::LOG_STATUS; bool LogLevelWasSetViaCLI = false; bool LogContext = false; diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx index f931e9d..6f3d0eb 100644 --- a/Source/cmakemain.cxx +++ b/Source/cmakemain.cxx @@ -71,7 +71,7 @@ const char* cmDocumentationUsageNote[][2] = { const char* cmDocumentationOptions[][2] = { CMAKE_STANDARD_OPTIONS_TABLE, { "--preset <preset>,--preset=<preset>", "Specify a configure preset." }, - { "--list-presets", "List available presets." }, + { "--list-presets[=<type>]", "List available presets." }, { "-E", "CMake command mode." }, { "-L[A][H]", "List non-advanced cached variables." }, { "--fresh", @@ -82,9 +82,9 @@ const char* cmDocumentationOptions[][2] = { { "-N", "View mode only." }, { "-P <file>", "Process script mode." }, { "--find-package", "Legacy pkg-config like mode. Do not use." }, - { "--graphviz=[file]", - "Generate graphviz of dependencies, see " - "CMakeGraphVizOptions.cmake for more." }, + { "--graphviz=<file>", + "Generate graphviz of dependencies, see CMakeGraphVizOptions.cmake for " + "more." }, { "--system-information [file]", "Dump information about this system." }, { "--log-level=<ERROR|WARNING|NOTICE|STATUS|VERBOSE|DEBUG|TRACE>", "Set the verbosity of messages from CMake files. " @@ -109,8 +109,7 @@ const char* cmDocumentationOptions[][2] = { { "--warn-uninitialized", "Warn about uninitialized values." }, { "--no-warn-unused-cli", "Don't warn about command line options." }, { "--check-system-vars", - "Find problems with variable usage in system " - "files." }, + "Find problems with variable usage in system files." }, { "--compile-no-warning-as-error", "Ignore COMPILE_WARNING_AS_ERROR property and " "CMAKE_COMPILE_WARNING_AS_ERROR variable." }, @@ -616,7 +615,7 @@ int do_build(int ac, char const* const* av) " <dir> = Project binary directory to be built.\n" " --preset <preset>, --preset=<preset>\n" " = Specify a build preset.\n" - " --list-presets\n" + " --list-presets[=<type>]\n" " = List available build presets.\n" " --parallel [<jobs>], -j [<jobs>]\n" " = Build in parallel using the given number of jobs. \n" @@ -627,14 +626,14 @@ int do_build(int ac, char const* const* av) " specifies a default parallel level when this " "option\n" " is not given.\n" - " --target <tgt>..., -t <tgt>... \n" + " -t <tgt>..., --target <tgt>...\n" " = Build <tgt> instead of default targets.\n" " --config <cfg> = For multi-configuration tools, choose <cfg>.\n" " --clean-first = Build target 'clean' first, then build.\n" " (To clean only, use --target 'clean'.)\n" " --resolve-package-references={on|only|off}\n" " = Restore/resolve package references during build.\n" - " --verbose, -v = Enable verbose output - if supported - including\n" + " -v, --verbose = Enable verbose output - if supported - including\n" " the build commands to be executed. \n" " -- = Pass remaining options to the native tool.\n" ; diff --git a/Source/cmcldeps.cxx b/Source/cmcldeps.cxx index 8921aa0..69eb19e 100644 --- a/Source/cmcldeps.cxx +++ b/Source/cmcldeps.cxx @@ -273,6 +273,7 @@ int main() std::string clrest = rest; // rc: /fo x.dir\x.rc.res -> cl: /out:x.dir\x.rc.res.dep.obj clrest = replace(clrest, "/fo ", "/out:"); + clrest = replace(clrest, "-fo ", "-out:"); clrest = replace(clrest, objfile, objfile + ".dep.obj "); cl = "\"" + cl + "\" /P /DRC_INVOKED /TC "; diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 9ab39f1..00c9bda 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -791,6 +791,10 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, } if (args[1] == "env") { +#ifndef CMAKE_BOOTSTRAP + cmSystemTools::EnvDiff env; +#endif + auto ai = args.cbegin() + 2; auto ae = args.cend(); for (; ai != ae; ++ai) { @@ -803,16 +807,40 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, } if (cmHasLiteralPrefix(a, "--unset=")) { // Unset environment variable. +#ifdef CMAKE_BOOTSTRAP cmSystemTools::UnPutEnv(a.substr(8)); +#else + env.UnPutEnv(a.substr(8)); +#endif + } else if (a == "--modify") { +#ifdef CMAKE_BOOTSTRAP + std::cerr + << "cmake -E env: --modify not available during bootstrapping\n"; + return 1; +#else + if (++ai == ae) { + std::cerr << "cmake -E env: --modify missing a parameter\n"; + return 1; + } + std::string const& op = *ai; + if (!env.ParseOperation(op)) { + std::cerr << "cmake -E env: invalid parameter to --modify: " << op + << '\n'; + return 1; + } +#endif } else if (!a.empty() && a[0] == '-') { // Environment variable and command names cannot start in '-', // so this must be an unknown option. - std::cerr << "cmake -E env: unknown option '" << a << '\'' - << std::endl; + std::cerr << "cmake -E env: unknown option '" << a << "'\n"; return 1; } else if (a.find('=') != std::string::npos) { // Set environment variable. +#ifdef CMAKE_BOOTSTRAP cmSystemTools::PutEnv(a); +#else + env.PutEnv(a); +#endif } else { // This is the beginning of the command. break; @@ -820,10 +848,14 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, } if (ai == ae) { - std::cerr << "cmake -E env: no command given" << std::endl; + std::cerr << "cmake -E env: no command given\n"; return 1; } +#ifndef CMAKE_BOOTSTRAP + env.ApplyToCurrentEnv(); +#endif + // Execute command from remaining arguments. std::vector<std::string> cmd(ai, ae); int retval; @@ -2242,13 +2274,18 @@ bool cmVSLink::Parse(std::vector<std::string>::const_iterator argBeg, // Parse the link command to extract information we need. for (; arg != argEnd; ++arg) { if (cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL:YES") == 0 || - cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL") == 0) { + cmSystemTools::Strucmp(arg->c_str(), "-INCREMENTAL:YES") == 0 || + cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL") == 0 || + cmSystemTools::Strucmp(arg->c_str(), "-INCREMENTAL") == 0) { this->Incremental = true; - } else if (cmSystemTools::Strucmp(arg->c_str(), "/MANIFEST:NO") == 0) { + } else if (cmSystemTools::Strucmp(arg->c_str(), "/MANIFEST:NO") == 0 || + cmSystemTools::Strucmp(arg->c_str(), "-MANIFEST:NO") == 0) { this->LinkGeneratesManifest = false; - } else if (cmHasLiteralPrefix(*arg, "/Fe")) { + } else if (cmHasLiteralPrefix(*arg, "/Fe") || + cmHasLiteralPrefix(*arg, "-Fe")) { this->TargetFile = arg->substr(3); - } else if (cmHasLiteralPrefix(*arg, "/out:")) { + } else if (cmHasLiteralPrefix(*arg, "/out:") || + cmHasLiteralPrefix(*arg, "-out:")) { this->TargetFile = arg->substr(5); } } diff --git a/Source/kwsys/Directory.cxx b/Source/kwsys/Directory.cxx index d520c14..f239576 100644 --- a/Source/kwsys/Directory.cxx +++ b/Source/kwsys/Directory.cxx @@ -43,12 +43,12 @@ public: { std::string Name; #if defined(_WIN32) && !defined(__CYGWIN__) - _wfinddata_t FindData; + WIN32_FIND_DATAW FindData; #endif FileData(std::string name #if defined(_WIN32) && !defined(__CYGWIN__) , - _wfinddata_t data + WIN32_FIND_DATAW data #endif ) : Name(std::move(name)) @@ -115,8 +115,8 @@ std::string Directory::GetFilePath(std::size_t i) const bool Directory::FileIsDirectory(std::size_t i) const { #if defined(_WIN32) && !defined(__CYGWIN__) - _wfinddata_t const& data = this->Internal->Files[i].FindData; - return (data.attrib & FILE_ATTRIBUTE_DIRECTORY) != 0; + auto const& data = this->Internal->Files[i].FindData; + return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; #else std::string const& path = this->GetFilePath(i); return kwsys::SystemTools::FileIsDirectory(path); @@ -127,9 +127,9 @@ bool Directory::FileIsSymlink(std::size_t i) const { std::string const& path = this->GetFilePath(i); #if defined(_WIN32) && !defined(__CYGWIN__) - _wfinddata_t const& data = this->Internal->Files[i].FindData; + auto const& data = this->Internal->Files[i].FindData; return kwsys::SystemTools::FileIsSymlinkWithAttr( - Encoding::ToWindowsExtendedPath(path), data.attrib); + Encoding::ToWindowsExtendedPath(path), data.dwFileAttributes); #else return kwsys::SystemTools::FileIsSymlink(path); #endif @@ -157,7 +157,7 @@ namespace KWSYS_NAMESPACE { Status Directory::Load(std::string const& name, std::string* errorMessage) { this->Clear(); - intptr_t srchHandle; + HANDLE srchHandle; char* buf; size_t bufLength; size_t n = name.size(); @@ -176,14 +176,14 @@ Status Directory::Load(std::string const& name, std::string* errorMessage) snprintf(buf, bufLength, "%s/*", name.c_str()); } } - struct _wfinddata_t data; // data of current file + WIN32_FIND_DATAW data; // data of current file // Now put them into the file array srchHandle = - _wfindfirst((wchar_t*)Encoding::ToWindowsExtendedPath(buf).c_str(), &data); + FindFirstFileW(Encoding::ToWindowsExtendedPath(buf).c_str(), &data); delete[] buf; - if (srchHandle == -1) { + if (srchHandle == INVALID_HANDLE_VALUE) { Status status = Status::POSIX_errno(); if (errorMessage) { *errorMessage = status.GetString(); @@ -193,10 +193,11 @@ Status Directory::Load(std::string const& name, std::string* errorMessage) // Loop through names do { - this->Internal->Files.emplace_back(Encoding::ToNarrow(data.name), data); - } while (_wfindnext(srchHandle, &data) != -1); + this->Internal->Files.emplace_back(Encoding::ToNarrow(data.cFileName), + data); + } while (FindNextFileW(srchHandle, &data)); this->Internal->Path = name; - if (_findclose(srchHandle) == -1) { + if (!FindClose(srchHandle)) { Status status = Status::POSIX_errno(); if (errorMessage) { *errorMessage = status.GetString(); @@ -209,7 +210,7 @@ Status Directory::Load(std::string const& name, std::string* errorMessage) unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name, std::string* errorMessage) { - intptr_t srchHandle; + HANDLE srchHandle; char* buf; size_t bufLength; size_t n = name.size(); @@ -222,13 +223,13 @@ unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name, buf = new char[n + 2 + 1]; snprintf(buf, bufLength, "%s/*", name.c_str()); } - struct _wfinddata_t data; // data of current file + WIN32_FIND_DATAW data; // data of current file // Now put them into the file array - srchHandle = _wfindfirst((wchar_t*)Encoding::ToWide(buf).c_str(), &data); + srchHandle = FindFirstFileW(Encoding::ToWide(buf).c_str(), &data); delete[] buf; - if (srchHandle == -1) { + if (srchHandle == INVALID_HANDLE_VALUE) { if (errorMessage) { if (unsigned int errorId = GetLastError()) { LPSTR message = nullptr; @@ -250,8 +251,8 @@ unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name, unsigned long count = 0; do { count++; - } while (_wfindnext(srchHandle, &data) != -1); - _findclose(srchHandle); + } while (FindNextFileW(srchHandle, &data)); + FindClose(srchHandle); return count; } diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx index 5889a4b..a20901c 100644 --- a/Source/kwsys/SystemTools.cxx +++ b/Source/kwsys/SystemTools.cxx @@ -536,9 +536,11 @@ public: StringMap TranslationMap; #endif #ifdef _WIN32 - static std::string GetCasePathName(std::string const& pathIn); + static std::string GetCasePathName(std::string const& pathIn, + bool const cache); static std::string GetActualCaseForPathCached(std::string const& path); static const char* GetEnvBuffered(const char* key); + std::map<std::string, std::string, SystemToolsPathCaseCmp> FindFileMap; std::map<std::string, std::string, SystemToolsPathCaseCmp> PathCaseMap; std::map<std::string, std::string> EnvMap; #endif @@ -571,7 +573,8 @@ public: static SystemToolsStatic* SystemToolsStatics; #ifdef _WIN32 -std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn) +std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn, + bool const cache) { std::string casePath; @@ -623,14 +626,31 @@ std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn) } else { std::string test_str = casePath; test_str += path_components[idx]; - WIN32_FIND_DATAW findData; - HANDLE hFind = - ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), &findData); - if (INVALID_HANDLE_VALUE != hFind) { - path_components[idx] = Encoding::ToNarrow(findData.cFileName); - ::FindClose(hFind); - } else { - converting = false; + + bool found_in_cache = false; + if (cache) { + auto const it = SystemToolsStatics->FindFileMap.find(test_str); + if (it != SystemToolsStatics->FindFileMap.end()) { + path_components[idx] = it->second; + found_in_cache = true; + } + } + + if (!found_in_cache) { + WIN32_FIND_DATAW findData; + HANDLE hFind = + ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), &findData); + if (INVALID_HANDLE_VALUE != hFind) { + auto case_file_name = Encoding::ToNarrow(findData.cFileName); + if (cache) { + SystemToolsStatics->FindFileMap.emplace(test_str, + case_file_name); + } + path_components[idx] = std::move(case_file_name); + ::FindClose(hFind); + } else { + converting = false; + } } } } @@ -642,19 +662,16 @@ std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn) std::string SystemToolsStatic::GetActualCaseForPathCached(std::string const& p) { - // Check to see if actual case has already been called - // for this path, and the result is stored in the PathCaseMap - auto& pcm = SystemToolsStatics->PathCaseMap; - { - auto itr = pcm.find(p); - if (itr != pcm.end()) { - return itr->second; - } - } - std::string casePath = SystemToolsStatic::GetCasePathName(p); - if (casePath.size() <= MAX_PATH) { - pcm[p] = casePath; + std::string casePath; + + auto it = SystemToolsStatics->PathCaseMap.find(p); + if (it != SystemToolsStatics->PathCaseMap.end()) { + casePath = it->second; + } else { + casePath = SystemToolsStatic::GetCasePathName(p, true); + SystemToolsStatics->PathCaseMap.emplace(p, casePath); } + return casePath; } #endif @@ -3067,17 +3084,14 @@ std::string SystemTools::GetRealPath(const std::string& path, return ret; } -bool SystemTools::FileIsDirectory(const std::string& inName) +// Remove any trailing slash from the name except in a root component. +static const char* RemoveTrailingSlashes( + const std::string& inName, char (&local_buffer)[KWSYS_SYSTEMTOOLS_MAXPATH], + std::string& string_buffer) { - if (inName.empty()) { - return false; - } size_t length = inName.size(); const char* name = inName.c_str(); - // Remove any trailing slash from the name except in a root component. - char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH]; - std::string string_buffer; size_t last = length - 1; if (last > 0 && (name[last] == '/' || name[last] == '\\') && strcmp(name, "/") != 0 && name[last - 1] != ':') { @@ -3091,6 +3105,19 @@ bool SystemTools::FileIsDirectory(const std::string& inName) } } + return name; +} + +bool SystemTools::FileIsDirectory(const std::string& inName) +{ + if (inName.empty()) { + return false; + } + + char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH]; + std::string string_buffer; + const auto name = RemoveTrailingSlashes(inName, local_buffer, string_buffer); + // Now check the file node type. #if defined(_WIN32) DWORD attr = @@ -3107,9 +3134,21 @@ bool SystemTools::FileIsDirectory(const std::string& inName) } } -bool SystemTools::FileIsExecutable(const std::string& name) +bool SystemTools::FileIsExecutable(const std::string& inName) { - return !FileIsDirectory(name) && TestFileAccess(name, TEST_FILE_EXECUTE); +#ifdef _WIN32 + char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH]; + std::string string_buffer; + const auto name = RemoveTrailingSlashes(inName, local_buffer, string_buffer); + const auto attr = + GetFileAttributesW(Encoding::ToWindowsExtendedPath(name).c_str()); + + // On Windows any file that exists and is not a directory is considered + // readable and therefore also executable: + return attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY); +#else + return !FileIsDirectory(inName) && TestFileAccess(inName, TEST_FILE_EXECUTE); +#endif } #if defined(_WIN32) @@ -3655,7 +3694,7 @@ std::string SystemTools::RelativePath(const std::string& local, std::string SystemTools::GetActualCaseForPath(const std::string& p) { #ifdef _WIN32 - return SystemToolsStatic::GetCasePathName(p); + return SystemToolsStatic::GetCasePathName(p, false); #else return p; #endif |