diff options
Diffstat (limited to 'Source')
204 files changed, 7080 insertions, 3502 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 61327be..30bef74 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -131,6 +131,8 @@ set(SRCS LexerParser/cmListFileLexer.c LexerParser/cmListFileLexer.in.l + cmAffinity.cxx + cmAffinity.h cmArchiveWrite.cxx cmBase32.cxx cmCacheManager.cxx @@ -244,6 +246,8 @@ set(SRCS cmGlobalGeneratorFactory.h cmGlobalUnixMakefileGenerator3.cxx cmGlobalUnixMakefileGenerator3.h + cmGlobVerificationManager.cxx + cmGlobVerificationManager.h cmGraphAdjacencyList.h cmGraphVizWriter.cxx cmGraphVizWriter.h @@ -371,6 +375,8 @@ set(SRCS cmCommand.h cmCommands.cxx cmCommands.h + cmAddCompileDefinitionsCommand.cxx + cmAddCompileDefinitionsCommand.h cmAddCompileOptionsCommand.cxx cmAddCompileOptionsCommand.h cmAddCustomCommandCommand.cxx @@ -553,6 +559,7 @@ set(SRCS cmSiteNameCommand.h cmSourceGroupCommand.cxx cmSourceGroupCommand.h + cmStringReplaceHelper.cxx cmStringCommand.cxx cmStringCommand.h cmSubdirCommand.cxx @@ -879,6 +886,7 @@ set(CPACK_SRCS CPack/cmCPackGenerator.cxx CPack/cmCPackLog.cxx CPack/cmCPackNSISGenerator.cxx + CPack/cmCPackNuGetGenerator.cxx CPack/cmCPackSTGZGenerator.cxx CPack/cmCPackTGZGenerator.cxx CPack/cmCPackTXZGenerator.cxx diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 2600706..0171ae8 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,5 +1,5 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 11) -set(CMake_VERSION_PATCH 2) -#set(CMake_VERSION_RC 0) +set(CMake_VERSION_PATCH 20180531) +#set(CMake_VERSION_RC 1) diff --git a/Source/CMakeVersionCompute.cmake b/Source/CMakeVersionCompute.cmake index 79264ed..72a5800 100644 --- a/Source/CMakeVersionCompute.cmake +++ b/Source/CMakeVersionCompute.cmake @@ -32,7 +32,12 @@ endif() # components in the RC file are 16-bit integers so we may have to # split the patch component. if(CMake_VERSION_PATCH MATCHES "^([0-9]+)([0-9][0-9][0-9][0-9])$") - set(CMake_RCVERSION ${CMake_VERSION_MAJOR},${CMake_VERSION_MINOR},${CMAKE_MATCH_1},${CMAKE_MATCH_2}) + set(CMake_RCVERSION_YEAR "${CMAKE_MATCH_1}") + set(CMake_RCVERSION_MONTH_DAY "${CMAKE_MATCH_2}") + string(REGEX REPLACE "^0+" "" CMake_RCVERSION_MONTH_DAY "${CMake_RCVERSION_MONTH_DAY}") + set(CMake_RCVERSION ${CMake_VERSION_MAJOR},${CMake_VERSION_MINOR},${CMake_RCVERSION_YEAR},${CMake_RCVERSION_MONTH_DAY}) + unset(CMake_RCVERSION_MONTH_DAY) + unset(CMake_RCVERSION_YEAR) else() set(CMake_RCVERSION ${CMake_VERSION_MAJOR},${CMake_VERSION_MINOR},${CMake_VERSION_PATCH}) endif() diff --git a/Source/CPack/cmCPackArchiveGenerator.cxx b/Source/CPack/cmCPackArchiveGenerator.cxx index 9ff547a..00fbdab 100644 --- a/Source/CPack/cmCPackArchiveGenerator.cxx +++ b/Source/CPack/cmCPackArchiveGenerator.cxx @@ -9,6 +9,7 @@ #include "cmSystemTools.h" #include "cmWorkingDirectory.h" +#include <cstring> #include <ostream> #include <utility> #include <vector> @@ -51,6 +52,7 @@ int cmCPackArchiveGenerator::InitializeInternal() this->SetOptionIfNotSet("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", "1"); return this->Superclass::InitializeInternal(); } + int cmCPackArchiveGenerator::addOneComponentToArchive( cmArchiveWrite& archive, cmCPackComponent* component) { @@ -61,6 +63,13 @@ int cmCPackArchiveGenerator::addOneComponentToArchive( localToplevel += "/" + component->Name; // Change to local toplevel cmWorkingDirectory workdir(localToplevel); + if (workdir.Failed()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Failed to change working directory to " + << localToplevel << " : " + << std::strerror(workdir.GetLastResult()) << std::endl); + return 0; + } std::string filePrefix; if (this->IsOn("CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY")) { filePrefix = this->GetOption("CPACK_PACKAGE_FILE_NAME"); @@ -237,6 +246,13 @@ int cmCPackArchiveGenerator::PackageFiles() // CASE 3 : NON COMPONENT package. DECLARE_AND_OPEN_ARCHIVE(packageFileNames[0], archive); cmWorkingDirectory workdir(toplevel); + if (workdir.Failed()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Failed to change working directory to " + << toplevel << " : " + << std::strerror(workdir.GetLastResult()) << std::endl); + return 0; + } for (std::string const& file : files) { // Get the relative path to the file std::string rp = cmSystemTools::RelativePath(toplevel, file); diff --git a/Source/CPack/cmCPackGenerator.cxx b/Source/CPack/cmCPackGenerator.cxx index d838b30..64aba10 100644 --- a/Source/CPack/cmCPackGenerator.cxx +++ b/Source/CPack/cmCPackGenerator.cxx @@ -6,6 +6,7 @@ #include "cmsys/Glob.hxx" #include "cmsys/RegularExpression.hxx" #include <algorithm> +#include <cstring> #include <memory> // IWYU pragma: keep #include <utility> @@ -404,6 +405,13 @@ int cmCPackGenerator::InstallProjectViaInstalledDirectories( cmCPackLogger(cmCPackLog::LOG_DEBUG, "Change dir to: " << goToDir << std::endl); cmWorkingDirectory workdir(goToDir); + if (workdir.Failed()) { + cmCPackLogger( + cmCPackLog::LOG_ERROR, "Failed to change working directory to " + << goToDir << " : " << std::strerror(workdir.GetLastResult()) + << std::endl); + return 0; + } for (auto const& symlinked : symlinkedFiles) { cmCPackLogger(cmCPackLog::LOG_DEBUG, "Will create a symlink: " << symlinked.second << "--> " << symlinked.first @@ -994,7 +1002,8 @@ int cmCPackGenerator::DoPackage() { // scope that enables package generators to run internal scripts with // latest CMake policies enabled cmMakefile::ScopePushPop pp{ this->MakefileMap }; - this->MakefileMap->SetPolicyVersion(cmVersion::GetCMakeVersion()); + this->MakefileMap->SetPolicyVersion(cmVersion::GetCMakeVersion(), + std::string()); if (!this->PackageFiles() || cmSystemTools::GetErrorOccuredFlag()) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem compressing the directory" diff --git a/Source/CPack/cmCPackGeneratorFactory.cxx b/Source/CPack/cmCPackGeneratorFactory.cxx index 47e7527..a395a8f 100644 --- a/Source/CPack/cmCPackGeneratorFactory.cxx +++ b/Source/CPack/cmCPackGeneratorFactory.cxx @@ -15,6 +15,7 @@ #include "cmCPackGenerator.h" #include "cmCPackLog.h" #include "cmCPackNSISGenerator.h" +#include "cmCPackNuGetGenerator.h" #include "cmCPackSTGZGenerator.h" #include "cmCPackTGZGenerator.h" #include "cmCPackTXZGenerator.h" @@ -105,6 +106,10 @@ cmCPackGeneratorFactory::cmCPackGeneratorFactory() this->RegisterGenerator("DEB", "Debian packages", cmCPackDebGenerator::CreateGenerator); } + if (cmCPackNuGetGenerator::CanGenerate()) { + this->RegisterGenerator("NuGet", "NuGet packages", + cmCPackNuGetGenerator::CreateGenerator); + } #ifdef __APPLE__ if (cmCPackDragNDropGenerator::CanGenerate()) { this->RegisterGenerator("DragNDrop", "Mac OSX Drag And Drop", diff --git a/Source/CPack/cmCPackNuGetGenerator.cxx b/Source/CPack/cmCPackNuGetGenerator.cxx new file mode 100644 index 0000000..2ebfb3d --- /dev/null +++ b/Source/CPack/cmCPackNuGetGenerator.cxx @@ -0,0 +1,140 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmCPackNuGetGenerator.h" + +#include "cmAlgorithms.h" +#include "cmCPackComponentGroup.h" +#include "cmCPackLog.h" +#include "cmSystemTools.h" + +#include <algorithm> +#include <iterator> +#include <map> +#include <ostream> +#include <string> +#include <utility> +#include <vector> + +bool cmCPackNuGetGenerator::SupportsComponentInstallation() const +{ + return IsOn("CPACK_NUGET_COMPONENT_INSTALL"); +} + +int cmCPackNuGetGenerator::PackageFiles() +{ + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Toplevel: " << toplevel << std::endl); + + /* Reset package file name list it will be populated after the + * `CPackNuGet.cmake` run */ + packageFileNames.clear(); + + /* Are we in the component packaging case */ + if (WantsComponentInstallation()) { + if (componentPackageMethod == ONE_PACKAGE) { + // CASE 1 : COMPONENT ALL-IN-ONE package + // Meaning that all per-component pre-installed files + // goes into the single package. + this->SetOption("CPACK_NUGET_ALL_IN_ONE", "TRUE"); + SetupGroupComponentVariables(true); + } else { + // CASE 2 : COMPONENT CLASSICAL package(s) (i.e. not all-in-one) + // There will be 1 package for each component group + // however one may require to ignore component group and + // in this case you'll get 1 package for each component. + SetupGroupComponentVariables(componentPackageMethod == + ONE_PACKAGE_PER_COMPONENT); + } + } else { + // CASE 3 : NON COMPONENT package. + this->SetOption("CPACK_NUGET_ORDINAL_MONOLITIC", "TRUE"); + } + + auto retval = this->ReadListFile("CPackNuGet.cmake"); + if (retval) { + AddGeneratedPackageNames(); + } else { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error while execution CPackNuGet.cmake" << std::endl); + } + + return retval; +} + +void cmCPackNuGetGenerator::SetupGroupComponentVariables(bool ignoreGroup) +{ + // The default behavior is to have one package by component group + // unless CPACK_COMPONENTS_IGNORE_GROUP is specified. + if (!ignoreGroup) { + std::vector<std::string> groups; + for (auto const& compG : this->ComponentGroups) { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "Packaging component group: " << compG.first << std::endl); + groups.push_back(compG.first); + auto compGUp = + cmSystemTools::UpperCase(cmSystemTools::MakeCidentifier(compG.first)); + + // Collect components for this group + std::vector<std::string> components; + std::transform(begin(compG.second.Components), + end(compG.second.Components), + std::back_inserter(components), + [](cmCPackComponent const* comp) { return comp->Name; }); + this->SetOption("CPACK_NUGET_" + compGUp + "_GROUP_COMPONENTS", + cmJoin(components, ";").c_str()); + } + if (!groups.empty()) { + this->SetOption("CPACK_NUGET_GROUPS", cmJoin(groups, ";").c_str()); + } + + // Handle Orphan components (components not belonging to any groups) + std::vector<std::string> components; + for (auto const& comp : this->Components) { + // Does the component belong to a group? + if (comp.second.Group == nullptr) { + cmCPackLogger( + cmCPackLog::LOG_VERBOSE, "Component <" + << comp.second.Name + << "> does not belong to any group, package it separately." + << std::endl); + components.push_back(comp.first); + } + } + if (!components.empty()) { + this->SetOption("CPACK_NUGET_COMPONENTS", + cmJoin(components, ";").c_str()); + } + + } else { + std::vector<std::string> components; + components.reserve(this->Components.size()); + std::transform(begin(this->Components), end(this->Components), + std::back_inserter(components), + [](std::pair<std::string, cmCPackComponent> const& comp) { + return comp.first; + }); + this->SetOption("CPACK_NUGET_COMPONENTS", cmJoin(components, ";").c_str()); + } +} + +void cmCPackNuGetGenerator::AddGeneratedPackageNames() +{ + const char* const files_list = this->GetOption("GEN_CPACK_OUTPUT_FILES"); + if (!files_list) { + cmCPackLogger( + cmCPackLog::LOG_ERROR, + "Error while execution CPackNuGet.cmake: No NuGet package has generated" + << std::endl); + return; + } + // add the generated packages to package file names list + std::string fileNames{ files_list }; + const char sep = ';'; + std::string::size_type pos1 = 0; + std::string::size_type pos2 = fileNames.find(sep, pos1 + 1); + while (pos2 != std::string::npos) { + packageFileNames.push_back(fileNames.substr(pos1, pos2 - pos1)); + pos1 = pos2 + 1; + pos2 = fileNames.find(sep, pos1 + 1); + } + packageFileNames.push_back(fileNames.substr(pos1, pos2 - pos1)); +} diff --git a/Source/CPack/cmCPackNuGetGenerator.h b/Source/CPack/cmCPackNuGetGenerator.h new file mode 100644 index 0000000..a59db2d --- /dev/null +++ b/Source/CPack/cmCPackNuGetGenerator.h @@ -0,0 +1,37 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmCPackNuGetGenerator_h +#define cmCPackNuGetGenerator_h + +#include "cmCPackGenerator.h" + +/** \class cmCPackNuGetGenerator + * \brief A generator for RPM packages + */ +class cmCPackNuGetGenerator : public cmCPackGenerator +{ +public: + cmCPackTypeMacro(cmCPackNuGetGenerator, cmCPackGenerator); + + // NOTE In fact, it is possible to have NuGet not only for Windows... + // https://docs.microsoft.com/en-us/nuget/install-nuget-client-tools + static bool CanGenerate() { return true; } + +protected: + bool SupportsComponentInstallation() const override; + int PackageFiles() override; + + const char* GetOutputExtension() override { return ".nupkg"; } + bool SupportsAbsoluteDestination() const override { return false; } + /** + * The method used to prepare variables when component + * install is used. + */ + void SetupGroupComponentVariables(bool ignoreGroup); + /** + * Populate \c packageFileNames vector of built packages. + */ + void AddGeneratedPackageNames(); +}; + +#endif diff --git a/Source/CPack/cpack.cxx b/Source/CPack/cpack.cxx index b6ff38b..87ef5b6 100644 --- a/Source/CPack/cpack.cxx +++ b/Source/CPack/cpack.cxx @@ -33,23 +33,25 @@ static const char* cmDocumentationName[][2] = { }; static const char* cmDocumentationUsage[][2] = { - { nullptr, " cpack -G <generator> [options]" }, + // clang-format off + { nullptr, " cpack [options]" }, { nullptr, nullptr } + // clang-format on }; static const char* cmDocumentationOptions[][2] = { - { "-G <generator>", "Use the specified generator to generate package." }, + { "-G <generators>", "Override/define CPACK_GENERATOR" }, { "-C <Configuration>", "Specify the project configuration" }, { "-D <var>=<value>", "Set a CPack variable." }, - { "--config <config file>", "Specify the config file." }, - { "--verbose,-V", "enable verbose output" }, + { "--config <configFile>", "Specify the config file." }, + { "--verbose,-V", "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)" }, - { "-P <package name>", "override/define CPACK_PACKAGE_NAME" }, - { "-R <package version>", "override/define CPACK_PACKAGE_VERSION" }, - { "-B <package directory>", "override/define CPACK_PACKAGE_DIRECTORY" }, - { "--vendor <vendor name>", "override/define CPACK_PACKAGE_VENDOR" }, + { "--debug", "Enable debug output (for CPack developers)" }, + { "-P <packageName>", "Override/define CPACK_PACKAGE_NAME" }, + { "-R <packageVersion>", "Override/define CPACK_PACKAGE_VERSION" }, + { "-B <packageDirectory>", "Override/define CPACK_PACKAGE_DIRECTORY" }, + { "--vendor <vendorName>", "Override/define CPACK_PACKAGE_VENDOR" }, { nullptr, nullptr } }; diff --git a/Source/CTest/cmCTestBuildAndTestHandler.cxx b/Source/CTest/cmCTestBuildAndTestHandler.cxx index 2e1ea4c..fccbc95 100644 --- a/Source/CTest/cmCTestBuildAndTestHandler.cxx +++ b/Source/CTest/cmCTestBuildAndTestHandler.cxx @@ -11,6 +11,7 @@ #include "cmsys/Process.h" #include <chrono> +#include <cstring> #include <ratio> #include <stdlib.h> @@ -196,6 +197,16 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring) cmSystemTools::MakeDirectory(this->BinaryDir); } cmWorkingDirectory workdir(this->BinaryDir); + if (workdir.Failed()) { + auto msg = "Failed to change working directory to " + this->BinaryDir + + " : " + std::strerror(workdir.GetLastResult()) + "\n"; + if (outstring) { + *outstring = msg; + } else { + cmCTestLog(this->CTest, ERROR_MESSAGE, msg); + } + return 1; + } if (this->BuildNoCMake) { // Make the generator available for the Build call below. @@ -242,9 +253,9 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring) config = "Debug"; } int retVal = cm.GetGlobalGenerator()->Build( - this->SourceDir, this->BinaryDir, this->BuildProject, tar, output, - this->BuildMakeProgram, config, !this->BuildNoClean, false, false, - remainingTime); + cmake::NO_BUILD_PARALLEL_LEVEL, this->SourceDir, this->BinaryDir, + this->BuildProject, tar, output, this->BuildMakeProgram, config, + !this->BuildNoClean, false, false, remainingTime); out << output; // if the build failed then return if (retVal) { @@ -307,7 +318,16 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring) // run the test from the this->BuildRunDir if set if (!this->BuildRunDir.empty()) { out << "Run test in directory: " << this->BuildRunDir << "\n"; - cmSystemTools::ChangeDirectory(this->BuildRunDir); + if (!workdir.SetDirectory(this->BuildRunDir)) { + out << "Failed to change working directory : " + << std::strerror(workdir.GetLastResult()) << "\n"; + if (outstring) { + *outstring = out.str(); + } else { + cmCTestLog(this->CTest, ERROR_MESSAGE, out.str()); + } + return 1; + } } out << "Running test command: \"" << fullPath << "\""; for (std::string const& testCommandArg : this->TestCommandArgs) { diff --git a/Source/CTest/cmCTestCoverageHandler.cxx b/Source/CTest/cmCTestCoverageHandler.cxx index 9c66e73..bafbe9a 100644 --- a/Source/CTest/cmCTestCoverageHandler.cxx +++ b/Source/CTest/cmCTestCoverageHandler.cxx @@ -23,6 +23,7 @@ #include "cmsys/RegularExpression.hxx" #include <algorithm> #include <chrono> +#include <cstring> #include <iomanip> #include <iterator> #include <sstream> @@ -927,7 +928,8 @@ int cmCTestCoverageHandler::HandleGCovCoverage( std::string gcovCommand = this->CTest->GetCTestConfiguration("CoverageCommand"); if (gcovCommand.empty()) { - cmCTestLog(this->CTest, WARNING, "Could not find gcov." << std::endl); + cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + "Could not find gcov." << std::endl, this->Quiet); return 0; } std::string gcovExtraFlags = @@ -975,7 +977,12 @@ int cmCTestCoverageHandler::HandleGCovCoverage( std::string testingDir = this->CTest->GetBinaryDir() + "/Testing"; std::string tempDir = testingDir + "/CoverageInfo"; - cmSystemTools::MakeDirectory(tempDir); + if (!cmSystemTools::MakeDirectory(tempDir)) { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "Unable to make directory: " << tempDir << std::endl); + cont->Error++; + return 0; + } cmWorkingDirectory workdir(tempDir); int gcovStyle = 0; @@ -1376,6 +1383,14 @@ int cmCTestCoverageHandler::HandleLCovCoverage( this->Quiet); std::string fileDir = cmSystemTools::GetFilenamePath(f); cmWorkingDirectory workdir(fileDir); + if (workdir.Failed()) { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "Unable to change working directory to " + << fileDir << " : " + << std::strerror(workdir.GetLastResult()) << std::endl); + cont->Error++; + continue; + } cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Current coverage dir: " << fileDir << std::endl, @@ -1600,6 +1615,12 @@ bool cmCTestCoverageHandler::FindLCovFiles(std::vector<std::string>& files) gl.RecurseThroughSymlinksOff(); std::string buildDir = this->CTest->GetCTestConfiguration("BuildDirectory"); cmWorkingDirectory workdir(buildDir); + if (workdir.Failed()) { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "Unable to change working directory to " << buildDir + << std::endl); + return false; + } // Run profmerge to merge all *.dyn files into dpi files if (!cmSystemTools::RunSingleCommand("profmerge")) { diff --git a/Source/CTest/cmCTestCurl.h b/Source/CTest/cmCTestCurl.h index 427a392..86d9489 100644 --- a/Source/CTest/cmCTestCurl.h +++ b/Source/CTest/cmCTestCurl.h @@ -16,7 +16,7 @@ class cmCTestCurl public: cmCTestCurl(cmCTest*); ~cmCTestCurl(); - bool UploadFile(std::string const& url, std::string const& file, + bool UploadFile(std::string const& local_file, std::string const& url, std::string const& fields, std::string& response); bool HttpRequest(std::string const& url, std::string const& fields, std::string& response); diff --git a/Source/CTest/cmCTestHandlerCommand.cxx b/Source/CTest/cmCTestHandlerCommand.cxx index 5a7baf5..1fff2fa 100644 --- a/Source/CTest/cmCTestHandlerCommand.cxx +++ b/Source/CTest/cmCTestHandlerCommand.cxx @@ -9,6 +9,7 @@ #include "cmWorkingDirectory.h" #include "cmake.h" +#include <cstring> #include <sstream> #include <stdlib.h> @@ -218,6 +219,21 @@ bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args, } cmWorkingDirectory workdir( this->CTest->GetCTestConfiguration("BuildDirectory")); + if (workdir.Failed()) { + this->SetError("failed to change directory to " + + this->CTest->GetCTestConfiguration("BuildDirectory") + + " : " + std::strerror(workdir.GetLastResult())); + if (capureCMakeError) { + this->Makefile->AddDefinition(this->Values[ct_CAPTURE_CMAKE_ERROR], + "-1"); + cmCTestLog(this->CTest, ERROR_MESSAGE, this->GetName() + << " " << this->GetError() << "\n"); + // return success because failure is recorded in CAPTURE_CMAKE_ERROR + return true; + } + return false; + } + int res = handler->ProcessHandler(); if (this->Values[ct_RETURN_VALUE] && *this->Values[ct_RETURN_VALUE]) { std::ostringstream str; diff --git a/Source/CTest/cmCTestLaunch.cxx b/Source/CTest/cmCTestLaunch.cxx index 30f76a9..e078645 100644 --- a/Source/CTest/cmCTestLaunch.cxx +++ b/Source/CTest/cmCTestLaunch.cxx @@ -347,28 +347,27 @@ void cmCTestLaunch::WriteXML() // Use cmGeneratedFileStream to atomically create the report file. cmGeneratedFileStream fxml(logXML.c_str()); cmXMLWriter xml(fxml, 2); - xml.StartElement("Failure"); - xml.Attribute("type", this->IsError() ? "Error" : "Warning"); - this->WriteXMLAction(xml); - this->WriteXMLCommand(xml); - this->WriteXMLResult(xml); - this->WriteXMLLabels(xml); - xml.EndElement(); // Failure + cmXMLElement e2(xml, "Failure"); + e2.Attribute("type", this->IsError() ? "Error" : "Warning"); + this->WriteXMLAction(e2); + this->WriteXMLCommand(e2); + this->WriteXMLResult(e2); + this->WriteXMLLabels(e2); } -void cmCTestLaunch::WriteXMLAction(cmXMLWriter& xml) +void cmCTestLaunch::WriteXMLAction(cmXMLElement& e2) { - xml.Comment("Meta-information about the build action"); - xml.StartElement("Action"); + e2.Comment("Meta-information about the build action"); + cmXMLElement e3(e2, "Action"); // TargetName if (!this->OptionTargetName.empty()) { - xml.Element("TargetName", this->OptionTargetName); + e3.Element("TargetName", this->OptionTargetName); } // Language if (!this->OptionLanguage.empty()) { - xml.Element("Language", this->OptionLanguage); + e3.Element("Language", this->OptionLanguage); } // SourceFile @@ -383,12 +382,12 @@ void cmCTestLaunch::WriteXMLAction(cmXMLWriter& xml) source = cmSystemTools::RelativePath(this->SourceDir, source); } - xml.Element("SourceFile", source); + e3.Element("SourceFile", source); } // OutputFile if (!this->OptionOutput.empty()) { - xml.Element("OutputFile", this->OptionOutput); + e3.Element("OutputFile", this->OptionOutput); } // OutputType @@ -407,97 +406,88 @@ void cmCTestLaunch::WriteXMLAction(cmXMLWriter& xml) outputType = "object file"; } if (outputType) { - xml.Element("OutputType", outputType); + e3.Element("OutputType", outputType); } - - xml.EndElement(); // Action } -void cmCTestLaunch::WriteXMLCommand(cmXMLWriter& xml) +void cmCTestLaunch::WriteXMLCommand(cmXMLElement& e2) { - xml.Comment("Details of command"); - xml.StartElement("Command"); + e2.Comment("Details of command"); + cmXMLElement e3(e2, "Command"); if (!this->CWD.empty()) { - xml.Element("WorkingDirectory", this->CWD); + e3.Element("WorkingDirectory", this->CWD); } for (std::string const& realArg : this->RealArgs) { - xml.Element("Argument", realArg); + e3.Element("Argument", realArg); } - xml.EndElement(); // Command } -void cmCTestLaunch::WriteXMLResult(cmXMLWriter& xml) +void cmCTestLaunch::WriteXMLResult(cmXMLElement& e2) { - xml.Comment("Result of command"); - xml.StartElement("Result"); + e2.Comment("Result of command"); + cmXMLElement e3(e2, "Result"); // StdOut - xml.StartElement("StdOut"); - this->DumpFileToXML(xml, this->LogOut); - xml.EndElement(); // StdOut + this->DumpFileToXML(e3, "StdOut", this->LogOut); // StdErr - xml.StartElement("StdErr"); - this->DumpFileToXML(xml, this->LogErr); - xml.EndElement(); // StdErr + this->DumpFileToXML(e3, "StdErr", this->LogErr); // ExitCondition - xml.StartElement("ExitCondition"); + cmXMLElement e4(e3, "ExitCondition"); cmsysProcess* cp = this->Process; switch (cmsysProcess_GetState(cp)) { case cmsysProcess_State_Starting: - xml.Content("No process has been executed"); + e4.Content("No process has been executed"); break; case cmsysProcess_State_Executing: - xml.Content("The process is still executing"); + e4.Content("The process is still executing"); break; case cmsysProcess_State_Disowned: - xml.Content("Disowned"); + e4.Content("Disowned"); break; case cmsysProcess_State_Killed: - xml.Content("Killed by parent"); + e4.Content("Killed by parent"); break; case cmsysProcess_State_Expired: - xml.Content("Killed when timeout expired"); + e4.Content("Killed when timeout expired"); break; case cmsysProcess_State_Exited: - xml.Content(this->ExitCode); + e4.Content(this->ExitCode); break; case cmsysProcess_State_Exception: - xml.Content("Terminated abnormally: "); - xml.Content(cmsysProcess_GetExceptionString(cp)); + e4.Content("Terminated abnormally: "); + e4.Content(cmsysProcess_GetExceptionString(cp)); break; case cmsysProcess_State_Error: - xml.Content("Error administrating child process: "); - xml.Content(cmsysProcess_GetErrorString(cp)); + e4.Content("Error administrating child process: "); + e4.Content(cmsysProcess_GetErrorString(cp)); break; - }; - xml.EndElement(); // ExitCondition - - xml.EndElement(); // Result + } } -void cmCTestLaunch::WriteXMLLabels(cmXMLWriter& xml) +void cmCTestLaunch::WriteXMLLabels(cmXMLElement& e2) { this->LoadLabels(); if (!this->Labels.empty()) { - xml.Comment("Interested parties"); - xml.StartElement("Labels"); + e2.Comment("Interested parties"); + cmXMLElement e3(e2, "Labels"); for (std::string const& label : this->Labels) { - xml.Element("Label", label); + e3.Element("Label", label); } - xml.EndElement(); // Labels } } -void cmCTestLaunch::DumpFileToXML(cmXMLWriter& xml, std::string const& fname) +void cmCTestLaunch::DumpFileToXML(cmXMLElement& e3, const char* tag, + std::string const& fname) { cmsys::ifstream fin(fname.c_str(), std::ios::in | std::ios::binary); std::string line; const char* sep = ""; + cmXMLElement e4(e3, tag); while (cmSystemTools::GetLineFromStream(fin, line)) { if (MatchesFilterPrefix(line)) { continue; @@ -507,8 +497,8 @@ void cmCTestLaunch::DumpFileToXML(cmXMLWriter& xml, std::string const& fname) } else if (this->Match(line, this->RegexWarning)) { line = "[CTest: warning matched] " + line; } - xml.Content(sep); - xml.Content(line); + e4.Content(sep); + e4.Content(line); sep = "\n"; } } diff --git a/Source/CTest/cmCTestLaunch.h b/Source/CTest/cmCTestLaunch.h index 29986ff..debbe59 100644 --- a/Source/CTest/cmCTestLaunch.h +++ b/Source/CTest/cmCTestLaunch.h @@ -10,7 +10,7 @@ #include <string> #include <vector> -class cmXMLWriter; +class cmXMLElement; /** \class cmCTestLaunch * \brief Launcher for make rules to report results for ctest @@ -89,11 +89,11 @@ private: // Methods to generate the xml fragment. void WriteXML(); - void WriteXMLAction(cmXMLWriter& xml); - void WriteXMLCommand(cmXMLWriter& xml); - void WriteXMLResult(cmXMLWriter& xml); - void WriteXMLLabels(cmXMLWriter& xml); - void DumpFileToXML(cmXMLWriter& xml, std::string const& fname); + void WriteXMLAction(cmXMLElement&); + void WriteXMLCommand(cmXMLElement&); + void WriteXMLResult(cmXMLElement&); + void WriteXMLLabels(cmXMLElement&); + void DumpFileToXML(cmXMLElement&, const char* tag, std::string const& fname); // Configuration void LoadConfig(); diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx index 50c2d86..14b5caa 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.cxx +++ b/Source/CTest/cmCTestMultiProcessHandler.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCTestMultiProcessHandler.h" +#include "cmAffinity.h" #include "cmCTest.h" #include "cmCTestRunTest.h" #include "cmCTestScriptHandler.h" @@ -19,6 +20,7 @@ #include <algorithm> #include <chrono> +#include <cstring> #include <iomanip> #include <list> #include <math.h> @@ -53,6 +55,8 @@ cmCTestMultiProcessHandler::cmCTestMultiProcessHandler() this->TestLoad = 0; this->Completed = 0; this->RunningCount = 0; + this->ProcessorsAvailable = cmAffinity::GetProcessorsAvailable(); + this->HaveAffinity = this->ProcessorsAvailable.size(); this->StopTimePassed = false; this->HasCycles = false; this->SerialTestRunning = false; @@ -127,6 +131,21 @@ bool cmCTestMultiProcessHandler::StartTestProcess(int test) return false; } + if (this->HaveAffinity && this->Properties[test]->WantAffinity) { + size_t needProcessors = this->GetProcessorsUsed(test); + if (needProcessors > this->ProcessorsAvailable.size()) { + return false; + } + std::vector<size_t> affinity; + affinity.reserve(needProcessors); + for (size_t i = 0; i < needProcessors; ++i) { + auto p = this->ProcessorsAvailable.begin(); + affinity.push_back(*p); + this->ProcessorsAvailable.erase(p); + } + this->Properties[test]->Affinity = std::move(affinity); + } + cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "test " << test << "\n", this->Quiet); this->TestRunningMap[test] = true; // mark the test as running @@ -151,13 +170,19 @@ bool cmCTestMultiProcessHandler::StartTestProcess(int test) } } - cmWorkingDirectory workdir(this->Properties[test]->Directory); - - // Lock the resources we'll be using + // Always lock the resources we'll be using, even if we fail to set the + // working directory because FinishTestProcess() will try to unlock them this->LockResources(test); - if (testRun->StartTest(this->Total)) { - return true; + cmWorkingDirectory workdir(this->Properties[test]->Directory); + if (workdir.Failed()) { + testRun->StartFailure("Failed to change working directory to " + + this->Properties[test]->Directory + " : " + + std::strerror(workdir.GetLastResult())); + } else { + if (testRun->StartTest(this->Total)) { + return true; + } } this->FinishTestProcess(testRun, false); @@ -200,6 +225,11 @@ inline size_t cmCTestMultiProcessHandler::GetProcessorsUsed(int test) if (processors > this->ParallelLevel) { processors = this->ParallelLevel; } + // Cap tests that want affinity to the maximum affinity available. + if (this->HaveAffinity && processors > this->HaveAffinity && + this->Properties[test]->WantAffinity) { + processors = this->HaveAffinity; + } return processors; } @@ -398,6 +428,11 @@ void cmCTestMultiProcessHandler::FinishTestProcess(cmCTestRunTest* runner, this->UnlockResources(test); this->RunningCount -= GetProcessorsUsed(test); + for (auto p : properties->Affinity) { + this->ProcessorsAvailable.insert(p); + } + properties->Affinity.clear(); + delete runner; if (started) { this->StartNextTests(); @@ -666,6 +701,8 @@ void cmCTestMultiProcessHandler::PrintTestList() count++; cmCTestTestHandler::cmCTestTestProperties& p = *it.second; + // Don't worry if this fails, we are only showing the test list, not + // running the tests cmWorkingDirectory workdir(p.Directory); cmCTestRunTest testRun(*this); diff --git a/Source/CTest/cmCTestMultiProcessHandler.h b/Source/CTest/cmCTestMultiProcessHandler.h index 7837ff9..19e1a35 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.h +++ b/Source/CTest/cmCTestMultiProcessHandler.h @@ -119,6 +119,8 @@ protected: // Number of tests that are complete size_t Completed; size_t RunningCount; + std::set<size_t> ProcessorsAvailable; + size_t HaveAffinity; bool StopTimePassed; // list of test properties (indices concurrent to the test map) PropertiesMap Properties; diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx index 30ad38c..8d8ebaa 100644 --- a/Source/CTest/cmCTestRunTest.cxx +++ b/Source/CTest/cmCTestRunTest.cxx @@ -14,6 +14,7 @@ #include "cmsys/RegularExpression.hxx" #include <chrono> #include <cmAlgorithms.h> +#include <cstring> #include <iomanip> #include <ratio> #include <sstream> @@ -248,11 +249,7 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started) *this->TestHandler->LogFile << "Test time = " << buf << std::endl; } - // Set the working directory to the tests directory to process Dart files. - { - cmWorkingDirectory workdir(this->TestProperties->Directory); - this->DartProcessing(); - } + this->DartProcessing(); // if this is doing MemCheck then all the output needs to be put into // Output since that is what is parsed by cmCTestMemCheckHandler @@ -338,6 +335,13 @@ bool cmCTestRunTest::StartAgain() this->RunAgain = false; // reset // change to tests directory cmWorkingDirectory workdir(this->TestProperties->Directory); + if (workdir.Failed()) { + this->StartFailure("Failed to change working directory to " + + this->TestProperties->Directory + " : " + + std::strerror(workdir.GetLastResult())); + return true; + } + this->StartTest(this->TotalNumberOfTests); return true; } @@ -386,6 +390,37 @@ void cmCTestRunTest::MemCheckPostProcess() handler->PostProcessTest(this->TestResult, this->Index); } +void cmCTestRunTest::StartFailure(std::string const& output) +{ + // Still need to log the Start message so the test summary records our + // attempt to start this test + cmCTestLog(this->CTest, HANDLER_OUTPUT, + std::setw(2 * getNumWidth(this->TotalNumberOfTests) + 8) + << "Start " + << std::setw(getNumWidth(this->TestHandler->GetMaxIndex())) + << this->TestProperties->Index << ": " + << this->TestProperties->Name << std::endl); + + this->ProcessOutput.clear(); + if (!output.empty()) { + *this->TestHandler->LogFile << output << std::endl; + cmCTestLog(this->CTest, ERROR_MESSAGE, output << std::endl); + } + + this->TestResult.Properties = this->TestProperties; + this->TestResult.ExecutionTime = cmDuration::zero(); + this->TestResult.CompressOutput = false; + this->TestResult.ReturnValue = -1; + this->TestResult.CompletionStatus = "Failed to start"; + this->TestResult.Status = cmCTestTestHandler::NOT_RUN; + this->TestResult.TestCount = this->TestProperties->Index; + this->TestResult.Name = this->TestProperties->Name; + this->TestResult.Path = this->TestProperties->Directory; + this->TestResult.Output = output; + this->TestResult.FullCommandLine.clear(); + this->TestProcess = cm::make_unique<cmProcess>(*this); +} + // Starts the execution of a test. Returns once it has started bool cmCTestRunTest::StartTest(size_t total) { @@ -515,7 +550,8 @@ bool cmCTestRunTest::StartTest(size_t total) } return this->ForkProcess(timeout, this->TestProperties->ExplicitTimeout, - &this->TestProperties->Environment); + &this->TestProperties->Environment, + &this->TestProperties->Affinity); } void cmCTestRunTest::ComputeArguments() @@ -591,7 +627,8 @@ void cmCTestRunTest::DartProcessing() } bool cmCTestRunTest::ForkProcess(cmDuration testTimeOut, bool explicitTimeout, - std::vector<std::string>* environment) + std::vector<std::string>* environment, + std::vector<size_t>* affinity) { this->TestProcess = cm::make_unique<cmProcess>(*this); this->TestProcess->SetId(this->Index); @@ -637,7 +674,8 @@ bool cmCTestRunTest::ForkProcess(cmDuration testTimeOut, bool explicitTimeout, cmSystemTools::AppendEnv(*environment); } - return this->TestProcess->StartProcess(this->MultiTestHandler.Loop); + return this->TestProcess->StartProcess(this->MultiTestHandler.Loop, + affinity); } void cmCTestRunTest::WriteLogOutputTop(size_t completed, size_t total) diff --git a/Source/CTest/cmCTestRunTest.h b/Source/CTest/cmCTestRunTest.h index 4d57357..3b1d674 100644 --- a/Source/CTest/cmCTestRunTest.h +++ b/Source/CTest/cmCTestRunTest.h @@ -74,6 +74,8 @@ public: bool StartAgain(); + void StartFailure(std::string const& output); + cmCTest* GetCTest() const { return this->CTest; } void FinalizeTest(); @@ -83,7 +85,8 @@ private: void DartProcessing(); void ExeNotFound(std::string exe); bool ForkProcess(cmDuration testTimeOut, bool explicitTimeout, - std::vector<std::string>* environment); + std::vector<std::string>* environment, + std::vector<size_t>* affinity); void WriteLogOutputTop(size_t completed, size_t total); // Run post processing of the process output for MemCheck void MemCheckPostProcess(); diff --git a/Source/CTest/cmCTestScriptHandler.cxx b/Source/CTest/cmCTestScriptHandler.cxx index e0bffd4..5fff730 100644 --- a/Source/CTest/cmCTestScriptHandler.cxx +++ b/Source/CTest/cmCTestScriptHandler.cxx @@ -527,7 +527,7 @@ int cmCTestScriptHandler::RunConfigurationScript( return result; } - // only run the curent script if we should + // only run the current script if we should if (this->Makefile && this->Makefile->IsOn("CTEST_RUN_CURRENT_SCRIPT") && this->ShouldRunCurrentScript) { return this->RunCurrentScript(); diff --git a/Source/CTest/cmCTestStartCommand.cxx b/Source/CTest/cmCTestStartCommand.cxx index 38ee623..367616c 100644 --- a/Source/CTest/cmCTestStartCommand.cxx +++ b/Source/CTest/cmCTestStartCommand.cxx @@ -28,41 +28,41 @@ bool cmCTestStartCommand::InitialPass(std::vector<std::string> const& args, } size_t cnt = 0; - const char* smodel = args[cnt].c_str(); + const char* smodel = nullptr; const char* src_dir = nullptr; const char* bld_dir = nullptr; - cnt++; - - this->CTest->SetSpecificTrack(nullptr); - if (cnt < args.size() - 1) { + while (cnt < args.size()) { if (args[cnt] == "TRACK") { cnt++; + if (cnt >= args.size() || args[cnt] == "APPEND" || + args[cnt] == "QUIET") { + this->SetError("TRACK argument missing track name"); + return false; + } this->CTest->SetSpecificTrack(args[cnt].c_str()); cnt++; - } - } - - if (cnt < args.size()) { - if (args[cnt] == "APPEND") { + } else if (args[cnt] == "APPEND") { cnt++; this->CreateNewTag = false; - } - } - if (cnt < args.size()) { - if (args[cnt] == "QUIET") { + } else if (args[cnt] == "QUIET") { cnt++; this->Quiet = true; - } - } - - if (cnt < args.size()) { - src_dir = args[cnt].c_str(); - cnt++; - if (cnt < args.size()) { + } else if (!smodel) { + smodel = args[cnt].c_str(); + cnt++; + } else if (!src_dir) { + src_dir = args[cnt].c_str(); + cnt++; + } else if (!bld_dir) { bld_dir = args[cnt].c_str(); + cnt++; + } else { + this->SetError("Too many arguments"); + return false; } } + if (!src_dir) { src_dir = this->Makefile->GetDefinition("CTEST_SOURCE_DIRECTORY"); } @@ -79,6 +79,11 @@ bool cmCTestStartCommand::InitialPass(std::vector<std::string> const& args, "as an argument or set CTEST_BINARY_DIRECTORY"); return false; } + if (!smodel && this->CreateNewTag) { + this->SetError("no test model specified and APPEND not specified. Specify " + "either a test model or the APPEND argument"); + return false; + } cmSystemTools::AddKeepPath(src_dir); cmSystemTools::AddKeepPath(bld_dir); @@ -92,11 +97,20 @@ bool cmCTestStartCommand::InitialPass(std::vector<std::string> const& args, this->CTest->SetCTestConfiguration("BuildDirectory", binaryDir.c_str(), this->Quiet); - cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "Run dashboard with model " - << smodel << std::endl - << " Source directory: " << src_dir << std::endl - << " Build directory: " << bld_dir << std::endl, - this->Quiet); + if (smodel) { + cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "Run dashboard with model " + << smodel << std::endl + << " Source directory: " << src_dir << std::endl + << " Build directory: " << bld_dir << std::endl, + this->Quiet); + } else { + cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "Run dashboard with " + "to-be-determined model" + << std::endl + << " Source directory: " << src_dir << std::endl + << " Build directory: " << bld_dir << std::endl, + this->Quiet); + } const char* track = this->CTest->GetSpecificTrack(); if (track) { cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, @@ -128,7 +142,12 @@ bool cmCTestStartCommand::InitialPass(std::vector<std::string> const& args, this->CTest->SetRunCurrentScript(false); this->CTest->SetSuppressUpdatingCTestConfiguration(true); - int model = this->CTest->GetTestModelFromString(smodel); + int model; + if (smodel) { + model = this->CTest->GetTestModelFromString(smodel); + } else { + model = cmCTest::UNKNOWN; + } this->CTest->SetTestModel(model); this->CTest->SetProduceXML(true); diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx index 08d05c8..3bab81e 100644 --- a/Source/CTest/cmCTestSubmitHandler.cxx +++ b/Source/CTest/cmCTestSubmitHandler.cxx @@ -7,6 +7,7 @@ #include "cm_jsoncpp_value.h" #include "cmsys/Process.h" #include <chrono> +#include <cstring> #include <sstream> #include <stdio.h> #include <stdlib.h> @@ -1532,6 +1533,15 @@ int cmCTestSubmitHandler::ProcessHandler() // change to the build directory so that we can uses a relative path // on windows since scp doesn't support "c:" a drive in the path cmWorkingDirectory workdir(buildDirectory); + if (workdir.Failed()) { + cmCTestLog(this->CTest, ERROR_MESSAGE, + " Failed to change directory to " + << buildDirectory << " : " + << std::strerror(workdir.GetLastResult()) << std::endl); + ofs << " Failed to change directory to " << buildDirectory << " : " + << std::strerror(workdir.GetLastResult()) << std::endl; + return -1; + } if (!this->SubmitUsingSCP(this->CTest->GetCTestConfiguration("ScpCommand"), "Testing/" + this->CTest->GetCurrentTag(), files, @@ -1551,6 +1561,15 @@ int cmCTestSubmitHandler::ProcessHandler() // change to the build directory so that we can uses a relative path // on windows since scp doesn't support "c:" a drive in the path cmWorkingDirectory workdir(buildDirectory); + if (workdir.Failed()) { + cmCTestLog(this->CTest, ERROR_MESSAGE, + " Failed to change directory to " + << buildDirectory << " : " + << std::strerror(workdir.GetLastResult()) << std::endl); + ofs << " Failed to change directory to " << buildDirectory << " : " + << std::strerror(workdir.GetLastResult()) << std::endl; + return -1; + } cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " Change directory: " << buildDirectory << std::endl, this->Quiet); diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx index 84d8926..cbaf984 100644 --- a/Source/CTest/cmCTestTestHandler.cxx +++ b/Source/CTest/cmCTestTestHandler.cxx @@ -6,6 +6,7 @@ #include <cmsys/Base64.h> #include <cmsys/Directory.hxx> #include <cmsys/RegularExpression.hxx> +#include <cstring> #include <functional> #include <iomanip> #include <iterator> @@ -14,7 +15,6 @@ #include <sstream> #include <stdio.h> #include <stdlib.h> -#include <string.h> #include <time.h> #include "cmAlgorithms.h" @@ -85,6 +85,11 @@ bool cmCTestSubdirCommand::InitialPass(std::vector<std::string> const& args, bool readit = false; { cmWorkingDirectory workdir(fname); + if (workdir.Failed()) { + this->SetError("Failed to change directory to " + fname + " : " + + std::strerror(workdir.GetLastResult())); + return false; + } const char* testFilename; if (cmSystemTools::FileExists("CTestTestfile.cmake")) { // does the CTestTestfile.cmake exist ? @@ -2165,6 +2170,9 @@ bool cmCTestTestHandler::SetTestsProperties( rt.Processors = 1; } } + if (key == "PROCESSOR_AFFINITY") { + rt.WantAffinity = cmSystemTools::IsOn(val.c_str()); + } if (key == "SKIP_RETURN_CODE") { rt.SkipReturnCode = atoi(val.c_str()); if (rt.SkipReturnCode < 0 || rt.SkipReturnCode > 255) { @@ -2336,6 +2344,7 @@ bool cmCTestTestHandler::AddTest(const std::vector<std::string>& args) test.ExplicitTimeout = false; test.Cost = 0; test.Processors = 1; + test.WantAffinity = false; test.SkipReturnCode = -1; test.PreviousRuns = 0; if (this->UseIncludeRegExpFlag && diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h index f4978b6..d2694a1 100644 --- a/Source/CTest/cmCTestTestHandler.h +++ b/Source/CTest/cmCTestTestHandler.h @@ -130,6 +130,8 @@ public: int Index; // Requested number of process slots int Processors; + bool WantAffinity; + std::vector<size_t> Affinity; // return code of test which will mark test as "not run" int SkipReturnCode; std::vector<std::string> Environment; diff --git a/Source/CTest/cmProcess.cxx b/Source/CTest/cmProcess.cxx index 09ed0a9..5c9b169 100644 --- a/Source/CTest/cmProcess.cxx +++ b/Source/CTest/cmProcess.cxx @@ -83,7 +83,7 @@ void cmProcess::SetCommandArguments(std::vector<std::string> const& args) this->Arguments = args; } -bool cmProcess::StartProcess(uv_loop_t& loop) +bool cmProcess::StartProcess(uv_loop_t& loop, std::vector<size_t>* affinity) { this->ProcessState = cmProcess::State::Error; if (this->Command.empty()) { @@ -138,6 +138,22 @@ bool cmProcess::StartProcess(uv_loop_t& loop) options.stdio_count = 3; // in, out and err options.exit_cb = &cmProcess::OnExitCB; options.stdio = stdio; +#if !defined(CMAKE_USE_SYSTEM_LIBUV) + std::vector<char> cpumask; + if (affinity && !affinity->empty()) { + cpumask.resize(static_cast<size_t>(uv_cpumask_size()), 0); + for (auto p : *affinity) { + cpumask[p] = 1; + } + options.cpumask = cpumask.data(); + options.cpumask_size = cpumask.size(); + } else { + options.cpumask = nullptr; + options.cpumask_size = 0; + } +#else + static_cast<void>(affinity); +#endif status = uv_read_start(pipe_reader, &cmProcess::OnAllocateCB, &cmProcess::OnReadCB); diff --git a/Source/CTest/cmProcess.h b/Source/CTest/cmProcess.h index 20e24b9..b2d87fa 100644 --- a/Source/CTest/cmProcess.h +++ b/Source/CTest/cmProcess.h @@ -36,7 +36,7 @@ public: void ChangeTimeout(cmDuration t); void ResetStartTime(); // Return true if the process starts - bool StartProcess(uv_loop_t& loop); + bool StartProcess(uv_loop_t& loop, std::vector<size_t>* affinity); enum class State { diff --git a/Source/Checks/Curses.cmake b/Source/Checks/Curses.cmake new file mode 100644 index 0000000..46dc770 --- /dev/null +++ b/Source/Checks/Curses.cmake @@ -0,0 +1,41 @@ +message(STATUS "Checking for curses support") + +# Try compiling a simple project using curses. +# Pass in any cache entries that the user may have set. +set(CMakeCheckCurses_ARGS "") +foreach(v + CURSES_INCLUDE_PATH + CURSES_CURSES_LIBRARY + CURSES_NCURSES_LIBRARY + CURSES_EXTRA_LIBRARY + CURSES_FORM_LIBRARY + ) + if(${v}) + list(APPEND CMakeCheckCurses_ARGS -D${v}=${${v}}) + endif() +endforeach() +file(REMOVE_RECURSE "${CMake_BINARY_DIR}/Source/Checks/Curses-build") +try_compile(CMakeCheckCurses_COMPILED + ${CMake_BINARY_DIR}/Source/Checks/Curses-build + ${CMake_SOURCE_DIR}/Source/Checks/Curses + CheckCurses # project name + CheckCurses # target name + CMAKE_FLAGS + "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS}" + ${CMakeCheckCurses_ARGS} + OUTPUT_VARIABLE CMakeCheckCurses_OUTPUT + ) + +# Covnert result from cache entry to normal variable. +set(CMakeCheckCurses_COMPILED "${CMakeCheckCurses_COMPILED}") +unset(CMakeCheckCurses_COMPILED CACHE) + +if(CMakeCheckCurses_COMPILED) + message(STATUS "Checking for curses support - Success") + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Checking for curses support passed with the following output:\n${CMakeCheckCurses_OUTPUT}\n\n") +else() + message(STATUS "Checking for curses support - Failed") + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Checking for curses support failed with the following output:\n${CMakeCheckCurses_OUTPUT}\n\n") +endif() diff --git a/Source/Checks/Curses/CMakeLists.txt b/Source/Checks/Curses/CMakeLists.txt new file mode 100644 index 0000000..17318a3 --- /dev/null +++ b/Source/Checks/Curses/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.1) +if(POLICY CMP0060) + cmake_policy(SET CMP0060 NEW) +endif() +project(CheckCurses C) + +set(CURSES_NEED_NCURSES TRUE) +find_package(Curses) +if(NOT CURSES_FOUND) + return() +endif() +include_directories(${CURSES_INCLUDE_DIRS}) +add_executable(CheckCurses CheckCurses.c) +target_link_libraries(CheckCurses ${CURSES_LIBRARIES}) + +foreach(h + CURSES_HAVE_CURSES_H + CURSES_HAVE_NCURSES_H + CURSES_HAVE_NCURSES_NCURSES_H + CURSES_HAVE_NCURSES_CURSES_H + ) + if(${h}) + target_compile_definitions(CheckCurses PRIVATE ${h}) + endif() +endforeach() diff --git a/Source/Checks/Curses/CheckCurses.c b/Source/Checks/Curses/CheckCurses.c new file mode 100644 index 0000000..857ae28 --- /dev/null +++ b/Source/Checks/Curses/CheckCurses.c @@ -0,0 +1,15 @@ +#if defined(CURSES_HAVE_NCURSES_H) +#include <ncurses.h> +#elif defined(CURSES_HAVE_NCURSES_NCURSES_H) +#include <ncurses/ncurses.h> +#elif defined(CURSES_HAVE_NCURSES_CURSES_H) +#include <ncurses/curses.h> +#else +#include <curses.h> +#endif + +int main() +{ + curses_version(); + return 0; +} diff --git a/Source/CursesDialog/cmCursesStandardIncludes.h b/Source/CursesDialog/cmCursesStandardIncludes.h index 332d2af..60bad94 100644 --- a/Source/CursesDialog/cmCursesStandardIncludes.h +++ b/Source/CursesDialog/cmCursesStandardIncludes.h @@ -5,6 +5,11 @@ #include "cmConfigure.h" // IWYU pragma: keep +// Record whether __attribute__ is currently defined. See purpose below. +#ifndef __attribute__ +#define cm_no__attribute__ +#endif + #if defined(__hpux) #define _BOOL_DEFINED #include <sys/time.h> @@ -29,4 +34,12 @@ inline void curses_clear() #undef erase #undef clear +// The curses headers on some platforms (e.g. Solaris) may +// define __attribute__ as a macro. This breaks C++ headers +// in some cases, so undefine it now. +#if defined(cm_no__attribute__) && defined(__attribute__) +#undef __attribute__ +#endif +#undef cm_no__attribute__ + #endif // cmCursesStandardIncludes_h diff --git a/Source/LexerParser/cmFortranParser.cxx b/Source/LexerParser/cmFortranParser.cxx index 2b3452f..00c8a8a 100644 --- a/Source/LexerParser/cmFortranParser.cxx +++ b/Source/LexerParser/cmFortranParser.cxx @@ -1563,7 +1563,7 @@ yyreduce: #line 119 "cmFortranParser.y" /* yacc.c:1646 */ { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); - cmFortranParser_RuleUse(parser, (yyvsp[-4].string)); + cmFortranParser_RuleSubmodule(parser, (yyvsp[-4].string), (yyvsp[-2].string)); free((yyvsp[-4].string)); free((yyvsp[-2].string)); } @@ -1574,7 +1574,7 @@ yyreduce: #line 125 "cmFortranParser.y" /* yacc.c:1646 */ { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); - cmFortranParser_RuleUse(parser, (yyvsp[-6].string)); + cmFortranParser_RuleSubmoduleNested(parser, (yyvsp[-6].string), (yyvsp[-4].string), (yyvsp[-2].string)); free((yyvsp[-6].string)); free((yyvsp[-4].string)); free((yyvsp[-2].string)); diff --git a/Source/LexerParser/cmFortranParser.y b/Source/LexerParser/cmFortranParser.y index acfb40a..5e09248 100644 --- a/Source/LexerParser/cmFortranParser.y +++ b/Source/LexerParser/cmFortranParser.y @@ -118,13 +118,13 @@ stmt: } | SUBMODULE LPAREN WORD RPAREN WORD other EOSTMT { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); - cmFortranParser_RuleUse(parser, $3); + cmFortranParser_RuleSubmodule(parser, $3, $5); free($3); free($5); } | SUBMODULE LPAREN WORD COLON WORD RPAREN WORD other EOSTMT { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); - cmFortranParser_RuleUse(parser, $3); + cmFortranParser_RuleSubmoduleNested(parser, $3, $5, $7); free($3); free($5); free($7); diff --git a/Source/cmAddCompileDefinitionsCommand.cxx b/Source/cmAddCompileDefinitionsCommand.cxx new file mode 100644 index 0000000..0474819 --- /dev/null +++ b/Source/cmAddCompileDefinitionsCommand.cxx @@ -0,0 +1,20 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmAddCompileDefinitionsCommand.h" + +#include "cmMakefile.h" + +class cmExecutionStatus; + +bool cmAddCompileDefinitionsCommand::InitialPass( + std::vector<std::string> const& args, cmExecutionStatus&) +{ + if (args.empty()) { + return true; + } + + for (std::string const& i : args) { + this->Makefile->AddCompileDefinition(i); + } + return true; +} diff --git a/Source/cmAddCompileDefinitionsCommand.h b/Source/cmAddCompileDefinitionsCommand.h new file mode 100644 index 0000000..e985dca --- /dev/null +++ b/Source/cmAddCompileDefinitionsCommand.h @@ -0,0 +1,31 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmAddCompileDefinitionsCommand_h +#define cmAddCompileDefinitionsCommand_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <string> +#include <vector> + +#include "cmCommand.h" + +class cmExecutionStatus; + +class cmAddCompileDefinitionsCommand : public cmCommand +{ +public: + /** + * This is a virtual constructor for the command. + */ + cmCommand* Clone() override { return new cmAddCompileDefinitionsCommand; } + + /** + * 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; +}; + +#endif diff --git a/Source/cmAffinity.cxx b/Source/cmAffinity.cxx new file mode 100644 index 0000000..bdf1f42 --- /dev/null +++ b/Source/cmAffinity.cxx @@ -0,0 +1,62 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmAffinity.h" + +#include "cm_uv.h" + +#ifndef CMAKE_USE_SYSTEM_LIBUV +#ifdef _WIN32 +#define CM_HAVE_CPU_AFFINITY +#include <windows.h> +#elif defined(__linux__) || defined(__FreeBSD__) +#define CM_HAVE_CPU_AFFINITY +#include <pthread.h> +#include <sched.h> +#if defined(__FreeBSD__) +#include <pthread_np.h> +#include <sys/cpuset.h> +#include <sys/param.h> +#endif +#if defined(__linux__) +typedef cpu_set_t cm_cpuset_t; +#else +typedef cpuset_t cm_cpuset_t; +#endif +#endif +#endif + +namespace cmAffinity { + +std::set<size_t> GetProcessorsAvailable() +{ + std::set<size_t> processorsAvailable; +#ifdef CM_HAVE_CPU_AFFINITY + int cpumask_size = uv_cpumask_size(); + if (cpumask_size > 0) { +#ifdef _WIN32 + DWORD_PTR procmask; + DWORD_PTR sysmask; + if (GetProcessAffinityMask(GetCurrentProcess(), &procmask, &sysmask) != + 0) { + for (int i = 0; i < cpumask_size; ++i) { + if (procmask & (((DWORD_PTR)1) << i)) { + processorsAvailable.insert(i); + } + } + } +#else + cm_cpuset_t cpuset; + CPU_ZERO(&cpuset); // NOLINT(clang-tidy) + if (pthread_getaffinity_np(pthread_self(), sizeof(cpuset), &cpuset) == 0) { + for (int i = 0; i < cpumask_size; ++i) { + if (CPU_ISSET(i, &cpuset)) { + processorsAvailable.insert(i); + } + } + } +#endif + } +#endif + return processorsAvailable; +} +} diff --git a/Source/cmAffinity.h b/Source/cmAffinity.h new file mode 100644 index 0000000..3775bae --- /dev/null +++ b/Source/cmAffinity.h @@ -0,0 +1,12 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once +#include "cmConfigure.h" // IWYU pragma: keep + +#include <cstddef> +#include <set> + +namespace cmAffinity { + +std::set<size_t> GetProcessorsAvailable(); +} diff --git a/Source/cmAlgorithms.h b/Source/cmAlgorithms.h index 3380b78..c4eb62b 100644 --- a/Source/cmAlgorithms.h +++ b/Source/cmAlgorithms.h @@ -13,6 +13,7 @@ #include <sstream> #include <string.h> #include <string> +#include <unordered_set> #include <utility> #include <vector> @@ -275,55 +276,19 @@ typename Range::const_iterator cmRemoveMatching(Range& r, MatchRange const& m) ContainerAlgorithms::BinarySearcher<MatchRange>(m)); } -namespace ContainerAlgorithms { - -template <typename Range, typename T = typename Range::value_type> -struct RemoveDuplicatesAPI -{ - typedef typename Range::const_iterator const_iterator; - typedef typename Range::const_iterator value_type; - - static bool lessThan(value_type a, value_type b) { return *a < *b; } - static value_type uniqueValue(const_iterator a) { return a; } - template <typename It> - static bool valueCompare(It it, const_iterator it2) - { - return **it != *it2; - } -}; - -template <typename Range, typename T> -struct RemoveDuplicatesAPI<Range, T*> -{ - typedef typename Range::const_iterator const_iterator; - typedef T* value_type; - - static bool lessThan(value_type a, value_type b) { return a < b; } - static value_type uniqueValue(const_iterator a) { return *a; } - template <typename It> - static bool valueCompare(It it, const_iterator it2) - { - return *it != *it2; - } -}; -} - template <typename Range> typename Range::const_iterator cmRemoveDuplicates(Range& r) { - typedef typename ContainerAlgorithms::RemoveDuplicatesAPI<Range> API; - typedef typename API::value_type T; - std::vector<T> unique; - unique.reserve(r.size()); + typedef typename Range::value_type T; + std::unordered_set<T> unique; std::vector<size_t> indices; size_t count = 0; const typename Range::const_iterator end = r.end(); for (typename Range::const_iterator it = r.begin(); it != end; ++it, ++count) { - const typename std::vector<T>::iterator low = std::lower_bound( - unique.begin(), unique.end(), API::uniqueValue(it), API::lessThan); - if (low == unique.end() || API::valueCompare(low, it)) { - unique.insert(low, API::uniqueValue(it)); + const typename std::unordered_set<T>::iterator occur = unique.find(*it); + if (occur == unique.end()) { + unique.insert(*it); } else { indices.push_back(count); } diff --git a/Source/cmCMakeMinimumRequired.cxx b/Source/cmCMakeMinimumRequired.cxx index bcc41fc..2b51976 100644 --- a/Source/cmCMakeMinimumRequired.cxx +++ b/Source/cmCMakeMinimumRequired.cxx @@ -42,12 +42,27 @@ bool cmCMakeMinimumRequired::InitialPass(std::vector<std::string> const& args, // Make sure there was a version to check. if (version_string.empty()) { - return this->EnforceUnknownArguments(); + return this->EnforceUnknownArguments(std::string()); + } + + // Separate the <min> version and any trailing ...<max> component. + std::string::size_type const dd = version_string.find("..."); + std::string const version_min = version_string.substr(0, dd); + std::string const version_max = dd != std::string::npos + ? version_string.substr(dd + 3, std::string::npos) + : std::string(); + if (dd != std::string::npos && + (version_min.empty() || version_max.empty())) { + std::ostringstream e; + e << "VERSION \"" << version_string + << "\" does not have a version on both sides of \"...\"."; + this->SetError(e.str()); + return false; } // Save the required version string. this->Makefile->AddDefinition("CMAKE_MINIMUM_REQUIRED_VERSION", - version_string.c_str()); + version_min.c_str()); // Get the current version number. unsigned int current_major = cmVersion::GetMajorVersion(); @@ -61,10 +76,10 @@ bool cmCMakeMinimumRequired::InitialPass(std::vector<std::string> const& args, unsigned int required_minor = 0; unsigned int required_patch = 0; unsigned int required_tweak = 0; - if (sscanf(version_string.c_str(), "%u.%u.%u.%u", &required_major, + if (sscanf(version_min.c_str(), "%u.%u.%u.%u", &required_major, &required_minor, &required_patch, &required_tweak) < 2) { std::ostringstream e; - e << "could not parse VERSION \"" << version_string << "\"."; + e << "could not parse VERSION \"" << version_min << "\"."; this->SetError(e.str()); return false; } @@ -78,7 +93,7 @@ bool cmCMakeMinimumRequired::InitialPass(std::vector<std::string> const& args, current_patch == required_patch && current_tweak < required_tweak)) { // The current version is too low. std::ostringstream e; - e << "CMake " << version_string + e << "CMake " << version_min << " or higher is required. You are running version " << cmVersion::GetCMakeVersion(); this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str()); @@ -87,7 +102,7 @@ bool cmCMakeMinimumRequired::InitialPass(std::vector<std::string> const& args, } // The version is not from the future, so enforce unknown arguments. - if (!this->EnforceUnknownArguments()) { + if (!this->EnforceUnknownArguments(version_max)) { return false; } @@ -95,22 +110,47 @@ bool cmCMakeMinimumRequired::InitialPass(std::vector<std::string> const& args, this->Makefile->IssueMessage( cmake::AUTHOR_WARNING, "Compatibility with CMake < 2.4 is not supported by CMake >= 3.0."); - this->Makefile->SetPolicyVersion("2.4"); + this->Makefile->SetPolicyVersion("2.4", version_max); } else { - this->Makefile->SetPolicyVersion(version_string.c_str()); + this->Makefile->SetPolicyVersion(version_min, version_max); } return true; } -bool cmCMakeMinimumRequired::EnforceUnknownArguments() +bool cmCMakeMinimumRequired::EnforceUnknownArguments( + std::string const& version_max) { - if (!this->UnknownArguments.empty()) { - std::ostringstream e; - e << "called with unknown argument \"" << this->UnknownArguments[0] - << "\"."; - this->SetError(e.str()); - return false; + if (this->UnknownArguments.empty()) { + return true; } - return true; + + // Consider the max version if at least two components were given. + unsigned int max_major = 0; + unsigned int max_minor = 0; + unsigned int max_patch = 0; + unsigned int max_tweak = 0; + if (sscanf(version_max.c_str(), "%u.%u.%u.%u", &max_major, &max_minor, + &max_patch, &max_tweak) >= 2) { + unsigned int current_major = cmVersion::GetMajorVersion(); + unsigned int current_minor = cmVersion::GetMinorVersion(); + unsigned int current_patch = cmVersion::GetPatchVersion(); + unsigned int current_tweak = cmVersion::GetTweakVersion(); + + if ((current_major < max_major) || + (current_major == max_major && current_minor < max_minor) || + (current_major == max_major && current_minor == max_minor && + current_patch < max_patch) || + (current_major == max_major && current_minor == max_minor && + current_patch == max_patch && current_tweak < max_tweak)) { + // A ...<max> version was given that is larger than the current + // version of CMake, so tolerate unknown arguments. + return true; + } + } + + std::ostringstream e; + e << "called with unknown argument \"" << this->UnknownArguments[0] << "\"."; + this->SetError(e.str()); + return false; } diff --git a/Source/cmCMakeMinimumRequired.h b/Source/cmCMakeMinimumRequired.h index 18d9460..f9b61e1 100644 --- a/Source/cmCMakeMinimumRequired.h +++ b/Source/cmCMakeMinimumRequired.h @@ -34,7 +34,7 @@ public: private: std::vector<std::string> UnknownArguments; - bool EnforceUnknownArguments(); + bool EnforceUnknownArguments(std::string const& version_max); }; #endif diff --git a/Source/cmCMakePolicyCommand.cxx b/Source/cmCMakePolicyCommand.cxx index 3ccc815..adf9ef8 100644 --- a/Source/cmCMakePolicyCommand.cxx +++ b/Source/cmCMakePolicyCommand.cxx @@ -95,7 +95,11 @@ bool cmCMakePolicyCommand::HandleSetMode(std::vector<std::string> const& args) bool cmCMakePolicyCommand::HandleGetMode(std::vector<std::string> const& args) { - if (args.size() != 3) { + bool parent_scope = false; + if (args.size() == 4 && args[3] == "PARENT_SCOPE") { + // Undocumented PARENT_SCOPE option for use within CMake. + parent_scope = true; + } else if (args.size() != 3) { this->SetError("GET must be given exactly 2 additional arguments."); return false; } @@ -115,7 +119,8 @@ bool cmCMakePolicyCommand::HandleGetMode(std::vector<std::string> const& args) } // Lookup the policy setting. - cmPolicies::PolicyStatus status = this->Makefile->GetPolicyStatus(pid); + cmPolicies::PolicyStatus status = + this->Makefile->GetPolicyStatus(pid, parent_scope); switch (status) { case cmPolicies::OLD: // Report that the policy is set to OLD. @@ -156,6 +161,23 @@ bool cmCMakePolicyCommand::HandleVersionMode( this->SetError("VERSION given too many arguments"); return false; } - this->Makefile->SetPolicyVersion(args[1].c_str()); + std::string const& version_string = args[1]; + + // Separate the <min> version and any trailing ...<max> component. + std::string::size_type const dd = version_string.find("..."); + std::string const version_min = version_string.substr(0, dd); + std::string const version_max = dd != std::string::npos + ? version_string.substr(dd + 3, std::string::npos) + : std::string(); + if (dd != std::string::npos && + (version_min.empty() || version_max.empty())) { + std::ostringstream e; + e << "VERSION \"" << version_string + << "\" does not have a version on both sides of \"...\"."; + this->SetError(e.str()); + return false; + } + + this->Makefile->SetPolicyVersion(version_min, version_max); return true; } diff --git a/Source/cmCPluginAPI.cxx b/Source/cmCPluginAPI.cxx index 1ec76ac..8e7e3ad 100644 --- a/Source/cmCPluginAPI.cxx +++ b/Source/cmCPluginAPI.cxx @@ -405,7 +405,8 @@ char CCONV* cmExpandVariablesInString(void* arg, const char* source, { cmMakefile* mf = static_cast<cmMakefile*>(arg); std::string barf = source; - std::string result = mf->ExpandVariablesInString(barf, escapeQuotes, atOnly); + std::string const& result = + mf->ExpandVariablesInString(barf, escapeQuotes, atOnly); return strdup(result.c_str()); } @@ -664,7 +665,7 @@ void CCONV cmSourceFileSetName(void* arg, const char* name, const char* dir, // First try and see whether the listed file can be found // as is without extensions added on. std::string hname = pathname; - if (cmSystemTools::FileExists(hname.c_str())) { + if (cmSystemTools::FileExists(hname)) { sf->SourceName = cmSystemTools::GetFilenamePath(name); if (!sf->SourceName.empty()) { sf->SourceName += "/"; @@ -691,7 +692,7 @@ void CCONV cmSourceFileSetName(void* arg, const char* name, const char* dir, hname = pathname; hname += "."; hname += *ext; - if (cmSystemTools::FileExists(hname.c_str())) { + if (cmSystemTools::FileExists(hname)) { sf->SourceExtension = *ext; sf->FullPath = hname; return; @@ -704,7 +705,7 @@ void CCONV cmSourceFileSetName(void* arg, const char* name, const char* dir, hname = pathname; hname += "."; hname += *ext; - if (cmSystemTools::FileExists(hname.c_str())) { + if (cmSystemTools::FileExists(hname)) { sf->SourceExtension = *ext; sf->FullPath = hname; return; diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index 3fccc38..2c32dea 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx @@ -474,11 +474,13 @@ int cmCTest::Initialize(const char* binary_dir, cmCTestStartCommand* command) day != lctime->tm_mday) { tag.clear(); } - std::string tagmode; - if (cmSystemTools::GetLineFromStream(tfin, tagmode)) { - if (tagmode.size() > 4 && !this->Parts[PartStart]) { - this->TestModel = cmCTest::GetTestModelFromString(tagmode.c_str()); - } + std::string track; + if (cmSystemTools::GetLineFromStream(tfin, track)) { + this->SpecificTrack = track; + } + std::string model; + if (cmSystemTools::GetLineFromStream(tfin, model)) { + this->TestModel = GetTestModelFromString(model.c_str()); } tfin.close(); } @@ -502,6 +504,17 @@ int cmCTest::Initialize(const char* binary_dir, cmCTestStartCommand* command) if (ofs) { ofs << tag << std::endl; ofs << this->GetTestModelString() << std::endl; + switch (this->TestModel) { + case cmCTest::EXPERIMENTAL: + ofs << "Experimental" << std::endl; + break; + case cmCTest::NIGHTLY: + ofs << "Nightly" << std::endl; + break; + case cmCTest::CONTINUOUS: + ofs << "Continuous" << std::endl; + break; + } } ofs.close(); if (nullptr == command) { @@ -512,8 +525,16 @@ int cmCTest::Initialize(const char* binary_dir, cmCTestStartCommand* command) } } } else { + std::string track; + std::string modelStr; + int model = cmCTest::UNKNOWN; + if (tfin) { cmSystemTools::GetLineFromStream(tfin, tag); + cmSystemTools::GetLineFromStream(tfin, track); + if (cmSystemTools::GetLineFromStream(tfin, modelStr)) { + model = GetTestModelFromString(modelStr.c_str()); + } tfin.close(); } @@ -523,6 +544,35 @@ int cmCTest::Initialize(const char* binary_dir, cmCTestStartCommand* command) return 0; } + if (this->TestModel == cmCTest::UNKNOWN) { + if (model == cmCTest::UNKNOWN) { + cmCTestLog(this, ERROR_MESSAGE, + "TAG file does not contain model and " + "no model specified in start command" + << std::endl); + return 0; + } + + this->SetTestModel(model); + } + + if (model != this->TestModel && model != cmCTest::UNKNOWN && + this->TestModel != cmCTest::UNKNOWN) { + cmCTestOptionalLog(this, WARNING, "Model given in TAG does not match " + "model given in ctest_start()" + << std::endl, + quiet); + } + + if (!this->SpecificTrack.empty() && track != this->SpecificTrack) { + cmCTestOptionalLog(this, WARNING, "Track given in TAG does not match " + "track given in ctest_start()" + << std::endl, + quiet); + } else { + this->SpecificTrack = track; + } + cmCTestOptionalLog(this, OUTPUT, " Use existing tag: " << tag << " - " << this->GetTestModelString() << std::endl, diff --git a/Source/cmCTest.h b/Source/cmCTest.h index 673a40e..603bb41 100644 --- a/Source/cmCTest.h +++ b/Source/cmCTest.h @@ -295,9 +295,10 @@ public: enum { - EXPERIMENTAL, - NIGHTLY, - CONTINUOUS + UNKNOWN = -1, + EXPERIMENTAL = 0, + NIGHTLY = 1, + CONTINUOUS = 2, }; /** provide some more detailed info on the return code for ctest */ @@ -347,7 +348,7 @@ public: const std::string& cmake_var, bool suppress = false); - /** Make string safe to be send as an URL */ + /** Make string safe to be sent as a URL */ static std::string MakeURLSafe(const std::string&); /** Decode a URL to the original string. */ diff --git a/Source/cmCacheManager.cxx b/Source/cmCacheManager.cxx index fab2445..13407e5 100644 --- a/Source/cmCacheManager.cxx +++ b/Source/cmCacheManager.cxx @@ -8,6 +8,7 @@ #include <sstream> #include <stdio.h> #include <string.h> +#include <string> #include "cmGeneratedFileStream.h" #include "cmMessenger.h" @@ -160,14 +161,14 @@ bool cmCacheManager::LoadCache(const std::string& path, bool internal, currentcwd += "/CMakeCache.txt"; oldcwd += "/CMakeCache.txt"; if (!cmSystemTools::SameFile(oldcwd, currentcwd)) { - std::string message = - std::string("The current CMakeCache.txt directory ") + currentcwd + - std::string(" is different than the directory ") + - std::string(this->GetInitializedCacheValue("CMAKE_CACHEFILE_DIR")) + - std::string(" where CMakeCache.txt was created. This may result " - "in binaries being created in the wrong place. If you " - "are not sure, reedit the CMakeCache.txt"); - cmSystemTools::Error(message.c_str()); + std::ostringstream message; + message << "The current CMakeCache.txt directory " << currentcwd + << " is different than the directory " + << this->GetInitializedCacheValue("CMAKE_CACHEFILE_DIR") + << " where CMakeCache.txt was created. This may result " + "in binaries being created in the wrong place. If you " + "are not sure, reedit the CMakeCache.txt"; + cmSystemTools::Error(message.str().c_str()); } } return true; @@ -243,19 +244,18 @@ bool cmCacheManager::SaveCache(const std::string& path, cmMessenger* messenger) } // before writing the cache, update the version numbers // to the - char temp[1024]; - sprintf(temp, "%d", cmVersion::GetMinorVersion()); - this->AddCacheEntry("CMAKE_CACHE_MINOR_VERSION", temp, - "Minor version of cmake used to create the " + this->AddCacheEntry("CMAKE_CACHE_MAJOR_VERSION", + std::to_string(cmVersion::GetMajorVersion()).c_str(), + "Major version of cmake used to create the " "current loaded cache", cmStateEnums::INTERNAL); - sprintf(temp, "%d", cmVersion::GetMajorVersion()); - this->AddCacheEntry("CMAKE_CACHE_MAJOR_VERSION", temp, - "Major version of cmake used to create the " + this->AddCacheEntry("CMAKE_CACHE_MINOR_VERSION", + std::to_string(cmVersion::GetMinorVersion()).c_str(), + "Minor version of cmake used to create the " "current loaded cache", cmStateEnums::INTERNAL); - sprintf(temp, "%d", cmVersion::GetPatchVersion()); - this->AddCacheEntry("CMAKE_CACHE_PATCH_VERSION", temp, + this->AddCacheEntry("CMAKE_CACHE_PATCH_VERSION", + std::to_string(cmVersion::GetPatchVersion()).c_str(), "Patch version of cmake used to create the " "current loaded cache", cmStateEnums::INTERNAL); diff --git a/Source/cmCommands.cxx b/Source/cmCommands.cxx index a1de8b1..a87b450 100644 --- a/Source/cmCommands.cxx +++ b/Source/cmCommands.cxx @@ -4,6 +4,7 @@ #include "cmPolicies.h" #include "cmState.h" +#include "cmAddCompileDefinitionsCommand.h" #include "cmAddCustomCommandCommand.h" #include "cmAddCustomTargetCommand.h" #include "cmAddDefinitionsCommand.h" @@ -253,6 +254,8 @@ void GetProjectCommands(cmState* state) state->AddBuiltinCommand("try_run", new cmTryRunCommand); #if defined(CMAKE_BUILD_WITH_CMAKE) + state->AddBuiltinCommand("add_compile_definitions", + new cmAddCompileDefinitionsCommand); state->AddBuiltinCommand("add_compile_options", new cmAddCompileOptionsCommand); state->AddBuiltinCommand("aux_source_directory", diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx index 8a5a6de..6e6e0be 100644 --- a/Source/cmComputeLinkInformation.cxx +++ b/Source/cmComputeLinkInformation.cxx @@ -240,21 +240,19 @@ because this need be done only for shared libraries without soname-s. cmComputeLinkInformation::cmComputeLinkInformation( const cmGeneratorTarget* target, const std::string& config) -{ // Store context information. - this->Target = target; - this->Makefile = this->Target->Target->GetMakefile(); - this->GlobalGenerator = - this->Target->GetLocalGenerator()->GetGlobalGenerator(); - this->CMakeInstance = this->GlobalGenerator->GetCMakeInstance(); - + : Target(target), + Makefile(target->Target->GetMakefile()), + GlobalGenerator(target->GetLocalGenerator()->GetGlobalGenerator()), + CMakeInstance(this->GlobalGenerator->GetCMakeInstance()) + // The configuration being linked. + , + Config(config) +{ // Check whether to recognize OpenBSD-style library versioned names. this->OpenBSD = this->Makefile->GetState()->GetGlobalPropertyAsBool( "FIND_LIBRARY_USE_OPENBSD_VERSIONING"); - // The configuration being linked. - this->Config = config; - // Allocate internals. this->OrderLinkerSearchPath = new cmOrderDirectories( this->GlobalGenerator, target, "linker search path"); @@ -406,17 +404,18 @@ cmComputeLinkInformation::~cmComputeLinkInformation() } cmComputeLinkInformation::ItemVector const& -cmComputeLinkInformation::GetItems() +cmComputeLinkInformation::GetItems() const { return this->Items; } std::vector<std::string> const& cmComputeLinkInformation::GetDirectories() + const { return this->OrderLinkerSearchPath->GetOrderedDirectories(); } -std::string cmComputeLinkInformation::GetRPathLinkString() +std::string cmComputeLinkInformation::GetRPathLinkString() const { // If there is no separate linker runtime search flag (-rpath-link) // there is no reason to compute a string. @@ -428,18 +427,19 @@ std::string cmComputeLinkInformation::GetRPathLinkString() return cmJoin(this->OrderDependentRPath->GetOrderedDirectories(), ":"); } -std::vector<std::string> const& cmComputeLinkInformation::GetDepends() +std::vector<std::string> const& cmComputeLinkInformation::GetDepends() const { return this->Depends; } std::vector<std::string> const& cmComputeLinkInformation::GetFrameworkPaths() + const { return this->FrameworkPaths; } const std::set<const cmGeneratorTarget*>& -cmComputeLinkInformation::GetSharedLibrariesLinked() +cmComputeLinkInformation::GetSharedLibrariesLinked() const { return this->SharedLibrariesLinked; } @@ -611,6 +611,9 @@ void cmComputeLinkInformation::AddItem(std::string const& item, if (!libName.empty()) { this->AddItem(libName, nullptr); } + } else if (tgt->GetType() == cmStateEnums::OBJECT_LIBRARY) { + // Ignore object library! + // Its object-files should already have been extracted for linking. } else { // Decide whether to use an import library. bool implib = @@ -1023,7 +1026,7 @@ void cmComputeLinkInformation::AddFullItem(std::string const& item) (generator.find("Visual Studio") != std::string::npos || generator.find("Xcode") != std::string::npos)) { std::string file = cmSystemTools::GetFilenameName(item); - if (!this->ExtractAnyLibraryName.find(file.c_str())) { + if (!this->ExtractAnyLibraryName.find(file)) { this->HandleBadFullItem(item, file); return; } @@ -1230,7 +1233,7 @@ void cmComputeLinkInformation::AddUserItem(std::string const& item, void cmComputeLinkInformation::AddFrameworkItem(std::string const& item) { // Try to separate the framework name and path. - if (!this->SplitFramework.find(item.c_str())) { + if (!this->SplitFramework.find(item)) { std::ostringstream e; e << "Could not parse framework path \"" << item << "\" " << "linked by target " << this->Target->GetName() << "."; @@ -1569,7 +1572,7 @@ void cmComputeLinkInformation::LoadImplicitLinkInfo() } std::vector<std::string> const& -cmComputeLinkInformation::GetRuntimeSearchPath() +cmComputeLinkInformation::GetRuntimeSearchPath() const { return this->OrderRuntimeSearchPath->GetOrderedDirectories(); } @@ -1635,7 +1638,7 @@ void cmComputeLinkInformation::AddLibraryRuntimeInfo( if (!is_shared_library) { // On some platforms (AIX) a shared library may look static. if (this->ArchivesMayBeShared) { - if (this->ExtractStaticLibraryName.find(file.c_str())) { + if (this->ExtractStaticLibraryName.find(file)) { // This is the name of a shared library or archive. is_shared_library = true; } @@ -1680,7 +1683,7 @@ static void cmCLI_ExpandListUnique(const char* str, } void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs, - bool for_install) + bool for_install) const { // Select whether to generate runtime search directories. bool outputRuntime = @@ -1794,7 +1797,7 @@ void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs, cmCLI_ExpandListUnique(this->RuntimeAlways.c_str(), runtimeDirs, emitted); } -std::string cmComputeLinkInformation::GetRPathString(bool for_install) +std::string cmComputeLinkInformation::GetRPathString(bool for_install) const { // Get the directories to use. std::vector<std::string> runtimeDirs; @@ -1822,7 +1825,7 @@ std::string cmComputeLinkInformation::GetRPathString(bool for_install) return rpath; } -std::string cmComputeLinkInformation::GetChrpathString() +std::string cmComputeLinkInformation::GetChrpathString() const { if (!this->RuntimeUseChrpath) { return ""; diff --git a/Source/cmComputeLinkInformation.h b/Source/cmComputeLinkInformation.h index f8c6214..65c12da 100644 --- a/Source/cmComputeLinkInformation.h +++ b/Source/cmComputeLinkInformation.h @@ -48,21 +48,21 @@ public: cmGeneratorTarget const* Target; }; typedef std::vector<Item> ItemVector; - ItemVector const& GetItems(); - std::vector<std::string> const& GetDirectories(); - std::vector<std::string> const& GetDepends(); - std::vector<std::string> const& GetFrameworkPaths(); + ItemVector const& GetItems() const; + std::vector<std::string> const& GetDirectories() const; + std::vector<std::string> const& GetDepends() const; + std::vector<std::string> const& GetFrameworkPaths() const; std::string GetLinkLanguage() const { return this->LinkLanguage; } - std::vector<std::string> const& GetRuntimeSearchPath(); + std::vector<std::string> const& GetRuntimeSearchPath() const; std::string const& GetRuntimeFlag() const { return this->RuntimeFlag; } std::string const& GetRuntimeSep() const { return this->RuntimeSep; } - void GetRPath(std::vector<std::string>& runtimeDirs, bool for_install); - std::string GetRPathString(bool for_install); - std::string GetChrpathString(); - std::set<cmGeneratorTarget const*> const& GetSharedLibrariesLinked(); + void GetRPath(std::vector<std::string>& runtimeDirs, bool for_install) const; + std::string GetRPathString(bool for_install) const; + std::string GetChrpathString() const; + std::set<cmGeneratorTarget const*> const& GetSharedLibrariesLinked() const; std::string const& GetRPathLinkFlag() const { return this->RPathLinkFlag; } - std::string GetRPathLinkString(); + std::string GetRPathLinkString() const; std::string GetConfig() const { return this->Config; } private: @@ -78,13 +78,13 @@ private: std::set<cmGeneratorTarget const*> SharedLibrariesLinked; // Context information. - cmGeneratorTarget const* Target; - cmMakefile* Makefile; - cmGlobalGenerator* GlobalGenerator; - cmake* CMakeInstance; + cmGeneratorTarget const* const Target; + cmMakefile* const Makefile; + cmGlobalGenerator* const GlobalGenerator; + cmake* const CMakeInstance; // Configuration information. - std::string Config; + std::string const Config; std::string LinkLanguage; // Modes for dealing with dependent shared libraries. diff --git a/Source/cmComputeTargetDepends.cxx b/Source/cmComputeTargetDepends.cxx index 18767a3..efdd3a5 100644 --- a/Source/cmComputeTargetDepends.cxx +++ b/Source/cmComputeTargetDepends.cxx @@ -211,11 +211,11 @@ void cmComputeTargetDepends::CollectTargetDepends(int depender_index) if (depender->GetType() != cmStateEnums::EXECUTABLE && depender->GetType() != cmStateEnums::STATIC_LIBRARY && depender->GetType() != cmStateEnums::SHARED_LIBRARY && - depender->GetType() != cmStateEnums::MODULE_LIBRARY) { + depender->GetType() != cmStateEnums::MODULE_LIBRARY && + depender->GetType() != cmStateEnums::OBJECT_LIBRARY) { this->GlobalGenerator->GetCMakeInstance()->IssueMessage( cmake::FATAL_ERROR, - "Only executables and non-OBJECT libraries may " - "reference target objects.", + "Only executables and libraries may reference target objects.", depender->GetBacktrace()); return; } diff --git a/Source/cmConfigureFileCommand.h b/Source/cmConfigureFileCommand.h index cff934b..5603c50 100644 --- a/Source/cmConfigureFileCommand.h +++ b/Source/cmConfigureFileCommand.h @@ -32,9 +32,9 @@ private: std::string InputFile; std::string OutputFile; - bool CopyOnly; - bool EscapeQuotes; - bool AtOnly; + bool CopyOnly = false; + bool EscapeQuotes = false; + bool AtOnly = false; }; #endif diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index 26e0db9..a9b7adf 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx @@ -818,7 +818,8 @@ 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, &cmakeFlags, output); + this->SrcFileSignature, cmake::NO_BUILD_PARALLEL_LEVEL, &cmakeFlags, + output); if (erroroc) { cmSystemTools::SetErrorOccured(); } diff --git a/Source/cmCoreTryCompile.h b/Source/cmCoreTryCompile.h index 6f35a54..ae714a6 100644 --- a/Source/cmCoreTryCompile.h +++ b/Source/cmCoreTryCompile.h @@ -46,7 +46,7 @@ protected: std::string BinaryDirectory; std::string OutputFile; std::string FindErrorMessage; - bool SrcFileSignature; + bool SrcFileSignature = false; private: std::vector<std::string> WarnCMP0067; diff --git a/Source/cmDepends.cxx b/Source/cmDepends.cxx index cdab671..4716e14 100644 --- a/Source/cmDepends.cxx +++ b/Source/cmDepends.cxx @@ -7,7 +7,6 @@ #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmSystemTools.h" -#include "cmWorkingDirectory.h" #include "cmsys/FStream.hxx" #include <sstream> @@ -15,8 +14,7 @@ #include <utility> cmDepends::cmDepends(cmLocalGenerator* lg, const char* targetDir) - : CompileDirectory() - , LocalGenerator(lg) + : LocalGenerator(lg) , Verbose(false) , FileComparison(nullptr) , TargetDirectory(targetDir) @@ -73,9 +71,6 @@ bool cmDepends::Finalize(std::ostream& /*unused*/, std::ostream& /*unused*/) bool cmDepends::Check(const char* makeFile, const char* internalFile, std::map<std::string, DependencyVector>& validDeps) { - // Dependency checks must be done in proper working directory. - cmWorkingDirectory workdir(this->CompileDirectory); - // Check whether dependencies must be regenerated. bool okay = true; cmsys::ifstream fin(internalFile); diff --git a/Source/cmDepends.h b/Source/cmDepends.h index a4fee3c..4b9e05a 100644 --- a/Source/cmDepends.h +++ b/Source/cmDepends.h @@ -31,9 +31,6 @@ public: path from the build directory to the target file. */ cmDepends(cmLocalGenerator* lg = nullptr, const char* targetDir = ""); - /** at what level will the compile be done from */ - void SetCompileDirectory(const char* dir) { this->CompileDirectory = dir; } - /** Set the local generator for the directory in which we are scanning dependencies. This is not a full local generator; it has been setup to do relative path conversions for the current @@ -95,9 +92,6 @@ protected: virtual bool Finalize(std::ostream& makeDepends, std::ostream& internalDepends); - // The directory in which the build rule for the target file is executed. - std::string CompileDirectory; - // The local generator. cmLocalGenerator* LocalGenerator; diff --git a/Source/cmDependsC.cxx b/Source/cmDependsC.cxx index 62bc8d9..34c0e94 100644 --- a/Source/cmDependsC.cxx +++ b/Source/cmDependsC.cxx @@ -96,9 +96,16 @@ bool cmDependsC::WriteDependencies(const std::set<std::string>& sources, std::set<std::string> dependencies; bool haveDeps = false; + std::string binDir = this->LocalGenerator->GetBinaryDirectory(); + + // Compute a path to the object file to write to the internal depend file. + // Any existing content of the internal depend file has already been + // loaded in ValidDeps with this path as a key. + std::string obj_i = this->LocalGenerator->ConvertToRelativePath(binDir, obj); + if (this->ValidDeps != nullptr) { std::map<std::string, DependencyVector>::const_iterator tmpIt = - this->ValidDeps->find(obj); + this->ValidDeps->find(obj_i); if (tmpIt != this->ValidDeps->end()) { dependencies.insert(tmpIt->second.begin(), tmpIt->second.end()); haveDeps = true; @@ -222,8 +229,6 @@ bool cmDependsC::WriteDependencies(const std::set<std::string>& sources, // written by the original local generator for this directory // convert the dependencies to paths relative to the home output // directory. We must do the same here. - std::string binDir = this->LocalGenerator->GetBinaryDirectory(); - std::string obj_i = this->LocalGenerator->ConvertToRelativePath(binDir, obj); std::string obj_m = cmSystemTools::ConvertToOutputPath(obj_i); internalDepends << obj_i << std::endl; diff --git a/Source/cmDependsFortran.cxx b/Source/cmDependsFortran.cxx index 1a66ca0..dbea15a 100644 --- a/Source/cmDependsFortran.cxx +++ b/Source/cmDependsFortran.cxx @@ -10,6 +10,7 @@ #include <string.h> #include <utility> +#include "cmAlgorithms.h" #include "cmFortranParser.h" /* Interface to parser object. */ #include "cmGeneratedFileStream.h" #include "cmLocalGenerator.h" @@ -23,6 +24,22 @@ // use lower case and some always use upper case. I do not know if any // use the case from the source code. +static void cmFortranModuleAppendUpperLower(std::string const& mod, + std::string& mod_upper, + std::string& mod_lower) +{ + std::string::size_type ext_len = 0; + if (cmHasLiteralSuffix(mod, ".mod")) { + ext_len = 4; + } else if (cmHasLiteralSuffix(mod, ".smod")) { + ext_len = 5; + } + std::string const& name = mod.substr(0, mod.size() - ext_len); + std::string const& ext = mod.substr(mod.size() - ext_len); + mod_upper += cmSystemTools::UpperCase(name) + ext; + mod_lower += mod; +} + class cmDependsFortranInternals { public: @@ -181,16 +198,13 @@ bool cmDependsFortran::Finalize(std::ostream& makeDepends, for (std::string const& i : provides) { std::string mod_upper = mod_dir; mod_upper += "/"; - mod_upper += cmSystemTools::UpperCase(i); - mod_upper += ".mod"; std::string mod_lower = mod_dir; mod_lower += "/"; - mod_lower += i; - mod_lower += ".mod"; + cmFortranModuleAppendUpperLower(i, mod_upper, mod_lower); std::string stamp = stamp_dir; stamp += "/"; stamp += i; - stamp += ".mod.stamp"; + stamp += ".stamp"; fcStream << "\n"; fcStream << " \"" << this->MaybeConvertToRelativePath(currentBinDir, mod_lower) @@ -270,7 +284,14 @@ void cmDependsFortran::MatchRemoteModules(std::istream& fin, if (line[0] == ' ') { if (doing_provides) { - this->ConsiderModule(line.c_str() + 1, stampDir); + std::string mod = line; + if (!cmHasLiteralSuffix(mod, ".mod") && + !cmHasLiteralSuffix(mod, ".smod")) { + // Support fortran.internal files left by older versions of CMake. + // They do not include the ".mod" extension. + mod += ".mod"; + } + this->ConsiderModule(mod.c_str() + 1, stampDir); } } else if (line == "provides") { doing_provides = true; @@ -292,7 +313,7 @@ void cmDependsFortran::ConsiderModule(const char* name, const char* stampDir) std::string stampFile = stampDir; stampFile += "/"; stampFile += name; - stampFile += ".mod.stamp"; + stampFile += ".stamp"; required->second = stampFile; } } @@ -366,7 +387,6 @@ bool cmDependsFortran::WriteDependenciesReal(const char* obj, // Always use lower case for the mod stamp file name. The // cmake_copy_f90_mod will call back to this class, which will // try various cases for the real mod file name. - std::string m = cmSystemTools::LowerCase(i); std::string modFile = mod_dir; modFile += "/"; modFile += i; @@ -375,8 +395,8 @@ bool cmDependsFortran::WriteDependenciesReal(const char* obj, cmOutputConverter::SHELL); std::string stampFile = stamp_dir; stampFile += "/"; - stampFile += m; - stampFile += ".mod.stamp"; + stampFile += i; + stampFile += ".stamp"; stampFile = this->MaybeConvertToRelativePath(binDir, stampFile); std::string const stampFileForShell = this->LocalGenerator->ConvertToOutputFormat(stampFile, @@ -423,10 +443,9 @@ bool cmDependsFortran::WriteDependenciesReal(const char* obj, bool cmDependsFortran::FindModule(std::string const& name, std::string& module) { // Construct possible names for the module file. - std::string mod_upper = cmSystemTools::UpperCase(name); - std::string mod_lower = name; - mod_upper += ".mod"; - mod_lower += ".mod"; + std::string mod_upper; + std::string mod_lower; + cmFortranModuleAppendUpperLower(name, mod_upper, mod_lower); // Search the include path for the module. std::string fullName; @@ -470,17 +489,19 @@ bool cmDependsFortran::CopyModule(const std::vector<std::string>& args) if (args.size() >= 5) { compilerId = args[4]; } + if (!cmHasLiteralSuffix(mod, ".mod") && !cmHasLiteralSuffix(mod, ".smod")) { + // Support depend.make files left by older versions of CMake. + // They do not include the ".mod" extension. + mod += ".mod"; + } std::string mod_dir = cmSystemTools::GetFilenamePath(mod); if (!mod_dir.empty()) { mod_dir += "/"; } std::string mod_upper = mod_dir; - mod_upper += cmSystemTools::UpperCase(cmSystemTools::GetFilenameName(mod)); std::string mod_lower = mod_dir; - mod_lower += cmSystemTools::LowerCase(cmSystemTools::GetFilenameName(mod)); - mod += ".mod"; - mod_upper += ".mod"; - mod_lower += ".mod"; + cmFortranModuleAppendUpperLower(cmSystemTools::GetFilenameName(mod), + mod_upper, mod_lower); if (cmSystemTools::FileExists(mod_upper, true)) { if (cmDependsFortran::ModulesDiffer(mod_upper.c_str(), stamp.c_str(), compilerId.c_str())) { diff --git a/Source/cmExportBuildAndroidMKGenerator.cxx b/Source/cmExportBuildAndroidMKGenerator.cxx index 817b5d9..0ceac85 100644 --- a/Source/cmExportBuildAndroidMKGenerator.cxx +++ b/Source/cmExportBuildAndroidMKGenerator.cxx @@ -41,7 +41,8 @@ void cmExportBuildAndroidMKGenerator::GenerateExpectedTargetsCode( } void cmExportBuildAndroidMKGenerator::GenerateImportTargetCode( - std::ostream& os, const cmGeneratorTarget* target) + std::ostream& os, cmGeneratorTarget const* target, + cmStateEnums::TargetType /*targetType*/) { std::string targetName = this->Namespace; targetName += target->GetExportName(); diff --git a/Source/cmExportBuildAndroidMKGenerator.h b/Source/cmExportBuildAndroidMKGenerator.h index c80839b..a9b6107 100644 --- a/Source/cmExportBuildAndroidMKGenerator.h +++ b/Source/cmExportBuildAndroidMKGenerator.h @@ -11,6 +11,7 @@ #include "cmExportBuildFileGenerator.h" #include "cmExportFileGenerator.h" +#include "cmStateTypes.h" class cmGeneratorTarget; @@ -47,8 +48,9 @@ protected: void GenerateImportHeaderCode(std::ostream& os, const std::string& config = "") override; void GenerateImportFooterCode(std::ostream& os) override; - void GenerateImportTargetCode(std::ostream& os, - const cmGeneratorTarget* target) override; + void GenerateImportTargetCode( + std::ostream& os, cmGeneratorTarget const* target, + cmStateEnums::TargetType /*targetType*/) override; void GenerateExpectedTargetsCode( std::ostream& os, const std::string& expectedTargets) override; void GenerateImportPropertyCode( diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx index f7aa6e8..72489bf 100644 --- a/Source/cmExportBuildFileGenerator.cxx +++ b/Source/cmExportBuildFileGenerator.cxx @@ -59,7 +59,7 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os) this->LG->GetMakefile()->GetBacktrace()); return false; } - if (te->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (this->GetExportTargetType(te) == cmStateEnums::INTERFACE_LIBRARY) { this->GenerateRequiredCMakeVersion(os, "3.0.0"); } } @@ -71,7 +71,7 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os) // Create all the imported targets. for (cmGeneratorTarget* gte : this->Exports) { - this->GenerateImportTargetCode(os, gte); + this->GenerateImportTargetCode(os, gte, this->GetExportTargetType(gte)); gte->Target->AppendBuildInterfaceIncludes(); @@ -97,6 +97,15 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os) properties, missingTargets); this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gte, properties); + + std::string errorMessage; + if (!this->PopulateExportProperties(gte, properties, errorMessage)) { + this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage( + cmake::FATAL_ERROR, errorMessage, + this->LG->GetMakefile()->GetBacktrace()); + return false; + } + const bool newCMP0022Behavior = gte->GetPolicyStatusCMP0022() != cmPolicies::WARN && gte->GetPolicyStatusCMP0022() != cmPolicies::OLD; @@ -128,12 +137,13 @@ void cmExportBuildFileGenerator::GenerateImportTargetsConfig( // Collect import properties for this target. ImportPropertyMap properties; - if (target->GetType() != cmStateEnums::INTERFACE_LIBRARY) { + if (this->GetExportTargetType(target) != cmStateEnums::INTERFACE_LIBRARY) { this->SetImportLocationProperty(config, suffix, target, properties); } if (!properties.empty()) { // Get the rest of the target details. - if (target->GetType() != cmStateEnums::INTERFACE_LIBRARY) { + if (this->GetExportTargetType(target) != + cmStateEnums::INTERFACE_LIBRARY) { this->SetImportDetailProperties(config, suffix, target, properties, missingTargets); this->SetImportLinkInterface(config, suffix, @@ -153,6 +163,21 @@ void cmExportBuildFileGenerator::GenerateImportTargetsConfig( } } +cmStateEnums::TargetType cmExportBuildFileGenerator::GetExportTargetType( + cmGeneratorTarget const* target) const +{ + cmStateEnums::TargetType targetType = target->GetType(); + // An object library exports as an interface library if we cannot + // tell clients where to find the objects. This is sufficient + // to support transitive usage requirements on other targets that + // use the object library. + if (targetType == cmStateEnums::OBJECT_LIBRARY && + !this->LG->GetGlobalGenerator()->HasKnownObjectFileLocation(nullptr)) { + targetType = cmStateEnums::INTERFACE_LIBRARY; + } + return targetType; +} + void cmExportBuildFileGenerator::SetExportSet(cmExportSet* exportSet) { this->ExportSet = exportSet; @@ -199,13 +224,14 @@ void cmExportBuildFileGenerator::SetImportLocationProperty( } // Add the import library for windows DLLs. - if (target->HasImportLibrary() && + if (target->HasImportLibrary(config) && mf->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) { std::string prop = "IMPORTED_IMPLIB"; prop += suffix; std::string value = target->GetFullPath(config, cmStateEnums::ImportLibraryArtifact); - target->GetImplibGNUtoMS(value, value, "${CMAKE_IMPORT_LIBRARY_SUFFIX}"); + target->GetImplibGNUtoMS(config, value, value, + "${CMAKE_IMPORT_LIBRARY_SUFFIX}"); properties[prop] = value; } } diff --git a/Source/cmExportBuildFileGenerator.h b/Source/cmExportBuildFileGenerator.h index 6457a77..ada2709 100644 --- a/Source/cmExportBuildFileGenerator.h +++ b/Source/cmExportBuildFileGenerator.h @@ -6,6 +6,7 @@ #include "cmConfigure.h" // IWYU pragma: keep #include "cmExportFileGenerator.h" +#include "cmStateTypes.h" #include <iosfwd> #include <string> @@ -53,6 +54,8 @@ protected: void GenerateImportTargetsConfig( std::ostream& os, const std::string& config, std::string const& suffix, std::vector<std::string>& missingTargets) override; + cmStateEnums::TargetType GetExportTargetType( + cmGeneratorTarget const* target) const; void HandleMissingTarget(std::string& link_libs, std::vector<std::string>& missingTargets, cmGeneratorTarget* depender, diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx index c8a727d..655ebe8 100644 --- a/Source/cmExportCommand.cxx +++ b/Source/cmExportCommand.cxx @@ -146,17 +146,6 @@ bool cmExportCommand::InitialPass(std::vector<std::string> const& args, } if (cmTarget* target = gg->FindTarget(currentTarget)) { - if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) { - std::string reason; - if (!this->Makefile->GetGlobalGenerator() - ->HasKnownObjectFileLocation(&reason)) { - std::ostringstream e; - e << "given OBJECT library \"" << currentTarget - << "\" which may not be exported" << reason << "."; - this->SetError(e.str()); - return false; - } - } if (target->GetType() == cmStateEnums::UTILITY) { this->SetError("given custom target \"" + currentTarget + "\" which may not be exported."); diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index 434abdc..bf9d341 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -11,6 +11,8 @@ #include "cmMakefile.h" #include "cmOutputConverter.h" #include "cmPolicies.h" +#include "cmProperty.h" +#include "cmPropertyMap.h" #include "cmStateTypes.h" #include "cmSystemTools.h" #include "cmTarget.h" @@ -775,6 +777,25 @@ void cmExportFileGenerator::SetImportDetailProperties( properties[prop] = m.str(); } } + + // Add information if this target is a managed target + if (target->GetManagedType(config) != + cmGeneratorTarget::ManagedType::Native) { + std::string prop = "IMPORTED_COMMON_LANGUAGE_RUNTIME"; + prop += suffix; + std::string propval; + if (auto* p = target->GetProperty("COMMON_LANGUAGE_RUNTIME")) { + propval = p; + } else if (target->HasLanguage("CSharp", config)) { + // C# projects do not have the /clr flag, so we set the property + // here to mark the target as (only) managed (i.e. no .lib file + // to link to). Otherwise the COMMON_LANGUAGE_RUNTIME target + // property would have to be set manually for C# targets to make + // exporting/importing work. + propval = "CSharp"; + } + properties[prop] = propval; + } } template <typename T> @@ -901,8 +922,10 @@ void cmExportFileGenerator::GenerateExpectedTargetsCode( "\n\n"; /* clang-format on */ } + void cmExportFileGenerator::GenerateImportTargetCode( - std::ostream& os, const cmGeneratorTarget* target) + std::ostream& os, cmGeneratorTarget const* target, + cmStateEnums::TargetType targetType) { // Construct the imported target name. std::string targetName = this->Namespace; @@ -911,7 +934,7 @@ void cmExportFileGenerator::GenerateImportTargetCode( // Create the imported target. os << "# Create imported target " << targetName << "\n"; - switch (target->GetType()) { + switch (targetType) { case cmStateEnums::EXECUTABLE: os << "add_executable(" << targetName << " IMPORTED)\n"; break; @@ -1095,3 +1118,41 @@ void cmExportFileGenerator::GenerateImportedFileChecksCode( os << ")\n\n"; } + +bool cmExportFileGenerator::PopulateExportProperties( + cmGeneratorTarget* gte, ImportPropertyMap& properties, + std::string& errorMessage) +{ + auto& targetProperties = gte->Target->GetProperties(); + const auto& exportProperties = targetProperties.find("EXPORT_PROPERTIES"); + if (exportProperties != targetProperties.end()) { + std::vector<std::string> propsToExport; + cmSystemTools::ExpandListArgument(exportProperties->second.GetValue(), + propsToExport); + for (auto& prop : propsToExport) { + /* Black list reserved properties */ + if (cmSystemTools::StringStartsWith(prop, "IMPORTED_") || + cmSystemTools::StringStartsWith(prop, "INTERFACE_")) { + std::ostringstream e; + e << "Target \"" << gte->Target->GetName() << "\" contains property \"" + << prop << "\" in EXPORT_PROPERTIES but IMPORTED_* and INTERFACE_* " + << "properties are reserved."; + errorMessage = e.str(); + return false; + } + auto propertyValue = targetProperties.GetPropertyValue(prop); + std::string evaluatedValue = cmGeneratorExpression::Preprocess( + propertyValue, cmGeneratorExpression::StripAllGeneratorExpressions); + if (evaluatedValue != propertyValue) { + std::ostringstream e; + e << "Target \"" << gte->Target->GetName() << "\" contains property \"" + << prop << "\" in EXPORT_PROPERTIES but this property contains a " + << "generator expression. This is not allowed."; + errorMessage = e.str(); + return false; + } + properties[prop] = propertyValue; + } + } + return true; +} diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h index 985c8f6..954e6c5 100644 --- a/Source/cmExportFileGenerator.h +++ b/Source/cmExportFileGenerator.h @@ -6,6 +6,7 @@ #include "cmConfigure.h" // IWYU pragma: keep #include "cmGeneratorExpression.h" +#include "cmStateTypes.h" #include "cmVersion.h" #include "cmVersionConfig.h" @@ -76,7 +77,8 @@ protected: virtual void GenerateImportFooterCode(std::ostream& os); void GenerateImportVersionCode(std::ostream& os); virtual void GenerateImportTargetCode(std::ostream& os, - cmGeneratorTarget const* target); + cmGeneratorTarget const* target, + cmStateEnums::TargetType targetType); virtual void GenerateImportPropertyCode(std::ostream& os, const std::string& config, cmGeneratorTarget const* target, @@ -166,6 +168,10 @@ protected: virtual void GenerateRequiredCMakeVersion(std::ostream& os, const char* versionString); + bool PopulateExportProperties(cmGeneratorTarget* gte, + ImportPropertyMap& properties, + std::string& errorMessage); + // The namespace in which the exports are placed in the generated file. std::string Namespace; diff --git a/Source/cmExportInstallAndroidMKGenerator.cxx b/Source/cmExportInstallAndroidMKGenerator.cxx index fe565e6..9bc8089 100644 --- a/Source/cmExportInstallAndroidMKGenerator.cxx +++ b/Source/cmExportInstallAndroidMKGenerator.cxx @@ -55,7 +55,8 @@ void cmExportInstallAndroidMKGenerator::GenerateImportFooterCode(std::ostream&) } void cmExportInstallAndroidMKGenerator::GenerateImportTargetCode( - std::ostream& os, const cmGeneratorTarget* target) + std::ostream& os, cmGeneratorTarget const* target, + cmStateEnums::TargetType /*targetType*/) { std::string targetName = this->Namespace; targetName += target->GetExportName(); diff --git a/Source/cmExportInstallAndroidMKGenerator.h b/Source/cmExportInstallAndroidMKGenerator.h index 91554ee..8883ffa 100644 --- a/Source/cmExportInstallAndroidMKGenerator.h +++ b/Source/cmExportInstallAndroidMKGenerator.h @@ -12,6 +12,7 @@ #include "cmExportFileGenerator.h" #include "cmExportInstallFileGenerator.h" +#include "cmStateTypes.h" class cmGeneratorTarget; class cmInstallExportGenerator; @@ -41,8 +42,9 @@ protected: void GenerateImportHeaderCode(std::ostream& os, const std::string& config = "") override; void GenerateImportFooterCode(std::ostream& os) override; - void GenerateImportTargetCode(std::ostream& os, - const cmGeneratorTarget* target) override; + void GenerateImportTargetCode( + std::ostream& os, cmGeneratorTarget const* target, + cmStateEnums::TargetType /*targetType*/) override; void GenerateExpectedTargetsCode( std::ostream& os, const std::string& expectedTargets) override; void GenerateImportPropertyCode( diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx index 954b561..63d04a6 100644 --- a/Source/cmExportInstallFileGenerator.cxx +++ b/Source/cmExportInstallFileGenerator.cxx @@ -75,11 +75,12 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) // Create all the imported targets. for (cmTargetExport* te : allTargets) { cmGeneratorTarget* gt = te->Target; + cmStateEnums::TargetType targetType = this->GetExportTargetType(te); requiresConfigFiles = - requiresConfigFiles || gt->GetType() != cmStateEnums::INTERFACE_LIBRARY; + requiresConfigFiles || targetType != cmStateEnums::INTERFACE_LIBRARY; - this->GenerateImportTargetCode(os, gt); + this->GenerateImportTargetCode(os, gt, targetType); ImportPropertyMap properties; @@ -103,6 +104,12 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) cmGeneratorExpression::InstallInterface, properties, missingTargets); + std::string errorMessage; + if (!this->PopulateExportProperties(gt, properties, errorMessage)) { + cmSystemTools::Error(errorMessage.c_str()); + return false; + } + const bool newCMP0022Behavior = gt->GetPolicyStatusCMP0022() != cmPolicies::WARN && gt->GetPolicyStatusCMP0022() != cmPolicies::OLD; @@ -114,7 +121,7 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) require2_8_12 = true; } } - if (gt->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (targetType == cmStateEnums::INTERFACE_LIBRARY) { require3_0_0 = true; } if (gt->GetProperty("INTERFACE_SOURCES")) { @@ -308,7 +315,7 @@ void cmExportInstallFileGenerator::GenerateImportTargetsConfig( // Add each target in the set to the export. for (cmTargetExport* te : *this->IEGen->GetExportSet()->GetTargetExports()) { // Collect import properties for this target. - if (te->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (this->GetExportTargetType(te) == cmStateEnums::INTERFACE_LIBRARY) { continue; } @@ -426,6 +433,19 @@ void cmExportInstallFileGenerator::SetImportLocationProperty( } } +cmStateEnums::TargetType cmExportInstallFileGenerator::GetExportTargetType( + cmTargetExport const* targetExport) const +{ + cmStateEnums::TargetType targetType = targetExport->Target->GetType(); + // An OBJECT library installed with no OBJECTS DESTINATION + // is transformed to an INTERFACE library. + if (targetType == cmStateEnums::OBJECT_LIBRARY && + targetExport->ObjectsGenerator == nullptr) { + targetType = cmStateEnums::INTERFACE_LIBRARY; + } + return targetType; +} + void cmExportInstallFileGenerator::HandleMissingTarget( std::string& link_libs, std::vector<std::string>& missingTargets, cmGeneratorTarget* depender, cmGeneratorTarget* dependee) diff --git a/Source/cmExportInstallFileGenerator.h b/Source/cmExportInstallFileGenerator.h index cda8433..ea607fb 100644 --- a/Source/cmExportInstallFileGenerator.h +++ b/Source/cmExportInstallFileGenerator.h @@ -6,6 +6,7 @@ #include "cmConfigure.h" // IWYU pragma: keep #include "cmExportFileGenerator.h" +#include "cmStateTypes.h" #include <iosfwd> #include <map> @@ -17,6 +18,7 @@ class cmGeneratorTarget; class cmGlobalGenerator; class cmInstallExportGenerator; class cmInstallTargetGenerator; +class cmTargetExport; /** \class cmExportInstallFileGenerator * \brief Generate a file exporting targets from an install tree. @@ -57,6 +59,8 @@ protected: void GenerateImportTargetsConfig( std::ostream& os, const std::string& config, std::string const& suffix, std::vector<std::string>& missingTargets) override; + cmStateEnums::TargetType GetExportTargetType( + cmTargetExport const* targetExport) const; void HandleMissingTarget(std::string& link_libs, std::vector<std::string>& missingTargets, cmGeneratorTarget* depender, diff --git a/Source/cmExportLibraryDependenciesCommand.h b/Source/cmExportLibraryDependenciesCommand.h index bf5e9bc..8414866 100644 --- a/Source/cmExportLibraryDependenciesCommand.h +++ b/Source/cmExportLibraryDependenciesCommand.h @@ -27,7 +27,7 @@ public: private: std::string Filename; - bool Append; + bool Append = false; void ConstFinalPass() const; }; diff --git a/Source/cmExportTryCompileFileGenerator.cxx b/Source/cmExportTryCompileFileGenerator.cxx index ae8cd37..87648cb 100644 --- a/Source/cmExportTryCompileFileGenerator.cxx +++ b/Source/cmExportTryCompileFileGenerator.cxx @@ -33,7 +33,7 @@ bool cmExportTryCompileFileGenerator::GenerateMainFile(std::ostream& os) this->Exports.pop_back(); if (emitted.insert(te).second) { emittedDeps.insert(te); - this->GenerateImportTargetCode(os, te); + this->GenerateImportTargetCode(os, te, te->GetType()); ImportPropertyMap properties; diff --git a/Source/cmExternalMakefileProjectGenerator.h b/Source/cmExternalMakefileProjectGenerator.h index 5cc6442..d48abca 100644 --- a/Source/cmExternalMakefileProjectGenerator.h +++ b/Source/cmExternalMakefileProjectGenerator.h @@ -62,7 +62,7 @@ protected: ///! Contains the names of the global generators support by this generator. std::vector<std::string> SupportedGlobalGenerators; ///! the global generator which creates the makefiles - const cmGlobalGenerator* GlobalGenerator; + const cmGlobalGenerator* GlobalGenerator = nullptr; std::string Name; }; diff --git a/Source/cmExtraCodeLiteGenerator.cxx b/Source/cmExtraCodeLiteGenerator.cxx index 4dbaa3f..c7c780c 100644 --- a/Source/cmExtraCodeLiteGenerator.cxx +++ b/Source/cmExtraCodeLiteGenerator.cxx @@ -408,7 +408,6 @@ void cmExtraCodeLiteGenerator::CreateProjectSourceEntries( const std::string& projectPath, const cmMakefile* mf, const std::string& projectType, const std::string& targetName) { - cmXMLWriter& xml(*_xml); FindMatchingHeaderfiles(cFiles, otherFiles); // Create 2 virtual folders: src and include @@ -469,10 +468,14 @@ void cmExtraCodeLiteGenerator::CreateProjectSourceEntries( xml.EndElement(); // ResourceCompiler xml.StartElement("General"); - std::string outputPath = mf->GetSafeDefinition("EXECUTABLE_OUTPUT_PATH"); + std::string outputPath = + mf->GetSafeDefinition("CMAKE_RUNTIME_OUTPUT_DIRECTORY"); + if (outputPath.empty()) { + outputPath = mf->GetSafeDefinition("EXECUTABLE_OUTPUT_PATH"); + } std::string relapath; if (!outputPath.empty()) { - relapath = cmSystemTools::RelativePath(this->WorkspacePath, outputPath); + relapath = cmSystemTools::RelativePath(projectPath, outputPath); xml.Attribute("OutputFile", relapath + "/$(ProjectName)"); } else { xml.Attribute("OutputFile", "$(IntermediateDirectory)/$(ProjectName)"); @@ -635,7 +638,7 @@ std::string cmExtraCodeLiteGenerator::GetBuildCommand( if (generator == "NMake Makefiles" || generator == "Ninja") { ss << make; } else if (generator == "MinGW Makefiles" || generator == "Unix Makefiles") { - ss << make << " -j " << this->CpuCount; + ss << make << " -f$(ProjectPath)/Makefile -j " << this->CpuCount; } if (!targetName.empty()) { ss << " " << targetName; diff --git a/Source/cmExtraEclipseCDT4Generator.cxx b/Source/cmExtraEclipseCDT4Generator.cxx index 258c9ca..e7279d9 100644 --- a/Source/cmExtraEclipseCDT4Generator.cxx +++ b/Source/cmExtraEclipseCDT4Generator.cxx @@ -1001,6 +1001,13 @@ void cmExtraEclipseCDT4Generator::CreateCProjectFile() const xml.EndElement(); // project xml.EndElement(); // storageModule + + // Append additional cproject contents without applying any XML formatting + if (const char* extraCProjectContents = + mf->GetState()->GetGlobalProperty("ECLIPSE_EXTRA_CPROJECT_CONTENTS")) { + fout << extraCProjectContents; + } + xml.EndElement(); // cproject } diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index d3dcc01..1e47687 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -160,6 +160,12 @@ bool cmFileCommand::InitialPass(std::vector<std::string> const& args, if (subCommand == "TO_NATIVE_PATH") { return this->HandleCMakePathCommand(args, true); } + if (subCommand == "TOUCH") { + return this->HandleTouchCommand(args, true); + } + if (subCommand == "TOUCH_NOCREATE") { + return this->HandleTouchCommand(args, false); + } if (subCommand == "TIMESTAMP") { return this->HandleTimestampCommand(args); } @@ -226,6 +232,14 @@ bool cmFileCommand::HandleWriteCommand(std::vector<std::string> const& args, } std::string message = cmJoin(cmMakeRange(i, args.end()), std::string()); file << message; + if (!file) { + std::string error = "write failed ("; + error += cmSystemTools::GetLastSystemError(); + error += "):\n "; + error += fileName; + this->SetError(error); + return false; + } file.close(); if (mode) { cmSystemTools::SetPermissions(fileName.c_str(), mode); @@ -751,9 +765,13 @@ bool cmFileCommand::HandleGlobCommand(std::vector<std::string> const& args, } } - std::string output; - bool first = true; - for (; i != args.end(); ++i) { + std::vector<std::string> files; + bool configureDepends = false; + bool warnConfigureLate = false; + bool warnFollowedSymlinks = false; + const cmake::WorkingMode workingMode = + this->Makefile->GetCMakeInstance()->GetWorkingMode(); + while (i != args.end()) { if (*i == "LIST_DIRECTORIES") { ++i; if (i != args.end()) { @@ -771,103 +789,139 @@ bool cmFileCommand::HandleGlobCommand(std::vector<std::string> const& args, this->SetError("LIST_DIRECTORIES missing bool value."); return false; } - continue; - } - - if (recurse && (*i == "FOLLOW_SYMLINKS")) { + ++i; + if (i == args.end()) { + this->SetError("GLOB requires a glob expression after the bool."); + return false; + } + } else if (*i == "FOLLOW_SYMLINKS") { + if (!recurse) { + this->SetError("FOLLOW_SYMLINKS is not a valid parameter for GLOB."); + return false; + } explicitFollowSymlinks = true; g.RecurseThroughSymlinksOn(); ++i; if (i == args.end()) { this->SetError( - "GLOB_RECURSE requires a glob expression after FOLLOW_SYMLINKS"); + "GLOB_RECURSE requires a glob expression after FOLLOW_SYMLINKS."); return false; } - } - - if (*i == "RELATIVE") { + } else if (*i == "RELATIVE") { ++i; // skip RELATIVE if (i == args.end()) { - this->SetError("GLOB requires a directory after the RELATIVE tag"); + this->SetError("GLOB requires a directory after the RELATIVE tag."); return false; } g.SetRelative(i->c_str()); ++i; if (i == args.end()) { - this->SetError("GLOB requires a glob expression after the directory"); + this->SetError("GLOB requires a glob expression after the directory."); return false; } - } - - cmsys::Glob::GlobMessages globMessages; - if (!cmsys::SystemTools::FileIsFullPath(*i)) { - std::string expr = this->Makefile->GetCurrentSourceDirectory(); - // Handle script mode - if (!expr.empty()) { - expr += "/" + *i; - g.FindFiles(expr, &globMessages); - } else { - g.FindFiles(*i, &globMessages); + } else if (*i == "CONFIGURE_DEPENDS") { + // Generated build system depends on glob results + if (!configureDepends && warnConfigureLate) { + this->Makefile->IssueMessage( + cmake::AUTHOR_WARNING, + "CONFIGURE_DEPENDS flag was given after a glob expression was " + "already evaluated."); + } + if (workingMode != cmake::NORMAL_MODE) { + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, + "CONFIGURE_DEPENDS is invalid for script and find package modes."); + return false; + } + configureDepends = true; + ++i; + if (i == args.end()) { + this->SetError( + "GLOB requires a glob expression after CONFIGURE_DEPENDS."); + return false; } } else { - g.FindFiles(*i, &globMessages); - } - - if (!globMessages.empty()) { - bool shouldExit = false; - for (cmsys::Glob::Message const& globMessage : globMessages) { - if (globMessage.type == cmsys::Glob::cyclicRecursion) { - this->Makefile->IssueMessage( - cmake::AUTHOR_WARNING, - "Cyclic recursion detected while globbing for '" + *i + "':\n" + - globMessage.content); + std::string expr = *i; + if (!cmsys::SystemTools::FileIsFullPath(*i)) { + expr = this->Makefile->GetCurrentSourceDirectory(); + // Handle script mode + if (!expr.empty()) { + expr += "/" + *i; } else { - this->Makefile->IssueMessage( - cmake::FATAL_ERROR, "Error has occurred while globbing for '" + - *i + "' - " + globMessage.content); - shouldExit = true; + expr = *i; } } - if (shouldExit) { - return false; + + cmsys::Glob::GlobMessages globMessages; + g.FindFiles(expr, &globMessages); + + if (!globMessages.empty()) { + bool shouldExit = false; + for (cmsys::Glob::Message const& globMessage : globMessages) { + if (globMessage.type == cmsys::Glob::cyclicRecursion) { + this->Makefile->IssueMessage( + cmake::AUTHOR_WARNING, + "Cyclic recursion detected while globbing for '" + *i + "':\n" + + globMessage.content); + } else { + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, "Error has occurred while globbing for '" + + *i + "' - " + globMessage.content); + shouldExit = true; + } + } + if (shouldExit) { + return false; + } } - } - std::vector<std::string>::size_type cc; - std::vector<std::string>& files = g.GetFiles(); - std::sort(files.begin(), files.end()); - for (cc = 0; cc < files.size(); cc++) { - if (!first) { - output += ";"; + if (recurse && !explicitFollowSymlinks && + g.GetFollowedSymlinkCount() != 0) { + warnFollowedSymlinks = true; } - output += files[cc]; - first = false; + + std::vector<std::string>& foundFiles = g.GetFiles(); + files.insert(files.end(), foundFiles.begin(), foundFiles.end()); + + if (configureDepends) { + std::sort(foundFiles.begin(), foundFiles.end()); + foundFiles.erase(std::unique(foundFiles.begin(), foundFiles.end()), + foundFiles.end()); + this->Makefile->GetCMakeInstance()->AddGlobCacheEntry( + recurse, (recurse ? g.GetRecurseListDirs() : g.GetListDirs()), + (recurse ? g.GetRecurseThroughSymlinks() : false), + (g.GetRelative() ? g.GetRelative() : ""), expr, foundFiles, variable, + this->Makefile->GetBacktrace()); + } else { + warnConfigureLate = true; + } + ++i; } } - if (recurse && !explicitFollowSymlinks) { - switch (status) { - case cmPolicies::REQUIRED_IF_USED: - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::NEW: - // Correct behavior, yay! - break; - case cmPolicies::OLD: - // Probably not really the expected behavior, but the author explicitly - // asked for the old behavior... no warning. - case cmPolicies::WARN: - // Possibly unexpected old behavior *and* we actually traversed - // symlinks without being explicitly asked to: warn the author. - if (g.GetFollowedSymlinkCount() != 0) { - this->Makefile->IssueMessage( - cmake::AUTHOR_WARNING, - cmPolicies::GetPolicyWarning(cmPolicies::CMP0009)); - } - break; - } + switch (status) { + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + // Correct behavior, yay! + break; + case cmPolicies::OLD: + // Probably not really the expected behavior, but the author explicitly + // asked for the old behavior... no warning. + case cmPolicies::WARN: + // Possibly unexpected old behavior *and* we actually traversed + // symlinks without being explicitly asked to: warn the author. + if (warnFollowedSymlinks) { + this->Makefile->IssueMessage( + cmake::AUTHOR_WARNING, + cmPolicies::GetPolicyWarning(cmPolicies::CMP0009)); + } + break; } - this->Makefile->AddDefinition(variable, output.c_str()); + std::sort(files.begin(), files.end()); + files.erase(std::unique(files.begin(), files.end()), files.end()); + this->Makefile->AddDefinition(variable, cmJoin(files, ";").c_str()); return true; } @@ -905,6 +959,38 @@ bool cmFileCommand::HandleMakeDirectoryCommand( return true; } +bool cmFileCommand::HandleTouchCommand(std::vector<std::string> const& args, + bool create) +{ + // File command has at least one argument + assert(args.size() > 1); + + std::vector<std::string>::const_iterator i = args.begin(); + + i++; // Get rid of subcommand + + for (; i != args.end(); ++i) { + std::string tfile = *i; + if (!cmsys::SystemTools::FileIsFullPath(tfile)) { + tfile = this->Makefile->GetCurrentSourceDirectory(); + tfile += "/" + *i; + } + if (!this->Makefile->CanIWriteThisFile(tfile)) { + std::string e = + "attempted to touch a file: " + tfile + " in a source directory."; + this->SetError(e); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + if (!cmSystemTools::Touch(tfile, create)) { + std::string error = "problem touching file: " + tfile; + this->SetError(error); + return false; + } + } + return true; +} + bool cmFileCommand::HandleDifferentCommand( std::vector<std::string> const& args) { @@ -1051,14 +1137,26 @@ protected: if (permissions) { #ifdef WIN32 if (Makefile->IsOn("CMAKE_CROSSCOMPILING")) { + // Store the mode in an NTFS alternate stream. std::string mode_t_adt_filename = std::string(toFile) + ":cmake_mode_t"; + // Writing to an NTFS alternate stream changes the modification + // time, so we need to save and restore its original value. + cmSystemToolsFileTime* file_time_orig = cmSystemTools::FileTimeNew(); + cmSystemTools::FileTimeGet(toFile, file_time_orig); + cmsys::ofstream permissionStream(mode_t_adt_filename.c_str()); if (permissionStream) { permissionStream << std::oct << permissions << std::endl; } + + permissionStream.close(); + + cmSystemTools::FileTimeSet(toFile, file_time_orig); + + cmSystemTools::FileTimeDelete(file_time_orig); } #endif diff --git a/Source/cmFileCommand.h b/Source/cmFileCommand.h index 17269f3..719dca2 100644 --- a/Source/cmFileCommand.h +++ b/Source/cmFileCommand.h @@ -39,6 +39,7 @@ protected: bool HandleHashCommand(std::vector<std::string> const& args); bool HandleStringsCommand(std::vector<std::string> const& args); bool HandleGlobCommand(std::vector<std::string> const& args, bool recurse); + bool HandleTouchCommand(std::vector<std::string> const& args, bool create); bool HandleMakeDirectoryCommand(std::vector<std::string> const& args); bool HandleRelativePathCommand(std::vector<std::string> const& args); diff --git a/Source/cmFileTimeComparison.h b/Source/cmFileTimeComparison.h index b1f8ed6..114989b 100644 --- a/Source/cmFileTimeComparison.h +++ b/Source/cmFileTimeComparison.h @@ -8,9 +8,9 @@ class cmFileTimeComparisonInternal; /** \class cmFileTimeComparison - * \brief Helper class for performing globbing searches. + * \brief Helper class for comparing file modification times. * - * Finds all files that match a given globbing expression. + * Compare file modification times or test if file modification times differ. */ class cmFileTimeComparison { diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx index 417cdd2..865595b 100644 --- a/Source/cmFindBase.cxx +++ b/Source/cmFindBase.cxx @@ -67,8 +67,6 @@ bool cmFindBase::ParseArguments(std::vector<std::string> const& argsIn) } this->AlreadyInCache = false; - this->SelectDefaultNoPackageRootPath(); - // Find the current root path mode. this->SelectDefaultRootPathMode(); @@ -206,16 +204,12 @@ void cmFindBase::FillPackageRootPath() { cmSearchPath& paths = this->LabeledPaths[PathLabel::PackageRoot]; - // Add package specific search prefixes - // NOTE: This should be using const_reverse_iterator but HP aCC and - // Oracle sunCC both currently have standard library issues - // with the reverse iterator APIs. - for (std::deque<std::string>::reverse_iterator pkg = - this->Makefile->FindPackageModuleStack.rbegin(); - pkg != this->Makefile->FindPackageModuleStack.rend(); ++pkg) { - std::string varName = *pkg + "_ROOT"; - paths.AddCMakePrefixPath(varName); - paths.AddEnvPrefixPath(varName); + // Add the PACKAGE_ROOT_PATH from each enclosing find_package call. + for (std::deque<std::vector<std::string>>::const_reverse_iterator pkgPaths = + this->Makefile->FindPackageRootPathStack.rbegin(); + pkgPaths != this->Makefile->FindPackageRootPathStack.rend(); + ++pkgPaths) { + paths.AddPrefixPaths(*pkgPaths); } paths.AddSuffixes(this->SearchPathSuffixes); @@ -225,8 +219,8 @@ void cmFindBase::FillCMakeVariablePath() { cmSearchPath& paths = this->LabeledPaths[PathLabel::CMake]; - // Add CMake varibles of the same name as the previous environment - // varibles CMAKE_*_PATH to be used most of the time with -D + // Add CMake variables of the same name as the previous environment + // variables CMAKE_*_PATH to be used most of the time with -D // command line options std::string var = "CMAKE_"; var += this->CMakePathName; diff --git a/Source/cmFindCommon.cxx b/Source/cmFindCommon.cxx index 4a467f3..64108d7 100644 --- a/Source/cmFindCommon.cxx +++ b/Source/cmFindCommon.cxx @@ -88,13 +88,6 @@ void cmFindCommon::InitializeSearchPathGroups() std::make_pair(PathLabel::Guess, cmSearchPath(this))); } -void cmFindCommon::SelectDefaultNoPackageRootPath() -{ - if (!this->Makefile->IsOn("__UNDOCUMENTED_CMAKE_FIND_PACKAGE_ROOT")) { - this->NoPackageRootPath = true; - } -} - void cmFindCommon::SelectDefaultRootPathMode() { // Check the policy variable for this find command type. diff --git a/Source/cmFindCommon.h b/Source/cmFindCommon.h index b237f1b..89ff174 100644 --- a/Source/cmFindCommon.h +++ b/Source/cmFindCommon.h @@ -84,9 +84,6 @@ protected: /** Compute final search path list (reroot + trailing slash). */ void ComputeFinalPaths(); - /** Decide whether to enable the PACKAGE_ROOT search entries. */ - void SelectDefaultNoPackageRootPath(); - /** Compute the current default root path mode. */ void SelectDefaultRootPathMode(); diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index 2f3a85b..46854f7 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx @@ -21,6 +21,7 @@ #include "cmAlgorithms.h" #include "cmMakefile.h" +#include "cmPolicies.h" #include "cmSearchPath.h" #include "cmState.h" #include "cmStateTypes.h" @@ -209,8 +210,6 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args, this->SortDirection = strcmp(sd, "ASC") == 0 ? Asc : Dec; } - this->SelectDefaultNoPackageRootPath(); - // Find the current root path mode. this->SelectDefaultRootPathMode(); @@ -454,6 +453,38 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args, return true; } + { + // Allocate a PACKAGE_ROOT_PATH for the current find_package call. + this->Makefile->FindPackageRootPathStack.emplace_back(); + std::vector<std::string>& rootPaths = + *this->Makefile->FindPackageRootPathStack.rbegin(); + + // Add root paths from <PackageName>_ROOT CMake and environment variables, + // subject to CMP0074. + switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0074)) { + case cmPolicies::WARN: + this->Makefile->MaybeWarnCMP0074(this->Name); + CM_FALLTHROUGH; + case cmPolicies::OLD: + // OLD behavior is to ignore the <pkg>_ROOT variables. + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, + cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0074)); + break; + case cmPolicies::NEW: { + // NEW behavior is to honor the <pkg>_ROOT variables. + std::string const rootVar = this->Name + "_ROOT"; + if (const char* pkgRoot = this->Makefile->GetDefinition(rootVar)) { + cmSystemTools::ExpandListArgument(pkgRoot, rootPaths, false); + } + cmSystemTools::GetPath(rootPaths, rootVar.c_str()); + } break; + } + } + this->SetModuleVariables(components); // See if there is a Find<package>.cmake module. @@ -589,9 +620,6 @@ void cmFindPackageCommand::SetModuleVariables(const std::string& components) exact += "_FIND_VERSION_EXACT"; this->AddFindDefinition(exact, this->VersionExact ? "1" : "0"); } - - // Push on to the package stack - this->Makefile->FindPackageModuleStack.push_back(this->Name); } void cmFindPackageCommand::AddFindDefinition(const std::string& var, @@ -1076,7 +1104,7 @@ void cmFindPackageCommand::AppendSuccessInformation() this->RestoreFindDefinitions(); // Pop the package stack - this->Makefile->FindPackageModuleStack.pop_back(); + this->Makefile->FindPackageRootPathStack.pop_back(); } void cmFindPackageCommand::ComputePrefixes() @@ -1116,16 +1144,14 @@ void cmFindPackageCommand::FillPrefixesPackageRoot() { cmSearchPath& paths = this->LabeledPaths[PathLabel::PackageRoot]; - // Add package specific search prefixes - // NOTE: This should be using const_reverse_iterator but HP aCC and - // Oracle sunCC both currently have standard library issues - // with the reverse iterator APIs. - for (std::deque<std::string>::reverse_iterator pkg = - this->Makefile->FindPackageModuleStack.rbegin(); - pkg != this->Makefile->FindPackageModuleStack.rend(); ++pkg) { - std::string varName = *pkg + "_ROOT"; - paths.AddCMakePath(varName); - paths.AddEnvPath(varName); + // Add the PACKAGE_ROOT_PATH from each enclosing find_package call. + for (std::deque<std::vector<std::string>>::const_reverse_iterator pkgPaths = + this->Makefile->FindPackageRootPathStack.rbegin(); + pkgPaths != this->Makefile->FindPackageRootPathStack.rend(); + ++pkgPaths) { + for (std::string const& path : *pkgPaths) { + paths.AddPath(path); + } } } @@ -2039,7 +2065,7 @@ bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in) common.push_back("lib"); common.push_back("share"); - // PREFIX/(lib/ARCH|lib|share)/cmake/(Foo|foo|FOO).*/ + // PREFIX/(lib/ARCH|lib*|share)/cmake/(Foo|foo|FOO).*/ { cmFindPackageFileList lister(this); lister / cmFileListGeneratorFixed(prefix) / @@ -2052,7 +2078,7 @@ bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in) } } - // PREFIX/(lib/ARCH|lib|share)/(Foo|foo|FOO).*/ + // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/ { cmFindPackageFileList lister(this); lister / cmFileListGeneratorFixed(prefix) / @@ -2064,7 +2090,7 @@ bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in) } } - // PREFIX/(lib/ARCH|lib|share)/(Foo|foo|FOO).*/(cmake|CMake)/ + // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/(cmake|CMake)/ { cmFindPackageFileList lister(this); lister / cmFileListGeneratorFixed(prefix) / @@ -2077,7 +2103,7 @@ bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in) } } - // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib|share)/cmake/(Foo|foo|FOO).*/ + // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/cmake/(Foo|foo|FOO).*/ { cmFindPackageFileList lister(this); lister / cmFileListGeneratorFixed(prefix) / @@ -2092,7 +2118,7 @@ bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in) } } - // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib|share)/(Foo|foo|FOO).*/ + // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/ { cmFindPackageFileList lister(this); lister / cmFileListGeneratorFixed(prefix) / @@ -2106,7 +2132,7 @@ bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in) } } - // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib|share)/(Foo|foo|FOO).*/(cmake|CMake)/ + // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/(cmake|CMake)/ { cmFindPackageFileList lister(this); lister / cmFileListGeneratorFixed(prefix) / diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h index 150a51d..68b5ec0 100644 --- a/Source/cmFindPackageCommand.h +++ b/Source/cmFindPackageCommand.h @@ -6,11 +6,25 @@ #include "cmConfigure.h" // IWYU pragma: keep #include "cm_kwiml.h" +#include <cstddef> #include <map> #include <set> #include <string> #include <vector> +// IWYU insists we should forward-declare instead of including <functional>, +// but we cannot forward-declare reliably because some C++ standard libraries +// put the template in an inline namespace. +#ifdef CMAKE_IWYU +/* clang-format off */ +namespace std { + template <class T> struct hash; +} +/* clang-format on */ +#else +#include <functional> +#endif + #include "cmFindCommon.h" class cmCommand; @@ -194,6 +208,24 @@ private: } }; std::vector<ConfigFileInfo> ConsideredConfigs; + + friend struct std::hash<ConfigFileInfo>; +}; + +namespace std { + +template <> +struct hash<cmFindPackageCommand::ConfigFileInfo> +{ + typedef cmFindPackageCommand::ConfigFileInfo argument_type; + typedef size_t result_type; + + result_type operator()(argument_type const& s) const noexcept + { + result_type const h(std::hash<std::string>{}(s.filename)); + return h; + } }; +} #endif diff --git a/Source/cmForEachCommand.cxx b/Source/cmForEachCommand.cxx index df288bd..9ff967c 100644 --- a/Source/cmForEachCommand.cxx +++ b/Source/cmForEachCommand.cxx @@ -29,10 +29,10 @@ bool cmForEachFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile& mf, cmExecutionStatus& inStatus) { - if (!cmSystemTools::Strucmp(lff.Name.c_str(), "foreach")) { + if (lff.Name.Lower == "foreach") { // record the number of nested foreach commands this->Depth++; - } else if (!cmSystemTools::Strucmp(lff.Name.c_str(), "endforeach")) { + } else if (lff.Name.Lower == "endforeach") { // if this is the endofreach for this statement if (!this->Depth) { // Remove the function blocker for this scope or bail. @@ -97,7 +97,7 @@ bool cmForEachFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff, bool cmForEachFunctionBlocker::ShouldRemove(const cmListFileFunction& lff, cmMakefile& mf) { - if (!cmSystemTools::Strucmp(lff.Name.c_str(), "endforeach")) { + if (lff.Name.Lower == "endforeach") { std::vector<std::string> expandedArguments; mf.ExpandArguments(lff.Arguments, expandedArguments); // if the endforeach has arguments then make sure diff --git a/Source/cmFortranParser.h b/Source/cmFortranParser.h index 3f5ad87..efcd100 100644 --- a/Source/cmFortranParser.h +++ b/Source/cmFortranParser.h @@ -39,11 +39,19 @@ int cmFortranParser_GetOldStartcond(cmFortranParser* parser); /* Callbacks for parser. */ void cmFortranParser_Error(cmFortranParser* parser, const char* message); -void cmFortranParser_RuleUse(cmFortranParser* parser, const char* name); +void cmFortranParser_RuleUse(cmFortranParser* parser, const char* module_name); void cmFortranParser_RuleLineDirective(cmFortranParser* parser, const char* filename); void cmFortranParser_RuleInclude(cmFortranParser* parser, const char* name); -void cmFortranParser_RuleModule(cmFortranParser* parser, const char* name); +void cmFortranParser_RuleModule(cmFortranParser* parser, + const char* module_name); +void cmFortranParser_RuleSubmodule(cmFortranParser* parser, + const char* module_name, + const char* submodule_name); +void cmFortranParser_RuleSubmoduleNested(cmFortranParser* parser, + const char* module_name, + const char* submodule_name, + const char* nested_submodule_name); void cmFortranParser_RuleDefine(cmFortranParser* parser, const char* name); void cmFortranParser_RuleUndef(cmFortranParser* parser, const char* name); void cmFortranParser_RuleIfdef(cmFortranParser* parser, const char* name); diff --git a/Source/cmFortranParserImpl.cxx b/Source/cmFortranParserImpl.cxx index dd4f16b..01cbb78 100644 --- a/Source/cmFortranParserImpl.cxx +++ b/Source/cmFortranParserImpl.cxx @@ -168,11 +168,16 @@ void cmFortranParser_Error(cmFortranParser* parser, const char* msg) parser->Error = msg ? msg : "unknown error"; } -void cmFortranParser_RuleUse(cmFortranParser* parser, const char* name) +void cmFortranParser_RuleUse(cmFortranParser* parser, const char* module_name) { - if (!parser->InPPFalseBranch) { - parser->Info.Requires.insert(cmSystemTools::LowerCase(name)); + if (parser->InPPFalseBranch) { + return; } + + // syntax: "use module_name" + // requires: "module_name.mod" + std::string const& mod_name = cmSystemTools::LowerCase(module_name); + parser->Info.Requires.insert(mod_name + ".mod"); } void cmFortranParser_RuleLineDirective(cmFortranParser* parser, @@ -225,11 +230,63 @@ void cmFortranParser_RuleInclude(cmFortranParser* parser, const char* name) } } -void cmFortranParser_RuleModule(cmFortranParser* parser, const char* name) +void cmFortranParser_RuleModule(cmFortranParser* parser, + const char* module_name) { - if (!parser->InPPFalseBranch && !parser->InInterface) { - parser->Info.Provides.insert(cmSystemTools::LowerCase(name)); + if (parser->InPPFalseBranch) { + return; } + + if (!parser->InInterface) { + // syntax: "module module_name" + // provides: "module_name.mod" + std::string const& mod_name = cmSystemTools::LowerCase(module_name); + parser->Info.Provides.insert(mod_name + ".mod"); + } +} + +void cmFortranParser_RuleSubmodule(cmFortranParser* parser, + const char* module_name, + const char* submodule_name) +{ + if (parser->InPPFalseBranch) { + return; + } + + // syntax: "submodule (module_name) submodule_name" + // requires: "module_name.mod" + // provides: "module_name@submodule_name.smod" + // + // FIXME: Some compilers split the submodule part of a module into a + // separate "module_name.smod" file. Whether it is generated or + // not depends on conditions more subtle than we currently detect. + // For now we depend directly on "module_name.mod". + + std::string const& mod_name = cmSystemTools::LowerCase(module_name); + std::string const& sub_name = cmSystemTools::LowerCase(submodule_name); + parser->Info.Requires.insert(mod_name + ".mod"); + parser->Info.Provides.insert(mod_name + "@" + sub_name + ".smod"); +} + +void cmFortranParser_RuleSubmoduleNested(cmFortranParser* parser, + const char* module_name, + const char* submodule_name, + const char* nested_submodule_name) +{ + if (parser->InPPFalseBranch) { + return; + } + + // syntax: "submodule (module_name:submodule_name) nested_submodule_name" + // requires: "module_name@submodule_name.smod" + // provides: "module_name@nested_submodule_name.smod" + + std::string const& mod_name = cmSystemTools::LowerCase(module_name); + std::string const& sub_name = cmSystemTools::LowerCase(submodule_name); + std::string const& nest_name = + cmSystemTools::LowerCase(nested_submodule_name); + parser->Info.Requires.insert(mod_name + "@" + sub_name + ".smod"); + parser->Info.Provides.insert(mod_name + "@" + nest_name + ".smod"); } void cmFortranParser_RuleDefine(cmFortranParser* parser, const char* macro) diff --git a/Source/cmFunctionCommand.cxx b/Source/cmFunctionCommand.cxx index 774df84..67c9e9a 100644 --- a/Source/cmFunctionCommand.cxx +++ b/Source/cmFunctionCommand.cxx @@ -9,7 +9,6 @@ #include "cmMakefile.h" #include "cmPolicies.h" #include "cmState.h" -#include "cmSystemTools.h" // define the class for function commands class cmFunctionHelperCommand : public cmCommand @@ -128,9 +127,9 @@ bool cmFunctionFunctionBlocker::IsFunctionBlocked( { // record commands until we hit the ENDFUNCTION // at the ENDFUNCTION call we shift gears and start looking for invocations - if (!cmSystemTools::Strucmp(lff.Name.c_str(), "function")) { + if (lff.Name.Lower == "function") { this->Depth++; - } else if (!cmSystemTools::Strucmp(lff.Name.c_str(), "endfunction")) { + } else if (lff.Name.Lower == "endfunction") { // if this is the endfunction for this function then execute if (!this->Depth) { // create a new command and add it to cmake @@ -157,7 +156,7 @@ bool cmFunctionFunctionBlocker::IsFunctionBlocked( bool cmFunctionFunctionBlocker::ShouldRemove(const cmListFileFunction& lff, cmMakefile& mf) { - if (!cmSystemTools::Strucmp(lff.Name.c_str(), "endfunction")) { + if (lff.Name.Lower == "endfunction") { std::vector<std::string> expandedArguments; mf.ExpandArguments(lff.Arguments, expandedArguments, this->GetStartingContext().FilePath.c_str()); diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx index f0eafb4..face282 100644 --- a/Source/cmGeneratorExpressionDAGChecker.cxx +++ b/Source/cmGeneratorExpressionDAGChecker.cxx @@ -154,6 +154,18 @@ bool cmGeneratorExpressionDAGChecker::GetTransitivePropertiesOnly() return top->TransitivePropertiesOnly; } +bool cmGeneratorExpressionDAGChecker::EvaluatingGenexExpression() +{ + const cmGeneratorExpressionDAGChecker* top = this; + const cmGeneratorExpressionDAGChecker* parent = this->Parent; + while (parent) { + top = parent; + parent = parent->Parent; + } + + return top->Property == "TARGET_GENEX_EVAL" || top->Property == "GENEX_EVAL"; +} + bool cmGeneratorExpressionDAGChecker::EvaluatingLinkLibraries(const char* tgt) { const cmGeneratorExpressionDAGChecker* top = this; diff --git a/Source/cmGeneratorExpressionDAGChecker.h b/Source/cmGeneratorExpressionDAGChecker.h index 3f73fca..a3a8f69 100644 --- a/Source/cmGeneratorExpressionDAGChecker.h +++ b/Source/cmGeneratorExpressionDAGChecker.h @@ -61,6 +61,7 @@ struct cmGeneratorExpressionDAGChecker void ReportError(cmGeneratorExpressionContext* context, const std::string& expr); + bool EvaluatingGenexExpression(); bool EvaluatingLinkLibraries(const char* tgt = nullptr); #define DECLARE_TRANSITIVE_PROPERTY_METHOD(METHOD) bool METHOD() const; diff --git a/Source/cmGeneratorExpressionEvaluator.h b/Source/cmGeneratorExpressionEvaluator.h index 92dac79..0561799 100644 --- a/Source/cmGeneratorExpressionEvaluator.h +++ b/Source/cmGeneratorExpressionEvaluator.h @@ -7,6 +7,7 @@ #include <stddef.h> #include <string> +#include <utility> #include <vector> struct cmGeneratorExpressionContext; @@ -64,17 +65,16 @@ private: struct GeneratorExpressionContent : public cmGeneratorExpressionEvaluator { GeneratorExpressionContent(const char* startContent, size_t length); - void SetIdentifier( - std::vector<cmGeneratorExpressionEvaluator*> const& identifier) + + void SetIdentifier(std::vector<cmGeneratorExpressionEvaluator*> identifier) { - this->IdentifierChildren = identifier; + this->IdentifierChildren = std::move(identifier); } void SetParameters( - std::vector<std::vector<cmGeneratorExpressionEvaluator*>> const& - parameters) + std::vector<std::vector<cmGeneratorExpressionEvaluator*>> parameters) { - this->ParamChildren = parameters; + this->ParamChildren = std::move(parameters); } Type GetType() const override diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index c1f1ee4..7b35bce 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx @@ -275,6 +275,203 @@ static const struct EqualNode : public cmGeneratorExpressionNode } } equalNode; +static const struct InListNode : public cmGeneratorExpressionNode +{ + InListNode() {} + + int NumExpectedParameters() const override { return 2; } + + std::string Evaluate( + const std::vector<std::string>& parameters, + cmGeneratorExpressionContext* /*context*/, + const GeneratorExpressionContent* /*content*/, + cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override + { + std::vector<std::string> values; + cmSystemTools::ExpandListArgument(parameters[1], values); + if (values.empty()) { + return "0"; + } + + return std::find(values.cbegin(), values.cend(), parameters.front()) == + values.cend() + ? "0" + : "1"; + } +} inListNode; + +static const struct TargetExistsNode : public cmGeneratorExpressionNode +{ + TargetExistsNode() {} + + int NumExpectedParameters() const override { return 1; } + + std::string Evaluate( + const std::vector<std::string>& parameters, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content, + cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override + { + if (parameters.size() != 1) { + reportError(context, content->GetOriginalExpression(), + "$<TARGET_EXISTS:...> expression requires one parameter"); + return std::string(); + } + + std::string targetName = parameters.front(); + if (targetName.empty() || + !cmGeneratorExpression::IsValidTargetName(targetName)) { + reportError(context, content->GetOriginalExpression(), + "$<TARGET_EXISTS:tgt> expression requires a non-empty " + "valid target name."); + return std::string(); + } + + return context->LG->GetMakefile()->FindTargetToUse(targetName) ? "1" : "0"; + } +} targetExistsNode; + +static const struct TargetNameIfExistsNode : public cmGeneratorExpressionNode +{ + TargetNameIfExistsNode() {} + + int NumExpectedParameters() const override { return 1; } + + std::string Evaluate( + const std::vector<std::string>& parameters, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content, + cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override + { + if (parameters.size() != 1) { + reportError(context, content->GetOriginalExpression(), + "$<TARGET_NAME_IF_EXISTS:...> expression requires one " + "parameter"); + return std::string(); + } + + std::string targetName = parameters.front(); + if (targetName.empty() || + !cmGeneratorExpression::IsValidTargetName(targetName)) { + reportError(context, content->GetOriginalExpression(), + "$<TARGET_NAME_IF_EXISTS:tgt> expression requires a " + "non-empty valid target name."); + return std::string(); + } + + return context->LG->GetMakefile()->FindTargetToUse(targetName) + ? targetName + : std::string(); + } +} targetNameIfExistsNode; + +struct GenexEvaluator : public cmGeneratorExpressionNode +{ + GenexEvaluator() {} + +protected: + std::string EvaluateExpression( + const std::string& genexOperator, const std::string& expression, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content, + cmGeneratorExpressionDAGChecker* dagCheckerParent) const + { + if (context->HeadTarget) { + cmGeneratorExpressionDAGChecker dagChecker( + context->Backtrace, context->HeadTarget->GetName(), genexOperator, + content, dagCheckerParent); + switch (dagChecker.Check()) { + case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: + case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: { + dagChecker.ReportError(context, content->GetOriginalExpression()); + return std::string(); + } + case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: + case cmGeneratorExpressionDAGChecker::DAG: + break; + } + + return this->EvaluateDependentExpression( + expression, context->LG, context, context->HeadTarget, + context->CurrentTarget, &dagChecker); + } + + return this->EvaluateDependentExpression( + expression, context->LG, context, context->HeadTarget, + context->CurrentTarget, dagCheckerParent); + } +}; + +static const struct TargetGenexEvalNode : public GenexEvaluator +{ + TargetGenexEvalNode() {} + + int NumExpectedParameters() const override { return 2; } + + bool AcceptsArbitraryContentParameter() const override { return true; } + + std::string Evaluate( + const std::vector<std::string>& parameters, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content, + cmGeneratorExpressionDAGChecker* dagCheckerParent) const override + { + const std::string& targetName = parameters.front(); + if (targetName.empty() || + !cmGeneratorExpression::IsValidTargetName(targetName)) { + reportError(context, content->GetOriginalExpression(), + "$<TARGET_GENEX_EVAL:tgt, ...> expression requires a " + "non-empty valid target name."); + return std::string(); + } + + const auto* target = context->LG->FindGeneratorTargetToUse(targetName); + if (!target) { + std::ostringstream e; + e << "$<TARGET_GENEX_EVAL:tgt, ...> target \"" << targetName + << "\" not found."; + reportError(context, content->GetOriginalExpression(), e.str()); + return std::string(); + } + + const std::string& expression = parameters[1]; + if (expression.empty()) { + return expression; + } + + cmGeneratorExpressionContext targetContext( + context->LG, context->Config, context->Quiet, target, target, + context->EvaluateForBuildsystem, context->Backtrace, context->Language); + + return this->EvaluateExpression("TARGET_GENEX_EVAL", expression, + &targetContext, content, dagCheckerParent); + } +} targetGenexEvalNode; + +static const struct GenexEvalNode : public GenexEvaluator +{ + GenexEvalNode() {} + + int NumExpectedParameters() const override { return 1; } + + bool AcceptsArbitraryContentParameter() const override { return true; } + + std::string Evaluate( + const std::vector<std::string>& parameters, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content, + cmGeneratorExpressionDAGChecker* dagCheckerParent) const override + { + const std::string& expression = parameters[0]; + if (expression.empty()) { + return expression; + } + + return this->EvaluateExpression("GENEX_EVAL", expression, context, content, + dagCheckerParent); + } +} genexEvalNode; + static const struct LowerCaseNode : public cmGeneratorExpressionNode { LowerCaseNode() {} @@ -816,16 +1013,7 @@ static const struct CompileLanguageNode : public cmGeneratorExpressionNode return std::string(); } - std::vector<std::string> enabledLanguages; cmGlobalGenerator* gg = context->LG->GetGlobalGenerator(); - gg->GetEnabledLanguages(enabledLanguages); - if (!parameters.empty() && - std::find(enabledLanguages.begin(), enabledLanguages.end(), - parameters.front()) == enabledLanguages.end()) { - reportError(context, content->GetOriginalExpression(), - "$<COMPILE_LANGUAGE:...> Unknown language."); - return std::string(); - } std::string genName = gg->GetName(); if (genName.find("Makefiles") == std::string::npos && genName.find("Ninja") == std::string::npos && @@ -1034,7 +1222,9 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode const char* prop = target->GetProperty(propertyName); if (dagCheckerParent) { - if (dagCheckerParent->EvaluatingLinkLibraries()) { + if (dagCheckerParent->EvaluatingGenexExpression()) { + // No check required. + } else if (dagCheckerParent->EvaluatingLinkLibraries()) { #define TRANSITIVE_PROPERTY_COMPARE(PROPERTY) \ (#PROPERTY == propertyName || "INTERFACE_" #PROPERTY == propertyName) || if (CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME( @@ -1585,7 +1775,8 @@ struct TargetFilesystemArtifactResultCreator<ArtifactLinkerTag> "executables with ENABLE_EXPORTS."); return std::string(); } - cmStateEnums::ArtifactType artifact = target->HasImportLibrary() + cmStateEnums::ArtifactType artifact = + target->HasImportLibrary(context->Config) ? cmStateEnums::ImportLibraryArtifact : cmStateEnums::RuntimeBinaryArtifact; return target->GetFullPath(context->Config, artifact); @@ -1827,6 +2018,7 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode( nodeMap["TARGET_BUNDLE_CONTENT_DIR"] = &targetBundleContentDirNode; nodeMap["STREQUAL"] = &strEqualNode; nodeMap["EQUAL"] = &equalNode; + nodeMap["IN_LIST"] = &inListNode; nodeMap["LOWER_CASE"] = &lowerCaseNode; nodeMap["UPPER_CASE"] = &upperCaseNode; nodeMap["MAKE_C_IDENTIFIER"] = &makeCIdentifierNode; @@ -1839,6 +2031,10 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode( nodeMap["TARGET_NAME"] = &targetNameNode; nodeMap["TARGET_OBJECTS"] = &targetObjectsNode; nodeMap["TARGET_POLICY"] = &targetPolicyNode; + nodeMap["TARGET_EXISTS"] = &targetExistsNode; + nodeMap["TARGET_NAME_IF_EXISTS"] = &targetNameIfExistsNode; + nodeMap["TARGET_GENEX_EVAL"] = &targetGenexEvalNode; + nodeMap["GENEX_EVAL"] = &genexEvalNode; nodeMap["BUILD_INTERFACE"] = &buildInterfaceNode; nodeMap["INSTALL_INTERFACE"] = &installInterfaceNode; nodeMap["INSTALL_PREFIX"] = &installPrefixNode; diff --git a/Source/cmGeneratorExpressionParser.cxx b/Source/cmGeneratorExpressionParser.cxx index 278de04..7b4dc7b 100644 --- a/Source/cmGeneratorExpressionParser.cxx +++ b/Source/cmGeneratorExpressionParser.cxx @@ -6,6 +6,7 @@ #include <assert.h> #include <stddef.h> +#include <utility> cmGeneratorExpressionParser::cmGeneratorExpressionParser( const std::vector<cmGeneratorExpressionToken>& tokens) @@ -92,7 +93,7 @@ void cmGeneratorExpressionParser::ParseGeneratorExpression( assert(this->it != this->Tokens.end()); ++this->it; --this->NestingLevel; - content->SetIdentifier(identifier); + content->SetIdentifier(std::move(identifier)); result.push_back(content); return; } @@ -198,8 +199,8 @@ void cmGeneratorExpressionParser::ParseGeneratorExpression( ((this->it - 1)->Content - startToken->Content) + (this->it - 1)->Length; GeneratorExpressionContent* content = new GeneratorExpressionContent(startToken->Content, contentLength); - content->SetIdentifier(identifier); - content->SetParameters(parameters); + content->SetIdentifier(std::move(identifier)); + content->SetParameters(std::move(parameters)); result.push_back(content); } diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 2bb01b2..0c99ed4 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -132,8 +132,8 @@ cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg) this->SourceEntries, true); this->DLLPlatform = - (this->Makefile->IsOn("WIN32") || this->Makefile->IsOn("CYGWIN") || - this->Makefile->IsOn("MINGW")); + strcmp(this->Makefile->GetSafeDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX"), + "") != 0; this->PolicyMap = t->PolicyMap; } @@ -240,13 +240,16 @@ const char* cmGeneratorTarget::GetOutputTargetType( case cmStateEnums::MODULE_LIBRARY: switch (artifact) { case cmStateEnums::RuntimeBinaryArtifact: - // Module import libraries are treated as archive targets. + // Module libraries are always treated as library targets. return "LIBRARY"; case cmStateEnums::ImportLibraryArtifact: - // Module libraries are always treated as library targets. + // Module import libraries are treated as archive targets. return "ARCHIVE"; } break; + case cmStateEnums::OBJECT_LIBRARY: + // Object libraries are always treated as object targets. + return "OBJECT"; case cmStateEnums::EXECUTABLE: switch (artifact) { case cmStateEnums::RuntimeBinaryArtifact: @@ -808,6 +811,26 @@ static void AddInterfaceEntries( } } +static void AddObjectEntries( + cmGeneratorTarget const* thisTarget, std::string const& config, + std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries) +{ + if (cmLinkImplementationLibraries const* impl = + thisTarget->GetLinkImplementationLibraries(config)) { + for (cmLinkImplItem const& lib : impl->Libraries) { + if (lib.Target && + lib.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) { + std::string genex = "$<TARGET_OBJECTS:" + lib + ">"; + cmGeneratorExpression ge(lib.Backtrace); + std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(genex); + cge->SetEvaluateForBuildsystem(true); + entries.push_back( + new cmGeneratorTarget::TargetPropertyEntry(std::move(cge), lib)); + } + } + } +} + static bool processSources( cmGeneratorTarget const* tgt, const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries, @@ -848,13 +871,10 @@ static bool processSources( std::ostringstream err; if (!targetName.empty()) { err << "Target \"" << targetName - << "\" contains relative " - "path in its INTERFACE_SOURCES:\n" - " \"" + << "\" contains relative path in its INTERFACE_SOURCES:\n \"" << src << "\""; } else { - err << "Found relative path while evaluating sources of " - "\"" + err << "Found relative path while evaluating sources of \"" << tgt->GetName() << "\":\n \"" << src << "\"\n"; } tgt->GetLocalGenerator()->IssueMessage(cmake::FATAL_ERROR, err.str()); @@ -931,23 +951,32 @@ void cmGeneratorTarget::GetSourceFiles(std::vector<std::string>& files, processSources(this, this->SourceEntries, files, uniqueSrcs, &dagChecker, config, debugSources); + // Collect INTERFACE_SOURCES of all direct link-dependencies. std::vector<cmGeneratorTarget::TargetPropertyEntry*> linkInterfaceSourcesEntries; - AddInterfaceEntries(this, config, "INTERFACE_SOURCES", linkInterfaceSourcesEntries); - std::vector<std::string>::size_type numFilesBefore = files.size(); bool contextDependentInterfaceSources = processSources(this, linkInterfaceSourcesEntries, files, uniqueSrcs, &dagChecker, config, debugSources); + // Collect TARGET_OBJECTS of direct object link-dependencies. + std::vector<cmGeneratorTarget::TargetPropertyEntry*> linkObjectsEntries; + AddObjectEntries(this, config, linkObjectsEntries); + std::vector<std::string>::size_type numFilesBefore2 = files.size(); + bool contextDependentObjects = + processSources(this, linkObjectsEntries, files, uniqueSrcs, &dagChecker, + config, debugSources); + if (!contextDependentDirectSources && - !(contextDependentInterfaceSources && numFilesBefore < files.size())) { + !(contextDependentInterfaceSources && numFilesBefore < files.size()) && + !(contextDependentObjects && numFilesBefore2 < files.size())) { this->LinkImplementationLanguageIsContextDependent = false; } cmDeleteAll(linkInterfaceSourcesEntries); + cmDeleteAll(linkObjectsEntries); } void cmGeneratorTarget::GetSourceFiles(std::vector<cmSourceFile*>& files, @@ -1053,9 +1082,6 @@ void cmGeneratorTarget::ComputeKindedSources(KindedSources& files, kind = SourceKindHeader; } else if (sf->GetPropertyAsBool("EXTERNAL_OBJECT")) { kind = SourceKindExternalObject; - if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) { - badObjLib.push_back(sf); - } } else if (!sf->GetLanguage().empty()) { kind = SourceKindObjectSource; } else if (ext == "def") { @@ -1673,6 +1699,7 @@ bool cmGeneratorTarget::HaveWellDefinedOutputFiles() const return this->GetType() == cmStateEnums::STATIC_LIBRARY || this->GetType() == cmStateEnums::SHARED_LIBRARY || this->GetType() == cmStateEnums::MODULE_LIBRARY || + this->GetType() == cmStateEnums::OBJECT_LIBRARY || this->GetType() == cmStateEnums::EXECUTABLE; } @@ -2001,8 +2028,13 @@ void cmGeneratorTarget::ComputeModuleDefinitionInfo( info.WindowsExportAllSymbols = this->Makefile->IsOn("CMAKE_SUPPORT_WINDOWS_EXPORT_ALL_SYMBOLS") && this->GetPropertyAsBool("WINDOWS_EXPORT_ALL_SYMBOLS"); +#if defined(_WIN32) && defined(CMAKE_BUILD_WITH_CMAKE) info.DefFileGenerated = info.WindowsExportAllSymbols || info.Sources.size() > 1; +#else + // Our __create_def helper is only available on Windows. + info.DefFileGenerated = false; +#endif if (info.DefFileGenerated) { info.DefFile = this->ObjectDirectory /* has slash */ + "exports.def"; } else if (!info.Sources.empty()) { @@ -2592,13 +2624,20 @@ std::vector<std::string> cmGeneratorTarget::GetIncludeDirectories( return includes; } +enum class OptionsParse +{ + None, + Shell +}; + static void processCompileOptionsInternal( cmGeneratorTarget const* tgt, const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries, std::vector<std::string>& options, std::unordered_set<std::string>& uniqueOptions, cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config, - bool debugOptions, const char* logName, std::string const& language) + bool debugOptions, const char* logName, std::string const& language, + OptionsParse parse) { for (cmGeneratorTarget::TargetPropertyEntry* entry : entries) { std::vector<std::string> entryOptions; @@ -2609,7 +2648,12 @@ static void processCompileOptionsInternal( std::string usedOptions; for (std::string const& opt : entryOptions) { if (uniqueOptions.insert(opt).second) { - options.push_back(opt); + if (parse == OptionsParse::Shell && + cmHasLiteralPrefix(opt, "SHELL:")) { + cmSystemTools::ParseUnixCommandLine(opt.c_str() + 6, options); + } else { + options.push_back(opt); + } if (debugOptions) { usedOptions += " * " + opt + "\n"; } @@ -2634,7 +2678,7 @@ static void processCompileOptions( { processCompileOptionsInternal(tgt, entries, options, uniqueOptions, dagChecker, config, debugOptions, "options", - language); + language, OptionsParse::Shell); } void cmGeneratorTarget::GetCompileOptions(std::vector<std::string>& result, @@ -2688,7 +2732,7 @@ static void processCompileFeatures( { processCompileOptionsInternal(tgt, entries, options, uniqueOptions, dagChecker, config, debugOptions, "features", - std::string()); + std::string(), OptionsParse::None); } void cmGeneratorTarget::GetCompileFeatures(std::vector<std::string>& result, @@ -2738,7 +2782,7 @@ static void processCompileDefinitions( { processCompileOptionsInternal(tgt, entries, options, uniqueOptions, dagChecker, config, debugOptions, - "definitions", language); + "definitions", language, OptionsParse::None); } void cmGeneratorTarget::GetCompileDefinitions( @@ -4568,13 +4612,24 @@ bool cmGeneratorTarget::ComputePDBOutputDir(const std::string& kind, // Select an output directory. if (const char* config_outdir = this->GetProperty(configProp)) { // Use the user-specified per-configuration output directory. - out = config_outdir; + cmGeneratorExpression ge; + std::unique_ptr<cmCompiledGeneratorExpression> cge = + ge.Parse(config_outdir); + out = cge->Evaluate(this->LocalGenerator, config); // Skip per-configuration subdirectory. conf.clear(); } else if (const char* outdir = this->GetProperty(propertyName)) { // Use the user-specified output directory. - out = outdir; + cmGeneratorExpression ge; + std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(outdir); + out = cge->Evaluate(this->LocalGenerator, config); + + // Skip per-configuration subdirectory if the value contained a + // generator expression. + if (out != outdir) { + conf.clear(); + } } if (out.empty()) { return false; @@ -4934,6 +4989,16 @@ void cmGeneratorTarget::ComputeImportInfo(std::string const& desired_config, } } + // Get information if target is managed assembly. + { + std::string linkProp = "IMPORTED_COMMON_LANGUAGE_RUNTIME"; + if (auto pc = this->GetProperty(linkProp + suffix)) { + info.Managed = this->CheckManagedType(pc); + } else if (auto p = this->GetProperty(linkProp)) { + info.Managed = this->CheckManagedType(p); + } + } + // Get the cyclic repetition count. if (this->GetType() == cmStateEnums::STATIC_LIBRARY) { std::string linkProp = "IMPORTED_LINK_INTERFACE_MULTIPLICITY"; @@ -5151,6 +5216,18 @@ void cmGeneratorTarget::GetLanguages(std::set<std::string>& languages, } } +bool cmGeneratorTarget::HasLanguage(std::string const& language, + std::string const& config, + bool exclusive) const +{ + std::set<std::string> languages; + this->GetLanguages(languages, config); + // add linker language (if it is different from compiler languages) + languages.insert(this->GetLinkerLanguage(config)); + return (languages.size() == 1 || !exclusive) && + languages.count(language) > 0; +} + void cmGeneratorTarget::ComputeLinkImplementationLanguages( const std::string& config, cmOptionalLinkImplementation& impl) const { @@ -5325,20 +5402,6 @@ cmGeneratorTarget* cmGeneratorTarget::FindTargetToLink( tgt = nullptr; } - if (tgt && tgt->GetType() == cmStateEnums::OBJECT_LIBRARY) { - std::ostringstream e; - e << "Target \"" << this->GetName() << "\" links to " - "OBJECT library \"" - << tgt->GetName() - << "\" but this is not " - "allowed. " - "One may link only to STATIC or SHARED libraries, or to executables " - "with the ENABLE_EXPORTS property set."; - cmake* cm = this->LocalGenerator->GetCMakeInstance(); - cm->IssueMessage(cmake::FATAL_ERROR, e.str(), this->GetBacktrace()); - tgt = nullptr; - } - return tgt; } @@ -5351,16 +5414,17 @@ std::string cmGeneratorTarget::GetPDBDirectory(const std::string& config) const return ""; } -bool cmGeneratorTarget::HasImplibGNUtoMS() const +bool cmGeneratorTarget::HasImplibGNUtoMS(std::string const& config) const { - return this->HasImportLibrary() && this->GetPropertyAsBool("GNUtoMS"); + return this->HasImportLibrary(config) && this->GetPropertyAsBool("GNUtoMS"); } -bool cmGeneratorTarget::GetImplibGNUtoMS(std::string const& gnuName, +bool cmGeneratorTarget::GetImplibGNUtoMS(std::string const& config, + std::string const& gnuName, std::string& out, const char* newExt) const { - if (this->HasImplibGNUtoMS() && gnuName.size() > 6 && + if (this->HasImplibGNUtoMS(config) && gnuName.size() > 6 && gnuName.substr(gnuName.size() - 6) == ".dll.a") { out = gnuName.substr(0, gnuName.size() - 6); out += newExt ? newExt : ".lib"; @@ -5375,11 +5439,14 @@ bool cmGeneratorTarget::IsExecutableWithExports() const this->GetPropertyAsBool("ENABLE_EXPORTS")); } -bool cmGeneratorTarget::HasImportLibrary() const +bool cmGeneratorTarget::HasImportLibrary(std::string const& config) const { return (this->IsDLLPlatform() && (this->GetType() == cmStateEnums::SHARED_LIBRARY || - this->IsExecutableWithExports())); + this->IsExecutableWithExports()) && + // Assemblies which have only managed code do not have + // import libraries. + this->GetManagedType(config) != ManagedType::Managed); } std::string cmGeneratorTarget::GetSupportDirectory() const @@ -5402,6 +5469,7 @@ bool cmGeneratorTarget::IsLinkable() const this->GetType() == cmStateEnums::SHARED_LIBRARY || this->GetType() == cmStateEnums::MODULE_LIBRARY || this->GetType() == cmStateEnums::UNKNOWN_LIBRARY || + this->GetType() == cmStateEnums::OBJECT_LIBRARY || this->GetType() == cmStateEnums::INTERFACE_LIBRARY || this->IsExecutableWithExports()); } @@ -5431,3 +5499,53 @@ bool cmGeneratorTarget::IsCFBundleOnApple() const return (this->GetType() == cmStateEnums::MODULE_LIBRARY && this->Makefile->IsOn("APPLE") && this->GetPropertyAsBool("BUNDLE")); } + +cmGeneratorTarget::ManagedType cmGeneratorTarget::CheckManagedType( + std::string const& propval) const +{ + // The type of the managed assembly (mixed unmanaged C++ and C++/CLI, + // or only C++/CLI) does only depend on whether the property is an empty + // string or contains any value at all. In Visual Studio generators + // this propval is prepended with /clr[:] which results in: + // + // 1. propval does not exist: no /clr flag, unmanaged target, has import + // lib + // 2. empty propval: add /clr as flag, mixed unmanaged/managed + // target, has import lib + // 3. any value (safe,pure): add /clr:[propval] as flag, target with + // managed code only, no import lib + return propval.empty() ? ManagedType::Mixed : ManagedType::Managed; +} + +cmGeneratorTarget::ManagedType cmGeneratorTarget::GetManagedType( + const std::string& config) const +{ + // Only libraries and executables can be managed targets. + if (this->GetType() > cmStateEnums::SHARED_LIBRARY) { + return ManagedType::Undefined; + } + + if (this->GetType() == cmStateEnums::STATIC_LIBRARY) { + return ManagedType::Native; + } + + // Check imported target. + if (this->IsImported()) { + if (cmGeneratorTarget::ImportInfo const* info = + this->GetImportInfo(config)) { + return info->Managed; + } + return ManagedType::Undefined; + } + + // Check for explicitly set clr target property. + if (auto* clr = this->GetProperty("COMMON_LANGUAGE_RUNTIME")) { + return this->CheckManagedType(clr); + } + + // C# targets are always managed. This language specific check + // is added to avoid that the COMMON_LANGUAGE_RUNTIME target property + // has to be set manually for C# targets. + return this->HasLanguage("CSharp", config) ? ManagedType::Managed + : ManagedType::Native; +} diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 2f6ce33..d4a553a 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -364,6 +364,12 @@ public: void GetLanguages(std::set<std::string>& languages, std::string const& config) const; + // Evaluate if the target uses the given language for compilation + // and/or linking. If 'exclusive' is true, 'language' is expected + // to be the only language used for the target. + bool HasLanguage(std::string const& language, std::string const& config, + bool exclusive = true) const; + void GetObjectLibrariesCMP0026( std::vector<cmGeneratorTarget*>& objlibs) const; @@ -566,17 +572,17 @@ public: std::string GetLinkerLanguage(const std::string& config) const; /** Does this target have a GNU implib to convert to MS format? */ - bool HasImplibGNUtoMS() const; + bool HasImplibGNUtoMS(std::string const& config) const; /** Convert the given GNU import library name (.dll.a) to a name with a new extension (.lib or ${CMAKE_IMPORT_LIBRARY_SUFFIX}). */ - bool GetImplibGNUtoMS(std::string const& gnuName, std::string& out, - const char* newExt = nullptr) const; + bool GetImplibGNUtoMS(std::string const& config, std::string const& gnuName, + std::string& out, const char* newExt = nullptr) const; bool IsExecutableWithExports() const; /** Return whether or not the target has a DLL import library. */ - bool HasImportLibrary() const; + bool HasImportLibrary(std::string const& config) const; /** Get a build-tree directory in which to place target support files. */ std::string GetSupportDirectory() const; @@ -597,6 +603,19 @@ public: /** Return whether this target is a CFBundle (plugin) on Apple. */ bool IsCFBundleOnApple() const; + /** Assembly types. The order of the values of this enum is relevant + because of smaller/larger comparison operations! */ + enum ManagedType + { + Undefined = 0, // target is no lib or executable + Native, // target compiles to unmanaged binary. + Mixed, // target compiles to mixed (managed and unmanaged) binary. + Managed // target compiles to managed binary. + }; + + /** Return the type of assembly this target compiles to. */ + ManagedType GetManagedType(const std::string& config) const; + struct SourceFileFlags GetTargetSourceFileFlags( const cmSourceFile* sf) const; @@ -741,10 +760,12 @@ private: { ImportInfo() : NoSOName(false) + , Managed(Native) , Multiplicity(0) { } bool NoSOName; + ManagedType Managed; unsigned int Multiplicity; std::string Location; std::string SOName; @@ -838,6 +859,8 @@ private: bool ComputePDBOutputDir(const std::string& kind, const std::string& config, std::string& out) const; + ManagedType CheckManagedType(std::string const& propval) const; + public: const std::vector<const cmGeneratorTarget*>& GetLinkImplementationClosure( const std::string& config) const; diff --git a/Source/cmGlobVerificationManager.cxx b/Source/cmGlobVerificationManager.cxx new file mode 100644 index 0000000..e23b6ea --- /dev/null +++ b/Source/cmGlobVerificationManager.cxx @@ -0,0 +1,172 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmGlobVerificationManager.h" + +#include "cmsys/FStream.hxx" +#include <sstream> + +#include "cmGeneratedFileStream.h" +#include "cmListFileCache.h" +#include "cmSystemTools.h" +#include "cmVersion.h" +#include "cmake.h" + +bool cmGlobVerificationManager::SaveVerificationScript(const std::string& path) +{ + if (this->Cache.empty()) { + return true; + } + + std::string scriptFile = path; + scriptFile += cmake::GetCMakeFilesDirectory(); + std::string stampFile = scriptFile; + cmSystemTools::MakeDirectory(scriptFile); + scriptFile += "/VerifyGlobs.cmake"; + stampFile += "/cmake.verify_globs"; + cmGeneratedFileStream verifyScriptFile(scriptFile.c_str()); + verifyScriptFile.SetCopyIfDifferent(true); + if (!verifyScriptFile) { + cmSystemTools::Error("Unable to open verification script file for save. ", + scriptFile.c_str()); + cmSystemTools::ReportLastSystemError(""); + return false; + } + + verifyScriptFile << std::boolalpha; + verifyScriptFile << "# CMAKE generated file: DO NOT EDIT!\n" + << "# Generated by CMake Version " + << cmVersion::GetMajorVersion() << "." + << cmVersion::GetMinorVersion() << "\n"; + + for (auto const& i : this->Cache) { + CacheEntryKey k = std::get<0>(i); + CacheEntryValue v = std::get<1>(i); + + if (!v.Initialized) { + continue; + } + + verifyScriptFile << "\n"; + + for (auto const& bt : v.Backtraces) { + verifyScriptFile << "# " << std::get<0>(bt); + std::get<1>(bt).PrintTitle(verifyScriptFile); + verifyScriptFile << "\n"; + } + + k.PrintGlobCommand(verifyScriptFile, "NEW_GLOB"); + verifyScriptFile << "\n"; + + verifyScriptFile << "set(OLD_GLOB\n"; + for (const std::string& file : v.Files) { + verifyScriptFile << " \"" << file << "\"\n"; + } + verifyScriptFile << " )\n"; + + verifyScriptFile << "if(NOT \"${NEW_GLOB}\" STREQUAL \"${OLD_GLOB}\")\n" + << " message(\"-- GLOB mismatch!\")\n" + << " file(TOUCH_NOCREATE \"" << stampFile << "\")\n" + << "endif()\n"; + } + verifyScriptFile.Close(); + + cmsys::ofstream verifyStampFile(stampFile.c_str()); + if (!verifyStampFile) { + cmSystemTools::Error("Unable to open verification stamp file for write. ", + stampFile.c_str()); + return false; + } + verifyStampFile << "# This file is generated by CMake for checking of the " + "VerifyGlobs.cmake file\n"; + this->VerifyScript = scriptFile; + this->VerifyStamp = stampFile; + return true; +} + +bool cmGlobVerificationManager::DoWriteVerifyTarget() const +{ + return !this->VerifyScript.empty() && !this->VerifyStamp.empty(); +} + +bool cmGlobVerificationManager::CacheEntryKey::operator<( + const CacheEntryKey& r) const +{ + if (this->Recurse < r.Recurse) { + return true; + } + if (this->Recurse > r.Recurse) { + return false; + } + if (this->ListDirectories < r.ListDirectories) { + return true; + } + if (this->ListDirectories > r.ListDirectories) { + return false; + } + if (this->FollowSymlinks < r.FollowSymlinks) { + return true; + } + if (this->FollowSymlinks > r.FollowSymlinks) { + return false; + } + if (this->Relative < r.Relative) { + return true; + } + if (this->Relative > r.Relative) { + return false; + } + if (this->Expression < r.Expression) { + return true; + } + if (this->Expression > r.Expression) { + return false; + } + return false; +} + +void cmGlobVerificationManager::CacheEntryKey::PrintGlobCommand( + std::ostream& out, const std::string& cmdVar) +{ + out << "file(GLOB" << (this->Recurse ? "_RECURSE " : " "); + out << cmdVar << " "; + if (this->Recurse && this->FollowSymlinks) { + out << "FOLLOW_SYMLINKS "; + } + out << "LIST_DIRECTORIES " << this->ListDirectories << " "; + if (!this->Relative.empty()) { + out << "RELATIVE \"" << this->Relative << "\" "; + } + out << "\"" << this->Expression << "\")"; +} + +void cmGlobVerificationManager::AddCacheEntry( + const bool recurse, const bool listDirectories, const bool followSymlinks, + const std::string& relative, const std::string& expression, + const std::vector<std::string>& files, const std::string& variable, + const cmListFileBacktrace& backtrace) +{ + CacheEntryKey key = CacheEntryKey(recurse, listDirectories, followSymlinks, + relative, expression); + CacheEntryValue& value = this->Cache[key]; + if (!value.Initialized) { + value.Files = files; + value.Initialized = true; + value.Backtraces.emplace_back(variable, backtrace); + } else if (value.Initialized && value.Files != files) { + std::ostringstream message; + message << std::boolalpha; + message << "The glob expression\n"; + key.PrintGlobCommand(message, variable); + backtrace.PrintTitle(message); + message << "\nwas already present in the glob cache but the directory\n" + "contents have changed during the configuration run.\n"; + message << "Matching glob expressions:"; + for (auto const& bt : value.Backtraces) { + message << "\n " << std::get<0>(bt); + std::get<1>(bt).PrintTitle(message); + } + cmSystemTools::Error(message.str().c_str()); + } else { + value.Backtraces.emplace_back(variable, backtrace); + } +} diff --git a/Source/cmGlobVerificationManager.h b/Source/cmGlobVerificationManager.h new file mode 100644 index 0000000..4508602 --- /dev/null +++ b/Source/cmGlobVerificationManager.h @@ -0,0 +1,89 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmGlobVerificationManager_h +#define cmGlobVerificationManager_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cmListFileCache.h" + +#include <iosfwd> +#include <map> +#include <string> +#include <utility> +#include <vector> + +/** \class cmGlobVerificationManager + * \brief Class for expressing build-time dependencies on glob expressions. + * + * Generates a CMake script which verifies glob outputs during prebuild. + * + */ +class cmGlobVerificationManager +{ +public: + cmGlobVerificationManager() {} + +protected: + ///! Save verification script for given makefile. + ///! Saves to output <path>/<CMakeFilesDirectory>/VerifyGlobs.cmake + bool SaveVerificationScript(const std::string& path); + + ///! Add an entry into the glob cache + void AddCacheEntry(bool recurse, bool listDirectories, bool followSymlinks, + const std::string& relative, + const std::string& expression, + const std::vector<std::string>& files, + const std::string& variable, + const cmListFileBacktrace& bt); + + ///! Check targets should be written in generated build system. + bool DoWriteVerifyTarget() const; + + ///! Get the paths to the generated script and stamp files + std::string const& GetVerifyScript() const { return this->VerifyScript; } + std::string const& GetVerifyStamp() const { return this->VerifyStamp; } + +private: + struct CacheEntryKey + { + const bool Recurse; + const bool ListDirectories; + const bool FollowSymlinks; + const std::string Relative; + const std::string Expression; + CacheEntryKey(const bool rec, const bool l, const bool s, + const std::string& rel, const std::string& e) + : Recurse(rec) + , ListDirectories(l) + , FollowSymlinks(s) + , Relative(rel) + , Expression(e) + { + } + bool operator<(const CacheEntryKey& r) const; + void PrintGlobCommand(std::ostream& out, const std::string& cmdVar); + }; + + struct CacheEntryValue + { + bool Initialized; + std::vector<std::string> Files; + std::vector<std::pair<std::string, cmListFileBacktrace>> Backtraces; + CacheEntryValue() + : Initialized(false) + { + } + }; + + typedef std::map<CacheEntryKey, CacheEntryValue> CacheEntryMap; + CacheEntryMap Cache; + std::string VerifyScript; + std::string VerifyStamp; + + // Only cmState should be able to add cache values. + // cmGlobVerificationManager should never be used directly. + friend class cmState; // allow access to add cache values +}; + +#endif diff --git a/Source/cmGlobalBorlandMakefileGenerator.cxx b/Source/cmGlobalBorlandMakefileGenerator.cxx index d2372a7..2389103 100644 --- a/Source/cmGlobalBorlandMakefileGenerator.cxx +++ b/Source/cmGlobalBorlandMakefileGenerator.cxx @@ -51,3 +51,32 @@ void cmGlobalBorlandMakefileGenerator::GetDocumentation( entry.Name = cmGlobalBorlandMakefileGenerator::GetActualName(); entry.Brief = "Generates Borland makefiles."; } + +void cmGlobalBorlandMakefileGenerator::GenerateBuildCommand( + std::vector<std::string>& makeCommand, const std::string& makeProgram, + const std::string& projectName, const std::string& projectDir, + const std::string& targetName, const std::string& config, bool fast, + int /*jobs*/, bool verbose, std::vector<std::string> const& makeOptions) +{ + this->cmGlobalUnixMakefileGenerator3::GenerateBuildCommand( + makeCommand, makeProgram, projectName, projectDir, targetName, config, + fast, cmake::NO_BUILD_PARALLEL_LEVEL, verbose, makeOptions); +} + +void cmGlobalBorlandMakefileGenerator::PrintBuildCommandAdvice( + std::ostream& os, int jobs) const +{ + if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) { + // Borland's make does not support parallel builds + // see http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Make + + /* clang-format off */ + os << + "Warning: Borland's make does not support parallel builds. " + "Ignoring parallel build command line option.\n"; + /* clang-format on */ + } + + this->cmGlobalUnixMakefileGenerator3::PrintBuildCommandAdvice( + os, cmake::NO_BUILD_PARALLEL_LEVEL); +} diff --git a/Source/cmGlobalBorlandMakefileGenerator.h b/Source/cmGlobalBorlandMakefileGenerator.h index 5578d76..85fee74 100644 --- a/Source/cmGlobalBorlandMakefileGenerator.h +++ b/Source/cmGlobalBorlandMakefileGenerator.h @@ -5,6 +5,8 @@ #include "cmGlobalNMakeMakefileGenerator.h" +#include <iosfwd> + /** \class cmGlobalBorlandMakefileGenerator * \brief Write a Borland makefiles. * @@ -21,7 +23,7 @@ public: } ///! Get the name for the generator. - virtual std::string GetName() const + std::string GetName() const override { return cmGlobalBorlandMakefileGenerator::GetActualName(); } @@ -31,17 +33,27 @@ public: static void GetDocumentation(cmDocumentationEntry& entry); ///! Create a local generator appropriate to this Global Generator - virtual cmLocalGenerator* CreateLocalGenerator(cmMakefile* mf); + cmLocalGenerator* CreateLocalGenerator(cmMakefile* mf) override; /** * Try to determine system information such as shared library * extension, pthreads, byte order etc. */ - virtual void EnableLanguage(std::vector<std::string> const& languages, - cmMakefile*, bool optional); + void EnableLanguage(std::vector<std::string> const& languages, cmMakefile*, + bool optional) override; + + bool AllowNotParallel() const override { return false; } + bool AllowDeleteOnError() const override { return false; } + +protected: + void GenerateBuildCommand( + std::vector<std::string>& makeCommand, const std::string& makeProgram, + const std::string& projectName, const std::string& projectDir, + const std::string& targetName, const std::string& config, bool fast, + int jobs, bool verbose, std::vector<std::string> const& makeOptions = + std::vector<std::string>()) override; - virtual bool AllowNotParallel() const { return false; } - virtual bool AllowDeleteOnError() const { return false; } + void PrintBuildCommandAdvice(std::ostream& os, int jobs) const override; }; #endif diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index c805b98..8a89f36 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -6,11 +6,11 @@ #include "cmsys/FStream.hxx" #include <algorithm> #include <assert.h> +#include <cstring> #include <iterator> #include <sstream> #include <stdio.h> #include <stdlib.h> -#include <string.h> #if defined(_WIN32) && !defined(__CYGWIN__) #include <windows.h> @@ -1741,7 +1741,7 @@ void cmGlobalGenerator::CheckTargetProperties() } } -int cmGlobalGenerator::TryCompile(const std::string& srcdir, +int cmGlobalGenerator::TryCompile(int jobs, const std::string& srcdir, const std::string& bindir, const std::string& projectName, const std::string& target, bool fast, @@ -1782,7 +1782,7 @@ int cmGlobalGenerator::TryCompile(const std::string& srcdir, } std::string config = mf->GetSafeDefinition("CMAKE_TRY_COMPILE_CONFIGURATION"); - return this->Build(srcdir, bindir, projectName, newTarget, output, "", + return this->Build(jobs, srcdir, bindir, projectName, newTarget, output, "", config, false, fast, false, this->TryCompileTimeout); } @@ -1790,13 +1790,21 @@ void cmGlobalGenerator::GenerateBuildCommand( std::vector<std::string>& makeCommand, const std::string& /*unused*/, const std::string& /*unused*/, const std::string& /*unused*/, const std::string& /*unused*/, const std::string& /*unused*/, - bool /*unused*/, bool /*unused*/, std::vector<std::string> const& /*unused*/) + bool /*unused*/, int /*unused*/, bool /*unused*/, + std::vector<std::string> const& /*unused*/) { makeCommand.push_back( "cmGlobalGenerator::GenerateBuildCommand not implemented"); } -int cmGlobalGenerator::Build(const std::string& /*unused*/, +void cmGlobalGenerator::PrintBuildCommandAdvice(std::ostream& /*os*/, + int /*jobs*/) const +{ + // Subclasses override this method if they e.g want to give a warning that + // they do not support certain build command line options +} + +int cmGlobalGenerator::Build(int jobs, const std::string& /*unused*/, const std::string& bindir, const std::string& projectName, const std::string& target, std::string& output, @@ -1806,6 +1814,8 @@ int cmGlobalGenerator::Build(const std::string& /*unused*/, cmSystemTools::OutputOption outputflag, std::vector<std::string> const& nativeOptions) { + bool hideconsole = cmSystemTools::GetRunCommandHideConsole(); + /** * Run an executable command and put the stdout in output. */ @@ -1813,16 +1823,25 @@ int cmGlobalGenerator::Build(const std::string& /*unused*/, output += "Change Dir: "; output += bindir; output += "\n"; + if (workdir.Failed()) { + cmSystemTools::SetRunCommandHideConsole(hideconsole); + cmSystemTools::Error("Failed to change directory: ", + std::strerror(workdir.GetLastResult())); + output += "Failed to change directory: "; + output += std::strerror(workdir.GetLastResult()); + output += "\n"; + return 1; + } int retVal; - bool hideconsole = cmSystemTools::GetRunCommandHideConsole(); cmSystemTools::SetRunCommandHideConsole(true); std::string outputBuffer; std::string* outputPtr = &outputBuffer; std::vector<std::string> makeCommand; this->GenerateBuildCommand(makeCommand, makeCommandCSTR, projectName, bindir, - target, config, fast, verbose, nativeOptions); + target, config, fast, jobs, verbose, + nativeOptions); // Workaround to convince VCExpress.exe to produce output. if (outputflag == cmSystemTools::OUTPUT_PASSTHROUGH && @@ -1836,7 +1855,7 @@ int cmGlobalGenerator::Build(const std::string& /*unused*/, if (clean) { std::vector<std::string> cleanCommand; this->GenerateBuildCommand(cleanCommand, makeCommandCSTR, projectName, - bindir, "clean", config, fast, verbose); + bindir, "clean", config, fast, jobs, verbose); output += "\nRun Clean Command:"; output += cmSystemTools::PrintSingleCommand(cleanCommand); output += "\n"; @@ -1883,6 +1902,13 @@ int cmGlobalGenerator::Build(const std::string& /*unused*/, retVal = 1; } + // The OpenWatcom tools do not return an error code when a link + // library is not found! + if (this->CMakeInstance->GetState()->UseWatcomWMake() && retVal == 0 && + output.find("W1008: cannot open") != std::string::npos) { + retVal = 1; + } + return retVal; } diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 34ed5b0..62c5441 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -147,9 +147,10 @@ public: * Try running cmake and building a file. This is used for dynamically * loaded commands, not as part of the usual build process. */ - int TryCompile(const std::string& srcdir, const std::string& bindir, - const std::string& projectName, const std::string& targetName, - bool fast, std::string& output, cmMakefile* mf); + int TryCompile(int jobs, const std::string& srcdir, + const std::string& bindir, const std::string& projectName, + const std::string& targetName, bool fast, std::string& output, + cmMakefile* mf); /** * Build a file given the following information. This is a more direct call @@ -157,7 +158,7 @@ public: * empty then all is assumed. clean indicates if a "make clean" should be * done first. */ - int Build(const std::string& srcdir, const std::string& bindir, + int Build(int jobs, const std::string& srcdir, const std::string& bindir, const std::string& projectName, const std::string& targetName, std::string& output, const std::string& makeProgram, const std::string& config, bool clean, bool fast, bool verbose, @@ -176,9 +177,11 @@ public: std::vector<std::string>& makeCommand, const std::string& makeProgram, const std::string& projectName, const std::string& projectDir, const std::string& targetName, const std::string& config, bool fast, - bool verbose, + int jobs, bool verbose, std::vector<std::string> const& makeOptions = std::vector<std::string>()); + virtual void PrintBuildCommandAdvice(std::ostream& os, int jobs) const; + /** Generate a "cmake --build" call for a given target and config. */ std::string GenerateCMakeBuildCommand(const std::string& target, const std::string& config, diff --git a/Source/cmGlobalGhsMultiGenerator.cxx b/Source/cmGlobalGhsMultiGenerator.cxx index 946ed80..f4ecff2 100644 --- a/Source/cmGlobalGhsMultiGenerator.cxx +++ b/Source/cmGlobalGhsMultiGenerator.cxx @@ -273,11 +273,18 @@ void cmGlobalGhsMultiGenerator::GenerateBuildCommand( std::vector<std::string>& makeCommand, const std::string& makeProgram, const std::string& /*projectName*/, const std::string& /*projectDir*/, const std::string& targetName, const std::string& /*config*/, bool /*fast*/, - bool /*verbose*/, std::vector<std::string> const& makeOptions) + int jobs, bool /*verbose*/, std::vector<std::string> const& makeOptions) { makeCommand.push_back( this->SelectMakeProgram(makeProgram, this->GetGhsBuildCommand())); + if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) { + makeCommand.push_back("-parallel"); + if (jobs != cmake::DEFAULT_BUILD_PARALLEL_LEVEL) { + makeCommand.push_back(std::to_string(jobs)); + } + } + makeCommand.insert(makeCommand.end(), makeOptions.begin(), makeOptions.end()); if (!targetName.empty()) { diff --git a/Source/cmGlobalGhsMultiGenerator.h b/Source/cmGlobalGhsMultiGenerator.h index 7d4b2ba..c5388ad 100644 --- a/Source/cmGlobalGhsMultiGenerator.h +++ b/Source/cmGlobalGhsMultiGenerator.h @@ -89,7 +89,7 @@ protected: std::vector<std::string>& makeCommand, const std::string& makeProgram, const std::string& projectName, const std::string& projectDir, const std::string& targetName, const std::string& config, bool fast, - bool verbose, + int jobs, bool verbose, std::vector<std::string> const& makeOptions = std::vector<std::string>()); private: diff --git a/Source/cmGlobalJOMMakefileGenerator.cxx b/Source/cmGlobalJOMMakefileGenerator.cxx index 18c45e0..0f41ea1 100644 --- a/Source/cmGlobalJOMMakefileGenerator.cxx +++ b/Source/cmGlobalJOMMakefileGenerator.cxx @@ -52,3 +52,29 @@ void cmGlobalJOMMakefileGenerator::PrintCompilerAdvice( } this->cmGlobalUnixMakefileGenerator3::PrintCompilerAdvice(os, lang, envVar); } + +void cmGlobalJOMMakefileGenerator::GenerateBuildCommand( + std::vector<std::string>& makeCommand, const std::string& makeProgram, + const std::string& projectName, const std::string& projectDir, + const std::string& targetName, const std::string& config, bool fast, + int jobs, bool verbose, std::vector<std::string> const& makeOptions) +{ + std::vector<std::string> jomMakeOptions; + + // Since we have full control over the invocation of JOM, let us + // make it quiet. + jomMakeOptions.push_back(this->MakeSilentFlag); + jomMakeOptions.insert(jomMakeOptions.end(), makeOptions.begin(), + makeOptions.end()); + + // JOM does parallel builds by default, the -j is only needed if a specific + // number is given + // see https://github.com/qt-labs/jom/blob/v1.1.2/src/jomlib/options.cpp + if (jobs == cmake::DEFAULT_BUILD_PARALLEL_LEVEL) { + jobs = cmake::NO_BUILD_PARALLEL_LEVEL; + } + + cmGlobalUnixMakefileGenerator3::GenerateBuildCommand( + makeCommand, makeProgram, projectName, projectDir, targetName, config, + fast, jobs, verbose, jomMakeOptions); +} diff --git a/Source/cmGlobalJOMMakefileGenerator.h b/Source/cmGlobalJOMMakefileGenerator.h index 2e8ee29..65f340c 100644 --- a/Source/cmGlobalJOMMakefileGenerator.h +++ b/Source/cmGlobalJOMMakefileGenerator.h @@ -5,6 +5,8 @@ #include "cmGlobalUnixMakefileGenerator3.h" +#include <iosfwd> + /** \class cmGlobalJOMMakefileGenerator * \brief Write a JOM makefiles. * @@ -19,7 +21,7 @@ public: return new cmGlobalGeneratorSimpleFactory<cmGlobalJOMMakefileGenerator>(); } ///! Get the name for the generator. - virtual std::string GetName() const + std::string GetName() const override { return cmGlobalJOMMakefileGenerator::GetActualName(); } @@ -34,12 +36,20 @@ public: * Try to determine system information such as shared library * extension, pthreads, byte order etc. */ - virtual void EnableLanguage(std::vector<std::string> const& languages, - cmMakefile*, bool optional); + void EnableLanguage(std::vector<std::string> const& languages, cmMakefile*, + bool optional) override; + +protected: + void GenerateBuildCommand( + std::vector<std::string>& makeCommand, const std::string& makeProgram, + const std::string& projectName, const std::string& projectDir, + const std::string& targetName, const std::string& config, bool fast, + int jobs, bool verbose, std::vector<std::string> const& makeOptions = + std::vector<std::string>()) override; private: void PrintCompilerAdvice(std::ostream& os, std::string const& lang, - const char* envVar) const; + const char* envVar) const override; }; #endif diff --git a/Source/cmGlobalNMakeMakefileGenerator.cxx b/Source/cmGlobalNMakeMakefileGenerator.cxx index da683fb..eb66bd1 100644 --- a/Source/cmGlobalNMakeMakefileGenerator.cxx +++ b/Source/cmGlobalNMakeMakefileGenerator.cxx @@ -52,3 +52,40 @@ void cmGlobalNMakeMakefileGenerator::PrintCompilerAdvice( } this->cmGlobalUnixMakefileGenerator3::PrintCompilerAdvice(os, lang, envVar); } + +void cmGlobalNMakeMakefileGenerator::GenerateBuildCommand( + std::vector<std::string>& makeCommand, const std::string& makeProgram, + const std::string& projectName, const std::string& projectDir, + const std::string& targetName, const std::string& config, bool fast, + int /*jobs*/, bool verbose, std::vector<std::string> const& makeOptions) +{ + std::vector<std::string> nmakeMakeOptions; + + // Since we have full control over the invocation of nmake, let us + // make it quiet. + nmakeMakeOptions.push_back(this->MakeSilentFlag); + nmakeMakeOptions.insert(nmakeMakeOptions.end(), makeOptions.begin(), + makeOptions.end()); + + this->cmGlobalUnixMakefileGenerator3::GenerateBuildCommand( + makeCommand, makeProgram, projectName, projectDir, targetName, config, + fast, cmake::NO_BUILD_PARALLEL_LEVEL, verbose, nmakeMakeOptions); +} + +void cmGlobalNMakeMakefileGenerator::PrintBuildCommandAdvice(std::ostream& os, + int jobs) const +{ + if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) { + // nmake does not support parallel build level + // see https://msdn.microsoft.com/en-us/library/afyyse50.aspx + + /* clang-format off */ + os << + "Warning: NMake does not support parallel builds. " + "Ignoring parallel build command line option.\n"; + /* clang-format on */ + } + + this->cmGlobalUnixMakefileGenerator3::PrintBuildCommandAdvice( + os, cmake::NO_BUILD_PARALLEL_LEVEL); +} diff --git a/Source/cmGlobalNMakeMakefileGenerator.h b/Source/cmGlobalNMakeMakefileGenerator.h index 05ab904..4b6382e 100644 --- a/Source/cmGlobalNMakeMakefileGenerator.h +++ b/Source/cmGlobalNMakeMakefileGenerator.h @@ -5,6 +5,8 @@ #include "cmGlobalUnixMakefileGenerator3.h" +#include <iosfwd> + /** \class cmGlobalNMakeMakefileGenerator * \brief Write a NMake makefiles. * @@ -20,7 +22,7 @@ public: cmGlobalNMakeMakefileGenerator>(); } ///! Get the name for the generator. - virtual std::string GetName() const + std::string GetName() const override { return cmGlobalNMakeMakefileGenerator::GetActualName(); } @@ -39,12 +41,22 @@ public: * Try to determine system information such as shared library * extension, pthreads, byte order etc. */ - virtual void EnableLanguage(std::vector<std::string> const& languages, - cmMakefile*, bool optional); + void EnableLanguage(std::vector<std::string> const& languages, cmMakefile*, + bool optional) override; + +protected: + void GenerateBuildCommand( + std::vector<std::string>& makeCommand, const std::string& makeProgram, + const std::string& projectName, const std::string& projectDir, + const std::string& targetName, const std::string& config, bool fast, + int jobs, bool verbose, std::vector<std::string> const& makeOptions = + std::vector<std::string>()) override; + + void PrintBuildCommandAdvice(std::ostream& os, int jobs) const override; private: void PrintCompilerAdvice(std::ostream& os, std::string const& lang, - const char* envVar) const; + const char* envVar) const override; }; #endif diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index b251f86..69bc3be 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -101,31 +101,6 @@ std::string cmGlobalNinjaGenerator::EncodeRuleName(std::string const& name) return encoded; } -static bool IsIdentChar(char c) -{ - return ('a' <= c && c <= 'z') || - ('+' <= c && c <= '9') || // +,-./ and numbers - ('A' <= c && c <= 'Z') || (c == '_') || (c == '$') || (c == '\\') || - (c == ' ') || (c == ':'); -} - -std::string cmGlobalNinjaGenerator::EncodeIdent(const std::string& ident, - std::ostream& vars) -{ - if (std::find_if(ident.begin(), ident.end(), - [](char c) { return !IsIdentChar(c); }) != ident.end()) { - static unsigned VarNum = 0; - std::ostringstream names; - names << "ident" << VarNum++; - vars << names.str() << " = " << ident << "\n"; - return "$" + names.str(); - } - std::string result = ident; - cmSystemTools::ReplaceString(result, " ", "$ "); - cmSystemTools::ReplaceString(result, ":", "$:"); - return result; -} - std::string cmGlobalNinjaGenerator::EncodeLiteral(const std::string& lit) { std::string result = lit; @@ -143,7 +118,10 @@ std::string cmGlobalNinjaGenerator::EncodePath(const std::string& path) else std::replace(result.begin(), result.end(), '/', '\\'); #endif - return EncodeLiteral(result); + result = EncodeLiteral(result); + cmSystemTools::ReplaceString(result, " ", "$ "); + cmSystemTools::ReplaceString(result, ":", "$:"); + return result; } void cmGlobalNinjaGenerator::WriteBuild( @@ -177,14 +155,14 @@ void cmGlobalNinjaGenerator::WriteBuild( // Write explicit dependencies. for (std::string const& explicitDep : explicitDeps) { - arguments += " " + EncodeIdent(EncodePath(explicitDep), os); + arguments += " " + EncodePath(explicitDep); } // Write implicit dependencies. if (!implicitDeps.empty()) { arguments += " |"; for (std::string const& implicitDep : implicitDeps) { - arguments += " " + EncodeIdent(EncodePath(implicitDep), os); + arguments += " " + EncodePath(implicitDep); } } @@ -192,7 +170,7 @@ void cmGlobalNinjaGenerator::WriteBuild( if (!orderOnlyDeps.empty()) { arguments += " ||"; for (std::string const& orderOnlyDep : orderOnlyDeps) { - arguments += " " + EncodeIdent(EncodePath(orderOnlyDep), os); + arguments += " " + EncodePath(orderOnlyDep); } } @@ -203,7 +181,7 @@ void cmGlobalNinjaGenerator::WriteBuild( // Write outputs files. build += "build"; for (std::string const& output : outputs) { - build += " " + EncodeIdent(EncodePath(output), os); + build += " " + EncodePath(output); if (this->ComputingUnknownDependencies) { this->CombinedBuildOutputs.insert(output); } @@ -211,7 +189,7 @@ void cmGlobalNinjaGenerator::WriteBuild( if (!implicitOuts.empty()) { build += " |"; for (std::string const& implicitOut : implicitOuts) { - build += " " + EncodeIdent(EncodePath(implicitOut), os); + build += " " + EncodePath(implicitOut); } } build += ":"; @@ -475,6 +453,7 @@ cmGlobalNinjaGenerator::cmGlobalNinjaGenerator(cmake* cm) , PolicyCMP0058(cmPolicies::WARN) , NinjaSupportsConsolePool(false) , NinjaSupportsImplicitOuts(false) + , NinjaSupportsManifestRestat(false) , NinjaSupportsDyndeps(0) { #ifdef _WIN32 @@ -597,6 +576,9 @@ void cmGlobalNinjaGenerator::CheckNinjaFeatures() this->NinjaSupportsImplicitOuts = !cmSystemTools::VersionCompare( cmSystemTools::OP_LESS, this->NinjaVersion.c_str(), this->RequiredNinjaVersionForImplicitOuts().c_str()); + this->NinjaSupportsManifestRestat = !cmSystemTools::VersionCompare( + cmSystemTools::OP_LESS, this->NinjaVersion.c_str(), + RequiredNinjaVersionForManifestRestat().c_str()); { // Our ninja branch adds ".dyndep-#" to its version number, // where '#' is a feature-specific version number. Extract it. @@ -692,7 +674,7 @@ void cmGlobalNinjaGenerator::GenerateBuildCommand( std::vector<std::string>& makeCommand, const std::string& makeProgram, const std::string& /*projectName*/, const std::string& /*projectDir*/, const std::string& targetName, const std::string& /*config*/, bool /*fast*/, - bool verbose, std::vector<std::string> const& makeOptions) + int jobs, bool verbose, std::vector<std::string> const& makeOptions) { makeCommand.push_back(this->SelectMakeProgram(makeProgram)); @@ -700,6 +682,12 @@ void cmGlobalNinjaGenerator::GenerateBuildCommand( makeCommand.push_back("-v"); } + if ((jobs != cmake::NO_BUILD_PARALLEL_LEVEL) && + (jobs != cmake::DEFAULT_BUILD_PARALLEL_LEVEL)) { + makeCommand.push_back("-j"); + makeCommand.push_back(std::to_string(jobs)); + } + makeCommand.insert(makeCommand.end(), makeOptions.begin(), makeOptions.end()); if (!targetName.empty()) { @@ -949,12 +937,14 @@ void cmGlobalNinjaGenerator::AddDependencyToAll(const std::string& input) void cmGlobalNinjaGenerator::WriteAssumedSourceDependencies() { for (auto const& asd : this->AssumedSourceDependencies) { - cmNinjaDeps deps; - std::copy(asd.second.begin(), asd.second.end(), std::back_inserter(deps)); + cmNinjaDeps orderOnlyDeps; + std::copy(asd.second.begin(), asd.second.end(), + std::back_inserter(orderOnlyDeps)); WriteCustomCommandBuild(/*command=*/"", /*description=*/"", "Assume dependencies for generated source file.", /*depfile*/ "", /*uses_terminal*/ false, - /*restat*/ true, cmNinjaDeps(1, asd.first), deps); + /*restat*/ true, cmNinjaDeps(1, asd.first), + cmNinjaDeps(), orderOnlyDeps); } } @@ -1223,11 +1213,13 @@ void cmGlobalNinjaGenerator::WriteUnknownExplicitDependencies(std::ostream& os) for (std::string const& file : files) { knownDependencies.insert(this->ConvertToNinjaPath(file)); } - // get list files which are implicit dependencies as well and will be phony - // for rebuild manifest - std::vector<std::string> const& lf = lg->GetMakefile()->GetListFiles(); - for (std::string const& j : lf) { - knownDependencies.insert(this->ConvertToNinjaPath(j)); + if (!this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION")) { + // get list files which are implicit dependencies as well and will be + // phony for rebuild manifest + std::vector<std::string> const& lf = lg->GetMakefile()->GetListFiles(); + for (std::string const& j : lf) { + knownDependencies.insert(this->ConvertToNinjaPath(j)); + } } std::vector<cmGeneratorExpressionEvaluationFile*> const& ef = lg->GetMakefile()->GetEvaluationFiles(); @@ -1335,6 +1327,9 @@ void cmGlobalNinjaGenerator::WriteTargetAll(std::ostream& os) void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os) { + if (this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION")) { + return; + } cmLocalGenerator* lg = this->LocalGenerators[0]; std::ostringstream cmd; @@ -1356,6 +1351,7 @@ void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os) /*generator=*/true); cmNinjaDeps implicitDeps; + cmNinjaDeps explicitDeps; for (cmLocalGenerator* localGen : this->LocalGenerators) { std::vector<std::string> const& lf = localGen->GetMakefile()->GetListFiles(); @@ -1365,10 +1361,6 @@ void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os) } implicitDeps.push_back(this->CMakeCacheFile); - std::sort(implicitDeps.begin(), implicitDeps.end()); - implicitDeps.erase(std::unique(implicitDeps.begin(), implicitDeps.end()), - implicitDeps.end()); - cmNinjaVars variables; // Use 'console' pool to get non buffered output of the CMake re-run call // Available since Ninja 1.5 @@ -1376,16 +1368,81 @@ void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os) variables["pool"] = "console"; } + cmake* cm = this->GetCMakeInstance(); + if (this->SupportsManifestRestat() && cm->DoWriteGlobVerifyTarget()) { + std::ostringstream verify_cmd; + verify_cmd << lg->ConvertToOutputFormat(cmSystemTools::GetCMakeCommand(), + cmOutputConverter::SHELL) + << " -P " + << lg->ConvertToOutputFormat(cm->GetGlobVerifyScript(), + cmOutputConverter::SHELL); + + WriteRule(*this->RulesFileStream, "VERIFY_GLOBS", verify_cmd.str(), + "Re-checking globbed directories...", + "Rule for re-checking globbed directories.", + /*depfile=*/"", + /*deptype=*/"", + /*rspfile=*/"", + /*rspcontent*/ "", + /*restat=*/"", + /*generator=*/true); + + std::string verifyForce = cm->GetGlobVerifyScript() + "_force"; + cmNinjaDeps verifyForceDeps(1, this->NinjaOutputPath(verifyForce)); + + this->WritePhonyBuild(os, "Phony target to force glob verification run.", + verifyForceDeps, cmNinjaDeps()); + + variables["restat"] = "1"; + std::string const verifyScriptFile = + this->NinjaOutputPath(cm->GetGlobVerifyScript()); + std::string const verifyStampFile = + this->NinjaOutputPath(cm->GetGlobVerifyStamp()); + this->WriteBuild(os, + "Re-run CMake to check if globbed directories changed.", + "VERIFY_GLOBS", + /*outputs=*/cmNinjaDeps(1, verifyStampFile), + /*implicitOuts=*/cmNinjaDeps(), + /*explicitDeps=*/cmNinjaDeps(), + /*implicitDeps=*/verifyForceDeps, + /*orderOnlyDeps=*/cmNinjaDeps(), variables); + + variables.erase("restat"); + implicitDeps.push_back(verifyScriptFile); + explicitDeps.push_back(verifyStampFile); + } else if (!this->SupportsManifestRestat() && + cm->DoWriteGlobVerifyTarget()) { + std::ostringstream msg; + msg << "The detected version of Ninja:\n" + << " " << this->NinjaVersion << "\n" + << "is less than the version of Ninja required by CMake for adding " + "restat dependencies to the build.ninja manifest regeneration " + "target:\n" + << " " << this->RequiredNinjaVersionForManifestRestat() << "\n"; + msg << "Any pre-check scripts, such as those generated for file(GLOB " + "CONFIGURE_DEPENDS), will not be run by Ninja."; + this->GetCMakeInstance()->IssueMessage(cmake::AUTHOR_WARNING, msg.str()); + } + + std::sort(implicitDeps.begin(), implicitDeps.end()); + implicitDeps.erase(std::unique(implicitDeps.begin(), implicitDeps.end()), + implicitDeps.end()); + std::string const ninjaBuildFile = this->NinjaOutputPath(NINJA_BUILD_FILE); this->WriteBuild(os, "Re-run CMake if any of its inputs changed.", "RERUN_CMAKE", /*outputs=*/cmNinjaDeps(1, ninjaBuildFile), - /*implicitOuts=*/cmNinjaDeps(), - /*explicitDeps=*/cmNinjaDeps(), implicitDeps, + /*implicitOuts=*/cmNinjaDeps(), explicitDeps, implicitDeps, /*orderOnlyDeps=*/cmNinjaDeps(), variables); + cmNinjaDeps missingInputs; + std::set_difference(std::make_move_iterator(implicitDeps.begin()), + std::make_move_iterator(implicitDeps.end()), + CustomCommandOutputs.begin(), CustomCommandOutputs.end(), + std::back_inserter(missingInputs)); + this->WritePhonyBuild(os, "A missing CMake input file is not an error.", - implicitDeps, cmNinjaDeps()); + missingInputs, cmNinjaDeps()); } std::string cmGlobalNinjaGenerator::ninjaCmd() const @@ -1408,6 +1465,11 @@ bool cmGlobalNinjaGenerator::SupportsImplicitOuts() const return this->NinjaSupportsImplicitOuts; } +bool cmGlobalNinjaGenerator::SupportsManifestRestat() const +{ + return this->NinjaSupportsManifestRestat; +} + void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os) { WriteRule(*this->RulesFileStream, "CLEAN", ninjaCmd() + " -t clean", @@ -1758,7 +1820,7 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( Json::Value tm = Json::objectValue; for (cmFortranObjectInfo const& object : objects) { for (std::string const& p : object.Provides) { - std::string const mod = module_dir + p + ".mod"; + std::string const mod = module_dir + p; mod_files[p] = mod; tm[p] = mod; } diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 7f80d08..17b9a7d 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -73,7 +73,6 @@ public: static void WriteDivider(std::ostream& os); static std::string EncodeRuleName(std::string const& name); - static std::string EncodeIdent(const std::string& ident, std::ostream& vars); static std::string EncodeLiteral(const std::string& lit); std::string EncodePath(const std::string& path); @@ -203,13 +202,11 @@ public: void EnableLanguage(std::vector<std::string> const& languages, cmMakefile* mf, bool optional) override; - void GenerateBuildCommand(std::vector<std::string>& makeCommand, - const std::string& makeProgram, - const std::string& projectName, - const std::string& projectDir, - const std::string& targetName, - const std::string& config, bool fast, bool verbose, - std::vector<std::string> const& makeOptions = + void GenerateBuildCommand( + std::vector<std::string>& makeCommand, const std::string& makeProgram, + const std::string& projectName, const std::string& projectDir, + const std::string& targetName, const std::string& config, bool fast, + int jobs, bool verbose, std::vector<std::string> const& makeOptions = std::vector<std::string>()) override; // Setup target names @@ -346,8 +343,10 @@ public: static std::string RequiredNinjaVersion() { return "1.3"; } static std::string RequiredNinjaVersionForConsolePool() { return "1.5"; } static std::string RequiredNinjaVersionForImplicitOuts() { return "1.7"; } + static std::string RequiredNinjaVersionForManifestRestat() { return "1.8"; } bool SupportsConsolePool() const; bool SupportsImplicitOuts() const; + bool SupportsManifestRestat() const; std::string NinjaOutputPath(std::string const& path) const; bool HasOutputPathPrefix() const { return !this->OutputPathPrefix.empty(); } @@ -460,6 +459,7 @@ private: std::string NinjaVersion; bool NinjaSupportsConsolePool; bool NinjaSupportsImplicitOuts; + bool NinjaSupportsManifestRestat; unsigned long NinjaSupportsDyndeps; private: diff --git a/Source/cmGlobalUnixMakefileGenerator3.cxx b/Source/cmGlobalUnixMakefileGenerator3.cxx index d990a6c..641b760 100644 --- a/Source/cmGlobalUnixMakefileGenerator3.cxx +++ b/Source/cmGlobalUnixMakefileGenerator3.cxx @@ -7,7 +7,6 @@ #include <sstream> #include <utility> -#include "cmAlgorithms.h" #include "cmDocumentationEntry.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorTarget.h" @@ -241,6 +240,10 @@ void cmGlobalUnixMakefileGenerator3::WriteMainMakefile2() lg->WriteMakeRule(makefileStream, "The main recursive preinstall target", "preinstall", depends, no_commands, true); + // Write an empty clean: + lg->WriteMakeRule(makefileStream, "The main recursive clean target", "clean", + depends, no_commands, true); + // Write out the "special" stuff lg->WriteSpecialTargetsTop(makefileStream); @@ -256,6 +259,10 @@ void cmGlobalUnixMakefileGenerator3::WriteMainMakefile2() void cmGlobalUnixMakefileGenerator3::WriteMainCMakefile() { + if (this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION")) { + return; + } + // Open the output file. This should not be copy-if-different // because the check-build-system step compares the makefile time to // see if the build system must be regenerated. @@ -293,6 +300,13 @@ void cmGlobalUnixMakefileGenerator3::WriteMainCMakefile() lfiles.insert(lfiles.end(), lg->GetMakefile()->GetListFiles().begin(), lg->GetMakefile()->GetListFiles().end()); } + + cmake* cm = this->GetCMakeInstance(); + if (cm->DoWriteGlobVerifyTarget()) { + lfiles.push_back(cm->GetGlobVerifyScript()); + lfiles.push_back(cm->GetGlobVerifyStamp()); + } + // Sort the list and remove duplicates. std::sort(lfiles.begin(), lfiles.end(), std::less<std::string>()); #if !defined(__VMS) // The Compaq STL on VMS crashes, so accept duplicates. @@ -479,31 +493,33 @@ void cmGlobalUnixMakefileGenerator3::GenerateBuildCommand( std::vector<std::string>& makeCommand, const std::string& makeProgram, const std::string& /*projectName*/, const std::string& /*projectDir*/, const std::string& targetName, const std::string& /*config*/, bool fast, - bool /*verbose*/, std::vector<std::string> const& makeOptions) + int jobs, bool /*verbose*/, std::vector<std::string> const& makeOptions) { + cmMakefile* mf; + if (!this->Makefiles.empty()) { + mf = this->Makefiles[0]; + } else { + cmStateSnapshot snapshot = this->CMakeInstance->GetCurrentSnapshot(); + snapshot.GetDirectory().SetCurrentSource( + this->CMakeInstance->GetHomeDirectory()); + snapshot.GetDirectory().SetCurrentBinary( + this->CMakeInstance->GetHomeOutputDirectory()); + snapshot.SetDefaultDefinitions(); + mf = new cmMakefile(this, snapshot); + } + makeCommand.push_back(this->SelectMakeProgram(makeProgram)); - // Since we have full control over the invocation of nmake, let us - // make it quiet. - if (cmHasLiteralPrefix(this->GetName(), "NMake Makefiles")) { - makeCommand.push_back("/NOLOGO"); + if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) { + makeCommand.push_back("-j"); + if (jobs != cmake::DEFAULT_BUILD_PARALLEL_LEVEL) { + makeCommand.push_back(std::to_string(jobs)); + } } + makeCommand.insert(makeCommand.end(), makeOptions.begin(), makeOptions.end()); if (!targetName.empty()) { - cmMakefile* mf; - if (!this->Makefiles.empty()) { - mf = this->Makefiles[0]; - } else { - cmStateSnapshot snapshot = this->CMakeInstance->GetCurrentSnapshot(); - snapshot.GetDirectory().SetCurrentSource( - this->CMakeInstance->GetHomeDirectory()); - snapshot.GetDirectory().SetCurrentBinary( - this->CMakeInstance->GetHomeOutputDirectory()); - snapshot.SetDefaultDefinitions(); - mf = new cmMakefile(this, snapshot); - } - std::string tname = targetName; if (fast) { tname += "/fast"; @@ -513,9 +529,9 @@ void cmGlobalUnixMakefileGenerator3::GenerateBuildCommand( conv.ConvertToRelativePath(mf->GetState()->GetBinaryDirectory(), tname); cmSystemTools::ConvertToOutputSlashes(tname); makeCommand.push_back(std::move(tname)); - if (this->Makefiles.empty()) { - delete mf; - } + } + if (this->Makefiles.empty()) { + delete mf; } } @@ -525,7 +541,10 @@ void cmGlobalUnixMakefileGenerator3::WriteConvenienceRules( std::vector<std::string> depends; std::vector<std::string> commands; - depends.push_back("cmake_check_build_system"); + bool regenerate = !this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION"); + if (regenerate) { + depends.push_back("cmake_check_build_system"); + } // write the target convenience rules for (cmLocalGenerator* localGen : this->LocalGenerators) { @@ -558,7 +577,9 @@ void cmGlobalUnixMakefileGenerator3::WriteConvenienceRules( tmp += "Makefile2"; commands.push_back(lg->GetRecursiveMakeCall(tmp.c_str(), name)); depends.clear(); - depends.push_back("cmake_check_build_system"); + if (regenerate) { + depends.push_back("cmake_check_build_system"); + } lg->WriteMakeRule(ruleFileStream, "Build rule for target.", name, depends, commands, true); @@ -609,7 +630,10 @@ void cmGlobalUnixMakefileGenerator3::WriteConvenienceRules2( // write the directory level rules for this local gen this->WriteDirectoryRules2(ruleFileStream, lg); - depends.push_back("cmake_check_build_system"); + bool regenerate = !this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION"); + if (regenerate) { + depends.push_back("cmake_check_build_system"); + } // for each target Generate the rule files for each target. const std::vector<cmGeneratorTarget*>& targets = lg->GetGeneratorTargets(); @@ -715,7 +739,9 @@ void cmGlobalUnixMakefileGenerator3::WriteConvenienceRules2( commands.push_back(progCmd.str()); } depends.clear(); - depends.push_back("cmake_check_build_system"); + if (regenerate) { + depends.push_back("cmake_check_build_system"); + } localName = lg->GetRelativeTargetDirectory(gtarget); localName += "/rule"; lg->WriteMakeRule(ruleFileStream, @@ -768,7 +794,7 @@ void cmGlobalUnixMakefileGenerator3::WriteConvenienceRules2( } } -// Build a map that contains a the set of targets used by each local +// Build a map that contains the set of targets used by each local // generator directory level. void cmGlobalUnixMakefileGenerator3::InitializeProgressMarks() { @@ -898,7 +924,9 @@ void cmGlobalUnixMakefileGenerator3::WriteHelpRule( "for this Makefile:"); lg->AppendEcho(commands, "... all (the default if no target is provided)"); lg->AppendEcho(commands, "... clean"); - lg->AppendEcho(commands, "... depend"); + if (!this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION")) { + lg->AppendEcho(commands, "... depend"); + } // Keep track of targets already listed. std::set<std::string> emittedTargets; diff --git a/Source/cmGlobalUnixMakefileGenerator3.h b/Source/cmGlobalUnixMakefileGenerator3.h index f9ce88c..097678f 100644 --- a/Source/cmGlobalUnixMakefileGenerator3.h +++ b/Source/cmGlobalUnixMakefileGenerator3.h @@ -127,13 +127,11 @@ public: std::string GetEmptyRuleHackDepends() { return this->EmptyRuleHackDepends; } // change the build command for speed - void GenerateBuildCommand(std::vector<std::string>& makeCommand, - const std::string& makeProgram, - const std::string& projectName, - const std::string& projectDir, - const std::string& targetName, - const std::string& config, bool fast, bool verbose, - std::vector<std::string> const& makeOptions = + void GenerateBuildCommand( + std::vector<std::string>& makeCommand, const std::string& makeProgram, + const std::string& projectName, const std::string& projectDir, + const std::string& targetName, const std::string& config, bool fast, + int jobs, bool verbose, std::vector<std::string> const& makeOptions = std::vector<std::string>()) override; /** Record per-target progress information. */ diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx index 73a5dae..8c20313 100644 --- a/Source/cmGlobalVisualStudio10Generator.cxx +++ b/Source/cmGlobalVisualStudio10Generator.cxx @@ -231,9 +231,66 @@ bool cmGlobalVisualStudio10Generator::SetGeneratorToolset( } } + if (!this->GeneratorToolsetVersion.empty() && + this->GeneratorToolsetVersion != "Test Toolset Version") { + // If a specific minor version of the toolset was requested, verify that it + // is compatible to the major version and that is exists on disk. + // If not clear the value. + std::string version = this->GeneratorToolsetVersion; + cmsys::RegularExpression regex("[0-9][0-9]\\.[0-9][0-9]"); + if (regex.find(version)) { + version = "v" + version.erase(2, 1); + } else { + // Version not recognized. Clear it. + version.clear(); + } + + if (version.find(this->GetPlatformToolsetString()) != 0) { + std::ostringstream e; + /* clang-format off */ + e << + "Generator\n" + " " << this->GetName() << "\n" + "given toolset and version specification\n" + " " << this->GetPlatformToolsetString() << ",version=" << + this->GeneratorToolsetVersion << "\n" + "contains an invalid version specification." + ; + /* clang-format on */ + mf->IssueMessage(cmake::FATAL_ERROR, e.str()); + + // Clear the configured tool-set + this->GeneratorToolsetVersion.clear(); + } + + std::string const toolsetPath = this->GetAuxiliaryToolset(); + if (!toolsetPath.empty() && !cmSystemTools::FileExists(toolsetPath)) { + + std::ostringstream e; + /* clang-format off */ + e << + "Generator\n" + " " << this->GetName() << "\n" + "given toolset and version specification\n" + " " << this->GetPlatformToolsetString() << ",version=" << + this->GeneratorToolsetVersion << "\n" + "does not seem to be installed at\n" << + " " << toolsetPath; + ; + /* clang-format on */ + mf->IssueMessage(cmake::FATAL_ERROR, e.str()); + + // Clear the configured tool-set + this->GeneratorToolsetVersion.clear(); + } + } + if (const char* toolset = this->GetPlatformToolset()) { mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET", toolset); } + if (const char* version = this->GetPlatformToolsetVersion()) { + mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET_VERSION", version); + } if (const char* hostArch = this->GetPlatformToolsetHostArchitecture()) { mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE", hostArch); } @@ -319,6 +376,10 @@ bool cmGlobalVisualStudio10Generator::ProcessGeneratorToolsetField( this->GeneratorToolsetCuda = value; return true; } + if (key == "version") { + this->GeneratorToolsetVersion = value; + return true; + } return false; } @@ -512,6 +573,25 @@ std::string const& cmGlobalVisualStudio10Generator::GetPlatformToolsetString() return empty; } +const char* cmGlobalVisualStudio10Generator::GetPlatformToolsetVersion() const +{ + std::string const& version = this->GetPlatformToolsetVersionString(); + if (version.empty()) { + return nullptr; + } + return version.c_str(); +} + +std::string const& +cmGlobalVisualStudio10Generator::GetPlatformToolsetVersionString() const +{ + if (!this->GeneratorToolsetVersion.empty()) { + return this->GeneratorToolsetVersion; + } + static std::string const empty; + return empty; +} + const char* cmGlobalVisualStudio10Generator::GetPlatformToolsetHostArchitecture() const { @@ -535,6 +615,11 @@ cmGlobalVisualStudio10Generator::GetPlatformToolsetCudaString() const return this->GeneratorToolsetCuda; } +std::string cmGlobalVisualStudio10Generator::GetAuxiliaryToolset() const +{ + return {}; +} + bool cmGlobalVisualStudio10Generator::FindMakeProgram(cmMakefile* mf) { if (!this->cmGlobalVisualStudio8Generator::FindMakeProgram(mf)) { @@ -636,126 +721,89 @@ bool cmGlobalVisualStudio10Generator::FindVCTargetsPath(cmMakefile* mf) cmsys::ofstream fout(vcxprojAbs.c_str()); cmXMLWriter xw(fout); - /* clang-format off */ - xw.StartDocument(); - xw.StartElement("Project"); - xw.Attribute("DefaultTargets", "Build"); - xw.Attribute("ToolsVersion", "4.0"); - xw.Attribute("xmlns", - "http://schemas.microsoft.com/developer/msbuild/2003"); - if (this->IsNsightTegra()) { - xw.StartElement("PropertyGroup"); - xw.Attribute("Label", "NsightTegraProject"); - xw.StartElement("NsightTegraProjectRevisionNumber"); - xw.Content("6"); - xw.EndElement(); // NsightTegraProjectRevisionNumber - xw.EndElement(); // PropertyGroup - } - xw.StartElement("ItemGroup"); - xw.Attribute("Label", "ProjectConfigurations"); - xw.StartElement("ProjectConfiguration"); - xw.Attribute("Include", "Debug|" + this->GetPlatformName()); - xw.StartElement("Configuration"); - xw.Content("Debug"); - xw.EndElement(); // Configuration - xw.StartElement("Platform"); - xw.Content(this->GetPlatformName()); - xw.EndElement(); // Platform - xw.EndElement(); // ProjectConfiguration - xw.EndElement(); // ItemGroup - xw.StartElement("PropertyGroup"); - xw.Attribute("Label", "Globals"); - xw.StartElement("ProjectGuid"); - xw.Content("{F3FC6D86-508D-3FB1-96D2-995F08B142EC}"); - xw.EndElement(); // ProjectGuid - xw.StartElement("Keyword"); - xw.Content("Win32Proj"); - xw.EndElement(); // Keyword - xw.StartElement("Platform"); - xw.Content(this->GetPlatformName()); - xw.EndElement(); // Platform - if (this->GetSystemName() == "WindowsPhone") { - xw.StartElement("ApplicationType"); - xw.Content("Windows Phone"); - xw.EndElement(); // ApplicationType - xw.StartElement("ApplicationTypeRevision"); - xw.Content(this->GetSystemVersion()); - xw.EndElement(); // ApplicationTypeRevision - } else if (this->GetSystemName() == "WindowsStore") { - xw.StartElement("ApplicationType"); - xw.Content("Windows Store"); - xw.EndElement(); // ApplicationType - xw.StartElement("ApplicationTypeRevision"); - xw.Content(this->GetSystemVersion()); - xw.EndElement(); // ApplicationTypeRevision - } - if (!this->WindowsTargetPlatformVersion.empty()) { - xw.StartElement("WindowsTargetPlatformVersion"); - xw.Content(this->WindowsTargetPlatformVersion); - xw.EndElement(); // WindowsTargetPlatformVersion - } - if (this->GetPlatformName() == "ARM64") { - xw.StartElement("WindowsSDKDesktopARM64Support"); - xw.Content("true"); - xw.EndElement(); // WindowsSDK64DesktopARMSupport - } - else if (this->GetPlatformName() == "ARM") { - xw.StartElement("WindowsSDKDesktopARMSupport"); - xw.Content("true"); - xw.EndElement(); // WindowsSDKDesktopARMSupport - } - xw.EndElement(); // PropertyGroup - xw.StartElement("Import"); - xw.Attribute("Project", - "$(VCTargetsPath)\\Microsoft.Cpp.Default.props"); - xw.EndElement(); // Import + cmXMLDocument doc(xw); + cmXMLElement eprj(doc, "Project"); + eprj.Attribute("DefaultTargets", "Build"); + eprj.Attribute("ToolsVersion", "4.0"); + eprj.Attribute("xmlns", + "http://schemas.microsoft.com/developer/msbuild/2003"); + if (this->IsNsightTegra()) { + cmXMLElement epg(eprj, "PropertyGroup"); + epg.Attribute("Label", "NsightTegraProject"); + cmXMLElement(epg, "NsightTegraProjectRevisionNumber").Content("6"); + } + { + cmXMLElement eig(eprj, "ItemGroup"); + eig.Attribute("Label", "ProjectConfigurations"); + cmXMLElement epc(eig, "ProjectConfiguration"); + epc.Attribute("Include", "Debug|" + this->GetPlatformName()); + cmXMLElement(epc, "Configuration").Content("Debug"); + cmXMLElement(epc, "Platform").Content(this->GetPlatformName()); + } + { + cmXMLElement epg(eprj, "PropertyGroup"); + epg.Attribute("Label", "Globals"); + cmXMLElement(epg, "ProjectGuid") + .Content("{F3FC6D86-508D-3FB1-96D2-995F08B142EC}"); + cmXMLElement(epg, "Keyword").Content("Win32Proj"); + cmXMLElement(epg, "Platform").Content(this->GetPlatformName()); + if (this->GetSystemName() == "WindowsPhone") { + cmXMLElement(epg, "ApplicationType").Content("Windows Phone"); + cmXMLElement(epg, "ApplicationTypeRevision") + .Content(this->GetSystemVersion()); + } else if (this->GetSystemName() == "WindowsStore") { + cmXMLElement(epg, "ApplicationType").Content("Windows Store"); + cmXMLElement(epg, "ApplicationTypeRevision") + .Content(this->GetSystemVersion()); + } + if (!this->WindowsTargetPlatformVersion.empty()) { + cmXMLElement(epg, "WindowsTargetPlatformVersion") + .Content(this->WindowsTargetPlatformVersion); + } + if (this->GetPlatformName() == "ARM64") { + cmXMLElement(epg, "WindowsSDKDesktopARM64Support").Content("true"); + } else if (this->GetPlatformName() == "ARM") { + cmXMLElement(epg, "WindowsSDKDesktopARMSupport").Content("true"); + } + } + cmXMLElement(eprj, "Import") + .Attribute("Project", "$(VCTargetsPath)\\Microsoft.Cpp.Default.props"); if (!this->GeneratorToolsetHostArchitecture.empty()) { - xw.StartElement("PropertyGroup"); - xw.StartElement("PreferredToolArchitecture"); - xw.Content(this->GeneratorToolsetHostArchitecture); - xw.EndElement(); // PreferredToolArchitecture - xw.EndElement(); // PropertyGroup + cmXMLElement epg(eprj, "PropertyGroup"); + cmXMLElement(epg, "PreferredToolArchitecture") + .Content(this->GeneratorToolsetHostArchitecture); } - xw.StartElement("PropertyGroup"); - xw.Attribute("Label", "Configuration"); - xw.StartElement("ConfigurationType"); + { + cmXMLElement epg(eprj, "PropertyGroup"); + epg.Attribute("Label", "Configuration"); + { + cmXMLElement ect(epg, "ConfigurationType"); + if (this->IsNsightTegra()) { + // Tegra-Android platform does not understand "Utility". + ect.Content("StaticLibrary"); + } else { + ect.Content("Utility"); + } + } + cmXMLElement(epg, "CharacterSet").Content("MultiByte"); if (this->IsNsightTegra()) { - // Tegra-Android platform does not understand "Utility". - xw.Content("StaticLibrary"); + cmXMLElement(epg, "NdkToolchainVersion") + .Content(this->GetPlatformToolsetString()); } else { - xw.Content("Utility"); + cmXMLElement(epg, "PlatformToolset") + .Content(this->GetPlatformToolsetString()); } - xw.EndElement(); // ConfigurationType - xw.StartElement("CharacterSet"); - xw.Content("MultiByte"); - xw.EndElement(); // CharacterSet - if (this->IsNsightTegra()) { - xw.StartElement("NdkToolchainVersion"); - xw.Content(this->GetPlatformToolsetString()); - xw.EndElement(); // NdkToolchainVersion - } else { - xw.StartElement("PlatformToolset"); - xw.Content(this->GetPlatformToolsetString()); - xw.EndElement(); // PlatformToolset - } - xw.EndElement(); // PropertyGroup - xw.StartElement("Import"); - xw.Attribute("Project", "$(VCTargetsPath)\\Microsoft.Cpp.props"); - xw.EndElement(); // Import - xw.StartElement("ItemDefinitionGroup"); - xw.StartElement("PostBuildEvent"); - xw.StartElement("Command"); - xw.Content("echo VCTargetsPath=$(VCTargetsPath)"); - xw.EndElement(); // Command - xw.EndElement(); // PostBuildEvent - xw.EndElement(); // ItemDefinitionGroup - xw.StartElement("Import"); - xw.Attribute("Project", - "$(VCTargetsPath)\\Microsoft.Cpp.targets"); - xw.EndElement(); // Import - xw.EndElement(); // Project - xw.EndDocument(); - /* clang-format on */ + } + cmXMLElement(eprj, "Import") + .Attribute("Project", "$(VCTargetsPath)\\Microsoft.Cpp.props"); + { + cmXMLElement eidg(eprj, "ItemDefinitionGroup"); + cmXMLElement epbe(eidg, "PostBuildEvent"); + cmXMLElement(epbe, "Command") + .Content("echo VCTargetsPath=$(VCTargetsPath)"); + } + cmXMLElement(eprj, "Import") + .Attribute("Project", "$(VCTargetsPath)\\Microsoft.Cpp.targets"); } std::vector<std::string> cmd; @@ -801,7 +849,7 @@ void cmGlobalVisualStudio10Generator::GenerateBuildCommand( std::vector<std::string>& makeCommand, const std::string& makeProgram, const std::string& projectName, const std::string& projectDir, const std::string& targetName, const std::string& config, bool fast, - bool verbose, std::vector<std::string> const& makeOptions) + int jobs, bool verbose, std::vector<std::string> const& makeOptions) { // Select the caller- or user-preferred make program, else MSBuild. std::string makeProgramSelected = @@ -842,7 +890,7 @@ void cmGlobalVisualStudio10Generator::GenerateBuildCommand( // Use devenv to build solutions containing Intel Fortran projects. cmGlobalVisualStudio7Generator::GenerateBuildCommand( makeCommand, makeProgram, projectName, projectDir, targetName, config, - fast, verbose, makeOptions); + fast, jobs, verbose, makeOptions); return; } @@ -850,6 +898,7 @@ void cmGlobalVisualStudio10Generator::GenerateBuildCommand( std::string realTarget = targetName; // msbuild.exe CxxOnly.sln /t:Build /p:Configuration=Debug /target:ALL_BUILD + // /m if (realTarget.empty()) { realTarget = "ALL_BUILD"; } @@ -878,6 +927,17 @@ void cmGlobalVisualStudio10Generator::GenerateBuildCommand( makeCommand.push_back(configArg); makeCommand.push_back(std::string("/p:VisualStudioVersion=") + this->GetIDEVersion()); + + if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) { + if (jobs == cmake::DEFAULT_BUILD_PARALLEL_LEVEL) { + makeCommand.push_back("/m"); + } else { + makeCommand.push_back(std::string("/m:") + std::to_string(jobs)); + } + // Having msbuild.exe and cl.exe using multiple jobs is discouraged + makeCommand.push_back("/p:CL_MPCount=1"); + } + makeCommand.insert(makeCommand.end(), makeOptions.begin(), makeOptions.end()); } diff --git a/Source/cmGlobalVisualStudio10Generator.h b/Source/cmGlobalVisualStudio10Generator.h index f2501c2..148f85f 100644 --- a/Source/cmGlobalVisualStudio10Generator.h +++ b/Source/cmGlobalVisualStudio10Generator.h @@ -24,13 +24,11 @@ public: bool SetGeneratorPlatform(std::string const& p, cmMakefile* mf) override; bool SetGeneratorToolset(std::string const& ts, cmMakefile* mf) override; - void GenerateBuildCommand(std::vector<std::string>& makeCommand, - const std::string& makeProgram, - const std::string& projectName, - const std::string& projectDir, - const std::string& targetName, - const std::string& config, bool fast, bool verbose, - std::vector<std::string> const& makeOptions = + void GenerateBuildCommand( + std::vector<std::string>& makeCommand, const std::string& makeProgram, + const std::string& projectName, const std::string& projectDir, + const std::string& targetName, const std::string& config, bool fast, + int jobs, bool verbose, std::vector<std::string> const& makeOptions = std::vector<std::string>()) override; ///! create the correct local generator @@ -54,6 +52,10 @@ public: const char* GetPlatformToolset() const; std::string const& GetPlatformToolsetString() const; + /** The toolset version. */ + const char* GetPlatformToolsetVersion() const; + std::string const& GetPlatformToolsetVersionString() const; + /** The toolset host architecture name (e.g. x64 for 64-bit host tools). */ const char* GetPlatformToolsetHostArchitecture() const; @@ -101,6 +103,8 @@ public: std::string Encoding() override; virtual const char* GetToolsVersion() { return "4.0"; } + virtual std::string GetAuxiliaryToolset() const; + bool FindMakeProgram(cmMakefile* mf) override; static std::string GetInstalledNsightTegraVersion(); @@ -135,6 +139,7 @@ protected: std::string const& GetMSBuildCommand(); std::string GeneratorToolset; + std::string GeneratorToolsetVersion; std::string GeneratorToolsetHostArchitecture; std::string GeneratorToolsetCuda; std::string DefaultPlatformToolset; diff --git a/Source/cmGlobalVisualStudio15Generator.cxx b/Source/cmGlobalVisualStudio15Generator.cxx index 014d93d..6af5793 100644 --- a/Source/cmGlobalVisualStudio15Generator.cxx +++ b/Source/cmGlobalVisualStudio15Generator.cxx @@ -158,6 +158,25 @@ bool cmGlobalVisualStudio15Generator::GetVSInstance(std::string& dir) const return vsSetupAPIHelper.GetVSInstanceInfo(dir); } +std::string cmGlobalVisualStudio15Generator::GetAuxiliaryToolset() const +{ + const char* version = this->GetPlatformToolsetVersion(); + if (version) { + std::string instancePath; + GetVSInstance(instancePath); + std::stringstream path; + path << instancePath; + path << "/VC/Auxiliary/Build/"; + path << version; + path << "/Microsoft.VCToolsVersion." << version << ".props"; + + std::string toolsetPath = path.str(); + cmSystemTools::ConvertToUnixSlashes(toolsetPath); + return toolsetPath; + } + return {}; +} + bool cmGlobalVisualStudio15Generator::InitializeWindows(cmMakefile* mf) { // If the Win 8.1 SDK is installed then we can select a SDK matching diff --git a/Source/cmGlobalVisualStudio15Generator.h b/Source/cmGlobalVisualStudio15Generator.h index 4f4e0b9..3b9cfc7 100644 --- a/Source/cmGlobalVisualStudio15Generator.h +++ b/Source/cmGlobalVisualStudio15Generator.h @@ -32,6 +32,8 @@ public: bool GetVSInstance(std::string& dir) const; + std::string GetAuxiliaryToolset() const override; + protected: bool InitializeWindows(cmMakefile* mf) override; bool SelectWindowsStoreToolset(std::string& toolset) const override; diff --git a/Source/cmGlobalVisualStudio71Generator.cxx b/Source/cmGlobalVisualStudio71Generator.cxx index 45cc583..0b086b0 100644 --- a/Source/cmGlobalVisualStudio71Generator.cxx +++ b/Source/cmGlobalVisualStudio71Generator.cxx @@ -98,7 +98,7 @@ void cmGlobalVisualStudio71Generator::WriteProject(std::ostream& fout, ext = ".vfproj"; project = "Project(\"{6989167D-11E4-40FE-8C1A-2192A86A7E90}\") = \""; } - if (this->TargetIsCSharpOnly(t)) { + if (t->HasLanguage("CSharp", "")) { ext = ".csproj"; project = "Project(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \""; } diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index c915dc5..158f484 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx @@ -199,7 +199,7 @@ void cmGlobalVisualStudio7Generator::GenerateBuildCommand( std::vector<std::string>& makeCommand, const std::string& makeProgram, const std::string& projectName, const std::string& /*projectDir*/, const std::string& targetName, const std::string& config, bool /*fast*/, - bool /*verbose*/, std::vector<std::string> const& makeOptions) + int /*jobs*/, bool /*verbose*/, std::vector<std::string> const& makeOptions) { // Select the caller- or user-preferred make program, else devenv. std::string makeProgramSelected = @@ -294,19 +294,6 @@ void cmGlobalVisualStudio7Generator::Generate() if (!cmSystemTools::GetErrorOccuredFlag()) { this->CallVisualStudioMacro(MacroReload); } - - if (this->Version == VS8 && !this->CMakeInstance->GetIsInTryCompile()) { - const char* cmakeWarnVS8 = - this->CMakeInstance->GetState()->GetCacheEntryValue("CMAKE_WARN_VS8"); - if (!cmakeWarnVS8 || !cmSystemTools::IsOff(cmakeWarnVS8)) { - this->CMakeInstance->IssueMessage( - cmake::WARNING, - "The \"Visual Studio 8 2005\" generator is deprecated " - "and will be removed in a future version of CMake." - "\n" - "Add CMAKE_WARN_VS8=OFF to the cache to disable this warning."); - } - } } void cmGlobalVisualStudio7Generator::OutputSLNFile( diff --git a/Source/cmGlobalVisualStudio7Generator.h b/Source/cmGlobalVisualStudio7Generator.h index 8d1bdc0..77d4a96 100644 --- a/Source/cmGlobalVisualStudio7Generator.h +++ b/Source/cmGlobalVisualStudio7Generator.h @@ -55,13 +55,11 @@ public: * Try running cmake and building a file. This is used for dynamically * loaded commands, not as part of the usual build process. */ - void GenerateBuildCommand(std::vector<std::string>& makeCommand, - const std::string& makeProgram, - const std::string& projectName, - const std::string& projectDir, - const std::string& targetName, - const std::string& config, bool fast, bool verbose, - std::vector<std::string> const& makeOptions = + void GenerateBuildCommand( + std::vector<std::string>& makeCommand, const std::string& makeProgram, + const std::string& projectName, const std::string& projectDir, + const std::string& targetName, const std::string& config, bool fast, + int jobs, bool verbose, std::vector<std::string> const& makeOptions = std::vector<std::string>()) override; /** diff --git a/Source/cmGlobalVisualStudio8Generator.cxx b/Source/cmGlobalVisualStudio8Generator.cxx index ab8ad70..117d051 100644 --- a/Source/cmGlobalVisualStudio8Generator.cxx +++ b/Source/cmGlobalVisualStudio8Generator.cxx @@ -11,75 +11,6 @@ #include "cmVisualStudioWCEPlatformParser.h" #include "cmake.h" -static const char vs8generatorName[] = "Visual Studio 8 2005"; - -class cmGlobalVisualStudio8Generator::Factory : public cmGlobalGeneratorFactory -{ -public: - cmGlobalGenerator* CreateGlobalGenerator(const std::string& name, - cmake* cm) const override - { - if (strncmp(name.c_str(), vs8generatorName, - sizeof(vs8generatorName) - 1) != 0) { - return 0; - } - - const char* p = name.c_str() + sizeof(vs8generatorName) - 1; - if (p[0] == '\0') { - return new cmGlobalVisualStudio8Generator(cm, name, ""); - } - - if (p[0] != ' ') { - return 0; - } - - ++p; - - if (!strcmp(p, "Win64")) { - return new cmGlobalVisualStudio8Generator(cm, name, "x64"); - } - - cmVisualStudioWCEPlatformParser parser(p); - parser.ParseVersion("8.0"); - if (!parser.Found()) { - return 0; - } - - cmGlobalVisualStudio8Generator* ret = - new cmGlobalVisualStudio8Generator(cm, name, p); - ret->WindowsCEVersion = parser.GetOSVersion(); - return ret; - } - - void GetDocumentation(cmDocumentationEntry& entry) const override - { - entry.Name = std::string(vs8generatorName) + " [arch]"; - entry.Brief = "Deprecated. Generates Visual Studio 2005 project files. " - "Optional [arch] can be \"Win64\"."; - } - - void GetGenerators(std::vector<std::string>& names) const override - { - names.push_back(vs8generatorName); - names.push_back(vs8generatorName + std::string(" Win64")); - cmVisualStudioWCEPlatformParser parser; - parser.ParseVersion("8.0"); - const std::vector<std::string>& availablePlatforms = - parser.GetAvailablePlatforms(); - for (std::string const& i : availablePlatforms) { - names.push_back("Visual Studio 8 2005 " + i); - } - } - - bool SupportsToolset() const override { return false; } - bool SupportsPlatform() const override { return true; } -}; - -cmGlobalGeneratorFactory* cmGlobalVisualStudio8Generator::NewFactory() -{ - return new Factory; -} - cmGlobalVisualStudio8Generator::cmGlobalVisualStudio8Generator( cmake* cm, const std::string& name, const std::string& platformName) : cmGlobalVisualStudio71Generator(cm, platformName) @@ -87,12 +18,6 @@ cmGlobalVisualStudio8Generator::cmGlobalVisualStudio8Generator( this->ProjectConfigurationSectionName = "ProjectConfigurationPlatforms"; this->Name = name; this->ExtraFlagTable = this->GetExtraFlagTableVS8(); - this->Version = VS8; - std::string vc8Express; - this->ExpressEdition = cmSystemTools::ReadRegistryValue( - "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VCExpress\\8.0\\Setup\\VC;" - "ProductDir", - vc8Express, cmSystemTools::KeyWOW64_32); } std::string cmGlobalVisualStudio8Generator::FindDevEnvCommand() @@ -143,13 +68,6 @@ bool cmGlobalVisualStudio8Generator::SetGeneratorPlatform(std::string const& p, } } -// output standard header for dsw file -void cmGlobalVisualStudio8Generator::WriteSLNHeader(std::ostream& fout) -{ - fout << "Microsoft Visual Studio Solution File, Format Version 9.00\n"; - fout << "# Visual Studio 2005\n"; -} - std::string cmGlobalVisualStudio8Generator::GetGenerateStampList() { return "generate.stamp.list"; @@ -165,62 +83,22 @@ bool cmGlobalVisualStudio8Generator::UseFolderProperty() return IsExpressEdition() ? false : cmGlobalGenerator::UseFolderProperty(); } -std::string cmGlobalVisualStudio8Generator::GetUserMacrosDirectory() -{ - // Some VS8 sp0 versions cannot run macros. - // See http://support.microsoft.com/kb/928209 - const char* vc8sp1Registry = - "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\8.0\\" - "InstalledProducts\\KB926601;"; - const char* vc8exSP1Registry = - "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\8.0\\" - "InstalledProducts\\KB926748;"; - std::string vc8sp1; - if (!cmSystemTools::ReadRegistryValue(vc8sp1Registry, vc8sp1) && - !cmSystemTools::ReadRegistryValue(vc8exSP1Registry, vc8sp1)) { - return ""; - } - - std::string base; - std::string path; - - // base begins with the VisualStudioProjectsLocation reg value... - if (cmSystemTools::ReadRegistryValue( - "HKEY_CURRENT_USER\\Software\\Microsoft\\VisualStudio\\8.0;" - "VisualStudioProjectsLocation", - base)) { - cmSystemTools::ConvertToUnixSlashes(base); - - // 8.0 macros folder: - path = base + "/VSMacros80"; - } - - // path is (correctly) still empty if we did not read the base value from - // the Registry value - return path; -} - -std::string cmGlobalVisualStudio8Generator::GetUserMacrosRegKeyBase() -{ - return "Software\\Microsoft\\VisualStudio\\8.0\\vsmacros"; -} - bool cmGlobalVisualStudio8Generator::AddCheckTarget() { // Add a special target on which all other targets depend that // checks the build system and optionally re-runs CMake. - const char* no_working_directory = 0; + // Skip the target if no regeneration is to be done. + if (this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION")) { + return false; + } + + const char* no_working_directory = nullptr; std::vector<std::string> no_depends; std::vector<cmLocalGenerator*> const& generators = this->LocalGenerators; cmLocalVisualStudio7Generator* lg = static_cast<cmLocalVisualStudio7Generator*>(generators[0]); cmMakefile* mf = lg->GetMakefile(); - // Skip the target if no regeneration is to be done. - if (mf->IsOn("CMAKE_SUPPRESS_REGENERATION")) { - return false; - } - cmCustomCommandLines noCommandLines; cmTarget* tgt = mf->AddUtilityCommand( CMAKE_CHECK_BUILD_SYSTEM_TARGET, cmMakefile::TargetOrigin::Generator, @@ -266,6 +144,30 @@ bool cmGlobalVisualStudio8Generator::AddCheckTarget() listFiles.insert(listFiles.end(), lmf->GetListFiles().begin(), lmf->GetListFiles().end()); } + + // Add a custom prebuild target to run the VerifyGlobs script. + cmake* cm = this->GetCMakeInstance(); + if (cm->DoWriteGlobVerifyTarget()) { + cmCustomCommandLine verifyCommandLine; + verifyCommandLine.push_back(cmSystemTools::GetCMakeCommand()); + verifyCommandLine.push_back("-P"); + verifyCommandLine.push_back(cm->GetGlobVerifyScript()); + cmCustomCommandLines verifyCommandLines; + verifyCommandLines.push_back(verifyCommandLine); + std::vector<std::string> byproducts; + byproducts.push_back(cm->GetGlobVerifyStamp()); + + mf->AddCustomCommandToTarget(CMAKE_CHECK_BUILD_SYSTEM_TARGET, byproducts, + no_depends, verifyCommandLines, + cmTarget::PRE_BUILD, "Checking File Globs", + no_working_directory, false); + + // Ensure ZERO_CHECK always runs in Visual Studio using MSBuild, + // otherwise the prebuild command will not be run. + tgt->SetProperty("VS_GLOBAL_DisableFastUpToDateCheck", "true"); + listFiles.push_back(cm->GetGlobVerifyStamp()); + } + // Sort the list of input files and remove duplicates. std::sort(listFiles.begin(), listFiles.end(), std::less<std::string>()); std::vector<std::string>::iterator new_end = @@ -273,8 +175,6 @@ bool cmGlobalVisualStudio8Generator::AddCheckTarget() listFiles.erase(new_end, listFiles.end()); // Create a rule to re-run CMake. - std::string stampName = cmake::GetCMakeFilesDirectoryPostSlash(); - stampName += "generate.stamp"; cmCustomCommandLine commandLine; commandLine.push_back(cmSystemTools::GetCMakeCommand()); std::string argH = "-H"; diff --git a/Source/cmGlobalVisualStudio8Generator.h b/Source/cmGlobalVisualStudio8Generator.h index af83e4f..6f64b9c 100644 --- a/Source/cmGlobalVisualStudio8Generator.h +++ b/Source/cmGlobalVisualStudio8Generator.h @@ -15,7 +15,6 @@ class cmGlobalVisualStudio8Generator : public cmGlobalVisualStudio71Generator public: cmGlobalVisualStudio8Generator(cmake* cm, const std::string& name, const std::string& platformName); - static cmGlobalGeneratorFactory* NewFactory(); ///! Get the name for the generator. std::string GetName() const override { return this->Name; } @@ -35,19 +34,6 @@ public: */ void Configure() override; - /** - * Where does this version of Visual Studio look for macros for the - * current user? Returns the empty string if this version of Visual - * Studio does not implement support for VB macros. - */ - std::string GetUserMacrosDirectory() override; - - /** - * What is the reg key path to "vsmacros" for this version of Visual - * Studio? - */ - std::string GetUserMacrosRegKeyBase() override; - /** Return true if the target project file should have the option LinkLibraryDependencies and link to .sln dependencies. */ bool NeedLinkLibraryDependencies(cmGeneratorTarget* target) override; @@ -75,7 +61,6 @@ protected: virtual bool NeedsDeploy(cmStateEnums::TargetType type) const; static cmIDEFlagTable const* GetExtraFlagTableVS8(); - void WriteSLNHeader(std::ostream& fout) override; void WriteSolutionConfigurations( std::ostream& fout, std::vector<std::string> const& configs) override; void WriteProjectConfigurations( @@ -93,9 +78,5 @@ protected: std::string Name; std::string WindowsCEVersion; bool ExpressEdition; - -private: - class Factory; - friend class Factory; }; #endif diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx index a4570e1..1175acf 100644 --- a/Source/cmGlobalVisualStudioGenerator.cxx +++ b/Source/cmGlobalVisualStudioGenerator.cxx @@ -734,44 +734,6 @@ bool cmGlobalVisualStudioGenerator::TargetIsFortranOnly( return false; } -bool cmGlobalVisualStudioGenerator::TargetIsCSharpOnly( - cmGeneratorTarget const* gt) -{ - // check to see if this is a C# build - std::set<std::string> languages; - { - // Issue diagnostic if the source files depend on the config. - std::vector<cmSourceFile*> sources; - if (!gt->GetConfigCommonSourceFiles(sources)) { - return false; - } - // Only "real" targets are allowed to be C# targets. - if (gt->Target->GetType() > cmStateEnums::OBJECT_LIBRARY) { - return false; - } - } - gt->GetLanguages(languages, ""); - if (languages.size() == 1) { - if (*languages.begin() == "CSharp") { - return true; - } - } - return false; -} - -bool cmGlobalVisualStudioGenerator::TargetCanBeReferenced( - cmGeneratorTarget const* gt) -{ - if (this->TargetIsCSharpOnly(gt)) { - return true; - } - if (gt->GetType() != cmStateEnums::SHARED_LIBRARY && - gt->GetType() != cmStateEnums::EXECUTABLE) { - return false; - } - return true; -} - bool cmGlobalVisualStudioGenerator::TargetCompare::operator()( cmGeneratorTarget const* l, cmGeneratorTarget const* r) const { diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h index 75b7f22..07bc9a3 100644 --- a/Source/cmGlobalVisualStudioGenerator.h +++ b/Source/cmGlobalVisualStudioGenerator.h @@ -32,7 +32,6 @@ public: /** Known versions of Visual Studio. */ enum VSVersion { - VS8 = 80, VS9 = 90, VS10 = 100, VS11 = 110, @@ -82,12 +81,6 @@ public: // return true if target is fortran only bool TargetIsFortranOnly(const cmGeneratorTarget* gt); - // return true if target is C# only - static bool TargetIsCSharpOnly(cmGeneratorTarget const* gt); - - // return true if target can be referenced by C# targets - bool TargetCanBeReferenced(cmGeneratorTarget const* gt); - /** Get the top-level registry key for this VS version. */ std::string GetRegistryBase(); diff --git a/Source/cmGlobalWatcomWMakeGenerator.cxx b/Source/cmGlobalWatcomWMakeGenerator.cxx index 94cdb38..558ef15 100644 --- a/Source/cmGlobalWatcomWMakeGenerator.cxx +++ b/Source/cmGlobalWatcomWMakeGenerator.cxx @@ -7,6 +7,8 @@ #include "cmState.h" #include "cmake.h" +#include <ostream> + cmGlobalWatcomWMakeGenerator::cmGlobalWatcomWMakeGenerator(cmake* cm) : cmGlobalUnixMakefileGenerator3(cm) { @@ -47,3 +49,31 @@ void cmGlobalWatcomWMakeGenerator::GetDocumentation( entry.Name = cmGlobalWatcomWMakeGenerator::GetActualName(); entry.Brief = "Generates Watcom WMake makefiles."; } + +void cmGlobalWatcomWMakeGenerator::GenerateBuildCommand( + std::vector<std::string>& makeCommand, const std::string& makeProgram, + const std::string& projectName, const std::string& projectDir, + const std::string& targetName, const std::string& config, bool fast, + int /*jobs*/, bool verbose, std::vector<std::string> const& makeOptions) +{ + this->cmGlobalUnixMakefileGenerator3::GenerateBuildCommand( + makeCommand, makeProgram, projectName, projectDir, targetName, config, + fast, cmake::NO_BUILD_PARALLEL_LEVEL, verbose, makeOptions); +} + +void cmGlobalWatcomWMakeGenerator::PrintBuildCommandAdvice(std::ostream& os, + int jobs) const +{ + if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) { + // wmake does not support parallel build level + + /* clang-format off */ + os << + "Warning: Watcom's WMake does not support parallel builds. " + "Ignoring parallel build command line option.\n"; + /* clang-format on */ + } + + this->cmGlobalUnixMakefileGenerator3::PrintBuildCommandAdvice( + os, cmake::NO_BUILD_PARALLEL_LEVEL); +} diff --git a/Source/cmGlobalWatcomWMakeGenerator.h b/Source/cmGlobalWatcomWMakeGenerator.h index e8b3a73..1729bf1 100644 --- a/Source/cmGlobalWatcomWMakeGenerator.h +++ b/Source/cmGlobalWatcomWMakeGenerator.h @@ -8,6 +8,7 @@ #include "cmGlobalGeneratorFactory.h" #include "cmGlobalUnixMakefileGenerator3.h" +#include <iosfwd> #include <string> #include <vector> @@ -47,6 +48,16 @@ public: bool AllowNotParallel() const override { return false; } bool AllowDeleteOnError() const override { return false; } + +protected: + void GenerateBuildCommand( + std::vector<std::string>& makeCommand, const std::string& makeProgram, + const std::string& projectName, const std::string& projectDir, + const std::string& targetName, const std::string& config, bool fast, + int jobs, bool verbose, std::vector<std::string> const& makeOptions = + std::vector<std::string>()) override; + + void PrintBuildCommandAdvice(std::ostream& os, int jobs) const override; }; #endif diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 2008a0b..f69f23e 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -325,7 +325,7 @@ void cmGlobalXCodeGenerator::GenerateBuildCommand( std::vector<std::string>& makeCommand, const std::string& makeProgram, const std::string& projectName, const std::string& /*projectDir*/, const std::string& targetName, const std::string& config, bool /*fast*/, - bool /*verbose*/, std::vector<std::string> const& makeOptions) + int jobs, bool /*verbose*/, std::vector<std::string> const& makeOptions) { // now build the test makeCommand.push_back( @@ -356,6 +356,14 @@ void cmGlobalXCodeGenerator::GenerateBuildCommand( } makeCommand.push_back("-configuration"); makeCommand.push_back(!config.empty() ? config : "Debug"); + + if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) { + makeCommand.push_back("-jobs"); + if (jobs != cmake::DEFAULT_BUILD_PARALLEL_LEVEL) { + makeCommand.push_back(std::to_string(jobs)); + } + } + makeCommand.insert(makeCommand.end(), makeOptions.begin(), makeOptions.end()); } @@ -458,7 +466,7 @@ void cmGlobalXCodeGenerator::AddExtraTargets( makeHelper.push_back(""); // placeholder, see below // Add ZERO_CHECK - bool regenerate = !mf->IsOn("CMAKE_SUPPRESS_REGENERATION"); + bool regenerate = !this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION"); bool generateTopLevelProjectOnly = mf->IsOn("CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY"); bool isTopLevel = @@ -466,7 +474,7 @@ void cmGlobalXCodeGenerator::AddExtraTargets( if (regenerate && (isTopLevel || !generateTopLevelProjectOnly)) { this->CreateReRunCMakeFile(root, gens); std::string file = - this->ConvertToRelativeForMake(this->CurrentReRunCMakeMakefile.c_str()); + this->ConvertToRelativeForMake(this->CurrentReRunCMakeMakefile); cmSystemTools::ReplaceString(file, "\\ ", " "); cmTarget* check = mf->AddUtilityCommand( CMAKE_CHECK_BUILD_SYSTEM_TARGET, cmMakefile::TargetOrigin::Generator, @@ -537,6 +545,12 @@ void cmGlobalXCodeGenerator::CreateReRunCMakeFile( std::vector<std::string>::iterator new_end = std::unique(lfiles.begin(), lfiles.end()); lfiles.erase(new_end, lfiles.end()); + + cmake* cm = this->GetCMakeInstance(); + if (cm->DoWriteGlobVerifyTarget()) { + lfiles.emplace_back(cm->GetGlobVerifyStamp()); + } + this->CurrentReRunCMakeMakefile = root->GetCurrentBinaryDirectory(); this->CurrentReRunCMakeMakefile += "/CMakeScripts"; cmSystemTools::MakeDirectory(this->CurrentReRunCMakeMakefile.c_str()); @@ -553,20 +567,34 @@ void cmGlobalXCodeGenerator::CreateReRunCMakeFile( for (const auto& lfile : lfiles) { makefileStream << "TARGETS += $(subst $(space),$(spaceplus),$(wildcard " - << this->ConvertToRelativeForMake(lfile.c_str()) << "))\n"; + << this->ConvertToRelativeForMake(lfile) << "))\n"; } + makefileStream << "\n"; std::string checkCache = root->GetBinaryDirectory(); checkCache += "/"; checkCache += cmake::GetCMakeFilesDirectoryPostSlash(); checkCache += "cmake.check_cache"; - makefileStream << "\n" - << this->ConvertToRelativeForMake(checkCache.c_str()) + if (cm->DoWriteGlobVerifyTarget()) { + makefileStream << ".NOTPARALLEL:\n\n"; + makefileStream << ".PHONY: all VERIFY_GLOBS\n\n"; + makefileStream << "all: VERIFY_GLOBS " + << this->ConvertToRelativeForMake(checkCache) << "\n\n"; + makefileStream << "VERIFY_GLOBS:\n"; + makefileStream << "\t" + << this->ConvertToRelativeForMake( + cmSystemTools::GetCMakeCommand()) + << " -P " + << this->ConvertToRelativeForMake(cm->GetGlobVerifyScript()) + << "\n\n"; + } + + makefileStream << this->ConvertToRelativeForMake(checkCache) << ": $(TARGETS)\n"; makefileStream << "\t" << this->ConvertToRelativeForMake( - cmSystemTools::GetCMakeCommand().c_str()) + cmSystemTools::GetCMakeCommand()) << " -H" << this->ConvertToRelativeForMake(root->GetSourceDirectory()) << " -B" @@ -1581,12 +1609,11 @@ void cmGlobalXCodeGenerator::AddCommandsToBuildPhase( } std::string cdir = this->CurrentLocalGenerator->GetCurrentBinaryDirectory(); - cdir = this->ConvertToRelativeForMake(cdir.c_str()); + cdir = this->ConvertToRelativeForMake(cdir); std::string makecmd = "make -C "; makecmd += cdir; makecmd += " -f "; - makecmd += - this->ConvertToRelativeForMake((makefile + "$CONFIGURATION").c_str()); + makecmd += this->ConvertToRelativeForMake((makefile + "$CONFIGURATION")); makecmd += " all"; buildphase->AddAttribute("shellScript", this->CreateString(makecmd)); buildphase->AddAttribute("showEnvVarsInLog", this->CreateString("0")); @@ -1621,8 +1648,7 @@ void cmGlobalXCodeGenerator::CreateCustomRulesMakefile( const std::vector<std::string>& outputs = ccg.GetOutputs(); if (!outputs.empty()) { for (auto const& output : outputs) { - makefileStream << "\\\n\t" - << this->ConvertToRelativeForMake(output.c_str()); + makefileStream << "\\\n\t" << this->ConvertToRelativeForMake(output); } } else { std::ostringstream str; @@ -1643,8 +1669,7 @@ void cmGlobalXCodeGenerator::CreateCustomRulesMakefile( // There is at least one output, start the rule for it const char* sep = ""; for (auto const& output : outputs) { - makefileStream << sep - << this->ConvertToRelativeForMake(output.c_str()); + makefileStream << sep << this->ConvertToRelativeForMake(output); sep = " "; } makefileStream << ": "; @@ -1656,8 +1681,7 @@ void cmGlobalXCodeGenerator::CreateCustomRulesMakefile( std::string dep; if (this->CurrentLocalGenerator->GetRealDependency(d, configName, dep)) { - makefileStream << "\\\n" - << this->ConvertToRelativeForMake(dep.c_str()); + makefileStream << "\\\n" << this->ConvertToRelativeForMake(dep); } } makefileStream << "\n"; @@ -1674,12 +1698,12 @@ void cmGlobalXCodeGenerator::CreateCustomRulesMakefile( // Build the command line in a single string. std::string cmd2 = ccg.GetCommand(c); cmSystemTools::ReplaceString(cmd2, "/./", "/"); - cmd2 = this->ConvertToRelativeForMake(cmd2.c_str()); + cmd2 = this->ConvertToRelativeForMake(cmd2); std::string cmd; std::string wd = ccg.GetWorkingDirectory(); if (!wd.empty()) { cmd += "cd "; - cmd += this->ConvertToRelativeForMake(wd.c_str()); + cmd += this->ConvertToRelativeForMake(wd); cmd += " && "; } cmd += cmd2; @@ -3200,7 +3224,7 @@ void cmGlobalXCodeGenerator::CreateXCodeDependHackTarget( gt->GetType() == cmStateEnums::SHARED_LIBRARY || gt->GetType() == cmStateEnums::MODULE_LIBRARY) { std::string tfull = gt->GetFullPath(configName); - std::string trel = this->ConvertToRelativeForMake(tfull.c_str()); + std::string trel = this->ConvertToRelativeForMake(tfull); // Add this target to the post-build phases of its dependencies. std::map<std::string, cmXCodeObject::StringVec>::const_iterator y = @@ -3228,7 +3252,7 @@ void cmGlobalXCodeGenerator::CreateXCodeDependHackTarget( target->GetDependLibraries().find(configName); if (x != target->GetDependLibraries().end()) { for (auto const& deplib : x->second) { - std::string file = this->ConvertToRelativeForMake(deplib.c_str()); + std::string file = this->ConvertToRelativeForMake(deplib); makefileStream << "\\\n\t" << file; dummyRules.insert(file); } @@ -3243,7 +3267,7 @@ void cmGlobalXCodeGenerator::CreateXCodeDependHackTarget( d += objLibName; d += ".a"; - std::string dependency = this->ConvertToRelativeForMake(d.c_str()); + std::string dependency = this->ConvertToRelativeForMake(d); makefileStream << "\\\n\t" << dependency; dummyRules.insert(dependency); } @@ -3251,8 +3275,7 @@ void cmGlobalXCodeGenerator::CreateXCodeDependHackTarget( // Write the action to remove the target if it is out of date. makefileStream << "\n"; makefileStream << "\t/bin/rm -f " - << this->ConvertToRelativeForMake(tfull.c_str()) - << "\n"; + << this->ConvertToRelativeForMake(tfull) << "\n"; // if building for more than one architecture // then remove those executables as well if (this->Architectures.size() > 1) { @@ -3264,8 +3287,7 @@ void cmGlobalXCodeGenerator::CreateXCodeDependHackTarget( universalFile += "/"; universalFile += gt->GetFullName(configName); makefileStream << "\t/bin/rm -f " - << this->ConvertToRelativeForMake( - universalFile.c_str()) + << this->ConvertToRelativeForMake(universalFile) << "\n"; } } diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h index 7c51177..f7f4428 100644 --- a/Source/cmGlobalXCodeGenerator.h +++ b/Source/cmGlobalXCodeGenerator.h @@ -66,13 +66,11 @@ public: * Try running cmake and building a file. This is used for dynalically * loaded commands, not as part of the usual build process. */ - void GenerateBuildCommand(std::vector<std::string>& makeCommand, - const std::string& makeProgram, - const std::string& projectName, - const std::string& projectDir, - const std::string& targetName, - const std::string& config, bool fast, bool verbose, - std::vector<std::string> const& makeOptions = + void GenerateBuildCommand( + std::vector<std::string>& makeCommand, const std::string& makeProgram, + const std::string& projectName, const std::string& projectDir, + const std::string& targetName, const std::string& config, bool fast, + int jobs, bool verbose, std::vector<std::string> const& makeOptions = std::vector<std::string>()) override; /** Append the subdirectory for the given configuration. */ diff --git a/Source/cmIDEOptions.cxx b/Source/cmIDEOptions.cxx index 354b757..f996788 100644 --- a/Source/cmIDEOptions.cxx +++ b/Source/cmIDEOptions.cxx @@ -25,7 +25,7 @@ cmIDEOptions::~cmIDEOptions() { } -void cmIDEOptions::HandleFlag(const char* flag) +void cmIDEOptions::HandleFlag(std::string const& flag) { // If the last option was -D then this option is the definition. if (this->DoingDefine) { @@ -49,26 +49,27 @@ void cmIDEOptions::HandleFlag(const char* flag) } // Look for known arguments. - if (flag[0] == '-' || (this->AllowSlash && flag[0] == '/')) { + size_t len = flag.length(); + if (len > 0 && (flag[0] == '-' || (this->AllowSlash && flag[0] == '/'))) { // Look for preprocessor definitions. - if (this->AllowDefine && flag[1] == 'D') { - if (flag[2] == '\0') { + if (this->AllowDefine && len > 1 && flag[1] == 'D') { + if (len <= 2) { // The next argument will have the definition. this->DoingDefine = true; } else { // Store this definition. - this->Defines.push_back(flag + 2); + this->Defines.push_back(flag.substr(2)); } return; } // Look for include directory. - if (this->AllowInclude && flag[1] == 'I') { - if (flag[2] == '\0') { + if (this->AllowInclude && len > 1 && flag[1] == 'I') { + if (len <= 2) { // The next argument will have the include directory. this->DoingInclude = true; } else { // Store this include directory. - this->Includes.push_back(flag + 2); + this->Includes.push_back(flag.substr(2)); } return; } @@ -92,8 +93,9 @@ void cmIDEOptions::HandleFlag(const char* flag) } bool cmIDEOptions::CheckFlagTable(cmIDEFlagTable const* table, - const char* flag, bool& flag_handled) + std::string const& flag, bool& flag_handled) { + const char* pf = flag.c_str() + 1; // Look for an entry in the flag table matching this flag. for (cmIDEFlagTable const* entry = table; entry->IDEName; ++entry) { bool entry_found = false; @@ -102,17 +104,17 @@ bool cmIDEOptions::CheckFlagTable(cmIDEFlagTable const* table, // the entry specifies UserRequired we must match only if a // non-empty value is given. int n = static_cast<int>(strlen(entry->commandFlag)); - if ((strncmp(flag + 1, entry->commandFlag, n) == 0 || + if ((strncmp(pf, entry->commandFlag, n) == 0 || (entry->special & cmIDEFlagTable::CaseInsensitive && - cmsysString_strncasecmp(flag + 1, entry->commandFlag, n))) && + cmsysString_strncasecmp(pf, entry->commandFlag, n))) && (!(entry->special & cmIDEFlagTable::UserRequired) || - static_cast<int>(strlen(flag + 1)) > n)) { - this->FlagMapUpdate(entry, flag + n + 1); + static_cast<int>(strlen(pf)) > n)) { + this->FlagMapUpdate(entry, std::string(pf + n)); entry_found = true; } - } else if (strcmp(flag + 1, entry->commandFlag) == 0 || + } else if (strcmp(pf, entry->commandFlag) == 0 || (entry->special & cmIDEFlagTable::CaseInsensitive && - cmsysString_strcasecmp(flag + 1, entry->commandFlag) == 0)) { + cmsysString_strcasecmp(pf, entry->commandFlag) == 0)) { if (entry->special & cmIDEFlagTable::UserFollowing) { // This flag expects a value in the following argument. this->DoingFollowing = entry; @@ -137,7 +139,7 @@ bool cmIDEOptions::CheckFlagTable(cmIDEFlagTable const* table, } void cmIDEOptions::FlagMapUpdate(cmIDEFlagTable const* entry, - const char* new_value) + std::string const& new_value) { if (entry->special & cmIDEFlagTable::UserIgnored) { // Ignore the user-specified value. @@ -157,9 +159,9 @@ void cmIDEOptions::AddDefine(const std::string& def) this->Defines.push_back(def); } -void cmIDEOptions::AddDefines(const char* defines) +void cmIDEOptions::AddDefines(std::string const& defines) { - if (defines) { + if (!defines.empty()) { // Expand the list of definitions. cmSystemTools::ExpandListArgument(defines, this->Defines); } @@ -179,9 +181,9 @@ void cmIDEOptions::AddInclude(const std::string& include) this->Includes.push_back(include); } -void cmIDEOptions::AddIncludes(const char* includes) +void cmIDEOptions::AddIncludes(std::string const& includes) { - if (includes) { + if (!includes.empty()) { // Expand the list of includes. cmSystemTools::ExpandListArgument(includes, this->Includes); } diff --git a/Source/cmIDEOptions.h b/Source/cmIDEOptions.h index 54cb524..a4e5757 100644 --- a/Source/cmIDEOptions.h +++ b/Source/cmIDEOptions.h @@ -22,12 +22,12 @@ public: // Store definitions, includes and flags. void AddDefine(const std::string& define); - void AddDefines(const char* defines); + void AddDefines(std::string const& defines); void AddDefines(const std::vector<std::string>& defines); std::vector<std::string> const& GetDefines() const; void AddInclude(const std::string& includes); - void AddIncludes(const char* includes); + void AddIncludes(std::string const& includes); void AddIncludes(const std::vector<std::string>& includes); std::vector<std::string> const& GetIncludes() const; @@ -95,11 +95,12 @@ protected: FlagTableCount = 16 }; cmIDEFlagTable const* FlagTable[FlagTableCount]; - void HandleFlag(const char* flag); - bool CheckFlagTable(cmIDEFlagTable const* table, const char* flag, + void HandleFlag(std::string const& flag); + bool CheckFlagTable(cmIDEFlagTable const* table, std::string const& flag, bool& flag_handled); - void FlagMapUpdate(cmIDEFlagTable const* entry, const char* new_value); - virtual void StoreUnknownFlag(const char* flag) = 0; + void FlagMapUpdate(cmIDEFlagTable const* entry, + std::string const& new_value); + virtual void StoreUnknownFlag(std::string const& flag) = 0; }; #endif diff --git a/Source/cmIfCommand.cxx b/Source/cmIfCommand.cxx index 4926f22..ae4041d 100644 --- a/Source/cmIfCommand.cxx +++ b/Source/cmIfCommand.cxx @@ -30,9 +30,9 @@ bool cmIfFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff, cmExecutionStatus& inStatus) { // we start by recording all the functions - if (!cmSystemTools::Strucmp(lff.Name.c_str(), "if")) { + if (lff.Name.Lower == "if") { this->ScopeDepth++; - } else if (!cmSystemTools::Strucmp(lff.Name.c_str(), "endif")) { + } else if (lff.Name.Lower == "endif") { this->ScopeDepth--; // if this is the endif for this if statement, then start executing if (!this->ScopeDepth) { @@ -48,15 +48,14 @@ bool cmIfFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff, int scopeDepth = 0; for (cmListFileFunction const& func : this->Functions) { // keep track of scope depth - if (!cmSystemTools::Strucmp(func.Name.c_str(), "if")) { + if (func.Name.Lower == "if") { scopeDepth++; } - if (!cmSystemTools::Strucmp(func.Name.c_str(), "endif")) { + if (func.Name.Lower == "endif") { scopeDepth--; } // watch for our state change - if (scopeDepth == 0 && - !cmSystemTools::Strucmp(func.Name.c_str(), "else")) { + if (scopeDepth == 0 && func.Name.Lower == "else") { if (this->ElseSeen) { cmListFileBacktrace bt = mf.GetBacktrace(func); @@ -76,8 +75,7 @@ bool cmIfFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff, if (!this->IsBlocking && mf.GetCMakeInstance()->GetTrace()) { mf.PrintCommandTrace(func); } - } else if (scopeDepth == 0 && - !cmSystemTools::Strucmp(func.Name.c_str(), "elseif")) { + } else if (scopeDepth == 0 && func.Name.Lower == "elseif") { if (this->ElseSeen) { cmListFileBacktrace bt = mf.GetBacktrace(func); mf.GetCMakeInstance()->IssueMessage( @@ -163,7 +161,7 @@ bool cmIfFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff, bool cmIfFunctionBlocker::ShouldRemove(const cmListFileFunction& lff, cmMakefile&) { - if (!cmSystemTools::Strucmp(lff.Name.c_str(), "endif")) { + if (lff.Name.Lower == "endif") { // if the endif has arguments, then make sure // they match the arguments of the matching if if (lff.Arguments.empty() || lff.Arguments == this->Args) { diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 394f976..b325b0c 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -5,6 +5,7 @@ #include "cmsys/Glob.hxx" #include <sstream> #include <stddef.h> +#include <string.h> #include <utility> #include "cmAlgorithms.h" @@ -32,16 +33,17 @@ class cmExecutionStatus; static cmInstallTargetGenerator* CreateInstallTargetGenerator( cmTarget& target, const cmInstallCommandArguments& args, bool impLib, - bool forceOpt = false) + bool forceOpt = false, bool namelink = false) { cmInstallGenerator::MessageLevel message = cmInstallGenerator::SelectMessageLevel(target.GetMakefile()); target.SetHaveInstallRule(true); + const char* component = namelink ? args.GetNamelinkComponent().c_str() + : args.GetComponent().c_str(); return new cmInstallTargetGenerator( target.GetName(), args.GetDestination().c_str(), impLib, - args.GetPermissions().c_str(), args.GetConfigurations(), - args.GetComponent().c_str(), message, args.GetExcludeFromAll(), - args.GetOptional() || forceOpt); + args.GetPermissions().c_str(), args.GetConfigurations(), component, + message, args.GetExcludeFromAll(), args.GetOptional() || forceOpt); } static cmInstallFilesGenerator* CreateInstallFilesGenerator( @@ -312,6 +314,20 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) "The NAMELINK_SKIP option may be specified only following LIBRARY."); return false; } + if (archiveArgs.HasNamelinkComponent() || + runtimeArgs.HasNamelinkComponent() || + objectArgs.HasNamelinkComponent() || + frameworkArgs.HasNamelinkComponent() || + bundleArgs.HasNamelinkComponent() || + privateHeaderArgs.HasNamelinkComponent() || + publicHeaderArgs.HasNamelinkComponent() || + resourceArgs.HasNamelinkComponent()) { + this->SetError( + "TARGETS given NAMELINK_COMPONENT option not in LIBRARY group. " + "The NAMELINK_COMPONENT option may be specified only following " + "LIBRARY."); + return false; + } if (libraryArgs.GetNamelinkOnly() && libraryArgs.GetNamelinkSkip()) { this->SetError("TARGETS given NAMELINK_ONLY and NAMELINK_SKIP. " "At most one of these two options may be specified."); @@ -334,8 +350,8 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) // Check whether this is a DLL platform. bool dll_platform = - (this->Makefile->IsOn("WIN32") || this->Makefile->IsOn("CYGWIN") || - this->Makefile->IsOn("MINGW")); + strcmp(this->Makefile->GetSafeDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX"), + "") != 0; for (std::string const& tgt : targetList.GetVector()) { @@ -360,17 +376,6 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) this->SetError(e.str()); return false; } - if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) { - std::string reason; - if (!this->Makefile->GetGlobalGenerator()->HasKnownObjectFileLocation( - &reason)) { - std::ostringstream e; - e << "TARGETS given OBJECT library \"" << tgt - << "\" which may not be installed" << reason << "."; - this->SetError(e.str()); - return false; - } - } // Store the target in the list to be installed. targets.push_back(target); } else { @@ -387,6 +392,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) // any files of the given type. bool installsArchive = false; bool installsLibrary = false; + bool installsNamelink = false; bool installsRuntime = false; bool installsObject = false; bool installsFramework = false; @@ -401,6 +407,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) cmTarget& target = *ti; cmInstallTargetGenerator* archiveGenerator = nullptr; cmInstallTargetGenerator* libraryGenerator = nullptr; + cmInstallTargetGenerator* namelinkGenerator = nullptr; cmInstallTargetGenerator* runtimeGenerator = nullptr; cmInstallTargetGenerator* objectGenerator = nullptr; cmInstallTargetGenerator* frameworkGenerator = nullptr; @@ -463,9 +470,18 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) } else { // The shared library uses the LIBRARY properties. if (!libraryArgs.GetDestination().empty()) { - libraryGenerator = - CreateInstallTargetGenerator(target, libraryArgs, false); - libraryGenerator->SetNamelinkMode(namelinkMode); + if (namelinkMode != cmInstallTargetGenerator::NamelinkModeOnly) { + libraryGenerator = + CreateInstallTargetGenerator(target, libraryArgs, false); + libraryGenerator->SetNamelinkMode( + cmInstallTargetGenerator::NamelinkModeSkip); + } + if (namelinkMode != cmInstallTargetGenerator::NamelinkModeSkip) { + namelinkGenerator = CreateInstallTargetGenerator( + target, libraryArgs, false, false, true); + namelinkGenerator->SetNamelinkMode( + cmInstallTargetGenerator::NamelinkModeOnly); + } namelinkOnly = (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly); } else { @@ -534,15 +550,23 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) case cmStateEnums::OBJECT_LIBRARY: { // Objects use OBJECT properties. if (!objectArgs.GetDestination().empty()) { + // Verify that we know where the objects are to install them. + std::string reason; + if (!this->Makefile->GetGlobalGenerator() + ->HasKnownObjectFileLocation(&reason)) { + std::ostringstream e; + e << "TARGETS given OBJECT library \"" << target.GetName() + << "\" whose objects may not be installed" << reason << "."; + this->SetError(e.str()); + return false; + } + objectGenerator = CreateInstallTargetGenerator(target, objectArgs, false); } else { - std::ostringstream e; - e << "TARGETS given no OBJECTS DESTINATION for object library " - "target \"" - << target.GetName() << "\"."; - this->SetError(e.str()); - return false; + // Installing an OBJECT library without a destination transforms + // it to an INTERFACE library. It installs no files but can be + // exported. } } break; case cmStateEnums::EXECUTABLE: { @@ -686,6 +710,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) // Keep track of whether we're installing anything in each category installsArchive = installsArchive || archiveGenerator != nullptr; installsLibrary = installsLibrary || libraryGenerator != nullptr; + installsNamelink = installsNamelink || namelinkGenerator != nullptr; installsRuntime = installsRuntime || runtimeGenerator != nullptr; installsObject = installsObject || objectGenerator != nullptr; installsFramework = installsFramework || frameworkGenerator != nullptr; @@ -698,6 +723,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) this->Makefile->AddInstallGenerator(archiveGenerator); this->Makefile->AddInstallGenerator(libraryGenerator); + this->Makefile->AddInstallGenerator(namelinkGenerator); this->Makefile->AddInstallGenerator(runtimeGenerator); this->Makefile->AddInstallGenerator(objectGenerator); this->Makefile->AddInstallGenerator(frameworkGenerator); @@ -737,6 +763,10 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) this->Makefile->GetGlobalGenerator()->AddInstallComponent( libraryArgs.GetComponent().c_str()); } + if (installsNamelink) { + this->Makefile->GetGlobalGenerator()->AddInstallComponent( + libraryArgs.GetNamelinkComponent().c_str()); + } if (installsRuntime) { this->Makefile->GetGlobalGenerator()->AddInstallComponent( runtimeArgs.GetComponent().c_str()); diff --git a/Source/cmInstallCommandArguments.cxx b/Source/cmInstallCommandArguments.cxx index 7b79ab5..2d6dc12 100644 --- a/Source/cmInstallCommandArguments.cxx +++ b/Source/cmInstallCommandArguments.cxx @@ -21,6 +21,7 @@ cmInstallCommandArguments::cmInstallCommandArguments( , ArgumentGroup() , Destination(&Parser, "DESTINATION", &ArgumentGroup) , Component(&Parser, "COMPONENT", &ArgumentGroup) + , NamelinkComponent(&Parser, "NAMELINK_COMPONENT", &ArgumentGroup) , ExcludeFromAll(&Parser, "EXCLUDE_FROM_ALL", &ArgumentGroup) , Rename(&Parser, "RENAME", &ArgumentGroup) , Permissions(&Parser, "PERMISSIONS", &ArgumentGroup) @@ -59,6 +60,14 @@ const std::string& cmInstallCommandArguments::GetComponent() const return unspecifiedComponent; } +const std::string& cmInstallCommandArguments::GetNamelinkComponent() const +{ + if (!this->NamelinkComponent.GetString().empty()) { + return this->NamelinkComponent.GetString(); + } + return this->GetComponent(); +} + const std::string& cmInstallCommandArguments::GetRename() const { if (!this->Rename.GetString().empty()) { @@ -125,6 +134,17 @@ bool cmInstallCommandArguments::GetNamelinkSkip() const return false; } +bool cmInstallCommandArguments::HasNamelinkComponent() const +{ + if (!this->NamelinkComponent.GetString().empty()) { + return true; + } + if (this->GenericArguments != nullptr) { + return this->GenericArguments->HasNamelinkComponent(); + } + return false; +} + const std::vector<std::string>& cmInstallCommandArguments::GetConfigurations() const { diff --git a/Source/cmInstallCommandArguments.h b/Source/cmInstallCommandArguments.h index a576e72..ee6e865 100644 --- a/Source/cmInstallCommandArguments.h +++ b/Source/cmInstallCommandArguments.h @@ -26,6 +26,7 @@ public: const std::string& GetDestination() const; const std::string& GetComponent() const; + const std::string& GetNamelinkComponent() const; bool GetExcludeFromAll() const; const std::string& GetRename() const; const std::string& GetPermissions() const; @@ -33,6 +34,7 @@ public: bool GetOptional() const; bool GetNamelinkOnly() const; bool GetNamelinkSkip() const; + bool HasNamelinkComponent() const; // once HandleDirectoryMode() is also switched to using // cmInstallCommandArguments then these two functions can become non-static @@ -45,6 +47,7 @@ private: cmInstallCommandArguments(); // disabled cmCAString Destination; cmCAString Component; + cmCAString NamelinkComponent; cmCAEnabler ExcludeFromAll; cmCAString Rename; cmCAStringVector Permissions; diff --git a/Source/cmInstallFilesCommand.h b/Source/cmInstallFilesCommand.h index 19f2559..a52f45e 100644 --- a/Source/cmInstallFilesCommand.h +++ b/Source/cmInstallFilesCommand.h @@ -48,7 +48,7 @@ protected: private: std::vector<std::string> FinalArgs; - bool IsFilesForm; + bool IsFilesForm = false; std::string Destination; std::vector<std::string> Files; }; diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx index a9b4908..e0afa2d 100644 --- a/Source/cmInstallTargetGenerator.cxx +++ b/Source/cmInstallTargetGenerator.cxx @@ -135,7 +135,7 @@ void cmInstallTargetGenerator::GenerateScriptForConfig( filesFrom.push_back(std::move(from1)); filesTo.push_back(std::move(to1)); std::string targetNameImportLib; - if (this->Target->GetImplibGNUtoMS(targetNameImport, + if (this->Target->GetImplibGNUtoMS(config, targetNameImport, targetNameImportLib)) { filesFrom.push_back(fromDirConfig + targetNameImportLib); filesTo.push_back(toDir + targetNameImportLib); @@ -201,7 +201,7 @@ void cmInstallTargetGenerator::GenerateScriptForConfig( filesFrom.push_back(std::move(from1)); filesTo.push_back(std::move(to1)); std::string targetNameImportLib; - if (this->Target->GetImplibGNUtoMS(targetNameImport, + if (this->Target->GetImplibGNUtoMS(config, targetNameImport, targetNameImportLib)) { filesFrom.push_back(fromDirConfig + targetNameImportLib); filesTo.push_back(toDir + targetNameImportLib); @@ -398,7 +398,7 @@ std::string cmInstallTargetGenerator::GetInstallFilename( targetNamePDB, config); if (nameType == NameImplib) { // Use the import library name. - if (!target->GetImplibGNUtoMS(targetNameImport, fname, + if (!target->GetImplibGNUtoMS(config, targetNameImport, fname, "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) { fname = targetNameImport; } @@ -419,7 +419,7 @@ std::string cmInstallTargetGenerator::GetInstallFilename( targetNameImport, targetNamePDB, config); if (nameType == NameImplib) { // Use the import library name. - if (!target->GetImplibGNUtoMS(targetNameImport, fname, + if (!target->GetImplibGNUtoMS(config, targetNameImport, fname, "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) { fname = targetNameImport; } diff --git a/Source/cmLinkLineDeviceComputer.cxx b/Source/cmLinkLineDeviceComputer.cxx index 3beeae3..557fa41 100644 --- a/Source/cmLinkLineDeviceComputer.cxx +++ b/Source/cmLinkLineDeviceComputer.cxx @@ -3,9 +3,9 @@ #include "cmLinkLineDeviceComputer.h" -#include <set> #include <sstream> +#include "cmAlgorithms.h" #include "cmComputeLinkInformation.h" #include "cmGeneratorTarget.h" #include "cmGlobalNinjaGenerator.h" @@ -32,38 +32,32 @@ std::string cmLinkLineDeviceComputer::ComputeLinkLibraries( ItemVector const& items = cli.GetItems(); std::string config = cli.GetConfig(); for (auto const& item : items) { - if (!item.Target) { - continue; - } - - bool skippable = false; - switch (item.Target->GetType()) { - case cmStateEnums::SHARED_LIBRARY: - case cmStateEnums::MODULE_LIBRARY: - case cmStateEnums::INTERFACE_LIBRARY: - skippable = true; - break; - case cmStateEnums::STATIC_LIBRARY: - // If a static library is resolving its device linking, it should - // be removed for other device linking - skippable = - item.Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS"); - break; - default: - break; - } - - if (skippable) { - continue; - } - - std::set<std::string> langs; - item.Target->GetLanguages(langs, config); - if (langs.count("CUDA") == 0) { - continue; + if (item.Target) { + bool skip = false; + switch (item.Target->GetType()) { + case cmStateEnums::MODULE_LIBRARY: + case cmStateEnums::INTERFACE_LIBRARY: + skip = true; + break; + case cmStateEnums::STATIC_LIBRARY: + skip = item.Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS"); + break; + default: + break; + } + if (skip) { + continue; + } } if (item.IsPath) { + // nvcc understands absolute paths to libraries ending in '.a' should + // be passed to nvlink. Other extensions like '.so' or '.dylib' are + // rejected by the nvcc front-end even though nvlink knows to ignore + // them. Bypass the front-end via '-Xnvlink'. + if (!cmHasLiteralSuffix(item.Value, ".a")) { + fout << "-Xnvlink "; + } fout << this->ConvertToOutputFormat( this->ConvertToLinkReference(item.Value)); } else { diff --git a/Source/cmListCommand.cxx b/Source/cmListCommand.cxx index ae4f0a8..31bc1c0 100644 --- a/Source/cmListCommand.cxx +++ b/Source/cmListCommand.cxx @@ -5,14 +5,19 @@ #include "cmsys/RegularExpression.hxx" #include <algorithm> #include <assert.h> +#include <functional> #include <iterator> +#include <set> #include <sstream> +#include <stdexcept> #include <stdio.h> #include <stdlib.h> // required for atoi #include "cmAlgorithms.h" +#include "cmGeneratorExpression.h" #include "cmMakefile.h" #include "cmPolicies.h" +#include "cmStringReplaceHelper.h" #include "cmSystemTools.h" #include "cmake.h" @@ -42,6 +47,9 @@ bool cmListCommand::InitialPass(std::vector<std::string> const& args, if (subCommand == "INSERT") { return this->HandleInsertCommand(args); } + if (subCommand == "JOIN") { + return this->HandleJoinCommand(args); + } if (subCommand == "REMOVE_AT") { return this->HandleRemoveAtCommand(args); } @@ -51,9 +59,15 @@ bool cmListCommand::InitialPass(std::vector<std::string> const& args, if (subCommand == "REMOVE_DUPLICATES") { return this->HandleRemoveDuplicatesCommand(args); } + if (subCommand == "TRANSFORM") { + return this->HandleTransformCommand(args); + } if (subCommand == "SORT") { return this->HandleSortCommand(args); } + if (subCommand == "SUBLIST") { + return this->HandleSublistCommand(args); + } if (subCommand == "REVERSE") { return this->HandleReverseCommand(args); } @@ -294,6 +308,34 @@ bool cmListCommand::HandleInsertCommand(std::vector<std::string> const& args) return true; } +bool cmListCommand::HandleJoinCommand(std::vector<std::string> const& args) +{ + if (args.size() != 4) { + std::ostringstream error; + error << "sub-command JOIN requires three arguments (" << args.size() - 1 + << " found)."; + this->SetError(error.str()); + return false; + } + + const std::string& listName = args[1]; + const std::string& glue = args[2]; + const std::string& variableName = args[3]; + + // expand the variable + std::vector<std::string> varArgsExpanded; + if (!this->GetList(varArgsExpanded, listName)) { + this->Makefile->AddDefinition(variableName, ""); + return true; + } + + std::string value = + cmJoin(cmMakeRange(varArgsExpanded.begin(), varArgsExpanded.end()), glue); + + this->Makefile->AddDefinition(variableName, value.c_str()); + return true; +} + bool cmListCommand::HandleRemoveItemCommand( std::vector<std::string> const& args) { @@ -373,6 +415,554 @@ bool cmListCommand::HandleRemoveDuplicatesCommand( return true; } +// Helpers for list(TRANSFORM <list> ...) +namespace { +using transform_type = std::function<std::string(const std::string&)>; + +class transform_error : public std::runtime_error +{ +public: + transform_error(const std::string& error) + : std::runtime_error(error) + { + } +}; + +class TransformSelector +{ +public: + virtual ~TransformSelector() {} + + std::string Tag; + + virtual bool Validate(std::size_t count = 0) = 0; + + virtual bool InSelection(const std::string&) = 0; + + virtual void Transform(std::vector<std::string>& list, + const transform_type& transform) + { + std::transform(list.begin(), list.end(), list.begin(), transform); + } + +protected: + TransformSelector(std::string&& tag) + : Tag(std::move(tag)) + { + } +}; +class TransformNoSelector : public TransformSelector +{ +public: + TransformNoSelector() + : TransformSelector("NO SELECTOR") + { + } + + bool Validate(std::size_t) override { return true; } + + bool InSelection(const std::string&) override { return true; } +}; +class TransformSelectorRegex : public TransformSelector +{ +public: + TransformSelectorRegex(const std::string& regex) + : TransformSelector("REGEX") + , Regex(regex) + { + } + + bool Validate(std::size_t) override { return this->Regex.is_valid(); } + + bool InSelection(const std::string& value) override + { + return this->Regex.find(value); + } + + cmsys::RegularExpression Regex; +}; +class TransformSelectorIndexes : public TransformSelector +{ +public: + std::vector<int> Indexes; + + bool InSelection(const std::string&) override { return true; } + + void Transform(std::vector<std::string>& list, + const transform_type& transform) override + { + this->Validate(list.size()); + + for (auto index : this->Indexes) { + list[index] = transform(list[index]); + } + } + +protected: + TransformSelectorIndexes(std::string&& tag) + : TransformSelector(std::move(tag)) + { + } + TransformSelectorIndexes(std::string&& tag, std::vector<int>&& indexes) + : TransformSelector(std::move(tag)) + , Indexes(indexes) + { + } + + int NormalizeIndex(int index, std::size_t count) + { + if (index < 0) { + index = static_cast<int>(count) + index; + } + if (index < 0 || count <= static_cast<std::size_t>(index)) { + std::ostringstream str; + str << "sub-command TRANSFORM, selector " << this->Tag + << ", index: " << index << " out of range (-" << count << ", " + << count - 1 << ")."; + throw transform_error(str.str()); + } + return index; + } +}; +class TransformSelectorAt : public TransformSelectorIndexes +{ +public: + TransformSelectorAt(std::vector<int>&& indexes) + : TransformSelectorIndexes("AT", std::move(indexes)) + { + } + + bool Validate(std::size_t count) override + { + decltype(Indexes) indexes; + + for (auto index : Indexes) { + indexes.push_back(this->NormalizeIndex(index, count)); + } + this->Indexes = std::move(indexes); + + return true; + } +}; +class TransformSelectorFor : public TransformSelectorIndexes +{ +public: + TransformSelectorFor(int start, int stop, int step) + : TransformSelectorIndexes("FOR") + , Start(start) + , Stop(stop) + , Step(step) + { + } + + bool Validate(std::size_t count) override + { + this->Start = this->NormalizeIndex(this->Start, count); + this->Stop = this->NormalizeIndex(this->Stop, count); + + // compute indexes + auto size = (this->Stop - this->Start + 1) / this->Step; + if ((this->Stop - this->Start + 1) % this->Step != 0) { + size += 1; + } + + this->Indexes.resize(size); + auto start = this->Start, step = this->Step; + std::generate(this->Indexes.begin(), this->Indexes.end(), + [&start, step]() -> int { + auto r = start; + start += step; + return r; + }); + + return true; + } + +private: + int Start, Stop, Step; +}; + +class TransformAction +{ +public: + virtual ~TransformAction() {} + + virtual std::string Transform(const std::string& input) = 0; +}; +class TransformReplace : public TransformAction +{ +public: + TransformReplace(const std::vector<std::string>& arguments, + cmMakefile* makefile) + : ReplaceHelper(arguments[0], arguments[1], makefile) + { + makefile->ClearMatches(); + + if (!this->ReplaceHelper.IsRegularExpressionValid()) { + std::ostringstream error; + error + << "sub-command TRANSFORM, action REPLACE: Failed to compile regex \"" + << arguments[0] << "\"."; + throw transform_error(error.str()); + } + if (!this->ReplaceHelper.IsReplaceExpressionValid()) { + std::ostringstream error; + error << "sub-command TRANSFORM, action REPLACE: " + << this->ReplaceHelper.GetError() << "."; + throw transform_error(error.str()); + } + } + + std::string Transform(const std::string& input) override + { + // Scan through the input for all matches. + std::string output; + + if (!this->ReplaceHelper.Replace(input, output)) { + std::ostringstream error; + error << "sub-command TRANSFORM, action REPLACE: " + << this->ReplaceHelper.GetError() << "."; + throw transform_error(error.str()); + } + + return output; + } + +private: + cmStringReplaceHelper ReplaceHelper; +}; +} + +bool cmListCommand::HandleTransformCommand( + std::vector<std::string> const& args) +{ + if (args.size() < 3) { + this->SetError( + "sub-command TRANSFORM requires an action to be specified."); + return false; + } + + // Structure collecting all elements of the command + struct Command + { + Command(const std::string& listName) + : ListName(listName) + , OutputName(listName) + { + } + + std::string Name; + std::string ListName; + std::vector<std::string> Arguments; + std::unique_ptr<TransformAction> Action; + std::unique_ptr<TransformSelector> Selector; + std::string OutputName; + } command(args[1]); + + // Descriptor of action + // Arity: number of arguments required for the action + // Transform: lambda function implementing the action + struct ActionDescriptor + { + ActionDescriptor(const std::string& name) + : Name(name) + { + } + ActionDescriptor(const std::string& name, int arity, + const transform_type& transform) + : Name(name) + , Arity(arity) + , Transform(transform) + { + } + + operator const std::string&() const { return Name; } + + std::string Name; + int Arity = 0; + transform_type Transform; + }; + + // Build a set of supported actions. + std::set<ActionDescriptor, + std::function<bool(const std::string&, const std::string&)>> + descriptors( + [](const std::string& x, const std::string& y) { return x < y; }); + descriptors = { { "APPEND", 1, + [&command](const std::string& s) -> std::string { + if (command.Selector->InSelection(s)) { + return s + command.Arguments[0]; + } + + return s; + } }, + { "PREPEND", 1, + [&command](const std::string& s) -> std::string { + if (command.Selector->InSelection(s)) { + return command.Arguments[0] + s; + } + + return s; + } }, + { "TOUPPER", 0, + [&command](const std::string& s) -> std::string { + if (command.Selector->InSelection(s)) { + return cmSystemTools::UpperCase(s); + } + + return s; + } }, + { "TOLOWER", 0, + [&command](const std::string& s) -> std::string { + if (command.Selector->InSelection(s)) { + return cmSystemTools::LowerCase(s); + } + + return s; + } }, + { "STRIP", 0, + [&command](const std::string& s) -> std::string { + if (command.Selector->InSelection(s)) { + return cmSystemTools::TrimWhitespace(s); + } + + return s; + } }, + { "GENEX_STRIP", 0, + [&command](const std::string& s) -> std::string { + if (command.Selector->InSelection(s)) { + return cmGeneratorExpression::Preprocess( + s, + cmGeneratorExpression::StripAllGeneratorExpressions); + } + + return s; + } }, + { "REPLACE", 2, + [&command](const std::string& s) -> std::string { + if (command.Selector->InSelection(s)) { + return command.Action->Transform(s); + } + + return s; + } } }; + + using size_type = std::vector<std::string>::size_type; + size_type index = 2; + + // Parse all possible function parameters + auto descriptor = descriptors.find(args[index]); + + if (descriptor == descriptors.end()) { + std::ostringstream error; + error << " sub-command TRANSFORM, " << args[index] << " invalid action."; + this->SetError(error.str()); + return false; + } + + // Action arguments + index += 1; + if (args.size() < index + descriptor->Arity) { + std::ostringstream error; + error << "sub-command TRANSFORM, action " << descriptor->Name + << " expects " << descriptor->Arity << " argument(s)."; + this->SetError(error.str()); + return false; + } + + command.Name = descriptor->Name; + index += descriptor->Arity; + if (descriptor->Arity > 0) { + command.Arguments = + std::vector<std::string>(args.begin() + 3, args.begin() + index); + } + + if (command.Name == "REPLACE") { + try { + command.Action = + cm::make_unique<TransformReplace>(command.Arguments, this->Makefile); + } catch (const transform_error& e) { + this->SetError(e.what()); + return false; + } + } + + const std::string REGEX{ "REGEX" }, AT{ "AT" }, FOR{ "FOR" }, + OUTPUT_VARIABLE{ "OUTPUT_VARIABLE" }; + + // handle optional arguments + while (args.size() > index) { + if ((args[index] == REGEX || args[index] == AT || args[index] == FOR) && + command.Selector) { + std::ostringstream error; + error << "sub-command TRANSFORM, selector already specified (" + << command.Selector->Tag << ")."; + this->SetError(error.str()); + return false; + } + + // REGEX selector + if (args[index] == REGEX) { + if (args.size() == ++index) { + this->SetError("sub-command TRANSFORM, selector REGEX expects " + "'regular expression' argument."); + return false; + } + + command.Selector = cm::make_unique<TransformSelectorRegex>(args[index]); + if (!command.Selector->Validate()) { + std::ostringstream error; + error << "sub-command TRANSFORM, selector REGEX failed to compile " + "regex \""; + error << args[index] << "\"."; + this->SetError(error.str()); + return false; + } + + index += 1; + continue; + } + + // AT selector + if (args[index] == AT) { + // get all specified indexes + std::vector<int> indexes; + while (args.size() > ++index) { + std::size_t pos; + int value; + + try { + value = std::stoi(args[index], &pos); + if (pos != args[index].length()) { + // this is not a number, stop processing + break; + } + indexes.push_back(value); + } catch (const std::invalid_argument&) { + // this is not a number, stop processing + break; + } + } + + if (indexes.empty()) { + this->SetError( + "sub-command TRANSFORM, selector AT expects at least one " + "numeric value."); + return false; + } + + command.Selector = + cm::make_unique<TransformSelectorAt>(std::move(indexes)); + + continue; + } + + // FOR selector + if (args[index] == FOR) { + if (args.size() <= ++index + 1) { + this->SetError("sub-command TRANSFORM, selector FOR expects, at least," + " two arguments."); + return false; + } + + int start = 0, stop = 0, step = 1; + bool valid = true; + try { + std::size_t pos; + + start = std::stoi(args[index], &pos); + if (pos != args[index].length()) { + // this is not a number + valid = false; + } else { + stop = std::stoi(args[++index], &pos); + if (pos != args[index].length()) { + // this is not a number + valid = false; + } + } + } catch (const std::invalid_argument&) { + // this is not numbers + valid = false; + } + if (!valid) { + this->SetError("sub-command TRANSFORM, selector FOR expects, " + "at least, two numeric values."); + return false; + } + // try to read a third numeric value for step + if (args.size() > ++index) { + try { + std::size_t pos; + + step = std::stoi(args[index], &pos); + if (pos != args[index].length()) { + // this is not a number + step = 1; + } else { + index += 1; + } + } catch (const std::invalid_argument&) { + // this is not number, ignore exception + } + } + + if (step < 0) { + this->SetError("sub-command TRANSFORM, selector FOR expects " + "non negative numeric value for <step>."); + } + + command.Selector = + cm::make_unique<TransformSelectorFor>(start, stop, step); + + continue; + } + + // output variable + if (args[index] == OUTPUT_VARIABLE) { + if (args.size() == ++index) { + this->SetError("sub-command TRANSFORM, OUTPUT_VARIABLE " + "expects variable name argument."); + return false; + } + + command.OutputName = args[index++]; + continue; + } + + std::ostringstream error; + error << "sub-command TRANSFORM, '" + << cmJoin(cmMakeRange(args).advance(index), " ") + << "': unexpected argument(s)."; + this->SetError(error.str()); + return false; + } + + // expand the list variable + std::vector<std::string> varArgsExpanded; + if (!this->GetList(varArgsExpanded, command.ListName)) { + this->Makefile->AddDefinition(command.OutputName, ""); + return true; + } + + if (!command.Selector) { + // no selector specified, apply transformation to all elements + command.Selector = cm::make_unique<TransformNoSelector>(); + } + + try { + command.Selector->Transform(varArgsExpanded, descriptor->Transform); + } catch (const transform_error& e) { + this->SetError(e.what()); + return false; + } + + this->Makefile->AddDefinition(command.OutputName, + cmJoin(varArgsExpanded, ";").c_str()); + + return true; +} + bool cmListCommand::HandleSortCommand(std::vector<std::string> const& args) { assert(args.size() >= 2); @@ -396,6 +986,55 @@ bool cmListCommand::HandleSortCommand(std::vector<std::string> const& args) return true; } +bool cmListCommand::HandleSublistCommand(std::vector<std::string> const& args) +{ + if (args.size() != 5) { + std::ostringstream error; + error << "sub-command SUBLIST requires four arguments (" << args.size() - 1 + << " found)."; + this->SetError(error.str()); + return false; + } + + const std::string& listName = args[1]; + const std::string& variableName = args[args.size() - 1]; + + // expand the variable + std::vector<std::string> varArgsExpanded; + if (!this->GetList(varArgsExpanded, listName) || varArgsExpanded.empty()) { + this->Makefile->AddDefinition(variableName, ""); + return true; + } + + const int start = atoi(args[2].c_str()); + const int length = atoi(args[3].c_str()); + + using size_type = decltype(varArgsExpanded)::size_type; + + if (start < 0 || size_type(start) >= varArgsExpanded.size()) { + std::ostringstream error; + error << "begin index: " << start << " is out of range 0 - " + << varArgsExpanded.size() - 1; + this->SetError(error.str()); + return false; + } + if (length < -1) { + std::ostringstream error; + error << "length: " << length << " should be -1 or greater"; + this->SetError(error.str()); + return false; + } + + const size_type end = + (length == -1 || size_type(start + length) > varArgsExpanded.size()) + ? varArgsExpanded.size() + : size_type(start + length); + std::vector<std::string> sublist(varArgsExpanded.begin() + start, + varArgsExpanded.begin() + end); + this->Makefile->AddDefinition(variableName, cmJoin(sublist, ";").c_str()); + return true; +} + bool cmListCommand::HandleRemoveAtCommand(std::vector<std::string> const& args) { if (args.size() < 3) { diff --git a/Source/cmListCommand.h b/Source/cmListCommand.h index 2965399..76a9856 100644 --- a/Source/cmListCommand.h +++ b/Source/cmListCommand.h @@ -37,10 +37,13 @@ protected: bool HandleAppendCommand(std::vector<std::string> const& args); bool HandleFindCommand(std::vector<std::string> const& args); bool HandleInsertCommand(std::vector<std::string> const& args); + bool HandleJoinCommand(std::vector<std::string> const& args); bool HandleRemoveAtCommand(std::vector<std::string> const& args); bool HandleRemoveItemCommand(std::vector<std::string> const& args); bool HandleRemoveDuplicatesCommand(std::vector<std::string> const& args); + bool HandleTransformCommand(std::vector<std::string> const& args); bool HandleSortCommand(std::vector<std::string> const& args); + bool HandleSublistCommand(std::vector<std::string> const& args); bool HandleReverseCommand(std::vector<std::string> const& args); bool HandleFilterCommand(std::vector<std::string> const& args); bool FilterRegex(std::vector<std::string> const& args, bool includeMatches, diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx index 64e634f..b468257 100644 --- a/Source/cmListFileCache.cxx +++ b/Source/cmListFileCache.cxx @@ -13,6 +13,14 @@ #include <assert.h> #include <sstream> +cmCommandContext::cmCommandName& cmCommandContext::cmCommandName::operator=( + std::string const& name) +{ + this->Original = name; + this->Lower = cmSystemTools::LowerCase(name); + return *this; +} + struct cmListFileParser { cmListFileParser(cmListFile* lf, cmListFileBacktrace const& lfbt, diff --git a/Source/cmListFileCache.h b/Source/cmListFileCache.h index 1f9e374..70f7166 100644 --- a/Source/cmListFileCache.h +++ b/Source/cmListFileCache.h @@ -23,11 +23,22 @@ class cmMessenger; struct cmCommandContext { - std::string Name; + struct cmCommandName + { + std::string Lower; + std::string Original; + cmCommandName() {} + cmCommandName(std::string const& name) { *this = name; } + cmCommandName& operator=(std::string const& name); + } Name; long Line; cmCommandContext() - : Name() - , Line(0) + : Line(0) + { + } + cmCommandContext(const char* name, int line) + : Name(name) + , Line(line) { } }; @@ -81,7 +92,7 @@ public: cmListFileContext lfc; lfc.FilePath = fileName; lfc.Line = lfcc.Line; - lfc.Name = lfcc.Name; + lfc.Name = lfcc.Name.Original; return lfc; } }; diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index e942ff4..c5370e4 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -202,6 +202,22 @@ void cmLocalGenerator::ComputeObjectMaxPath() this->ObjectMaxPathViolations.clear(); } +void cmLocalGenerator::MoveSystemIncludesToEnd( + std::vector<std::string>& includeDirs, const std::string& config, + const std::string& lang, const cmGeneratorTarget* target) const +{ + if (!target) { + return; + } + + std::stable_sort( + includeDirs.begin(), includeDirs.end(), + [&target, &config, &lang](std::string const& a, std::string const& b) { + return !target->IsSystemIncludeDirectory(a, config, lang) && + target->IsSystemIncludeDirectory(b, config, lang); + }); +} + void cmLocalGenerator::TraceDependencies() { std::vector<std::string> configs; @@ -651,7 +667,7 @@ std::string cmLocalGenerator::ConvertToIncludeReference( } std::string cmLocalGenerator::GetIncludeFlags( - const std::vector<std::string>& includes, cmGeneratorTarget* target, + const std::vector<std::string>& includeDirs, cmGeneratorTarget* target, const std::string& lang, bool forceFullPaths, bool forResponseFile, const std::string& config) { @@ -659,6 +675,9 @@ std::string cmLocalGenerator::GetIncludeFlags( return ""; } + std::vector<std::string> includes = includeDirs; + this->MoveSystemIncludesToEnd(includes, config, lang, target); + OutputFormat shellFormat = forResponseFile ? RESPONSE : SHELL; std::ostringstream includeFlags; @@ -924,6 +943,8 @@ void cmLocalGenerator::GetIncludeDirectories(std::vector<std::string>& dirs, } } + this->MoveSystemIncludesToEnd(dirs, config, lang, target); + // Add standard include directories for this language. // We do not filter out implicit directories here. std::string const standardIncludesVar = @@ -1561,6 +1582,7 @@ void cmLocalGenerator::AddCompilerRequirementFlag( static std::map<std::string, std::vector<std::string>> langStdMap; if (langStdMap.empty()) { // Maintain sorted order, most recent first. + langStdMap["CXX"].push_back("20"); langStdMap["CXX"].push_back("17"); langStdMap["CXX"].push_back("14"); langStdMap["CXX"].push_back("11"); diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index 533ac56..9ba62cc 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -411,6 +411,10 @@ private: int targetType); void ComputeObjectMaxPath(); + void MoveSystemIncludesToEnd(std::vector<std::string>& includeDirs, + const std::string& config, + const std::string& lang, + cmGeneratorTarget const* target) const; }; #if defined(CMAKE_BUILD_WITH_CMAKE) diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index 8c889fc..b5ae939 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -10,6 +10,7 @@ #include <stdio.h> #include <utility> +#include "cmCryptoHash.h" #include "cmCustomCommand.h" #include "cmCustomCommandGenerator.h" #include "cmGeneratedFileStream.h" @@ -24,6 +25,7 @@ #include "cmStateTypes.h" #include "cmSystemTools.h" #include "cmake.h" +#include "cmsys/FStream.hxx" cmLocalNinjaGenerator::cmLocalNinjaGenerator(cmGlobalGenerator* gg, cmMakefile* mf) @@ -195,6 +197,16 @@ void cmLocalNinjaGenerator::WriteNinjaRequiredVersion(std::ostream& os) this->GetGlobalNinjaGenerator()->RequiredNinjaVersionForConsolePool(); } + // The Ninja generator writes rules which require support for restat + // when rebuilding build.ninja manifest (>= 1.8) + if (this->GetGlobalNinjaGenerator()->SupportsManifestRestat() && + this->GetCMakeInstance()->DoWriteGlobVerifyTarget() && + !this->GetGlobalNinjaGenerator()->GlobalSettingIsOn( + "CMAKE_SUPPRESS_REGENERATION")) { + requiredVersion = + this->GetGlobalNinjaGenerator()->RequiredNinjaVersionForManifestRestat(); + } + cmGlobalNinjaGenerator::WriteComment( os, "Minimal version of Ninja required by this file"); os << "ninja_required_version = " << requiredVersion << std::endl @@ -239,8 +251,7 @@ void cmLocalNinjaGenerator::WriteNinjaFilesInclusion(std::ostream& os) cmGlobalNinjaGenerator* ng = this->GetGlobalNinjaGenerator(); std::string const ninjaRulesFile = ng->NinjaOutputPath(cmGlobalNinjaGenerator::NINJA_RULES_FILE); - std::string const rulesFilePath = - ng->EncodeIdent(ng->EncodePath(ninjaRulesFile), os); + std::string const rulesFilePath = ng->EncodePath(ninjaRulesFile); cmGlobalNinjaGenerator::WriteInclude(os, rulesFilePath, "Include rules file."); os << "\n"; @@ -286,8 +297,51 @@ void cmLocalNinjaGenerator::AppendCustomCommandDeps( } } +std::string cmLocalNinjaGenerator::WriteCommandScript( + std::vector<std::string> const& cmdLines, std::string const& customStep, + cmGeneratorTarget const* target) const +{ + std::string scriptPath; + if (target) { + scriptPath = target->GetSupportDirectory(); + } else { + scriptPath = this->GetCurrentBinaryDirectory(); + scriptPath += cmake::GetCMakeFilesDirectory(); + } + cmSystemTools::MakeDirectory(scriptPath); + scriptPath += '/'; + scriptPath += customStep; +#ifdef _WIN32 + scriptPath += ".bat"; +#else + scriptPath += ".sh"; +#endif + + cmsys::ofstream script(scriptPath.c_str()); + +#ifndef _WIN32 + script << "set -e\n\n"; +#endif + + for (auto const& i : cmdLines) { + std::string cmd = i; + // The command line was built assuming it would be written to + // the build.ninja file, so it uses '$$' for '$'. Remove this + // for the raw shell script. + cmSystemTools::ReplaceString(cmd, "$$", "$"); +#ifdef _WIN32 + script << cmd << " || exit /b" << '\n'; +#else + script << cmd << '\n'; +#endif + } + + return scriptPath; +} + std::string cmLocalNinjaGenerator::BuildCommandLine( - const std::vector<std::string>& cmdLines) + std::vector<std::string> const& cmdLines, std::string const& customStep, + cmGeneratorTarget const* target) const { // If we have no commands but we need to build a command anyway, use noop. // This happens when building a POST_BUILD value for link targets that @@ -296,6 +350,35 @@ std::string cmLocalNinjaGenerator::BuildCommandLine( return cmGlobalNinjaGenerator::SHELL_NOOP; } + // If this is a custom step then we will have no '$VAR' ninja placeholders. + // This means we can deal with long command sequences by writing to a script. + // Do this if the command lines are on the scale of the OS limit. + if (!customStep.empty()) { + size_t cmdLinesTotal = 0; + for (std::string const& cmd : cmdLines) { + cmdLinesTotal += cmd.length() + 6; + } + if (cmdLinesTotal > cmSystemTools::CalculateCommandLineLengthLimit() / 2) { + std::string const scriptPath = + this->WriteCommandScript(cmdLines, customStep, target); + std::string cmd +#ifndef _WIN32 + = "/bin/sh " +#endif + ; + cmd += this->ConvertToOutputFormat( + this->GetGlobalNinjaGenerator()->ConvertToNinjaPath(scriptPath), + cmOutputConverter::SHELL); + + // Add an unused argument based on script content so that Ninja + // knows when the command lines change. + cmd += " "; + cmCryptoHash hash(cmCryptoHash::AlgoSHA256); + cmd += hash.HashFile(scriptPath).substr(0, 16); + return cmd; + } + } + std::ostringstream cmd; for (std::vector<std::string>::const_iterator li = cmdLines.begin(); li != cmdLines.end(); ++li) @@ -406,10 +489,16 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement( "Phony custom command for " + ninjaOutputs[0], ninjaOutputs, ninjaDeps, cmNinjaDeps(), orderOnlyDeps, cmNinjaVars()); } else { + std::string customStep = cmSystemTools::GetFilenameName(ninjaOutputs[0]); + // Hash full path to make unique. + customStep += '-'; + cmCryptoHash hash(cmCryptoHash::AlgoSHA256); + customStep += hash.HashString(ninjaOutputs[0]).substr(0, 7); + this->GetGlobalNinjaGenerator()->WriteCustomCommandBuild( - this->BuildCommandLine(cmdLines), this->ConstructComment(ccg), - "Custom command for " + ninjaOutputs[0], cc->GetDepfile(), - cc->GetUsesTerminal(), + this->BuildCommandLine(cmdLines, customStep), + this->ConstructComment(ccg), "Custom command for " + ninjaOutputs[0], + cc->GetDepfile(), cc->GetUsesTerminal(), /*restat*/ !symbolic || !byproducts.empty(), ninjaOutputs, ninjaDeps, orderOnlyDeps); } diff --git a/Source/cmLocalNinjaGenerator.h b/Source/cmLocalNinjaGenerator.h index 95d8a61..f772fb0 100644 --- a/Source/cmLocalNinjaGenerator.h +++ b/Source/cmLocalNinjaGenerator.h @@ -59,7 +59,10 @@ public: return this->HomeRelativeOutputPath; } - std::string BuildCommandLine(const std::vector<std::string>& cmdLines); + std::string BuildCommandLine( + std::vector<std::string> const& cmdLines, + std::string const& customStep = std::string(), + cmGeneratorTarget const* target = nullptr) const; void AppendTargetOutputs(cmGeneratorTarget* target, cmNinjaDeps& outputs); void AppendTargetDepends( @@ -98,6 +101,10 @@ private: std::string MakeCustomLauncher(cmCustomCommandGenerator const& ccg); + std::string WriteCommandScript(std::vector<std::string> const& cmdLines, + std::string const& customStep, + cmGeneratorTarget const* target) const; + std::string HomeRelativeOutputPath; typedef std::map<cmCustomCommand const*, std::set<cmGeneratorTarget*>> diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index ddd8cc4..cf2a7b9 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -760,8 +760,17 @@ void cmLocalUnixMakefileGenerator3::WriteSpecialTargetsBottom( // Write special "cmake_check_build_system" target to run cmake with // the --check-build-system flag. - { + if (!this->GlobalGenerator->GlobalSettingIsOn( + "CMAKE_SUPPRESS_REGENERATION")) { // Build command to run CMake to check if anything needs regenerating. + std::vector<std::string> commands; + cmake* cm = this->GlobalGenerator->GetCMakeInstance(); + if (cm->DoWriteGlobVerifyTarget()) { + std::string rescanRule = "$(CMAKE_COMMAND) -P "; + rescanRule += this->ConvertToOutputFormat(cm->GetGlobVerifyScript(), + cmOutputConverter::SHELL); + commands.push_back(rescanRule); + } std::string cmakefileName = cmake::GetCMakeFilesDirectoryPostSlash(); cmakefileName += "Makefile.cmake"; std::string runRule = @@ -772,7 +781,6 @@ void cmLocalUnixMakefileGenerator3::WriteSpecialTargetsBottom( runRule += " 0"; std::vector<std::string> no_depends; - std::vector<std::string> commands; commands.push_back(std::move(runRule)); if (!this->IsRootMakefile()) { this->CreateCDCommand(commands, this->GetBinaryDirectory(), @@ -1580,7 +1588,11 @@ void cmLocalUnixMakefileGenerator3::WriteLocalAllRules( std::string recursiveTarget = this->GetCurrentBinaryDirectory(); recursiveTarget += "/all"; - depends.push_back("cmake_check_build_system"); + bool regenerate = + !this->GlobalGenerator->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION"); + if (regenerate) { + depends.push_back("cmake_check_build_system"); + } std::string progressDir = this->GetBinaryDirectory(); progressDir += cmake::GetCMakeFilesDirectory(); @@ -1643,7 +1655,7 @@ void cmLocalUnixMakefileGenerator3::WriteLocalAllRules( if (!noall || cmSystemTools::IsOff(noall)) { // Drive the build before installing. depends.push_back("all"); - } else { + } else if (regenerate) { // At least make sure the build system is up to date. depends.push_back("cmake_check_build_system"); } @@ -1657,24 +1669,33 @@ void cmLocalUnixMakefileGenerator3::WriteLocalAllRules( this->WriteMakeRule(ruleFileStream, "Prepare targets for installation.", "preinstall/fast", depends, commands, true); - // write the depend rule, really a recompute depends rule - depends.clear(); - commands.clear(); - std::string cmakefileName = cmake::GetCMakeFilesDirectoryPostSlash(); - cmakefileName += "Makefile.cmake"; - { - std::string runRule = - "$(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)"; - runRule += " --check-build-system "; - runRule += - this->ConvertToOutputFormat(cmakefileName, cmOutputConverter::SHELL); - runRule += " 1"; - commands.push_back(std::move(runRule)); + if (regenerate) { + // write the depend rule, really a recompute depends rule + depends.clear(); + commands.clear(); + cmake* cm = this->GlobalGenerator->GetCMakeInstance(); + if (cm->DoWriteGlobVerifyTarget()) { + std::string rescanRule = "$(CMAKE_COMMAND) -P "; + rescanRule += this->ConvertToOutputFormat(cm->GetGlobVerifyScript(), + cmOutputConverter::SHELL); + commands.push_back(rescanRule); + } + std::string cmakefileName = cmake::GetCMakeFilesDirectoryPostSlash(); + cmakefileName += "Makefile.cmake"; + { + std::string runRule = + "$(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)"; + runRule += " --check-build-system "; + runRule += + this->ConvertToOutputFormat(cmakefileName, cmOutputConverter::SHELL); + runRule += " 1"; + commands.push_back(std::move(runRule)); + } + this->CreateCDCommand(commands, this->GetBinaryDirectory(), + this->GetCurrentBinaryDirectory()); + this->WriteMakeRule(ruleFileStream, "clear depends", "depend", depends, + commands, true); } - this->CreateCDCommand(commands, this->GetBinaryDirectory(), - this->GetCurrentBinaryDirectory()); - this->WriteMakeRule(ruleFileStream, "clear depends", "depend", depends, - commands, true); } void cmLocalUnixMakefileGenerator3::ClearDependencies(cmMakefile* mf, diff --git a/Source/cmLocalVisualStudio10Generator.cxx b/Source/cmLocalVisualStudio10Generator.cxx index 2803d4a..5b6e781 100644 --- a/Source/cmLocalVisualStudio10Generator.cxx +++ b/Source/cmLocalVisualStudio10Generator.cxx @@ -62,21 +62,47 @@ cmLocalVisualStudio10Generator::~cmLocalVisualStudio10Generator() { } +void cmLocalVisualStudio10Generator::GenerateTargetsDepthFirst( + cmGeneratorTarget* target, std::vector<cmGeneratorTarget*>& remaining) +{ + if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + return; + } + // Find this target in the list of remaining targets. + auto it = std::find(remaining.begin(), remaining.end(), target); + if (it == remaining.end()) { + // This target was already handled. + return; + } + // Remove this target from the list of remaining targets because + // we are handling it now. + *it = nullptr; + auto& deps = this->GlobalGenerator->GetTargetDirectDepends(target); + for (auto& d : deps) { + // FIXME: Revise CreateSingleVCProj so we do not have to drop `const` here. + auto dependee = const_cast<cmGeneratorTarget*>(&*d); + GenerateTargetsDepthFirst(dependee, remaining); + // Take the union of visited source files of custom commands + auto visited = GetSourcesVisited(dependee); + GetSourcesVisited(target).insert(visited.begin(), visited.end()); + } + if (static_cast<cmGlobalVisualStudioGenerator*>(this->GlobalGenerator) + ->TargetIsFortranOnly(target)) { + this->CreateSingleVCProj(target->GetName(), target); + } else { + cmVisualStudio10TargetGenerator tg( + target, static_cast<cmGlobalVisualStudio10Generator*>( + this->GetGlobalGenerator())); + tg.Generate(); + } +} + void cmLocalVisualStudio10Generator::Generate() { - const std::vector<cmGeneratorTarget*>& tgts = this->GetGeneratorTargets(); - for (cmGeneratorTarget* l : tgts) { - if (l->GetType() == cmStateEnums::INTERFACE_LIBRARY) { - continue; - } - if (static_cast<cmGlobalVisualStudioGenerator*>(this->GlobalGenerator) - ->TargetIsFortranOnly(l)) { - this->CreateSingleVCProj(l->GetName(), l); - } else { - cmVisualStudio10TargetGenerator tg( - l, static_cast<cmGlobalVisualStudio10Generator*>( - this->GetGlobalGenerator())); - tg.Generate(); + std::vector<cmGeneratorTarget*> remaining = this->GetGeneratorTargets(); + for (auto& t : remaining) { + if (t) { + GenerateTargetsDepthFirst(t, remaining); } } this->WriteStampFiles(); diff --git a/Source/cmLocalVisualStudio10Generator.h b/Source/cmLocalVisualStudio10Generator.h index bcdc307..a4150b9 100644 --- a/Source/cmLocalVisualStudio10Generator.h +++ b/Source/cmLocalVisualStudio10Generator.h @@ -33,10 +33,19 @@ public: void ReadAndStoreExternalGUID(const std::string& name, const char* path) override; + std::set<cmSourceFile const*>& GetSourcesVisited(cmGeneratorTarget* target) + { + return SourcesVisited[target]; + }; + protected: const char* ReportErrorLabel() const override; bool CustomCommandUseLocal() const override { return true; } private: + void GenerateTargetsDepthFirst(cmGeneratorTarget* target, + std::vector<cmGeneratorTarget*>& remaining); + + std::map<cmGeneratorTarget*, std::set<cmSourceFile const*>> SourcesVisited; }; #endif diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index 13503ad..3460289 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -160,10 +160,21 @@ void cmLocalVisualStudio7Generator::WriteStampFiles() depName += ".depend"; cmsys::ofstream depFile(depName.c_str()); depFile << "# CMake generation dependency list for this directory.\n"; - std::vector<std::string> const& listFiles = this->Makefile->GetListFiles(); - for (std::vector<std::string>::const_iterator lf = listFiles.begin(); - lf != listFiles.end(); ++lf) { - depFile << *lf << std::endl; + + std::vector<std::string> listFiles(this->Makefile->GetListFiles()); + cmake* cm = this->GlobalGenerator->GetCMakeInstance(); + if (cm->DoWriteGlobVerifyTarget()) { + listFiles.push_back(cm->GetGlobVerifyStamp()); + } + + // Sort the list of input files and remove duplicates. + std::sort(listFiles.begin(), listFiles.end(), std::less<std::string>()); + std::vector<std::string>::iterator new_end = + std::unique(listFiles.begin(), listFiles.end()); + listFiles.erase(new_end, listFiles.end()); + + for (const std::string& lf : listFiles) { + depFile << lf << "\n"; } } @@ -210,7 +221,8 @@ void cmLocalVisualStudio7Generator::CreateSingleVCProj( cmSourceFile* cmLocalVisualStudio7Generator::CreateVCProjBuildRule() { - if (this->Makefile->IsOn("CMAKE_SUPPRESS_REGENERATION")) { + if (this->GlobalGenerator->GlobalSettingIsOn( + "CMAKE_SUPPRESS_REGENERATION")) { return nullptr; } @@ -227,6 +239,18 @@ cmSourceFile* cmLocalVisualStudio7Generator::CreateVCProjBuildRule() return nullptr; } + std::vector<std::string> listFiles = this->Makefile->GetListFiles(); + cmake* cm = this->GlobalGenerator->GetCMakeInstance(); + if (cm->DoWriteGlobVerifyTarget()) { + listFiles.push_back(cm->GetGlobVerifyStamp()); + } + + // Sort the list of input files and remove duplicates. + std::sort(listFiles.begin(), listFiles.end(), std::less<std::string>()); + std::vector<std::string>::iterator new_end = + std::unique(listFiles.begin(), listFiles.end()); + listFiles.erase(new_end, listFiles.end()); + std::string stampName = this->GetCurrentBinaryDirectory(); stampName += "/"; stampName += cmake::GetCMakeFilesDirectoryPostSlash(); @@ -244,17 +268,14 @@ cmSourceFile* cmLocalVisualStudio7Generator::CreateVCProjBuildRule() commandLine.push_back(args); commandLine.push_back("--check-stamp-file"); commandLine.push_back(stampName); - - std::vector<std::string> const& listFiles = this->Makefile->GetListFiles(); - cmCustomCommandLines commandLines; commandLines.push_back(commandLine); const char* no_working_directory = 0; std::string fullpathStampName = cmSystemTools::CollapseFullPath(stampName.c_str()); this->Makefile->AddCustomCommandToOutput( - fullpathStampName.c_str(), listFiles, makefileIn.c_str(), commandLines, - comment.c_str(), no_working_directory, true, false); + fullpathStampName, listFiles, makefileIn, commandLines, comment.c_str(), + no_working_directory, true, false); if (cmSourceFile* file = this->Makefile->GetSource(makefileIn.c_str())) { // Finalize the source file path now since we're adding this after // the generator validated all project-named sources. @@ -791,11 +812,9 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( << "\\$(ConfigurationName)\"\n"; } targetOptions.OutputAdditionalIncludeDirectories( - fout, "\t\t\t\t", "\n", - this->FortranProject ? "Fortran" : langForClCompile); - targetOptions.OutputFlagMap(fout, "\t\t\t\t"); - targetOptions.OutputPreprocessorDefinitions(fout, "\t\t\t\t", "\n", - langForClCompile); + fout, 4, this->FortranProject ? "Fortran" : langForClCompile); + targetOptions.OutputFlagMap(fout, 4); + targetOptions.OutputPreprocessorDefinitions(fout, 4, langForClCompile); fout << "\t\t\t\tObjectFile=\"$(IntDir)\\\"\n"; if (target->GetType() <= cmStateEnums::OBJECT_LIBRARY) { // Specify the compiler program database file if configured. @@ -814,12 +833,10 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( "\t\t\t\tName=\"MASM\"\n" ; /* clang-format on */ - targetOptions.OutputAdditionalIncludeDirectories(fout, "\t\t\t\t", "\n", - "ASM_MASM"); + targetOptions.OutputAdditionalIncludeDirectories(fout, 4, "ASM_MASM"); // Use same preprocessor definitions as VCCLCompilerTool. - targetOptions.OutputPreprocessorDefinitions(fout, "\t\t\t\t", "\n", - "ASM_MASM"); - masmOptions.OutputFlagMap(fout, "\t\t\t\t"); + targetOptions.OutputPreprocessorDefinitions(fout, 4, "ASM_MASM"); + masmOptions.OutputFlagMap(fout, 4); /* clang-format off */ fout << "\t\t\t\tObjectFile=\"$(IntDir)\\\"\n" @@ -836,18 +853,16 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( tool = "VFResourceCompilerTool"; } fout << "\t\t\t<Tool\n\t\t\t\tName=\"" << tool << "\"\n"; - targetOptions.OutputAdditionalIncludeDirectories(fout, "\n\t\t\t\t", "", - "RC"); + targetOptions.OutputAdditionalIncludeDirectories(fout, 4, "RC"); // add the -D flags to the RC tool - targetOptions.OutputPreprocessorDefinitions(fout, "\n\t\t\t\t", "", "RC"); - fout << "/>\n"; + targetOptions.OutputPreprocessorDefinitions(fout, 4, "RC"); + fout << "\t\t\t/>\n"; tool = "VCMIDLTool"; if (this->FortranProject) { tool = "VFMIDLTool"; } fout << "\t\t\t<Tool\n\t\t\t\tName=\"" << tool << "\"\n"; - targetOptions.OutputAdditionalIncludeDirectories(fout, "\n\t\t\t\t", "", - "MIDL"); + targetOptions.OutputAdditionalIncludeDirectories(fout, 4, "MIDL"); fout << "\t\t\t\tMkTypLibCompatible=\"false\"\n"; if (gg->GetPlatformName() == "x64") { fout << "\t\t\t\tTargetEnvironment=\"3\"\n"; @@ -1072,7 +1087,7 @@ void cmLocalVisualStudio7Generator::OutputBuildTool( fout << "\t\t\t\tOutputFile=\"" << this->ConvertToXMLOutputPathSingle(temp.c_str()) << "\"\n"; this->WriteTargetVersionAttribute(fout, target); - linkOptions.OutputFlagMap(fout, "\t\t\t\t"); + linkOptions.OutputFlagMap(fout, 4); fout << "\t\t\t\tAdditionalLibraryDirectories=\""; this->OutputLibraryDirectories(fout, cli.GetDirectories()); fout << "\"\n"; @@ -1157,7 +1172,7 @@ void cmLocalVisualStudio7Generator::OutputBuildTool( fout << "\t\t\t\tOutputFile=\"" << this->ConvertToXMLOutputPathSingle(temp.c_str()) << "\"\n"; this->WriteTargetVersionAttribute(fout, target); - linkOptions.OutputFlagMap(fout, "\t\t\t\t"); + linkOptions.OutputFlagMap(fout, 4); fout << "\t\t\t\tAdditionalLibraryDirectories=\""; this->OutputLibraryDirectories(fout, cli.GetDirectories()); fout << "\"\n"; @@ -1688,18 +1703,17 @@ bool cmLocalVisualStudio7Generator::WriteGroup( } Options fileOptions(this, tool, table, gg->ExtraFlagTable); fileOptions.Parse(fc.CompileFlags.c_str()); - fileOptions.AddDefines(fc.CompileDefs.c_str()); - fileOptions.AddDefines(fc.CompileDefsConfig.c_str()); + fileOptions.AddDefines(fc.CompileDefs); + fileOptions.AddDefines(fc.CompileDefsConfig); // validate source level include directories std::vector<std::string> includes; this->AppendIncludeDirectories(includes, fc.IncludeDirs, **sf); fileOptions.AddIncludes(includes); - fileOptions.OutputFlagMap(fout, "\t\t\t\t\t"); + fileOptions.OutputFlagMap(fout, 5); fileOptions.OutputAdditionalIncludeDirectories( - fout, "\t\t\t\t\t", "\n", + fout, 5, ppLang == "CXX" && this->FortranProject ? "Fortran" : ppLang); - fileOptions.OutputPreprocessorDefinitions(fout, "\t\t\t\t\t", "\n", - ppLang); + fileOptions.OutputPreprocessorDefinitions(fout, 5, ppLang); } if (!fc.AdditionalDeps.empty()) { fout << "\t\t\t\t\tAdditionalDependencies=\"" << fc.AdditionalDeps @@ -2075,6 +2089,19 @@ std::string cmLocalVisualStudio7Generator::ConvertToXMLOutputPathSingle( return ret; } +void cmVS7GeneratorOptions::OutputFlag(std::ostream& fout, int indent, + const char* flag, + const std::string& content) +{ + fout.fill('\t'); + fout.width(indent); + // write an empty string to get the fill level indent to print + fout << ""; + fout << flag << "=\""; + fout << cmLocalVisualStudio7GeneratorEscapeForXML(content); + fout << "\"\n"; +} + // This class is used to parse an existing vs 7 project // and extract the GUID class cmVS7XMLParser : public cmXMLParser diff --git a/Source/cmLocalVisualStudio7Generator.h b/Source/cmLocalVisualStudio7Generator.h index 02e6931..b093e6f 100644 --- a/Source/cmLocalVisualStudio7Generator.h +++ b/Source/cmLocalVisualStudio7Generator.h @@ -21,6 +21,19 @@ class cmMakefile; class cmSourceFile; class cmSourceGroup; +class cmVS7GeneratorOptions : public cmVisualStudioGeneratorOptions +{ +public: + cmVS7GeneratorOptions(cmLocalVisualStudioGenerator* lg, Tool tool, + cmVS7FlagTable const* table = nullptr, + cmVS7FlagTable const* extraTable = nullptr) + : cmVisualStudioGeneratorOptions(lg, tool, table, extraTable) + { + } + void OutputFlag(std::ostream& fout, int indent, const char* tag, + const std::string& content) override; +}; + /** \class cmLocalVisualStudio7Generator * \brief Write Visual Studio .NET project files. * @@ -70,7 +83,7 @@ protected: void CreateSingleVCProj(const std::string& lname, cmGeneratorTarget* tgt); private: - typedef cmVisualStudioGeneratorOptions Options; + typedef cmVS7GeneratorOptions Options; typedef cmLocalVisualStudio7GeneratorFCInfo FCInfo; std::string GetBuildTypeLinkerFlags(std::string rootLinkerFlags, const std::string& configName); diff --git a/Source/cmMacroCommand.cxx b/Source/cmMacroCommand.cxx index 07943e3..23d93a3 100644 --- a/Source/cmMacroCommand.cxx +++ b/Source/cmMacroCommand.cxx @@ -93,7 +93,7 @@ bool cmMacroHelperCommand::InvokeInitialPass( argVs.reserve(expandedArgs.size()); char argvName[60]; for (unsigned int j = 0; j < expandedArgs.size(); ++j) { - sprintf(argvName, "${ARGV%i}", j); + sprintf(argvName, "${ARGV%u}", j); argVs.push_back(argvName); } // Invoke all the functions that were collected in the block. @@ -161,9 +161,9 @@ bool cmMacroFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff, { // record commands until we hit the ENDMACRO // at the ENDMACRO call we shift gears and start looking for invocations - if (!cmSystemTools::Strucmp(lff.Name.c_str(), "macro")) { + if (lff.Name.Lower == "macro") { this->Depth++; - } else if (!cmSystemTools::Strucmp(lff.Name.c_str(), "endmacro")) { + } else if (lff.Name.Lower == "endmacro") { // if this is the endmacro for this macro then execute if (!this->Depth) { mf.AppendProperty("MACROS", this->Args[0].c_str()); @@ -191,7 +191,7 @@ bool cmMacroFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff, bool cmMacroFunctionBlocker::ShouldRemove(const cmListFileFunction& lff, cmMakefile& mf) { - if (!cmSystemTools::Strucmp(lff.Name.c_str(), "endmacro")) { + if (lff.Name.Lower == "endmacro") { std::vector<std::string> expandedArguments; mf.ExpandArguments(lff.Arguments, expandedArguments, this->GetStartingContext().FilePath.c_str()); diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 604ca2c..d67d280 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -6,12 +6,12 @@ #include "cmsys/RegularExpression.hxx" #include <algorithm> #include <assert.h> +#include <cstring> #include <ctype.h> #include <iterator> #include <memory> // IWYU pragma: keep #include <sstream> #include <stdlib.h> -#include <string.h> #include <utility> #include "cmAlgorithms.h" @@ -158,6 +158,32 @@ bool cmMakefile::CheckCMP0037(std::string const& targetName, return true; } +void cmMakefile::MaybeWarnCMP0074(std::string const& pkg) +{ + // Warn if a <pkg>_ROOT variable we may use is set. + std::string const varName = pkg + "_ROOT"; + const char* var = this->GetDefinition(varName); + std::string env; + cmSystemTools::GetEnv(varName, env); + + bool const haveVar = var && *var; + bool const haveEnv = !env.empty(); + if ((haveVar || haveEnv) && this->WarnedCMP0074.insert(varName).second) { + std::ostringstream w; + w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0074) << "\n"; + if (haveVar) { + w << "CMake variable " << varName << " is set to:\n" + << " " << var << "\n"; + } + if (haveEnv) { + w << "Environment variable " << varName << " is set to:\n" + << " " << env << "\n"; + } + w << "For compatibility, CMake is ignoring the variable."; + this->IssueMessage(cmake::AUTHOR_WARNING, w.str()); + } +} + cmStringRange cmMakefile::GetIncludeDirectoriesEntries() const { return this->StateSnapshot.GetDirectory().GetIncludeDirectoriesEntries(); @@ -198,7 +224,7 @@ cmListFileBacktrace cmMakefile::GetBacktrace() const cmListFileBacktrace cmMakefile::GetBacktrace(cmCommandContext const& cc) const { cmListFileContext lfc; - lfc.Name = cc.Name; + lfc.Name = cc.Name.Original; lfc.Line = cc.Line; lfc.FilePath = this->StateSnapshot.GetExecutionListFile(); return this->Backtrace.Push(lfc); @@ -239,7 +265,7 @@ void cmMakefile::PrintCommandTrace(const cmListFileFunction& lff) const std::ostringstream msg; msg << full_path << "(" << lff.Line << "): "; - msg << lff.Name << "("; + msg << lff.Name.Original << "("; bool expand = this->GetCMakeInstance()->GetTraceExpand(); std::string temp; for (cmListFileArgument const& arg : lff.Arguments) { @@ -291,14 +317,13 @@ bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff, return result; } - std::string name = lff.Name; - // Place this call on the call stack. cmMakefileCall stack_manager(this, lff, status); static_cast<void>(stack_manager); // Lookup the command prototype. - if (cmCommand* proto = this->GetState()->GetCommand(name)) { + if (cmCommand* proto = + this->GetState()->GetCommandByExactName(lff.Name.Lower)) { // Clone the prototype. std::unique_ptr<cmCommand> pcmd(proto->Clone()); pcmd->SetMakefile(this); @@ -315,7 +340,8 @@ bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff, if (!invokeSucceeded || hadNestedError) { if (!hadNestedError) { // The command invocation requested that we report an error. - std::string const error = name + " " + pcmd->GetError(); + std::string const error = + std::string(lff.Name.Original) + " " + pcmd->GetError(); this->IssueMessage(cmake::FATAL_ERROR, error); } result = false; @@ -330,7 +356,7 @@ bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff, } else { if (!cmSystemTools::GetFatalErrorOccured()) { std::string error = "Unknown CMake command \""; - error += lff.Name; + error += lff.Name.Original; error += "\"."; this->IssueMessage(cmake::FATAL_ERROR, error); result = false; @@ -1104,6 +1130,17 @@ cmTarget* cmMakefile::AddUtilityCommand( return target; } +static void s_AddDefineFlag(std::string const& flag, std::string& dflags) +{ + // remove any \n\r + std::string::size_type initSize = dflags.size(); + dflags += ' '; + dflags += flag; + std::string::iterator flagStart = dflags.begin() + initSize + 1; + std::replace(flagStart, dflags.end(), '\n', ' '); + std::replace(flagStart, dflags.end(), '\r', ' '); +} + void cmMakefile::AddDefineFlag(std::string const& flag) { if (flag.empty()) { @@ -1111,7 +1148,7 @@ void cmMakefile::AddDefineFlag(std::string const& flag) } // Update the string used for the old DEFINITIONS property. - this->AddDefineFlag(flag, this->DefineFlagsOrig); + s_AddDefineFlag(flag, this->DefineFlagsOrig); // If this is really a definition, update COMPILE_DEFINITIONS. if (this->ParseDefineFlag(flag, false)) { @@ -1119,17 +1156,24 @@ void cmMakefile::AddDefineFlag(std::string const& flag) } // Add this flag that does not look like a definition. - this->AddDefineFlag(flag, this->DefineFlags); + s_AddDefineFlag(flag, this->DefineFlags); } -void cmMakefile::AddDefineFlag(std::string const& flag, std::string& dflags) +static void s_RemoveDefineFlag(std::string const& flag, std::string& dflags) { - // remove any \n\r - std::string::size_type initSize = dflags.size(); - dflags += std::string(" ") + flag; - std::string::iterator flagStart = dflags.begin() + initSize + 1; - std::replace(flagStart, dflags.end(), '\n', ' '); - std::replace(flagStart, dflags.end(), '\r', ' '); + std::string::size_type const len = flag.length(); + // Remove all instances of the flag that are surrounded by + // whitespace or the beginning/end of the string. + for (std::string::size_type lpos = dflags.find(flag, 0); + lpos != std::string::npos; lpos = dflags.find(flag, lpos)) { + std::string::size_type rpos = lpos + len; + if ((lpos <= 0 || isspace(dflags[lpos - 1])) && + (rpos >= dflags.size() || isspace(dflags[rpos]))) { + dflags.erase(lpos, len); + } else { + ++lpos; + } + } } void cmMakefile::RemoveDefineFlag(std::string const& flag) @@ -1138,9 +1182,9 @@ void cmMakefile::RemoveDefineFlag(std::string const& flag) if (flag.empty()) { return; } - std::string::size_type const len = flag.length(); + // Update the string used for the old DEFINITIONS property. - this->RemoveDefineFlag(flag, len, this->DefineFlagsOrig); + s_RemoveDefineFlag(flag, this->DefineFlagsOrig); // If this is really a definition, update COMPILE_DEFINITIONS. if (this->ParseDefineFlag(flag, true)) { @@ -1148,25 +1192,12 @@ void cmMakefile::RemoveDefineFlag(std::string const& flag) } // Remove this flag that does not look like a definition. - this->RemoveDefineFlag(flag, len, this->DefineFlags); + s_RemoveDefineFlag(flag, this->DefineFlags); } -void cmMakefile::RemoveDefineFlag(std::string const& flag, - std::string::size_type len, - std::string& dflags) +void cmMakefile::AddCompileDefinition(std::string const& option) { - // Remove all instances of the flag that are surrounded by - // whitespace or the beginning/end of the string. - for (std::string::size_type lpos = dflags.find(flag, 0); - lpos != std::string::npos; lpos = dflags.find(flag, lpos)) { - std::string::size_type rpos = lpos + len; - if ((lpos <= 0 || isspace(dflags[lpos - 1])) && - (rpos >= dflags.size() || isspace(dflags[rpos]))) { - dflags.erase(lpos, len); - } else { - ++lpos; - } - } + this->AppendProperty("COMPILE_DEFINITIONS", option.c_str()); } void cmMakefile::AddCompileOption(std::string const& option) @@ -1423,7 +1454,7 @@ void cmMakefile::Configure() bool hasVersion = false; // search for the right policy command for (cmListFileFunction const& func : listFile.Functions) { - if (cmSystemTools::LowerCase(func.Name) == "cmake_minimum_required") { + if (func.Name.Lower == "cmake_minimum_required") { hasVersion = true; break; } @@ -1450,8 +1481,7 @@ void cmMakefile::Configure() allowedCommands.insert("message"); isProblem = false; for (cmListFileFunction const& func : listFile.Functions) { - std::string name = cmSystemTools::LowerCase(func.Name); - if (allowedCommands.find(name) == allowedCommands.end()) { + if (allowedCommands.find(func.Name.Lower) == allowedCommands.end()) { isProblem = true; break; } @@ -1464,13 +1494,13 @@ void cmMakefile::Configure() this->SetCheckCMP0000(true); // Implicitly set the version for the user. - this->SetPolicyVersion("2.4"); + this->SetPolicyVersion("2.4", std::string()); } } bool hasProject = false; // search for a project command for (cmListFileFunction const& func : listFile.Functions) { - if (cmSystemTools::LowerCase(func.Name) == "project") { + if (func.Name.Lower == "project") { hasProject = true; break; } @@ -1478,7 +1508,7 @@ void cmMakefile::Configure() // if no project command is found, add one if (!hasProject) { cmListFileFunction project; - project.Name = "PROJECT"; + project.Name.Lower = "project"; project.Arguments.emplace_back("Project", cmListFileArgument::Unquoted, 0); listFile.Functions.insert(listFile.Functions.begin(), project); @@ -1810,12 +1840,10 @@ void cmMakefile::AddGlobalLinkInformation(cmTarget& target) std::vector<std::string> linkDirs; cmSystemTools::ExpandListArgument(linkDirsProp, linkDirs); - for (std::string const& linkDir : linkDirs) { - std::string newdir = linkDir; - // remove trailing slashes - if (*linkDir.rbegin() == '/') { - newdir = linkDir.substr(0, linkDir.size() - 1); - } + for (std::string& linkDir : linkDirs) { + // Sanitize the path the same way the link_directories command does + // in case projects set the LINK_DIRECTORIES property directly. + cmSystemTools::ConvertToUnixSlashes(linkDir); target.AddLinkDirectory(linkDir); } } @@ -1867,7 +1895,7 @@ cmTarget* cmMakefile::AddLibrary(const std::string& lname, // Clear its dependencies. Otherwise, dependencies might persist // over changes in CMakeLists.txt, making the information stale and // hence useless. - target->ClearDependencyInformation(*this, lname); + target->ClearDependencyInformation(*this); if (excludeFromAll) { target->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); } @@ -2383,12 +2411,13 @@ std::vector<std::string> cmMakefile::GetDefinitions() const return res; } -const char* cmMakefile::ExpandVariablesInString(std::string& source) const +const std::string& cmMakefile::ExpandVariablesInString( + std::string& source) const { return this->ExpandVariablesInString(source, false, false); } -const char* cmMakefile::ExpandVariablesInString( +const std::string& cmMakefile::ExpandVariablesInString( std::string& source, bool escapeQuotes, bool noEscapes, bool atOnly, const char* filename, long line, bool removeEmpty, bool replaceAt) const { @@ -2404,7 +2433,7 @@ const char* cmMakefile::ExpandVariablesInString( this->IssueMessage(cmake::INTERNAL_ERROR, "ExpandVariablesInString @ONLY called " "on something with escapes."); - return source.c_str(); + return source; } // Variables used in the WARN case. @@ -2486,7 +2515,7 @@ const char* cmMakefile::ExpandVariablesInString( this->IssueMessage(cmake::AUTHOR_WARNING, msg); } - return source.c_str(); + return source; } cmake::MessageType cmMakefile::ExpandVariablesInStringOld( @@ -3140,6 +3169,14 @@ void cmMakefile::SetArgcArgv(const std::vector<std::string>& args) cmSourceFile* cmMakefile::GetSource(const std::string& sourceName, cmSourceFileLocationKind kind) const { + // First check "Known" paths (avoids the creation of cmSourceFileLocation) + if (kind == cmSourceFileLocationKind::Known) { + auto sfsi = this->KnownFileSearchIndex.find(sourceName); + if (sfsi != this->KnownFileSearchIndex.end()) { + return sfsi->second; + } + } + cmSourceFileLocation sfl(this, sourceName, kind); auto name = this->GetCMakeInstance()->StripExtension(sfl.GetName()); #if defined(_WIN32) || defined(__APPLE__) @@ -3172,6 +3209,10 @@ cmSourceFile* cmMakefile::CreateSource(const std::string& sourceName, name = cmSystemTools::LowerCase(name); #endif this->SourceFileSearchIndex[name].push_back(sf); + // for "Known" paths add direct lookup (used for faster lookup in GetSource) + if (kind == cmSourceFileLocationKind::Known) { + this->KnownFileSearchIndex[sourceName] = sf; + } return sf; } @@ -3227,7 +3268,7 @@ void cmMakefile::EnableLanguage(std::vector<std::string> const& lang, int cmMakefile::TryCompile(const std::string& srcdir, const std::string& bindir, const std::string& projectName, - const std::string& targetName, bool fast, + const std::string& targetName, bool fast, int jobs, const std::vector<std::string>* cmakeArgs, std::string& output) { @@ -3240,6 +3281,14 @@ int cmMakefile::TryCompile(const std::string& srcdir, // change to the tests directory and run cmake // use the cmake object instead of calling cmake cmWorkingDirectory workdir(bindir); + if (workdir.Failed()) { + this->IssueMessage(cmake::FATAL_ERROR, + "Failed to set working directory to " + bindir + " : " + + std::strerror(workdir.GetLastResult())); + cmSystemTools::SetFatalErrorOccured(); + this->IsSourceFileTryCompile = false; + return 1; + } // make sure the same generator is used // use this program as the cmake to be run, it should not @@ -3331,7 +3380,7 @@ int cmMakefile::TryCompile(const std::string& srcdir, // finally call the generator to actually build the resulting project int ret = this->GetGlobalGenerator()->TryCompile( - srcdir, bindir, projectName, targetName, fast, output, this); + jobs, srcdir, bindir, projectName, targetName, fast, output, this); this->IsSourceFileTryCompile = false; return ret; @@ -3643,6 +3692,20 @@ void cmMakefile::AppendProperty(const std::string& prop, const char* value, const char* cmMakefile::GetProperty(const std::string& prop) const { + // Check for computed properties. + static std::string output; + if (prop == "TESTS") { + std::vector<std::string> keys; + // get list of keys + std::transform(this->Tests.begin(), this->Tests.end(), + std::back_inserter(keys), + [](decltype(this->Tests)::value_type const& pair) { + return pair.first; + }); + output = cmJoin(keys, ";"); + return output.c_str(); + } + return this->StateSnapshot.GetDirectory().GetProperty(prop); } @@ -3782,7 +3845,16 @@ void cmMakefile::RaiseScope(const std::string& var, const char* varDef) std::ostringstream m; m << "Cannot set \"" << var << "\": current scope has no parent."; this->IssueMessage(cmake::AUTHOR_WARNING, m.str()); + return; } + +#ifdef CMAKE_BUILD_WITH_CMAKE + cmVariableWatch* vv = this->GetVariableWatch(); + if (vv) { + vv->VariableAccessed(var, cmVariableWatch::VARIABLE_MODIFIED_ACCESS, + varDef, this); + } +#endif } cmTarget* cmMakefile::AddImportedTarget(const std::string& name, @@ -4023,10 +4095,10 @@ const char* cmMakefile::GetDefineFlagsCMP0059() const return this->DefineFlagsOrig.c_str(); } -cmPolicies::PolicyStatus cmMakefile::GetPolicyStatus( - cmPolicies::PolicyID id) const +cmPolicies::PolicyStatus cmMakefile::GetPolicyStatus(cmPolicies::PolicyID id, + bool parent_scope) const { - return this->StateSnapshot.GetPolicy(id); + return this->StateSnapshot.GetPolicy(id, parent_scope); } bool cmMakefile::PolicyOptionalWarningEnabled(std::string const& var) @@ -4117,9 +4189,10 @@ void cmMakefile::PopSnapshot(bool reportError) assert(this->StateSnapshot.IsValid()); } -bool cmMakefile::SetPolicyVersion(const char* version) +bool cmMakefile::SetPolicyVersion(std::string const& version_min, + std::string const& version_max) { - return cmPolicies::ApplyPolicyVersion(this, version); + return cmPolicies::ApplyPolicyVersion(this, version_min, version_max); } bool cmMakefile::HasCMP0054AlreadyBeenReported( @@ -4164,7 +4237,7 @@ static const char* const CXX_FEATURES[] = { nullptr FOR_EACH_CXX_FEATURE( #undef FEATURE_STRING static const char* const C_STANDARDS[] = { "90", "99", "11" }; -static const char* const CXX_STANDARDS[] = { "98", "11", "14", "17" }; +static const char* const CXX_STANDARDS[] = { "98", "11", "14", "17", "20" }; bool cmMakefile::AddRequiredTargetFeature(cmTarget* target, const std::string& feature, @@ -4414,8 +4487,9 @@ bool cmMakefile::HaveCxxStandardAvailable(cmTarget const* target, bool needCxx11 = false; bool needCxx14 = false; bool needCxx17 = false; + bool needCxx20 = false; this->CheckNeededCxxLanguage(feature, needCxx98, needCxx11, needCxx14, - needCxx17); + needCxx17, needCxx20); const char* existingCxxStandard = target->GetProperty("CXX_STANDARD"); if (!existingCxxStandard) { @@ -4435,7 +4509,8 @@ bool cmMakefile::HaveCxxStandardAvailable(cmTarget const* target, /* clang-format off */ const char* const* needCxxLevel = - needCxx17 ? &CXX_STANDARDS[3] + needCxx20 ? &CXX_STANDARDS[4] + : needCxx17 ? &CXX_STANDARDS[3] : needCxx14 ? &CXX_STANDARDS[2] : needCxx11 ? &CXX_STANDARDS[1] : needCxx98 ? &CXX_STANDARDS[0] @@ -4447,7 +4522,8 @@ bool cmMakefile::HaveCxxStandardAvailable(cmTarget const* target, void cmMakefile::CheckNeededCxxLanguage(const std::string& feature, bool& needCxx98, bool& needCxx11, - bool& needCxx14, bool& needCxx17) const + bool& needCxx14, bool& needCxx17, + bool& needCxx20) const { if (const char* propCxx98 = this->GetDefinition("CMAKE_CXX98_COMPILE_FEATURES")) { @@ -4473,6 +4549,12 @@ void cmMakefile::CheckNeededCxxLanguage(const std::string& feature, cmSystemTools::ExpandListArgument(propCxx17, props); needCxx17 = std::find(props.begin(), props.end(), feature) != props.end(); } + if (const char* propCxx20 = + this->GetDefinition("CMAKE_CXX20_COMPILE_FEATURES")) { + std::vector<std::string> props; + cmSystemTools::ExpandListArgument(propCxx20, props); + needCxx20 = std::find(props.begin(), props.end(), feature) != props.end(); + } } bool cmMakefile::AddRequiredTargetCxxFeature(cmTarget* target, @@ -4483,9 +4565,10 @@ bool cmMakefile::AddRequiredTargetCxxFeature(cmTarget* target, bool needCxx11 = false; bool needCxx14 = false; bool needCxx17 = false; + bool needCxx20 = false; this->CheckNeededCxxLanguage(feature, needCxx98, needCxx11, needCxx14, - needCxx17); + needCxx17, needCxx20); const char* existingCxxStandard = target->GetProperty("CXX_STANDARD"); const char* const* existingCxxLevel = nullptr; @@ -4530,7 +4613,8 @@ bool cmMakefile::AddRequiredTargetCxxFeature(cmTarget* target, /* clang-format off */ const char* const* needCxxLevel = - needCxx17 ? &CXX_STANDARDS[3] + needCxx20 ? &CXX_STANDARDS[4] + : needCxx17 ? &CXX_STANDARDS[3] : needCxx14 ? &CXX_STANDARDS[2] : needCxx11 ? &CXX_STANDARDS[1] : needCxx98 ? &CXX_STANDARDS[0] diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index 5a30790..ac7baae 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -102,7 +102,8 @@ public: */ int TryCompile(const std::string& srcdir, const std::string& bindir, const std::string& projectName, const std::string& targetName, - bool fast, const std::vector<std::string>* cmakeArgs, + bool fast, int jobs, + const std::vector<std::string>* cmakeArgs, std::string& output); bool GetIsSourceFileTryCompile() const; @@ -168,6 +169,7 @@ public: */ void AddDefineFlag(std::string const& definition); void RemoveDefineFlag(std::string const& definition); + void AddCompileDefinition(std::string const& definition); void AddCompileOption(std::string const& option); /** Create a new imported target with the name and type given. */ @@ -284,8 +286,10 @@ public: */ bool SetPolicy(cmPolicies::PolicyID id, cmPolicies::PolicyStatus status); bool SetPolicy(const char* id, cmPolicies::PolicyStatus status); - cmPolicies::PolicyStatus GetPolicyStatus(cmPolicies::PolicyID id) const; - bool SetPolicyVersion(const char* version); + cmPolicies::PolicyStatus GetPolicyStatus(cmPolicies::PolicyID id, + bool parent_scope = false) const; + bool SetPolicyVersion(std::string const& version_min, + std::string const& version_max); void RecordPolicies(cmPolicies::PolicyMap& pm); //@} @@ -562,12 +566,11 @@ public: * entry in the this->Definitions map. Also \@var\@ is * expanded to match autoconf style expansions. */ - const char* ExpandVariablesInString(std::string& source) const; - const char* ExpandVariablesInString(std::string& source, bool escapeQuotes, - bool noEscapes, bool atOnly = false, - const char* filename = nullptr, - long line = -1, bool removeEmpty = false, - bool replaceAt = false) const; + const std::string& ExpandVariablesInString(std::string& source) const; + const std::string& ExpandVariablesInString( + std::string& source, bool escapeQuotes, bool noEscapes, + bool atOnly = false, const char* filename = nullptr, long line = -1, + bool removeEmpty = false, bool replaceAt = false) const; /** * Remove any remaining variables in the string. Anything with ${var} or @@ -832,9 +835,11 @@ public: void RemoveExportBuildFileGeneratorCMP0024(cmExportBuildFileGenerator* gen); void AddExportBuildFileGenerator(cmExportBuildFileGenerator* gen); - // Maintain a stack of package names to determine the depth of find modules - // we are currently being called with - std::deque<std::string> FindPackageModuleStack; + // Maintain a stack of package roots to allow nested PACKAGE_ROOT_PATH + // searches + std::deque<std::vector<std::string>> FindPackageRootPathStack; + + void MaybeWarnCMP0074(std::string const& pkg); protected: // add link libraries and directories to the target @@ -861,6 +866,9 @@ protected: typedef std::unordered_map<std::string, SourceFileVec> SourceFileMap; SourceFileMap SourceFileSearchIndex; + // For "Known" paths we can store a direct filename to cmSourceFile map + std::unordered_map<std::string, cmSourceFile*> KnownFileSearchIndex; + // Tests std::map<std::string, cmTest*> Tests; @@ -878,9 +886,6 @@ protected: std::string DefineFlags; // Track the value of the computed DEFINITIONS property. - void AddDefineFlag(std::string const& flag, std::string&); - void RemoveDefineFlag(std::string const& flag, std::string::size_type, - std::string&); std::string DefineFlagsOrig; #if defined(CMAKE_BUILD_WITH_CMAKE) @@ -985,7 +990,7 @@ private: bool& needC99, bool& needC11) const; void CheckNeededCxxLanguage(const std::string& feature, bool& needCxx98, bool& needCxx11, bool& needCxx14, - bool& needCxx17) const; + bool& needCxx17, bool& needCxx20) const; bool HaveCStandardAvailable(cmTarget const* target, const std::string& feature) const; @@ -998,6 +1003,7 @@ private: bool WarnUnused; bool CheckSystemVars; bool CheckCMP0000; + std::set<std::string> WarnedCMP0074; bool IsSourceFileTryCompile; mutable bool SuppressWatches; }; diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx index 9bbc043..1e59f44 100644 --- a/Source/cmMakefileExecutableTargetGenerator.cxx +++ b/Source/cmMakefileExecutableTargetGenerator.cxx @@ -477,8 +477,8 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) this->LocalGenerator->GetCurrentBinaryDirectory(), targetFullPathImport)); std::string implib; - if (this->GeneratorTarget->GetImplibGNUtoMS(targetFullPathImport, - implib)) { + if (this->GeneratorTarget->GetImplibGNUtoMS( + this->ConfigName, targetFullPathImport, implib)) { exeCleanFiles.push_back(this->LocalGenerator->MaybeConvertToRelativePath( this->LocalGenerator->GetCurrentBinaryDirectory(), implib)); } diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx index 9299ffe..c538992 100644 --- a/Source/cmMakefileLibraryTargetGenerator.cxx +++ b/Source/cmMakefileLibraryTargetGenerator.cxx @@ -5,6 +5,7 @@ #include <algorithm> #include <memory> // IWYU pragma: keep #include <sstream> +#include <stddef.h> #include <vector> #include "cmGeneratedFileStream.h" @@ -641,8 +642,8 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules( this->LocalGenerator->GetCurrentBinaryDirectory(), targetFullPathImport)); std::string implib; - if (this->GeneratorTarget->GetImplibGNUtoMS(targetFullPathImport, - implib)) { + if (this->GeneratorTarget->GetImplibGNUtoMS( + this->ConfigName, targetFullPathImport, implib)) { libCleanFiles.push_back(this->LocalGenerator->MaybeConvertToRelativePath( this->LocalGenerator->GetCurrentBinaryDirectory(), implib)); } @@ -732,10 +733,14 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules( // Archiving rules never use a response file. useResponseFileForObjects = false; - // Limit the length of individual object lists to less than the - // 32K command line length limit on Windows. We could make this a - // platform file variable but this should work everywhere. - archiveCommandLimit = 30000; + // Limit the length of individual object lists to less than half of + // the command line length limit (leaving half for other flags). + // This may result in several calls to the archiver. + if (size_t limit = cmSystemTools::CalculateCommandLineLengthLimit()) { + archiveCommandLimit = limit / 2; + } else { + archiveCommandLimit = 8000; + } } // Expand the rule variables. diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index 73cf1f0..3998823 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -643,6 +643,18 @@ void cmMakefileTargetGenerator::WriteObjectBuildFile( source.GetFullPath(), workingDirectory, compileCommand); } + // See if we need to use a compiler launcher like ccache or distcc + std::string compilerLauncher; + if (!compileCommands.empty() && (lang == "C" || lang == "CXX" || + lang == "Fortran" || lang == "CUDA")) { + std::string const clauncher_prop = lang + "_COMPILER_LAUNCHER"; + const char* clauncher = + this->GeneratorTarget->GetProperty(clauncher_prop); + if (clauncher && *clauncher) { + compilerLauncher = clauncher; + } + } + // Maybe insert an include-what-you-use runner. if (!compileCommands.empty() && (lang == "C" || lang == "CXX")) { std::string const iwyu_prop = lang + "_INCLUDE_WHAT_YOU_USE"; @@ -656,6 +668,13 @@ void cmMakefileTargetGenerator::WriteObjectBuildFile( if ((iwyu && *iwyu) || (tidy && *tidy) || (cpplint && *cpplint) || (cppcheck && *cppcheck)) { std::string run_iwyu = "$(CMAKE_COMMAND) -E __run_co_compile"; + if (!compilerLauncher.empty()) { + // In __run_co_compile case the launcher command is supplied + // via --launcher=<maybe-list> and consumed + run_iwyu += " --launcher="; + run_iwyu += this->LocalGenerator->EscapeForShell(compilerLauncher); + compilerLauncher.clear(); + } if (iwyu && *iwyu) { run_iwyu += " --iwyu="; run_iwyu += this->LocalGenerator->EscapeForShell(iwyu); @@ -682,21 +701,15 @@ void cmMakefileTargetGenerator::WriteObjectBuildFile( } } - // Maybe insert a compiler launcher like ccache or distcc - if (!compileCommands.empty() && (lang == "C" || lang == "CXX" || - lang == "Fortran" || lang == "CUDA")) { - std::string const clauncher_prop = lang + "_COMPILER_LAUNCHER"; - const char* clauncher = - this->GeneratorTarget->GetProperty(clauncher_prop); - if (clauncher && *clauncher) { - std::vector<std::string> launcher_cmd; - cmSystemTools::ExpandListArgument(clauncher, launcher_cmd, true); - for (std::string& i : launcher_cmd) { - i = this->LocalGenerator->EscapeForShell(i); - } - std::string const& run_launcher = cmJoin(launcher_cmd, " ") + " "; - compileCommands.front().insert(0, run_launcher); + // If compiler launcher was specified and not consumed above, it + // goes to the beginning of the command line. + if (!compileCommands.empty() && !compilerLauncher.empty()) { + std::vector<std::string> args; + cmSystemTools::ExpandListArgument(compilerLauncher, args, true); + for (std::string& i : args) { + i = this->LocalGenerator->EscapeForShell(i); } + compileCommands.front().insert(0, cmJoin(args, " ") + " "); } std::string launcher; @@ -1389,7 +1402,7 @@ std::string cmMakefileTargetGenerator::GetLinkRule( const std::string& linkRuleVar) { std::string linkRule = this->Makefile->GetRequiredDefinition(linkRuleVar); - if (this->GeneratorTarget->HasImplibGNUtoMS()) { + if (this->GeneratorTarget->HasImplibGNUtoMS(this->ConfigName)) { std::string ruleVar = "CMAKE_"; ruleVar += this->GeneratorTarget->GetLinkerLanguage(this->ConfigName); ruleVar += "_GNUtoMS_RULE"; diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index ddbc772..542bb0a 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -187,7 +187,7 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkRule(bool useResponseFile) std::string responseFlag; if (!useResponseFile) { vars.Objects = "$in"; - vars.LinkLibraries = "$LINK_LIBRARIES"; + vars.LinkLibraries = "$LINK_PATH $LINK_LIBRARIES"; } else { std::string cmakeVarLang = "CMAKE_"; cmakeVarLang += this->TargetLinkLanguage; @@ -482,7 +482,7 @@ std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeLinkCmd() const char* linkCmd = mf->GetDefinition(linkCmdVar); if (linkCmd) { std::string linkCmdStr = linkCmd; - if (this->GetGeneratorTarget()->HasImplibGNUtoMS()) { + if (this->GetGeneratorTarget()->HasImplibGNUtoMS(this->ConfigName)) { std::string ruleVar = "CMAKE_"; ruleVar += this->GeneratorTarget->GetLinkerLanguage(this->ConfigName); ruleVar += "_GNUtoMS_RULE"; @@ -881,7 +881,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() targetOutputImplib, cmOutputConverter::SHELL); vars["TARGET_IMPLIB"] = impLibPath; EnsureParentDirectoryExists(impLibPath); - if (genTarget.HasImportLibrary()) { + if (genTarget.HasImportLibrary(cfgName)) { byproducts.push_back(targetOutputImplib); } } @@ -976,8 +976,10 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() preLinkCmdLines.push_back("cd " + homeOutDir); } - vars["PRE_LINK"] = localGen.BuildCommandLine(preLinkCmdLines); - std::string postBuildCmdLine = localGen.BuildCommandLine(postBuildCmdLines); + vars["PRE_LINK"] = localGen.BuildCommandLine(preLinkCmdLines, "pre-link", + this->GeneratorTarget); + std::string postBuildCmdLine = localGen.BuildCommandLine( + postBuildCmdLines, "post-build", this->GeneratorTarget); cmNinjaVars symlinkVars; bool const symlinkNeeded = diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index a6a3efb..7dfce57 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -445,72 +445,18 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) cmMakefile* mf = this->GetMakefile(); std::string flags = "$FLAGS"; - std::string rspfile; - std::string rspcontent; + std::string responseFlag; bool const lang_supports_response = !(lang == "RC" || lang == "CUDA"); if (lang_supports_response && this->ForceResponseFile()) { std::string const responseFlagVar = "CMAKE_" + lang + "_RESPONSE_FILE_FLAG"; - std::string responseFlag = - this->Makefile->GetSafeDefinition(responseFlagVar); + responseFlag = this->Makefile->GetSafeDefinition(responseFlagVar); if (responseFlag.empty()) { responseFlag = "@"; } - rspfile = "$RSP_FILE"; - responseFlag += rspfile; - rspcontent = " $DEFINES $INCLUDES $FLAGS"; - flags = std::move(responseFlag); - vars.Defines = ""; - vars.Includes = ""; } - // Tell ninja dependency format so all deps can be loaded into a database - std::string deptype; - std::string depfile; - std::string cldeps; - if (explicitPP) { - // The explicit preprocessing step will handle dependency scanning. - } else if (this->NeedDepTypeMSVC(lang)) { - deptype = "msvc"; - depfile.clear(); - flags += " /showIncludes"; - } else if (mf->IsOn("CMAKE_NINJA_CMCLDEPS_" + lang)) { - // For the MS resource compiler we need cmcldeps, but skip dependencies - // for source-file try_compile cases because they are always fresh. - if (!mf->GetIsSourceFileTryCompile()) { - deptype = "gcc"; - depfile = "$DEP_FILE"; - const std::string cl = mf->GetDefinition("CMAKE_C_COMPILER") - ? mf->GetSafeDefinition("CMAKE_C_COMPILER") - : mf->GetSafeDefinition("CMAKE_CXX_COMPILER"); - cldeps = "\""; - cldeps += cmSystemTools::GetCMClDepsCommand(); - cldeps += "\" " + lang + " " + vars.Source + " $DEP_FILE $out \""; - cldeps += mf->GetSafeDefinition("CMAKE_CL_SHOWINCLUDES_PREFIX"); - cldeps += "\" \"" + cl + "\" "; - } - } else { - deptype = "gcc"; - const char* langdeptype = mf->GetDefinition("CMAKE_NINJA_DEPTYPE_" + lang); - if (langdeptype) { - deptype = langdeptype; - } - depfile = "$DEP_FILE"; - const std::string flagsName = "CMAKE_DEPFILE_FLAGS_" + lang; - std::string depfileFlags = mf->GetSafeDefinition(flagsName); - if (!depfileFlags.empty()) { - cmSystemTools::ReplaceString(depfileFlags, "<DEPFILE>", "$DEP_FILE"); - cmSystemTools::ReplaceString(depfileFlags, "<OBJECT>", "$out"); - cmSystemTools::ReplaceString(depfileFlags, "<CMAKE_C_COMPILER>", - mf->GetDefinition("CMAKE_C_COMPILER")); - flags += " " + depfileFlags; - } - } - - vars.Flags = flags.c_str(); - vars.DependencyFile = depfile.c_str(); - std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander( this->GetLocalGenerator()->CreateRulePlaceholderExpander()); @@ -550,7 +496,7 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) vars.Source = "$in"; // Preprocessing and compilation use the same flags. - ppVars.Flags = vars.Flags; + std::string ppFlags = flags; // Move preprocessor definitions to the preprocessor rule. ppVars.Defines = vars.Defines; @@ -560,6 +506,20 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) // compilation rule still needs them for the INCLUDE directive. ppVars.Includes = vars.Includes; + // If using a response file, move defines, includes, and flags into it. + std::string ppRspFile; + std::string ppRspContent; + if (!responseFlag.empty()) { + ppRspFile = "$RSP_FILE"; + ppRspContent = std::string(" ") + ppVars.Defines + " " + + ppVars.Includes + " " + ppFlags; + ppFlags = responseFlag + ppRspFile; + ppVars.Defines = ""; + ppVars.Includes = ""; + } + + ppVars.Flags = ppFlags.c_str(); + // Rule for preprocessing source file. std::vector<std::string> ppCmds; cmSystemTools::ExpandListArgument(ppCmd, ppCmds); @@ -588,13 +548,11 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) ppComment << "Rule for preprocessing " << lang << " files."; std::ostringstream ppDesc; ppDesc << "Building " << lang << " preprocessed $out"; - this->GetGlobalGenerator()->AddRule(this->LanguagePreprocessRule(lang), - ppCmdLine, ppDesc.str(), - ppComment.str(), ppDepfile, ppDeptype, - /*rspfile*/ "", - /*rspcontent*/ "", - /*restat*/ "", - /*generator*/ false); + this->GetGlobalGenerator()->AddRule( + this->LanguagePreprocessRule(lang), ppCmdLine, ppDesc.str(), + ppComment.str(), ppDepfile, ppDeptype, ppRspFile, ppRspContent, + /*restat*/ "", + /*generator*/ false); } if (needDyndep) { @@ -631,6 +589,64 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) /*generator*/ false); } + // If using a response file, move defines, includes, and flags into it. + std::string rspfile; + std::string rspcontent; + if (!responseFlag.empty()) { + rspfile = "$RSP_FILE"; + rspcontent = + std::string(" ") + vars.Defines + " " + vars.Includes + " " + flags; + flags = responseFlag + rspfile; + vars.Defines = ""; + vars.Includes = ""; + } + + // Tell ninja dependency format so all deps can be loaded into a database + std::string deptype; + std::string depfile; + std::string cldeps; + if (explicitPP) { + // The explicit preprocessing step will handle dependency scanning. + } else if (this->NeedDepTypeMSVC(lang)) { + deptype = "msvc"; + depfile.clear(); + flags += " /showIncludes"; + } else if (mf->IsOn("CMAKE_NINJA_CMCLDEPS_" + lang)) { + // For the MS resource compiler we need cmcldeps, but skip dependencies + // for source-file try_compile cases because they are always fresh. + if (!mf->GetIsSourceFileTryCompile()) { + deptype = "gcc"; + depfile = "$DEP_FILE"; + const std::string cl = mf->GetDefinition("CMAKE_C_COMPILER") + ? mf->GetSafeDefinition("CMAKE_C_COMPILER") + : mf->GetSafeDefinition("CMAKE_CXX_COMPILER"); + cldeps = "\""; + cldeps += cmSystemTools::GetCMClDepsCommand(); + cldeps += "\" " + lang + " " + vars.Source + " $DEP_FILE $out \""; + cldeps += mf->GetSafeDefinition("CMAKE_CL_SHOWINCLUDES_PREFIX"); + cldeps += "\" \"" + cl + "\" "; + } + } else { + deptype = "gcc"; + const char* langdeptype = mf->GetDefinition("CMAKE_NINJA_DEPTYPE_" + lang); + if (langdeptype) { + deptype = langdeptype; + } + depfile = "$DEP_FILE"; + const std::string flagsName = "CMAKE_DEPFILE_FLAGS_" + lang; + std::string depfileFlags = mf->GetSafeDefinition(flagsName); + if (!depfileFlags.empty()) { + cmSystemTools::ReplaceString(depfileFlags, "<DEPFILE>", "$DEP_FILE"); + cmSystemTools::ReplaceString(depfileFlags, "<OBJECT>", "$out"); + cmSystemTools::ReplaceString(depfileFlags, "<CMAKE_C_COMPILER>", + mf->GetDefinition("CMAKE_C_COMPILER")); + flags += " " + depfileFlags; + } + } + + vars.Flags = flags.c_str(); + vars.DependencyFile = depfile.c_str(); + // Rule for compiling object file. std::vector<std::string> compileCmds; if (lang == "CUDA") { @@ -653,6 +669,17 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) cmSystemTools::ExpandListArgument(compileCmd, compileCmds); } + // See if we need to use a compiler launcher like ccache or distcc + std::string compilerLauncher; + if (!compileCmds.empty() && + (lang == "C" || lang == "CXX" || lang == "Fortran" || lang == "CUDA")) { + std::string const clauncher_prop = lang + "_COMPILER_LAUNCHER"; + const char* clauncher = this->GeneratorTarget->GetProperty(clauncher_prop); + if (clauncher && *clauncher) { + compilerLauncher = clauncher; + } + } + // Maybe insert an include-what-you-use runner. if (!compileCmds.empty() && (lang == "C" || lang == "CXX")) { std::string const iwyu_prop = lang + "_INCLUDE_WHAT_YOU_USE"; @@ -668,6 +695,13 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) std::string run_iwyu = this->GetLocalGenerator()->ConvertToOutputFormat( cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL); run_iwyu += " -E __run_co_compile"; + if (!compilerLauncher.empty()) { + // In __run_co_compile case the launcher command is supplied + // via --launcher=<maybe-list> and consumed + run_iwyu += " --launcher="; + run_iwyu += this->LocalGenerator->EscapeForShell(compilerLauncher); + compilerLauncher.clear(); + } if (iwyu && *iwyu) { run_iwyu += " --iwyu="; run_iwyu += this->GetLocalGenerator()->EscapeForShell(iwyu); @@ -693,20 +727,15 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) } } - // Maybe insert a compiler launcher like ccache or distcc - if (!compileCmds.empty() && - (lang == "C" || lang == "CXX" || lang == "Fortran" || lang == "CUDA")) { - std::string const clauncher_prop = lang + "_COMPILER_LAUNCHER"; - const char* clauncher = this->GeneratorTarget->GetProperty(clauncher_prop); - if (clauncher && *clauncher) { - std::vector<std::string> launcher_cmd; - cmSystemTools::ExpandListArgument(clauncher, launcher_cmd, true); - for (std::string& i : launcher_cmd) { - i = this->LocalGenerator->EscapeForShell(i); - } - std::string const& run_launcher = cmJoin(launcher_cmd, " ") + " "; - compileCmds.front().insert(0, run_launcher); + // If compiler launcher was specified and not consumed above, it + // goes to the beginning of the command line. + if (!compileCmds.empty() && !compilerLauncher.empty()) { + std::vector<std::string> args; + cmSystemTools::ExpandListArgument(compilerLauncher, args, true); + for (std::string& i : args) { + i = this->LocalGenerator->EscapeForShell(i); } + compileCmds.front().insert(0, cmJoin(args, " ") + " "); } if (!compileCmds.empty()) { @@ -792,6 +821,19 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements() orderOnlyDeps.erase(std::unique(orderOnlyDeps.begin(), orderOnlyDeps.end()), orderOnlyDeps.end()); + // The phony target must depend on at least one input or ninja will explain + // that "output ... of phony edge with no inputs doesn't exist" and consider + // the phony output "dirty". + if (orderOnlyDeps.empty()) { + // Any path that always exists will work here. It would be nice to + // use just "." but that is not supported by Ninja < 1.7. + std::string tgtDir; + tgtDir += this->LocalGenerator->GetCurrentBinaryDirectory(); + tgtDir += "/"; + tgtDir += this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget); + orderOnlyDeps.push_back(this->ConvertToNinjaPath(tgtDir)); + } + { cmNinjaDeps orderOnlyTarget; orderOnlyTarget.push_back(this->OrderDependsTargetForTarget()); @@ -852,13 +894,36 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( std::string const objectFileDir = cmSystemTools::GetFilenamePath(objectFileName); + bool const lang_supports_response = + !(language == "RC" || language == "CUDA"); + int const commandLineLengthLimit = + ((lang_supports_response && this->ForceResponseFile())) ? -1 : 0; + cmNinjaVars vars; vars["FLAGS"] = this->ComputeFlagsForObject(source, language); vars["DEFINES"] = this->ComputeDefines(source, language); vars["INCLUDES"] = this->ComputeIncludes(source, language); + if (!this->NeedDepTypeMSVC(language)) { - vars["DEP_FILE"] = this->GetLocalGenerator()->ConvertToOutputFormat( - objectFileName + ".d", cmOutputConverter::SHELL); + bool replaceExt(false); + if (!language.empty()) { + std::string repVar = "CMAKE_"; + repVar += language; + repVar += "_DEPFILE_EXTENSION_REPLACE"; + replaceExt = this->Makefile->IsOn(repVar); + } + if (!replaceExt) { + // use original code + vars["DEP_FILE"] = this->GetLocalGenerator()->ConvertToOutputFormat( + objectFileName + ".d", cmOutputConverter::SHELL); + } else { + // Replace the original source file extension with the + // depend file extension. + std::string dependFileName = + cmSystemTools::GetFilenameWithoutLastExtension(objectFileName) + ".d"; + vars["DEP_FILE"] = this->GetLocalGenerator()->ConvertToOutputFormat( + objectFileDir + "/" + dependFileName, cmOutputConverter::SHELL); + } } this->ExportObjectCompileCommand( @@ -983,9 +1048,12 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(), ppVars); + std::string const ppRspFile = ppFileName + ".rsp"; + this->GetGlobalGenerator()->WriteBuild( this->GetBuildFileStream(), ppComment, ppRule, ppOutputs, ppImplicitOuts, - ppExplicitDeps, ppImplicitDeps, ppOrderOnlyDeps, ppVars); + ppExplicitDeps, ppImplicitDeps, ppOrderOnlyDeps, ppVars, ppRspFile, + commandLineLengthLimit); } if (needDyndep) { std::string const dyndep = this->GetDyndepFilePath(language); @@ -1005,10 +1073,6 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( this->SetMsvcTargetPdbVariable(vars); - bool const lang_supports_response = - !(language == "RC" || language == "CUDA"); - int const commandLineLengthLimit = - ((lang_supports_response && this->ForceResponseFile())) ? -1 : 0; std::string const rspfile = objectFileName + ".rsp"; this->GetGlobalGenerator()->WriteBuild( diff --git a/Source/cmNinjaUtilityTargetGenerator.cxx b/Source/cmNinjaUtilityTargetGenerator.cxx index 7adeb8e..cc6d4b9 100644 --- a/Source/cmNinjaUtilityTargetGenerator.cxx +++ b/Source/cmNinjaUtilityTargetGenerator.cxx @@ -96,8 +96,8 @@ void cmNinjaUtilityTargetGenerator::Generate() this->GetBuildFileStream(), "Utility command for " + this->GetTargetName(), outputs, deps); } else { - std::string command = - this->GetLocalGenerator()->BuildCommandLine(commands); + std::string command = this->GetLocalGenerator()->BuildCommandLine( + commands, "utility", this->GeneratorTarget); const char* echoStr = this->GetGeneratorTarget()->GetProperty("EchoString"); std::string desc; diff --git a/Source/cmOutputConverter.cxx b/Source/cmOutputConverter.cxx index 25db929..fd42c53 100644 --- a/Source/cmOutputConverter.cxx +++ b/Source/cmOutputConverter.cxx @@ -6,7 +6,7 @@ #include <assert.h> #include <ctype.h> #include <set> -#include <sstream> +#include <string.h> #include <vector> #include "cmAlgorithms.h" @@ -341,12 +341,13 @@ redirection character (for example, ^>, ^<, or ^| ). If you need to use the caret character itself (^), use two in a row (^^). */ -int cmOutputConverter::Shell__CharIsWhitespace(char c) +/* Some helpers to identify character classes */ +static int Shell__CharIsWhitespace(char c) { return ((c == ' ') || (c == '\t')); } -int cmOutputConverter::Shell__CharNeedsQuotesOnUnix(char c) +static int Shell__CharNeedsQuotesOnUnix(char c) { return ((c == '\'') || (c == '`') || (c == ';') || (c == '#') || (c == '&') || (c == '$') || (c == '(') || (c == ')') || (c == '~') || @@ -354,12 +355,17 @@ int cmOutputConverter::Shell__CharNeedsQuotesOnUnix(char c) (c == '\\')); } -int cmOutputConverter::Shell__CharNeedsQuotesOnWindows(char c) +static int Shell__CharNeedsQuotesOnWindows(char c) { return ((c == '\'') || (c == '#') || (c == '&') || (c == '<') || (c == '>') || (c == '|') || (c == '^')); } +static int Shell__CharIsMakeVariableName(char c) +{ + return c && (c == '_' || isalpha((static_cast<int>(c)))); +} + int cmOutputConverter::Shell__CharNeedsQuotes(char c, int flags) { /* On Windows the built-in command shell echo never needs quotes. */ @@ -386,11 +392,6 @@ int cmOutputConverter::Shell__CharNeedsQuotes(char c, int flags) return 0; } -int cmOutputConverter::Shell__CharIsMakeVariableName(char c) -{ - return c && (c == '_' || isalpha((static_cast<int>(c)))); -} - const char* cmOutputConverter::Shell__SkipMakeVariables(const char* c) { while (*c == '$' && *(c + 1) == '(') { @@ -481,7 +482,9 @@ int cmOutputConverter::Shell__ArgumentNeedsQuotes(const char* in, int flags) std::string cmOutputConverter::Shell__GetArgument(const char* in, int flags) { - std::ostringstream out; + /* Output will be at least as long as input string. */ + std::string out; + out.reserve(strlen(in)); /* String iterator. */ const char* c; @@ -495,11 +498,11 @@ std::string cmOutputConverter::Shell__GetArgument(const char* in, int flags) /* Add the opening quote for this argument. */ if (flags & Shell_Flag_WatcomQuote) { if (flags & Shell_Flag_IsUnix) { - out << '"'; + out += '"'; } - out << '\''; + out += '\''; } else { - out << '"'; + out += '"'; } } @@ -511,7 +514,7 @@ std::string cmOutputConverter::Shell__GetArgument(const char* in, int flags) if (skip != c) { /* Copy to the end of the make variable references. */ while (c != skip) { - out << *c++; + out += *c++; } /* The make variable reference eliminates any escaping needed @@ -531,7 +534,7 @@ std::string cmOutputConverter::Shell__GetArgument(const char* in, int flags) quoted argument. */ if (*c == '\\' || *c == '"' || *c == '`' || *c == '$') { /* This character needs a backslash to escape it. */ - out << '\\'; + out += '\\'; } } else if (flags & Shell_Flag_EchoWindows) { /* On Windows the built-in command shell echo never needs escaping. */ @@ -545,11 +548,11 @@ std::string cmOutputConverter::Shell__GetArgument(const char* in, int flags) backslashes. */ while (windows_backslashes > 0) { --windows_backslashes; - out << '\\'; + out += '\\'; } /* Add the backslash to escape the double-quote. */ - out << '\\'; + out += '\\'; } else { /* We encountered a normal character. This eliminates any escaping needed for preceding backslashes. */ @@ -562,7 +565,7 @@ std::string cmOutputConverter::Shell__GetArgument(const char* in, int flags) if (flags & Shell_Flag_Make) { /* In Makefiles a dollar is written $$. The make tool will replace it with just $ before passing it to the shell. */ - out << "$$"; + out += "$$"; } else if (flags & Shell_Flag_VSIDE) { /* In a VS IDE a dollar is written "$". If this is written in an un-quoted argument it starts a quoted segment, inserts @@ -570,30 +573,30 @@ std::string cmOutputConverter::Shell__GetArgument(const char* in, int flags) argument it ends quoting, inserts the $ and restarts quoting. Either way the $ is isolated from surrounding text to avoid looking like a variable reference. */ - out << "\"$\""; + out += "\"$\""; } else { /* Otherwise a dollar is written just $. */ - out << '$'; + out += '$'; } } else if (*c == '#') { if ((flags & Shell_Flag_Make) && (flags & Shell_Flag_WatcomWMake)) { /* In Watcom WMake makefiles a pound is written $#. The make tool will replace it with just # before passing it to the shell. */ - out << "$#"; + out += "$#"; } else { /* Otherwise a pound is written just #. */ - out << '#'; + out += '#'; } } else if (*c == '%') { if ((flags & Shell_Flag_VSIDE) || ((flags & Shell_Flag_Make) && ((flags & Shell_Flag_MinGWMake) || (flags & Shell_Flag_NMake)))) { /* In the VS IDE, NMake, or MinGW make a percent is written %%. */ - out << "%%"; + out += "%%"; } else { /* Otherwise a percent is written just %. */ - out << '%'; + out += '%'; } } else if (*c == ';') { if (flags & Shell_Flag_VSIDE) { @@ -602,14 +605,14 @@ std::string cmOutputConverter::Shell__GetArgument(const char* in, int flags) inserts the ; and ends the segment. If it is written in a quoted argument it ends quoting, inserts the ; and restarts quoting. Either way the ; is isolated. */ - out << "\";\""; + out += "\";\""; } else { /* Otherwise a semicolon is written just ;. */ - out << ';'; + out += ';'; } } else { /* Store this character. */ - out << *c; + out += *c; } } @@ -617,19 +620,19 @@ std::string cmOutputConverter::Shell__GetArgument(const char* in, int flags) /* Add enough backslashes to escape any trailing ones. */ while (windows_backslashes > 0) { --windows_backslashes; - out << '\\'; + out += '\\'; } /* Add the closing quote for this argument. */ if (flags & Shell_Flag_WatcomQuote) { - out << '\''; + out += '\''; if (flags & Shell_Flag_IsUnix) { - out << '"'; + out += '"'; } } else { - out << '"'; + out += '"'; } } - return out.str(); + return out; } diff --git a/Source/cmOutputConverter.h b/Source/cmOutputConverter.h index ae15055..ed7143c 100644 --- a/Source/cmOutputConverter.h +++ b/Source/cmOutputConverter.h @@ -117,11 +117,7 @@ public: private: cmState* GetState() const; - static int Shell__CharIsWhitespace(char c); - static int Shell__CharNeedsQuotesOnUnix(char c); - static int Shell__CharNeedsQuotesOnWindows(char c); static int Shell__CharNeedsQuotes(char c, int flags); - static int Shell__CharIsMakeVariableName(char c); static const char* Shell__SkipMakeVariables(const char* c); static int Shell__ArgumentNeedsQuotes(const char* in, int flags); static std::string Shell__GetArgument(const char* in, int flags); diff --git a/Source/cmPolicies.cxx b/Source/cmPolicies.cxx index e7d1b72..dba22b3 100644 --- a/Source/cmPolicies.cxx +++ b/Source/cmPolicies.cxx @@ -153,31 +153,26 @@ static bool GetPolicyDefault(cmMakefile* mf, std::string const& policy, return true; } -bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf, const char* version) +bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf, + std::string const& version_min, + std::string const& version_max) { - std::string ver = "2.4.0"; - - if (version && strlen(version) > 0) { - ver = version; - } - - unsigned int majorVer = 2; - unsigned int minorVer = 0; - unsigned int patchVer = 0; - unsigned int tweakVer = 0; - - // parse the string - if (sscanf(ver.c_str(), "%u.%u.%u.%u", &majorVer, &minorVer, &patchVer, - &tweakVer) < 2) { + // Parse components of the minimum version. + unsigned int minMajor = 2; + unsigned int minMinor = 0; + unsigned int minPatch = 0; + unsigned int minTweak = 0; + if (sscanf(version_min.c_str(), "%u.%u.%u.%u", &minMajor, &minMinor, + &minPatch, &minTweak) < 2) { std::ostringstream e; - e << "Invalid policy version value \"" << ver << "\". " + e << "Invalid policy version value \"" << version_min << "\". " << "A numeric major.minor[.patch[.tweak]] must be given."; mf->IssueMessage(cmake::FATAL_ERROR, e.str()); return false; } // it is an error if the policy version is less than 2.4 - if (majorVer < 2 || (majorVer == 2 && minorVer < 4)) { + if (minMajor < 2 || (minMajor == 2 && minMinor < 4)) { mf->IssueMessage( cmake::FATAL_ERROR, "Compatibility with CMake < 2.4 is not supported by CMake >= 3.0. " @@ -188,19 +183,19 @@ bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf, const char* version) // It is an error if the policy version is greater than the running // CMake. - if (majorVer > cmVersion::GetMajorVersion() || - (majorVer == cmVersion::GetMajorVersion() && - minorVer > cmVersion::GetMinorVersion()) || - (majorVer == cmVersion::GetMajorVersion() && - minorVer == cmVersion::GetMinorVersion() && - patchVer > cmVersion::GetPatchVersion()) || - (majorVer == cmVersion::GetMajorVersion() && - minorVer == cmVersion::GetMinorVersion() && - patchVer == cmVersion::GetPatchVersion() && - tweakVer > cmVersion::GetTweakVersion())) { + if (minMajor > cmVersion::GetMajorVersion() || + (minMajor == cmVersion::GetMajorVersion() && + minMinor > cmVersion::GetMinorVersion()) || + (minMajor == cmVersion::GetMajorVersion() && + minMinor == cmVersion::GetMinorVersion() && + minPatch > cmVersion::GetPatchVersion()) || + (minMajor == cmVersion::GetMajorVersion() && + minMinor == cmVersion::GetMinorVersion() && + minPatch == cmVersion::GetPatchVersion() && + minTweak > cmVersion::GetTweakVersion())) { std::ostringstream e; e << "An attempt was made to set the policy version of CMake to \"" - << version << "\" which is greater than this version of CMake. " + << version_min << "\" which is greater than this version of CMake. " << "This is not allowed because the greater version may have new " << "policies not known to this CMake. " << "You may need a newer CMake version to build this project."; @@ -208,6 +203,52 @@ bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf, const char* version) return false; } + unsigned int polMajor = minMajor; + unsigned int polMinor = minMinor; + unsigned int polPatch = minPatch; + + if (!version_max.empty()) { + // Parse components of the maximum version. + unsigned int maxMajor = 0; + unsigned int maxMinor = 0; + unsigned int maxPatch = 0; + unsigned int maxTweak = 0; + if (sscanf(version_max.c_str(), "%u.%u.%u.%u", &maxMajor, &maxMinor, + &maxPatch, &maxTweak) < 2) { + std::ostringstream e; + e << "Invalid policy max version value \"" << version_max << "\". " + << "A numeric major.minor[.patch[.tweak]] must be given."; + mf->IssueMessage(cmake::FATAL_ERROR, e.str()); + return false; + } + + // It is an error if the min version is greater than the max version. + if (minMajor > maxMajor || (minMajor == maxMajor && minMinor > maxMinor) || + (minMajor == maxMajor && minMinor == maxMinor && + minPatch > maxPatch) || + (minMajor == maxMajor && minMinor == maxMinor && + minPatch == maxPatch && minTweak > maxTweak)) { + std::ostringstream e; + e << "Policy VERSION range \"" << version_min << "..." << version_max + << "\"" + << " specifies a larger minimum than maximum."; + mf->IssueMessage(cmake::FATAL_ERROR, e.str()); + return false; + } + + // Use the max version as the policy version. + polMajor = maxMajor; + polMinor = maxMinor; + polPatch = maxPatch; + } + + return cmPolicies::ApplyPolicyVersion(mf, polMajor, polMinor, polPatch); +} + +bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf, unsigned int majorVer, + unsigned int minorVer, + unsigned int patchVer) +{ // now loop over all the policies and set them as appropriate std::vector<cmPolicies::PolicyID> ancientPolicies; for (PolicyID pid = cmPolicies::CMP0000; pid != cmPolicies::CMPCOUNT; diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index c39f927..7190c36 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -214,7 +214,15 @@ class cmMakefile; 3, 10, 0, cmPolicies::WARN) \ SELECT(POLICY, CMP0072, \ "FindOpenGL prefers GLVND by default when available.", 3, 11, 0, \ - cmPolicies::WARN) + cmPolicies::WARN) \ + SELECT(POLICY, CMP0073, \ + "Do not produce legacy _LIB_DEPENDS cache entries.", 3, 12, 0, \ + cmPolicies::WARN) \ + SELECT(POLICY, CMP0074, "find_package uses PackageName_ROOT variables.", 3, \ + 12, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0075, \ + "Include file check macros honor CMAKE_REQUIRED_LIBRARIES.", 3, 12, \ + 0, cmPolicies::WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) #define CM_FOR_EACH_POLICY_ID(POLICY) \ @@ -238,14 +246,13 @@ class cmMakefile; F(CMP0063) \ F(CMP0065) \ F(CMP0068) \ - F(CMP0069) + F(CMP0069) \ + F(CMP0073) /** \class cmPolicies * \brief Handles changes in CMake behavior and policies * - * See the cmake wiki section on - * <a href="https://cmake.org/Wiki/CMake/Policies">policies</a> - * for an overview of this class's purpose + * See the cmake-policies(7) manual for an overview of this class's purpose. */ class cmPolicies { @@ -284,7 +291,11 @@ public: static cmPolicies::PolicyStatus GetPolicyStatus(cmPolicies::PolicyID id); ///! Set a policy level for this listfile - static bool ApplyPolicyVersion(cmMakefile* mf, const char* version); + static bool ApplyPolicyVersion(cmMakefile* mf, + std::string const& version_min, + std::string const& version_max); + static bool ApplyPolicyVersion(cmMakefile* mf, unsigned int majorVer, + unsigned int minorVer, unsigned int patchVer); ///! return a warning string for a given policy static std::string GetPolicyWarning(cmPolicies::PolicyID id); diff --git a/Source/cmProjectCommand.cxx b/Source/cmProjectCommand.cxx index dfa1858..a25bd6b 100644 --- a/Source/cmProjectCommand.cxx +++ b/Source/cmProjectCommand.cxx @@ -3,9 +3,11 @@ #include "cmProjectCommand.h" #include "cmsys/RegularExpression.hxx" +#include <functional> #include <sstream> #include <stdio.h> +#include "cmAlgorithms.h" #include "cmMakefile.h" #include "cmPolicies.h" #include "cmStateTypes.h" @@ -66,12 +68,19 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, bool haveVersion = false; bool haveLanguages = false; bool haveDescription = false; + bool haveHomepage = false; std::string version; std::string description; + std::string homepage; std::vector<std::string> languages; + std::function<void()> missedValueReporter; + auto resetReporter = [&missedValueReporter]() { + missedValueReporter = std::function<void()>(); + }; enum Doing { DoingDescription, + DoingHomepage, DoingLanguages, DoingVersion }; @@ -85,7 +94,18 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, return true; } haveLanguages = true; + if (missedValueReporter) { + missedValueReporter(); + } doing = DoingLanguages; + if (!languages.empty()) { + std::string msg = + "the following parameters must be specified after LANGUAGES " + "keyword: "; + msg += cmJoin(languages, ", "); + msg += '.'; + this->Makefile->IssueMessage(cmake::WARNING, msg); + } } else if (args[i] == "VERSION") { if (haveVersion) { this->Makefile->IssueMessage(cmake::FATAL_ERROR, @@ -94,7 +114,17 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, return true; } haveVersion = true; + if (missedValueReporter) { + missedValueReporter(); + } doing = DoingVersion; + missedValueReporter = [this, &resetReporter]() { + this->Makefile->IssueMessage( + cmake::WARNING, + "VERSION keyword not followed by a value or was followed by a " + "value that expanded to nothing."); + resetReporter(); + }; } else if (args[i] == "DESCRIPTION") { if (haveDescription) { this->Makefile->IssueMessage( @@ -103,23 +133,61 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, return true; } haveDescription = true; + if (missedValueReporter) { + missedValueReporter(); + } doing = DoingDescription; + missedValueReporter = [this, &resetReporter]() { + this->Makefile->IssueMessage( + cmake::WARNING, + "DESCRIPTION keyword not followed by a value or was followed " + "by a value that expanded to nothing."); + resetReporter(); + }; + } else if (args[i] == "HOMEPAGE_URL") { + if (haveHomepage) { + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, "HOMEPAGE_URL may be specified at most once."); + cmSystemTools::SetFatalErrorOccured(); + return true; + } + haveHomepage = true; + doing = DoingHomepage; + missedValueReporter = [this, &resetReporter]() { + this->Makefile->IssueMessage( + cmake::WARNING, + "HOMEPAGE_URL keyword not followed by a value or was followed " + "by a value that expanded to nothing."); + resetReporter(); + }; } else if (doing == DoingVersion) { doing = DoingLanguages; version = args[i]; + resetReporter(); } else if (doing == DoingDescription) { doing = DoingLanguages; description = args[i]; + resetReporter(); + } else if (doing == DoingHomepage) { + doing = DoingLanguages; + homepage = args[i]; + resetReporter(); } else // doing == DoingLanguages { languages.push_back(args[i]); } } - if (haveVersion && !haveLanguages && !languages.empty()) { + if (missedValueReporter) { + missedValueReporter(); + } + + if ((haveVersion || haveDescription || haveHomepage) && !haveLanguages && + !languages.empty()) { this->Makefile->IssueMessage( cmake::FATAL_ERROR, - "project with VERSION must use LANGUAGES before language names."); + "project with VERSION, DESCRIPTION or HOMEPAGE_URL must " + "use LANGUAGES before language names."); cmSystemTools::SetFatalErrorOccured(); return true; } @@ -181,6 +249,12 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, vv = projectName + "_VERSION_TWEAK"; this->Makefile->AddDefinition("PROJECT_VERSION_TWEAK", vb[3]); this->Makefile->AddDefinition(vv, vb[3]); + // Also, try set top level variables + TopLevelCMakeVarCondSet("CMAKE_PROJECT_VERSION", vs.c_str()); + TopLevelCMakeVarCondSet("CMAKE_PROJECT_VERSION_MAJOR", vb[0]); + TopLevelCMakeVarCondSet("CMAKE_PROJECT_VERSION_MINOR", vb[1]); + TopLevelCMakeVarCondSet("CMAKE_PROJECT_VERSION_PATCH", vb[2]); + TopLevelCMakeVarCondSet("CMAKE_PROJECT_VERSION_TWEAK", vb[3]); } else if (cmp0048 != cmPolicies::OLD) { // Set project VERSION variables to empty std::vector<std::string> vv; @@ -194,6 +268,13 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, vv.push_back(projectName + "_VERSION_MINOR"); vv.push_back(projectName + "_VERSION_PATCH"); vv.push_back(projectName + "_VERSION_TWEAK"); + if (this->Makefile->IsRootMakefile()) { + vv.push_back("CMAKE_PROJECT_VERSION"); + vv.push_back("CMAKE_PROJECT_VERSION_MAJOR"); + vv.push_back("CMAKE_PROJECT_VERSION_MINOR"); + vv.push_back("CMAKE_PROJECT_VERSION_PATCH"); + vv.push_back("CMAKE_PROJECT_VERSION_TWEAK"); + } std::string vw; for (std::string const& i : vv) { const char* v = this->Makefile->GetDefinition(i); @@ -216,18 +297,16 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, if (haveDescription) { this->Makefile->AddDefinition("PROJECT_DESCRIPTION", description.c_str()); - // Set the CMAKE_PROJECT_DESCRIPTION variable to be the highest-level - // project name in the tree. If there are two project commands - // in the same CMakeLists.txt file, and it is the top level - // CMakeLists.txt file, then go with the last one. - if (!this->Makefile->GetDefinition("CMAKE_PROJECT_DESCRIPTION") || - (this->Makefile->IsRootMakefile())) { - this->Makefile->AddDefinition("CMAKE_PROJECT_DESCRIPTION", - description.c_str()); - this->Makefile->AddCacheDefinition( - "CMAKE_PROJECT_DESCRIPTION", description.c_str(), - "Value Computed by CMake", cmStateEnums::STATIC); - } + this->Makefile->AddDefinition(projectName + "_DESCRIPTION", + description.c_str()); + TopLevelCMakeVarCondSet("CMAKE_PROJECT_DESCRIPTION", description.c_str()); + } + + if (haveHomepage) { + this->Makefile->AddDefinition("PROJECT_HOMEPAGE_URL", homepage.c_str()); + this->Makefile->AddDefinition(projectName + "_HOMEPAGE_URL", + homepage.c_str()); + TopLevelCMakeVarCondSet("CMAKE_PROJECT_HOMEPAGE_URL", homepage.c_str()); } if (languages.empty()) { @@ -250,3 +329,18 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, } return true; } + +void cmProjectCommand::TopLevelCMakeVarCondSet(const char* const name, + const char* const value) +{ + // Set the CMAKE_PROJECT_XXX variable to be the highest-level + // project name in the tree. If there are two project commands + // in the same CMakeLists.txt file, and it is the top level + // CMakeLists.txt file, then go with the last one. + if (!this->Makefile->GetDefinition(name) || + (this->Makefile->IsRootMakefile())) { + this->Makefile->AddDefinition(name, value); + this->Makefile->AddCacheDefinition(name, value, "Value Computed by CMake", + cmStateEnums::STATIC); + } +} diff --git a/Source/cmProjectCommand.h b/Source/cmProjectCommand.h index 80fa235..365d448 100644 --- a/Source/cmProjectCommand.h +++ b/Source/cmProjectCommand.h @@ -34,6 +34,9 @@ public: */ bool InitialPass(std::vector<std::string> const& args, cmExecutionStatus& status) override; + +private: + void TopLevelCMakeVarCondSet(const char* name, const char* value); }; #endif diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h index 67f61b1..a3e16ac 100644 --- a/Source/cmQtAutoGen.h +++ b/Source/cmQtAutoGen.h @@ -34,7 +34,7 @@ public: /// @brief Returns the generator name in upper case static std::string GeneratorNameUpper(GeneratorT genType); - /// @brief Returns a the string escaped and enclosed in quotes + /// @brief Returns the string escaped and enclosed in quotes static std::string Quoted(std::string const& text); static std::string QuotedCommand(std::vector<std::string> const& command); diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx index 4aa1b1f..bf184d8 100644 --- a/Source/cmQtAutoGenerator.cxx +++ b/Source/cmQtAutoGenerator.cxx @@ -681,7 +681,7 @@ bool cmQtAutoGenerator::Run(std::string const& infoFile, auto makefile = cm::make_unique<cmMakefile>(&gg, snapshot); // The OLD/WARN behavior for policy CMP0053 caused a speed regression. // https://gitlab.kitware.com/cmake/cmake/issues/17570 - makefile->SetPolicyVersion("3.9"); + makefile->SetPolicyVersion("3.9", std::string()); gg.SetCurrentMakefile(makefile.get()); success = this->Init(makefile.get()); } diff --git a/Source/cmQtAutoGeneratorMocUic.cxx b/Source/cmQtAutoGeneratorMocUic.cxx index e2fd158..80db6ac 100644 --- a/Source/cmQtAutoGeneratorMocUic.cxx +++ b/Source/cmQtAutoGeneratorMocUic.cxx @@ -434,7 +434,7 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, JobHandleT jobHandle(new JobMocT(std::move(jobPre.SourceFile), FileName, std::move(jobPre.IncludeString))); if (jobPre.self) { - // Read depdendencies from this source + // Read dependencies from this source static_cast<JobMocT&>(*jobHandle).FindDependencies(wrk, meta.Content); } if (!wrk.Gen().ParallelJobPushMoc(jobHandle)) { @@ -452,7 +452,7 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocHeader(WorkerT& wrk, if (!macroName.empty()) { JobHandleT jobHandle( new JobMocT(std::string(FileName), std::string(), std::string())); - // Read depdendencies from this source + // Read dependencies from this source static_cast<JobMocT&>(*jobHandle).FindDependencies(wrk, meta.Content); success = wrk.Gen().ParallelJobPushMoc(jobHandle); } @@ -1381,7 +1381,7 @@ bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile) // Compare list sizes if (sources.size() != options.size()) { std::ostringstream ost; - ost << "files/options lists sizes missmatch (" << sources.size() << "/" + ost << "files/options lists sizes mismatch (" << sources.size() << "/" << options.size() << ")"; Log().ErrorFile(GeneratorT::UIC, InfoFile(), ost.str()); return false; @@ -1884,7 +1884,7 @@ bool cmQtAutoGeneratorMocUic::ParallelJobPushMoc(JobHandleT& jobHandle) error += Quoted(mocJob.IncluderFile); error += " and\n "; error += Quoted(otherJob.IncluderFile); - error += "\ncontain the the same moc include string "; + error += "\ncontain the same moc include string "; error += Quoted(mocJob.IncludeString); error += "\nbut the moc file would be generated from different " "source files\n "; @@ -1933,7 +1933,7 @@ bool cmQtAutoGeneratorMocUic::ParallelJobPushUic(JobHandleT& jobHandle) error += Quoted(uicJob.IncluderFile); error += " and\n "; error += Quoted(otherJob.IncluderFile); - error += "\ncontain the the same uic include string "; + error += "\ncontain the same uic include string "; error += Quoted(uicJob.IncludeString); error += "\nbut the uic file would be generated from different " "source files\n "; diff --git a/Source/cmRST.cxx b/Source/cmRST.cxx index 112be4d..d9e5bcb 100644 --- a/Source/cmRST.cxx +++ b/Source/cmRST.cxx @@ -34,11 +34,13 @@ cmRST::cmRST(std::ostream& os, std::string const& docroot) , NoteDirective("^.. note::[ \t]*(.*)$") , ModuleRST("^#\\[(=*)\\[\\.rst:$") , CMakeRole("(:cmake)?:(" - "command|generator|variable|module|policy|" + "command|generator|variable|envvar|module|policy|" "prop_cache|prop_dir|prop_gbl|prop_inst|prop_sf|" "prop_test|prop_tgt|" "manual" "):`(<*([^`<]|[^` \t]<)*)([ \t]+<[^`]*>)?`") + , InlineLink("`(<*([^`<]|[^` \t]<)*)([ \t]+<[^`]*>)?`_") + , InlineLiteral("``([^`]*)``") , Substitution("(^|[^A-Za-z0-9_])" "((\\|[^| \t\r\n]([^|\r\n]*[^| \t\r\n])?\\|)(__|_|))" "([^A-Za-z0-9_]|$)") @@ -245,18 +247,62 @@ void cmRST::OutputLine(std::string const& line_in, bool inlineMarkup) if (inlineMarkup) { std::string line = this->ReplaceSubstitutions(line_in); std::string::size_type pos = 0; - while (this->CMakeRole.find(line.c_str() + pos)) { - this->OS << line.substr(pos, this->CMakeRole.start()); - std::string text = this->CMakeRole.match(3); - // If a command reference has no explicit target and - // no explicit "(...)" then add "()" to the text. - if (this->CMakeRole.match(2) == "command" && - this->CMakeRole.match(5).empty() && - text.find_first_of("()") == std::string::npos) { - text += "()"; + for (;;) { + std::string::size_type* first = nullptr; + std::string::size_type role_start = std::string::npos; + std::string::size_type link_start = std::string::npos; + std::string::size_type lit_start = std::string::npos; + if (this->CMakeRole.find(line.c_str() + pos)) { + role_start = this->CMakeRole.start(); + first = &role_start; + } + if (this->InlineLiteral.find(line.c_str() + pos)) { + lit_start = this->InlineLiteral.start(); + if (!first || lit_start < *first) { + first = &lit_start; + } + } + if (this->InlineLink.find(line.c_str() + pos)) { + link_start = this->InlineLink.start(); + if (!first || link_start < *first) { + first = &link_start; + } + } + if (first == &role_start) { + this->OS << line.substr(pos, role_start); + std::string text = this->CMakeRole.match(3); + // If a command reference has no explicit target and + // no explicit "(...)" then add "()" to the text. + if (this->CMakeRole.match(2) == "command" && + this->CMakeRole.match(5).empty() && + text.find_first_of("()") == std::string::npos) { + text += "()"; + } + this->OS << "``" << text << "``"; + pos += this->CMakeRole.end(); + } else if (first == &lit_start) { + this->OS << line.substr(pos, lit_start); + std::string text = this->InlineLiteral.match(1); + pos += this->InlineLiteral.end(); + this->OS << "``" << text << "``"; + } else if (first == &link_start) { + this->OS << line.substr(pos, link_start); + std::string text = this->InlineLink.match(1); + bool escaped = false; + for (char c : text) { + if (escaped) { + escaped = false; + this->OS << c; + } else if (c == '\\') { + escaped = true; + } else { + this->OS << c; + } + } + pos += this->InlineLink.end(); + } else { + break; } - this->OS << "``" << text << "``"; - pos += this->CMakeRole.end(); } this->OS << line.substr(pos) << "\n"; } else { diff --git a/Source/cmRST.h b/Source/cmRST.h index d1a8e27..ee47867 100644 --- a/Source/cmRST.h +++ b/Source/cmRST.h @@ -85,6 +85,8 @@ private: cmsys::RegularExpression NoteDirective; cmsys::RegularExpression ModuleRST; cmsys::RegularExpression CMakeRole; + cmsys::RegularExpression InlineLink; + cmsys::RegularExpression InlineLiteral; cmsys::RegularExpression Substitution; cmsys::RegularExpression TocTreeLink; diff --git a/Source/cmRulePlaceholderExpander.h b/Source/cmRulePlaceholderExpander.h index 7b19210..a7d8cee 100644 --- a/Source/cmRulePlaceholderExpander.h +++ b/Source/cmRulePlaceholderExpander.h @@ -24,7 +24,7 @@ public: this->TargetImpLib = targetImpLib; } - // Create a struct to hold the varibles passed into + // Create a struct to hold the variables passed into // ExpandRuleVariables struct RuleVariables { diff --git a/Source/cmSearchPath.h b/Source/cmSearchPath.h index fd0c7c5..2a576ed 100644 --- a/Source/cmSearchPath.h +++ b/Source/cmSearchPath.h @@ -39,10 +39,10 @@ public: void AddCMakePrefixPath(const std::string& variable); void AddEnvPrefixPath(const std::string& variable, bool stripBin = false); void AddSuffixes(const std::vector<std::string>& suffixes); - -protected: void AddPrefixPaths(const std::vector<std::string>& paths, const char* base = nullptr); + +protected: void AddPathInternal(const std::string& path, const char* base = nullptr); cmFindCommon* FC; diff --git a/Source/cmSourceGroupCommand.cxx b/Source/cmSourceGroupCommand.cxx index 8c9b63c..c3df313 100644 --- a/Source/cmSourceGroupCommand.cxx +++ b/Source/cmSourceGroupCommand.cxx @@ -101,9 +101,13 @@ bool addFilesToItsSourceGroups(const std::string& root, tokenizedPath = tokenizePath(sgFilesPath); } - if (tokenizedPath.size() > 1) { + if (!tokenizedPath.empty()) { tokenizedPath.pop_back(); + if (tokenizedPath.empty()) { + tokenizedPath.push_back(""); + } + sg = makefile.GetOrCreateSourceGroup(tokenizedPath); if (!sg) { diff --git a/Source/cmState.cxx b/Source/cmState.cxx index bb891b5..a57be4d 100644 --- a/Source/cmState.cxx +++ b/Source/cmState.cxx @@ -13,6 +13,7 @@ #include "cmCommand.h" #include "cmDefinitions.h" #include "cmDisallowedCommand.h" +#include "cmGlobVerificationManager.h" #include "cmListFileCache.h" #include "cmStatePrivate.h" #include "cmStateSnapshot.h" @@ -31,11 +32,13 @@ cmState::cmState() , MSYSShell(false) { this->CacheManager = new cmCacheManager; + this->GlobVerificationManager = new cmGlobVerificationManager; } cmState::~cmState() { delete this->CacheManager; + delete this->GlobVerificationManager; cmDeleteAll(this->BuiltinCommands); cmDeleteAll(this->ScriptedCommands); } @@ -207,6 +210,39 @@ void cmState::AddCacheEntry(const std::string& key, const char* value, this->CacheManager->AddCacheEntry(key, value, helpString, type); } +bool cmState::DoWriteGlobVerifyTarget() const +{ + return this->GlobVerificationManager->DoWriteVerifyTarget(); +} + +std::string const& cmState::GetGlobVerifyScript() const +{ + return this->GlobVerificationManager->GetVerifyScript(); +} + +std::string const& cmState::GetGlobVerifyStamp() const +{ + return this->GlobVerificationManager->GetVerifyStamp(); +} + +bool cmState::SaveVerificationScript(const std::string& path) +{ + return this->GlobVerificationManager->SaveVerificationScript(path); +} + +void cmState::AddGlobCacheEntry(bool recurse, bool listDirectories, + bool followSymlinks, + const std::string& relative, + const std::string& expression, + const std::vector<std::string>& files, + const std::string& variable, + cmListFileBacktrace const& backtrace) +{ + this->GlobVerificationManager->AddCacheEntry( + recurse, listDirectories, followSymlinks, relative, expression, files, + variable, backtrace); +} + void cmState::RemoveCacheEntry(std::string const& key) { this->CacheManager->RemoveCacheEntry(key); @@ -426,13 +462,17 @@ void cmState::AddScriptedCommand(std::string const& name, cmCommand* command) cmCommand* cmState::GetCommand(std::string const& name) const { - std::string sName = cmSystemTools::LowerCase(name); + return GetCommandByExactName(cmSystemTools::LowerCase(name)); +} + +cmCommand* cmState::GetCommandByExactName(std::string const& name) const +{ std::map<std::string, cmCommand*>::const_iterator pos; - pos = this->ScriptedCommands.find(sName); + pos = this->ScriptedCommands.find(name); if (pos != this->ScriptedCommands.end()) { return pos->second; } - pos = this->BuiltinCommands.find(sName); + pos = this->BuiltinCommands.find(name); if (pos != this->BuiltinCommands.end()) { return pos->second; } diff --git a/Source/cmState.h b/Source/cmState.h index 6cbf82d..38bdfec 100644 --- a/Source/cmState.h +++ b/Source/cmState.h @@ -12,6 +12,7 @@ #include "cmDefinitions.h" #include "cmLinkedTree.h" +#include "cmListFileCache.h" #include "cmPolicies.h" #include "cmProperty.h" #include "cmPropertyDefinitionMap.h" @@ -21,6 +22,7 @@ class cmCacheManager; class cmCommand; +class cmGlobVerificationManager; class cmPropertyDefinition; class cmStateSnapshot; class cmMessenger; @@ -123,7 +125,11 @@ public: bool GetIsGeneratorMultiConfig() const; void SetIsGeneratorMultiConfig(bool b); + // Returns a command from its name, case insensitive, or nullptr cmCommand* GetCommand(std::string const& name) const; + // Returns a command from its name, or nullptr + cmCommand* GetCommandByExactName(std::string const& name) const; + void AddBuiltinCommand(std::string const& name, cmCommand* command); void AddDisallowedCommand(std::string const& name, cmCommand* command, cmPolicies::PolicyID policy, const char* message); @@ -165,12 +171,24 @@ private: const char* helpString, cmStateEnums::CacheEntryType type); + bool DoWriteGlobVerifyTarget() const; + std::string const& GetGlobVerifyScript() const; + std::string const& GetGlobVerifyStamp() const; + bool SaveVerificationScript(const std::string& path); + void AddGlobCacheEntry(bool recurse, bool listDirectories, + bool followSymlinks, const std::string& relative, + const std::string& expression, + const std::vector<std::string>& files, + const std::string& variable, + cmListFileBacktrace const& bt); + std::map<cmProperty::ScopeType, cmPropertyDefinitionMap> PropertyDefinitions; std::vector<std::string> EnabledLanguages; std::map<std::string, cmCommand*> BuiltinCommands; std::map<std::string, cmCommand*> ScriptedCommands; cmPropertyMap GlobalProperties; cmCacheManager* CacheManager; + cmGlobVerificationManager* GlobVerificationManager; cmLinkedTree<cmStateDetail::BuildsystemDirectoryStateType> BuildsystemDirectory; diff --git a/Source/cmStateSnapshot.cxx b/Source/cmStateSnapshot.cxx index 479ecd2..0229a77 100644 --- a/Source/cmStateSnapshot.cxx +++ b/Source/cmStateSnapshot.cxx @@ -6,7 +6,7 @@ #include <algorithm> #include <assert.h> #include <iterator> -#include <stdio.h> +#include <string> #include "cmAlgorithms.h" #include "cmDefinitions.h" @@ -160,8 +160,8 @@ void cmStateSnapshot::SetPolicy(cmPolicies::PolicyID id, } } -cmPolicies::PolicyStatus cmStateSnapshot::GetPolicy( - cmPolicies::PolicyID id) const +cmPolicies::PolicyStatus cmStateSnapshot::GetPolicy(cmPolicies::PolicyID id, + bool parent_scope) const { cmPolicies::PolicyStatus status = cmPolicies::GetPolicyStatus(id); @@ -180,6 +180,10 @@ cmPolicies::PolicyStatus cmStateSnapshot::GetPolicy( cmLinkedTree<cmStateDetail::PolicyStackEntry>::iterator root = dir->DirectoryEnd->PolicyRoot; for (; leaf != root; ++leaf) { + if (parent_scope) { + parent_scope = false; + continue; + } if (leaf->IsDefined(id)) { status = leaf->Get(id); return status; @@ -328,15 +332,14 @@ void cmStateSnapshot::SetDefaultDefinitions() this->SetDefinition("CMAKE_HOST_SOLARIS", "1"); #endif - char temp[1024]; - sprintf(temp, "%d", cmVersion::GetMinorVersion()); - this->SetDefinition("CMAKE_MINOR_VERSION", temp); - sprintf(temp, "%d", cmVersion::GetMajorVersion()); - this->SetDefinition("CMAKE_MAJOR_VERSION", temp); - sprintf(temp, "%d", cmVersion::GetPatchVersion()); - this->SetDefinition("CMAKE_PATCH_VERSION", temp); - sprintf(temp, "%d", cmVersion::GetTweakVersion()); - this->SetDefinition("CMAKE_TWEAK_VERSION", temp); + this->SetDefinition("CMAKE_MAJOR_VERSION", + std::to_string(cmVersion::GetMajorVersion())); + this->SetDefinition("CMAKE_MINOR_VERSION", + std::to_string(cmVersion::GetMinorVersion())); + this->SetDefinition("CMAKE_PATCH_VERSION", + std::to_string(cmVersion::GetPatchVersion())); + this->SetDefinition("CMAKE_TWEAK_VERSION", + std::to_string(cmVersion::GetTweakVersion())); this->SetDefinition("CMAKE_VERSION", cmVersion::GetCMakeVersion()); this->SetDefinition("CMAKE_FILES_DIRECTORY", diff --git a/Source/cmStateSnapshot.h b/Source/cmStateSnapshot.h index 94d6274..af5653b 100644 --- a/Source/cmStateSnapshot.h +++ b/Source/cmStateSnapshot.h @@ -43,7 +43,8 @@ public: cmStateEnums::SnapshotType GetType() const; void SetPolicy(cmPolicies::PolicyID id, cmPolicies::PolicyStatus status); - cmPolicies::PolicyStatus GetPolicy(cmPolicies::PolicyID id) const; + cmPolicies::PolicyStatus GetPolicy(cmPolicies::PolicyID id, + bool parent_scope = false) const; bool HasDefinedPolicyCMP0011(); void PushPolicy(cmPolicies::PolicyMap const& entry, bool weak); bool PopPolicy(); diff --git a/Source/cmStringCommand.cxx b/Source/cmStringCommand.cxx index 55af078..dabec47 100644 --- a/Source/cmStringCommand.cxx +++ b/Source/cmStringCommand.cxx @@ -13,6 +13,7 @@ #include "cmCryptoHash.h" #include "cmGeneratorExpression.h" #include "cmMakefile.h" +#include "cmStringReplaceHelper.h" #include "cmSystemTools.h" #include "cmTimestamp.h" #include "cmUuid.h" @@ -68,6 +69,9 @@ bool cmStringCommand::InitialPass(std::vector<std::string> const& args, if (subCommand == "CONCAT") { return this->HandleConcatCommand(args); } + if (subCommand == "JOIN") { + return this->HandleJoinCommand(args); + } if (subCommand == "SUBSTRING") { return this->HandleSubstringCommand(args); } @@ -341,46 +345,17 @@ bool cmStringCommand::RegexReplace(std::vector<std::string> const& args) std::string const& regex = args[2]; std::string const& replace = args[3]; std::string const& outvar = args[4]; + cmStringReplaceHelper replaceHelper(regex, replace, this->Makefile); - // Pull apart the replace expression to find the escaped [0-9] values. - std::vector<RegexReplacement> replacement; - std::string::size_type l = 0; - while (l < replace.length()) { - std::string::size_type r = replace.find('\\', l); - if (r == std::string::npos) { - r = replace.length(); - replacement.push_back(replace.substr(l, r - l)); - } else { - if (r - l > 0) { - replacement.push_back(replace.substr(l, r - l)); - } - if (r == (replace.length() - 1)) { - this->SetError("sub-command REGEX, mode REPLACE: " - "replace-expression ends in a backslash."); - return false; - } - if ((replace[r + 1] >= '0') && (replace[r + 1] <= '9')) { - replacement.push_back(replace[r + 1] - '0'); - } else if (replace[r + 1] == 'n') { - replacement.push_back("\n"); - } else if (replace[r + 1] == '\\') { - replacement.push_back("\\"); - } else { - std::string e = "sub-command REGEX, mode REPLACE: Unknown escape \""; - e += replace.substr(r, 2); - e += "\" in replace-expression."; - this->SetError(e); - return false; - } - r += 2; - } - l = r; + if (!replaceHelper.IsReplaceExpressionValid()) { + this->SetError("sub-command REGEX, mode REPLACE: " + + replaceHelper.GetError() + "."); + return false; } this->Makefile->ClearMatches(); - // Compile the regular expression. - cmsys::RegularExpression re; - if (!re.compile(regex.c_str())) { + + if (!replaceHelper.IsRegularExpressionValid()) { std::string e = "sub-command REGEX, mode REPLACE failed to compile regex \"" + regex + "\"."; @@ -389,60 +364,16 @@ bool cmStringCommand::RegexReplace(std::vector<std::string> const& args) } // Concatenate all the last arguments together. - std::string input = cmJoin(cmMakeRange(args).advance(5), std::string()); - - // Scan through the input for all matches. + const std::string input = + cmJoin(cmMakeRange(args).advance(5), std::string()); std::string output; - std::string::size_type base = 0; - while (re.find(input.c_str() + base)) { - this->Makefile->ClearMatches(); - this->Makefile->StoreMatches(re); - std::string::size_type l2 = re.start(); - std::string::size_type r = re.end(); - - // Concatenate the part of the input that was not matched. - output += input.substr(base, l2); - - // Make sure the match had some text. - if (r - l2 == 0) { - std::string e = "sub-command REGEX, mode REPLACE regex \"" + regex + - "\" matched an empty string."; - this->SetError(e); - return false; - } - - // Concatenate the replacement for the match. - for (RegexReplacement const& i : replacement) { - if (i.number < 0) { - // This is just a plain-text part of the replacement. - output += i.value; - } else { - // Replace with part of the match. - int n = i.number; - std::string::size_type start = re.start(n); - std::string::size_type end = re.end(n); - std::string::size_type len = input.length() - base; - if ((start != std::string::npos) && (end != std::string::npos) && - (start <= len) && (end <= len)) { - output += input.substr(base + start, end - start); - } else { - std::string e = - "sub-command REGEX, mode REPLACE: replace expression \"" + - replace + "\" contains an out-of-range escape for regex \"" + - regex + "\"."; - this->SetError(e); - return false; - } - } - } - // Move past the match. - base += r; + if (!replaceHelper.Replace(input, output)) { + this->SetError("sub-command REGEX, mode REPLACE: " + + replaceHelper.GetError() + "."); + return false; } - // Concatenate the text after the last match. - output += input.substr(base, input.length() - base); - // Store the output in the provided variable. this->Makefile->AddDefinition(outvar, output.c_str()); return true; @@ -677,8 +608,26 @@ bool cmStringCommand::HandleConcatCommand(std::vector<std::string> const& args) return false; } - std::string const& variableName = args[1]; - std::string value = cmJoin(cmMakeRange(args).advance(2), std::string()); + return this->joinImpl(args, std::string(), 1); +} + +bool cmStringCommand::HandleJoinCommand(std::vector<std::string> const& args) +{ + if (args.size() < 3) { + this->SetError("sub-command JOIN requires at least two arguments."); + return false; + } + + return this->joinImpl(args, args[1], 2); +} + +bool cmStringCommand::joinImpl(std::vector<std::string> const& args, + std::string const& glue, const size_t varIdx) +{ + std::string const& variableName = args[varIdx]; + // NOTE Items to concat/join placed right after the variable for + // both `CONCAT` and `JOIN` sub-commands. + std::string value = cmJoin(cmMakeRange(args).advance(varIdx + 1), glue); this->Makefile->AddDefinition(variableName, value.c_str()); return true; diff --git a/Source/cmStringCommand.h b/Source/cmStringCommand.h index b287e37..cbff73e 100644 --- a/Source/cmStringCommand.h +++ b/Source/cmStringCommand.h @@ -5,6 +5,7 @@ #include "cmConfigure.h" // IWYU pragma: keep +#include <cstddef> #include <string> #include <vector> @@ -48,6 +49,7 @@ protected: bool HandleAppendCommand(std::vector<std::string> const& args); bool HandlePrependCommand(std::vector<std::string> const& args); bool HandleConcatCommand(std::vector<std::string> const& args); + bool HandleJoinCommand(std::vector<std::string> const& args); bool HandleStripCommand(std::vector<std::string> const& args); bool HandleRandomCommand(std::vector<std::string> const& args); bool HandleFindCommand(std::vector<std::string> const& args); @@ -56,28 +58,8 @@ protected: bool HandleGenexStripCommand(std::vector<std::string> const& args); bool HandleUuidCommand(std::vector<std::string> const& args); - class RegexReplacement - { - public: - RegexReplacement(const char* s) - : number(-1) - , value(s) - { - } - RegexReplacement(const std::string& s) - : number(-1) - , value(s) - { - } - RegexReplacement(int n) - : number(n) - , value() - { - } - RegexReplacement() {} - int number; - std::string value; - }; + bool joinImpl(std::vector<std::string> const& args, std::string const& glue, + size_t varIdx); }; #endif diff --git a/Source/cmStringReplaceHelper.cxx b/Source/cmStringReplaceHelper.cxx new file mode 100644 index 0000000..69b7ced --- /dev/null +++ b/Source/cmStringReplaceHelper.cxx @@ -0,0 +1,117 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmStringReplaceHelper.h" + +#include "cmMakefile.h" +#include <sstream> + +cmStringReplaceHelper::cmStringReplaceHelper(const std::string& regex, + const std::string& replace_expr, + cmMakefile* makefile) + : RegExString(regex) + , RegularExpression(regex) + , ReplaceExpression(replace_expr) + , Makefile(makefile) +{ + this->ParseReplaceExpression(); +} + +bool cmStringReplaceHelper::Replace(const std::string& input, + std::string& output) +{ + output.clear(); + + // Scan through the input for all matches. + std::string::size_type base = 0; + while (this->RegularExpression.find(input.c_str() + base)) { + if (this->Makefile != nullptr) { + this->Makefile->ClearMatches(); + this->Makefile->StoreMatches(this->RegularExpression); + } + auto l2 = this->RegularExpression.start(); + auto r = this->RegularExpression.end(); + + // Concatenate the part of the input that was not matched. + output += input.substr(base, l2); + + // Make sure the match had some text. + if (r - l2 == 0) { + std::ostringstream error; + error << "regex \"" << this->RegExString << "\" matched an empty string"; + this->ErrorString = error.str(); + return false; + } + + // Concatenate the replacement for the match. + for (const auto& replacement : this->Replacements) { + if (replacement.Number < 0) { + // This is just a plain-text part of the replacement. + output += replacement.Value; + } else { + // Replace with part of the match. + auto n = replacement.Number; + auto start = this->RegularExpression.start(n); + auto end = this->RegularExpression.end(n); + auto len = input.length() - base; + if ((start != std::string::npos) && (end != std::string::npos) && + (start <= len) && (end <= len)) { + output += input.substr(base + start, end - start); + } else { + std::ostringstream error; + error << "replace expression \"" << this->ReplaceExpression + << "\" contains an out-of-range escape for regex \"" + << this->RegExString << "\""; + this->ErrorString = error.str(); + return false; + } + } + } + + // Move past the match. + base += r; + } + + // Concatenate the text after the last match. + output += input.substr(base, input.length() - base); + + return true; +} + +void cmStringReplaceHelper::ParseReplaceExpression() +{ + std::string::size_type l = 0; + while (l < this->ReplaceExpression.length()) { + auto r = this->ReplaceExpression.find('\\', l); + if (r == std::string::npos) { + r = this->ReplaceExpression.length(); + this->Replacements.push_back(this->ReplaceExpression.substr(l, r - l)); + } else { + if (r - l > 0) { + this->Replacements.push_back(this->ReplaceExpression.substr(l, r - l)); + } + if (r == (this->ReplaceExpression.length() - 1)) { + this->ValidReplaceExpression = false; + this->ErrorString = "replace-expression ends in a backslash"; + return; + } + if ((this->ReplaceExpression[r + 1] >= '0') && + (this->ReplaceExpression[r + 1] <= '9')) { + this->Replacements.push_back(this->ReplaceExpression[r + 1] - '0'); + } else if (this->ReplaceExpression[r + 1] == 'n') { + this->Replacements.push_back("\n"); + } else if (this->ReplaceExpression[r + 1] == '\\') { + this->Replacements.push_back("\\"); + } else { + this->ValidReplaceExpression = false; + std::ostringstream error; + error << "Unknown escape \"" << this->ReplaceExpression.substr(r, 2) + << "\" in replace-expression"; + this->ErrorString = error.str(); + return; + } + r += 2; + } + l = r; + } +} diff --git a/Source/cmStringReplaceHelper.h b/Source/cmStringReplaceHelper.h new file mode 100644 index 0000000..938325a --- /dev/null +++ b/Source/cmStringReplaceHelper.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. */ +#ifndef cmStringReplaceHelper_h +#define cmStringReplaceHelper_h + +#include "cmsys/RegularExpression.hxx" + +#include <string> +#include <vector> + +class cmMakefile; + +class cmStringReplaceHelper +{ +public: + cmStringReplaceHelper(const std::string& regex, + const std::string& replace_expr, + cmMakefile* makefile = nullptr); + + bool IsRegularExpressionValid() const + { + return this->RegularExpression.is_valid(); + } + bool IsReplaceExpressionValid() const + { + return this->ValidReplaceExpression; + } + + bool Replace(const std::string& input, std::string& output); + + const std::string& GetError() { return this->ErrorString; } + +private: + class RegexReplacement + { + public: + RegexReplacement(const char* s) + : Number(-1) + , Value(s) + { + } + RegexReplacement(const std::string& s) + : Number(-1) + , Value(s) + { + } + RegexReplacement(int n) + : Number(n) + , Value() + { + } + RegexReplacement() {} + + int Number; + std::string Value; + }; + + void ParseReplaceExpression(); + + std::string ErrorString; + std::string RegExString; + cmsys::RegularExpression RegularExpression; + bool ValidReplaceExpression = true; + std::string ReplaceExpression; + std::vector<RegexReplacement> Replacements; + cmMakefile* Makefile = nullptr; +}; + +#endif diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index eeb73c3..cf1068f 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -949,10 +949,12 @@ cmSystemTools::WindowsFileRetry cmSystemTools::GetWindowsFileRetry() } return retry; } +#endif -std::string cmSystemTools::GetRealPath(const std::string& path, - std::string* errorMessage) +std::string cmSystemTools::GetRealPathResolvingWindowsSubst( + const std::string& path, std::string* errorMessage) { +#ifdef _WIN32 // uv_fs_realpath uses Windows Vista API so fallback to kwsys if not found std::string resolved_path; uv_fs_t req; @@ -981,8 +983,10 @@ std::string cmSystemTools::GetRealPath(const std::string& path, resolved_path = path; } return resolved_path; -} +#else + return cmsys::SystemTools::GetRealPath(path, errorMessage); #endif +} void cmSystemTools::InitializeLibUV() { @@ -1509,7 +1513,7 @@ cmSystemTools::SaveRestoreEnvironment::~SaveRestoreEnvironment() void cmSystemTools::EnableVSConsoleOutput() { #ifdef _WIN32 - // Visual Studio 8 2005 (devenv.exe or VCExpress.exe) will not + // Visual Studio tools like devenv may not // display output to the console unless this environment variable is // set. We need it to capture the output of these build tools. // Note for future work that one could pass "/out \\.\pipe\NAME" to @@ -1696,7 +1700,8 @@ void list_item_verbose(FILE* out, struct archive_entry* entry) fflush(out); } -long copy_data(struct archive* ar, struct archive* aw) +// Return 'true' on success +bool copy_data(struct archive* ar, struct archive* aw) { long r; const void* buff; @@ -1708,22 +1713,28 @@ long copy_data(struct archive* ar, struct archive* aw) #endif for (;;) { + // Return value: + // * ARCHIVE_OK - read succeed + // * ARCHIVE_EOF - no more data to read left r = archive_read_data_block(ar, &buff, &size, &offset); if (r == ARCHIVE_EOF) { - return (ARCHIVE_OK); + return true; } if (r != ARCHIVE_OK) { - return (r); + return false; } - r = archive_write_data_block(aw, buff, size, offset); - if (r != ARCHIVE_OK) { + // Return value: + // * >= ARCHIVE_OK - write succeed + // * < ARCHIVE_OK - write failed + const la_ssize_t w_size = archive_write_data_block(aw, buff, size, offset); + if (w_size < ARCHIVE_OK) { cmSystemTools::Message("archive_write_data_block()", archive_error_string(aw)); - return (r); + return false; } } #if !defined(__clang__) && !defined(__HP_aCC) - return r; /* this should not happen but it quiets some compilers */ + return false; /* this should not happen but it quiets some compilers */ #endif } @@ -1776,7 +1787,10 @@ bool extract_tar(const char* outFileName, bool verbose, bool extract) r = archive_write_header(ext, entry); if (r == ARCHIVE_OK) { - copy_data(a, ext); + if (!copy_data(a, ext)) { + cmSystemTools::Error("Problem with copy_data"); + break; + } r = archive_write_finish_entry(ext); if (r != ARCHIVE_OK) { cmSystemTools::Error("Problem with archive_write_finish_entry(): ", diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index a53afde..4390c86 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -500,12 +500,14 @@ public: unsigned int Delay; }; static WindowsFileRetry GetWindowsFileRetry(); - - /** Get the real path for a given path, removing all symlinks. */ - static std::string GetRealPath(const std::string& path, - std::string* errorMessage = 0); #endif + /** Get the real path for a given path, removing all symlinks. + This variant of GetRealPath also works on Windows but will + resolve subst drives too. */ + static std::string GetRealPathResolvingWindowsSubst( + const std::string& path, std::string* errorMessage = nullptr); + /** Perform one-time initialization of libuv. */ static void InitializeLibUV(); diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index cd11c4b..7dcba74 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -114,15 +114,12 @@ const char* cmTargetPropertyComputer::GetSources<cmTarget>( } if (!noMessage) { e << "Target \"" << tgt->GetName() - << "\" contains " - "$<TARGET_OBJECTS> generator expression in its sources " - "list. " - "This content was not previously part of the SOURCES " - "property " - "when that property was read at configure time. Code " - "reading " - "that property needs to be adapted to ignore the generator " - "expression using the string(GENEX_STRIP) command."; + << "\" contains $<TARGET_OBJECTS> generator expression in its " + "sources list. This content was not previously part of the " + "SOURCES property when that property was read at configure " + "time. Code reading that property needs to be adapted to " + "ignore the generator expression using the string(GENEX_STRIP) " + "command."; messenger->IssueMessage(messageType, e.str(), context); } if (addContent) { @@ -189,18 +186,10 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, this->ImportedGloballyVisible = vis == VisibilityImportedGlobally; this->BuildInterfaceIncludesAppended = false; - // only add dependency information for library targets - if (this->TargetTypeValue >= cmStateEnums::STATIC_LIBRARY && - this->TargetTypeValue <= cmStateEnums::MODULE_LIBRARY) { - this->RecordDependencies = true; - } else { - this->RecordDependencies = false; - } - // Check whether this is a DLL platform. this->DLLPlatform = - (this->Makefile->IsOn("WIN32") || this->Makefile->IsOn("CYGWIN") || - this->Makefile->IsOn("MINGW")); + strcmp(this->Makefile->GetSafeDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX"), + "") != 0; // Check whether we are targeting an Android platform. this->IsAndroid = @@ -286,6 +275,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, this->SetPropertyDefault("CUDA_SEPARABLE_COMPILATION", nullptr); this->SetPropertyDefault("LINK_SEARCH_START_STATIC", nullptr); this->SetPropertyDefault("LINK_SEARCH_END_STATIC", nullptr); + this->SetPropertyDefault("FOLDER", nullptr); } // Collect the set of configuration types. @@ -409,6 +399,10 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, this->SetPropertyDefault("JOB_POOL_COMPILE", nullptr); this->SetPropertyDefault("JOB_POOL_LINK", nullptr); } + + if (this->TargetTypeValue <= cmStateEnums::UTILITY) { + this->SetPropertyDefault("DOTNET_TARGET_FRAMEWORK_VERSION", nullptr); + } } cmGlobalGenerator* cmTarget::GetGlobalGenerator() const @@ -509,7 +503,7 @@ std::string cmTarget::ProcessSourceItemCMP0049(const std::string& s) { std::string src = s; - // For backwards compatibility replace varibles in source names. + // For backwards compatibility replace variables in source names. // This should eventually be removed. this->Makefile->ExpandVariablesInString(src); if (src != s) { @@ -638,27 +632,11 @@ const std::vector<std::string>& cmTarget::GetLinkDirectories() const return this->LinkDirectories; } -void cmTarget::ClearDependencyInformation(cmMakefile& mf, - const std::string& target) +void cmTarget::ClearDependencyInformation(cmMakefile& mf) { - // Clear the dependencies. The cache variable must exist iff we are - // recording dependency information for this target. - std::string depname = target; + std::string depname = this->GetName(); depname += "_LIB_DEPENDS"; - if (this->RecordDependencies) { - mf.AddCacheDefinition(depname, "", "Dependencies for target", - cmStateEnums::STATIC); - } else { - if (mf.GetDefinition(depname)) { - std::string message = "Target "; - message += target; - message += " has dependency information when it shouldn't.\n"; - message += "Your cache is probably stale. Please remove the entry\n "; - message += depname; - message += "\nfrom the cache."; - cmSystemTools::Error(message.c_str()); - } - } + mf.RemoveCacheDefinition(depname); } std::string cmTarget::GetDebugGeneratorExpressions( @@ -742,19 +720,15 @@ void cmTarget::AddLinkLibrary(cmMakefile& mf, const std::string& lib, } if (cmGeneratorExpression::Find(lib) != std::string::npos || - (tgt && tgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) || + (tgt && (tgt->GetType() == cmStateEnums::INTERFACE_LIBRARY || + tgt->GetType() == cmStateEnums::OBJECT_LIBRARY)) || (this->Name == lib)) { return; } - { - cmTarget::LibraryID tmp; - tmp.first = lib; - tmp.second = llt; - this->OriginalLinkLibraries.emplace_back(lib, llt); - } + this->OriginalLinkLibraries.emplace_back(lib, llt); - // Add the explicit dependency information for this target. This is + // Add the explicit dependency information for libraries. This is // simply a set of libraries separated by ";". There should always // be a trailing ";". These library names are not canonical, in that // they may be "-framework x", "-ly", "/path/libz.a", etc. @@ -762,7 +736,10 @@ void cmTarget::AddLinkLibrary(cmMakefile& mf, const std::string& lib, // may be purposefully duplicated to handle recursive dependencies, // and we removing one instance will break the link line. Duplicates // will be appropriately eliminated at emit time. - if (this->RecordDependencies) { + if (this->TargetTypeValue >= cmStateEnums::STATIC_LIBRARY && + this->TargetTypeValue <= cmStateEnums::MODULE_LIBRARY && + (this->GetPolicyStatusCMP0073() == cmPolicies::OLD || + this->GetPolicyStatusCMP0073() == cmPolicies::WARN)) { std::string targetEntry = this->Name; targetEntry += "_LIB_DEPENDS"; std::string dependencies; diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 56f3e3a..62c4e22 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -137,7 +137,7 @@ public: /** * Clear the dependency information recorded for this target, if any. */ - void ClearDependencyInformation(cmMakefile& mf, const std::string& target); + void ClearDependencyInformation(cmMakefile& mf); void AddLinkLibrary(cmMakefile& mf, const std::string& lib, cmTargetLinkLibraryType llt); @@ -310,7 +310,6 @@ private: cmTargetInternalPointer Internal; cmStateEnums::TargetType TargetTypeValue; bool HaveInstallRule; - bool RecordDependencies; bool DLLPlatform; bool IsAndroid; bool IsImportedTarget; diff --git a/Source/cmTargetLinkLibrariesCommand.cxx b/Source/cmTargetLinkLibrariesCommand.cxx index 9e4575a..53f1213 100644 --- a/Source/cmTargetLinkLibrariesCommand.cxx +++ b/Source/cmTargetLinkLibrariesCommand.cxx @@ -94,16 +94,6 @@ bool cmTargetLinkLibrariesCommand::InitialPass( return true; } - // OBJECT libraries are not allowed on the LHS of the command. - if (this->Target->GetType() == cmStateEnums::OBJECT_LIBRARY) { - std::ostringstream e; - e << "Object library target \"" << args[0] << "\" " - << "may not link to anything."; - this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str()); - cmSystemTools::SetFatalErrorOccured(); - return true; - } - // Having a UTILITY library on the LHS is a bug. if (this->Target->GetType() == cmStateEnums::UTILITY) { std::ostringstream e; @@ -374,7 +364,7 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib, if (this->CurrentProcessingState != ProcessingKeywordLinkInterface && this->CurrentProcessingState != ProcessingPlainLinkInterface) { - // Assure that the target on the LHS was created in the current directory. + // Find target on the LHS locally cmTarget* t = this->Makefile->FindLocalNonAliasTarget(this->Target->GetName()); if (!t) { @@ -387,11 +377,18 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib, } } } + + // If no local target has been found, find it in the global scope + if (!t) { + t = this->Makefile->GetGlobalGenerator()->FindTarget( + this->Target->GetName(), true); + } + if (!t) { std::ostringstream e; e << "Attempt to add link library \"" << lib << "\" to target \"" << this->Target->GetName() - << "\" which is not built in this directory."; + << "\" which does not exist or is an alias target."; this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str()); return false; } @@ -401,14 +398,15 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib, if (tgt && (tgt->GetType() != cmStateEnums::STATIC_LIBRARY) && (tgt->GetType() != cmStateEnums::SHARED_LIBRARY) && (tgt->GetType() != cmStateEnums::UNKNOWN_LIBRARY) && + (tgt->GetType() != cmStateEnums::OBJECT_LIBRARY) && (tgt->GetType() != cmStateEnums::INTERFACE_LIBRARY) && !tgt->IsExecutableWithExports()) { std::ostringstream e; e << "Target \"" << lib << "\" of type " << cmState::GetTargetTypeName(tgt->GetType()) << " may not be linked into another target. One may link only to " - "INTERFACE, STATIC or SHARED libraries, or to executables with the " - "ENABLE_EXPORTS property set."; + "INTERFACE, OBJECT, STATIC or SHARED libraries, or to executables " + "with the ENABLE_EXPORTS property set."; this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str()); } diff --git a/Source/cmTargetLinkLibrariesCommand.h b/Source/cmTargetLinkLibrariesCommand.h index a2f3ecd..54f8cf4 100644 --- a/Source/cmTargetLinkLibrariesCommand.h +++ b/Source/cmTargetLinkLibrariesCommand.h @@ -43,7 +43,7 @@ private: void LinkLibraryTypeSpecifierWarning(int left, int right); static const char* LinkLibraryTypeNames[3]; - cmTarget* Target; + cmTarget* Target = nullptr; enum ProcessingState { ProcessingLinkLibraries, @@ -55,7 +55,7 @@ private: ProcessingKeywordPrivateInterface }; - ProcessingState CurrentProcessingState; + ProcessingState CurrentProcessingState = ProcessingLinkLibraries; bool HandleLibrary(const std::string& lib, cmTargetLinkLibraryType llt); }; diff --git a/Source/cmTargetPropCommandBase.h b/Source/cmTargetPropCommandBase.h index 3c736fc..943285d 100644 --- a/Source/cmTargetPropCommandBase.h +++ b/Source/cmTargetPropCommandBase.h @@ -28,7 +28,7 @@ public: protected: std::string Property; - cmTarget* Target; + cmTarget* Target = nullptr; virtual void HandleInterfaceContent(cmTarget* tgt, const std::vector<std::string>& content, diff --git a/Source/cmTimestamp.cxx b/Source/cmTimestamp.cxx index f1e9283..14cf6e9 100644 --- a/Source/cmTimestamp.cxx +++ b/Source/cmTimestamp.cxx @@ -33,7 +33,8 @@ std::string cmTimestamp::FileModificationTime(const char* path, const std::string& formatString, bool utcFlag) { - std::string real_path = cmSystemTools::GetRealPath(path); + std::string real_path = + cmSystemTools::GetRealPathResolvingWindowsSubst(path); if (!cmsys::SystemTools::FileExists(real_path)) { return std::string(); diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 8a9df8f..f16db76 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -8,7 +8,7 @@ #include "cmGeneratedFileStream.h" #include "cmGeneratorTarget.h" #include "cmGlobalVisualStudio10Generator.h" -#include "cmLocalVisualStudio7Generator.h" +#include "cmLocalVisualStudio10Generator.h" #include "cmMakefile.h" #include "cmSourceFile.h" #include "cmSystemTools.h" @@ -28,12 +28,158 @@ static std::string cmVS10EscapeXML(std::string arg) return arg; } -static std::string cmVS10EscapeQuotes(std::string arg) +static std::string cmVS10EscapeAttr(std::string arg) { + cmSystemTools::ReplaceString(arg, "&", "&"); + cmSystemTools::ReplaceString(arg, "<", "<"); + cmSystemTools::ReplaceString(arg, ">", ">"); cmSystemTools::ReplaceString(arg, "\"", """); return arg; } +struct cmVisualStudio10TargetGenerator::Elem +{ + std::ostream& S; + const int Indent; + bool HasElements = false; + bool HasContent = false; + std::string Tag; + + Elem(std::ostream& s) + : S(s) + , Indent(0) + { + } + Elem(const Elem&) = delete; + Elem(Elem& par) + : S(par.S) + , Indent(par.Indent + 1) + { + par.SetHasElements(); + } + Elem(Elem& par, const char* tag) + : S(par.S) + , Indent(par.Indent + 1) + { + par.SetHasElements(); + this->StartElement(tag); + } + void SetHasElements() + { + if (!HasElements) { + this->S << ">\n"; + HasElements = true; + } + } + std::ostream& WriteString(const char* line); + Elem& StartElement(const std::string& tag) + { + this->Tag = tag; + this->WriteString("<") << tag; + return *this; + } + void Element(const char* tag, const std::string& val) + { + Elem(*this, tag).Content(val); + } + Elem& Attribute(const char* an, const std::string& av) + { + this->S << " " << an << "=\"" << cmVS10EscapeAttr(av) << "\""; + return *this; + } + // This method for now assumes that this->Tag has been set, e.g. by calling + // StartElement(). + void Content(const std::string& val) + { + if (!this->HasContent) { + this->S << ">"; + this->HasContent = true; + } + this->S << cmVS10EscapeXML(val); + } + ~Elem() + { + // Do not emit element which has not been started + if (Tag.empty()) { + return; + } + + if (HasElements) { + this->WriteString("</") << this->Tag << ">"; + if (this->Indent > 0) { + this->S << '\n'; + } else { + // special case: don't print EOL at EOF + } + } else if (HasContent) { + this->S << "</" << this->Tag << ">\n"; + } else { + this->S << " />\n"; + } + } + + void WritePlatformConfigTag(const char* tag, const std::string& cond, + const std::string& content); +}; + +class cmVS10GeneratorOptions : public cmVisualStudioGeneratorOptions +{ +public: + typedef cmVisualStudio10TargetGenerator::Elem Elem; + cmVS10GeneratorOptions(cmLocalVisualStudioGenerator* lg, Tool tool, + cmVS7FlagTable const* table, + cmVisualStudio10TargetGenerator* g = nullptr) + : cmVisualStudioGeneratorOptions(lg, tool, table) + , TargetGenerator(g) + { + } + + void OutputFlag(std::ostream& /*fout*/, int /*indent*/, const char* tag, + const std::string& content) override + { + if (!this->GetConfiguration().empty()) { + // if there are configuration specific flags, then + // use the configuration specific tag for PreprocessorDefinitions + const std::string cond = + this->TargetGenerator->CalcCondition(this->GetConfiguration()); + this->Parent->WritePlatformConfigTag(tag, cond, content); + } else { + this->Parent->Element(tag, content); + } + } + +private: + cmVisualStudio10TargetGenerator* const TargetGenerator; + Elem* Parent = nullptr; + friend cmVisualStudio10TargetGenerator::OptionsHelper; +}; + +struct cmVisualStudio10TargetGenerator::OptionsHelper +{ + cmVS10GeneratorOptions& O; + OptionsHelper(cmVS10GeneratorOptions& o, Elem& e) + : O(o) + { + O.Parent = &e; + } + ~OptionsHelper() { O.Parent = nullptr; } + + void OutputPreprocessorDefinitions(const std::string& lang) + { + O.OutputPreprocessorDefinitions(O.Parent->S, O.Parent->Indent + 1, lang); + } + void OutputAdditionalIncludeDirectories(const std::string& lang) + { + O.OutputAdditionalIncludeDirectories(O.Parent->S, O.Parent->Indent + 1, + lang); + } + void OutputFlagMap() { O.OutputFlagMap(O.Parent->S, O.Parent->Indent + 1); } + void PrependInheritedString(std::string const& key) + { + O.PrependInheritedString(key); + } +}; + static std::string cmVS10EscapeComment(std::string comment) { // MSBuild takes the CDATA of a <Message></Message> element and just @@ -74,9 +220,7 @@ static std::string computeProjectFileExtension(cmGeneratorTarget const* t, { std::string res; res = ".vcxproj"; - std::string lang = t->GetLinkerLanguage(config); - if (cmGlobalVisualStudioGenerator::TargetIsCSharpOnly(t) || - lang == "CSharp") { + if (t->HasLanguage("CSharp", config)) { res = ".csproj"; } return res; @@ -84,16 +228,16 @@ static std::string computeProjectFileExtension(cmGeneratorTarget const* t, cmVisualStudio10TargetGenerator::cmVisualStudio10TargetGenerator( cmGeneratorTarget* target, cmGlobalVisualStudio10Generator* gg) + : GeneratorTarget(target) + , Makefile(target->Target->GetMakefile()) + , Platform(gg->GetPlatformName()) + , Name(target->GetName()) + , GUID(gg->GetGUID(this->Name)) + , GlobalGenerator(gg) + , LocalGenerator( + (cmLocalVisualStudio10Generator*)target->GetLocalGenerator()) { - this->GlobalGenerator = gg; - this->GeneratorTarget = target; - this->Makefile = target->Target->GetMakefile(); this->Makefile->GetConfigurations(this->Configurations); - this->LocalGenerator = - (cmLocalVisualStudio7Generator*)this->GeneratorTarget->GetLocalGenerator(); - this->Name = this->GeneratorTarget->GetName(); - this->GUID = this->GlobalGenerator->GetGUID(this->Name); - this->Platform = gg->GetPlatformName(); this->NsightTegra = gg->IsNsightTegra(); for (int i = 0; i < 4; ++i) { this->NsightTegraVersion[i] = 0; @@ -104,7 +248,6 @@ cmVisualStudio10TargetGenerator::cmVisualStudio10TargetGenerator( this->MSTools = !this->NsightTegra; this->Managed = false; this->TargetCompileAsWinRT = false; - this->BuildFileStream = 0; this->IsMissingFiles = false; this->DefaultArtifactDir = this->LocalGenerator->GetCurrentBinaryDirectory() + std::string("/") + @@ -116,56 +259,40 @@ cmVisualStudio10TargetGenerator::cmVisualStudio10TargetGenerator( cmVisualStudio10TargetGenerator::~cmVisualStudio10TargetGenerator() { - if (!this->BuildFileStream) { - return; - } - if (this->BuildFileStream->Close()) { - this->GlobalGenerator->FileReplacedDuringGenerate(this->PathToProjectFile); - } - delete this->BuildFileStream; } -void cmVisualStudio10TargetGenerator::WritePlatformConfigTag( - const char* tag, const std::string& config, int indentLevel, - const char* attribute, const char* end, std::ostream* stream) - +std::string cmVisualStudio10TargetGenerator::CalcCondition( + const std::string& config) const { - if (!stream) { - stream = this->BuildFileStream; - } - stream->fill(' '); - stream->width(indentLevel * 2); - (*stream) << ""; // applies indentation - (*stream) << "<" << tag << " Condition=\""; - (*stream) << "'$(Configuration)|$(Platform)'=='"; - (*stream) << config << "|" << this->Platform; - (*stream) << "'"; + std::ostringstream oss; + oss << "'$(Configuration)|$(Platform)'=='"; + oss << config << "|" << this->Platform; + oss << "'"; // handle special case for 32 bit C# targets if (this->ProjectType == csproj && this->Platform == "Win32") { - (*stream) << " Or "; - (*stream) << "'$(Configuration)|$(Platform)'=='"; - (*stream) << config << "|x86"; - (*stream) << "'"; - } - (*stream) << "\""; - if (attribute) { - (*stream) << attribute; - } - // close the tag - (*stream) << ">"; - if (end) { - (*stream) << end; + oss << " Or "; + oss << "'$(Configuration)|$(Platform)'=='"; + oss << config << "|x86"; + oss << "'"; } + return oss.str(); +} + +void cmVisualStudio10TargetGenerator::Elem::WritePlatformConfigTag( + const char* tag, const std::string& cond, const std::string& content) +{ + Elem(*this, tag).Attribute("Condition", cond).Content(content); } -void cmVisualStudio10TargetGenerator::WriteString(const char* line, - int indentLevel) +std::ostream& cmVisualStudio10TargetGenerator::Elem::WriteString( + const char* line) { - this->BuildFileStream->fill(' '); - this->BuildFileStream->width(indentLevel * 2); + this->S.fill(' '); + this->S.width(this->Indent * 2); // write an empty string to get the fill level indent to print - (*this->BuildFileStream) << ""; - (*this->BuildFileStream) << line; + this->S << ""; + this->S << line; + return this->S; } #define VS10_CXX_DEFAULT_PROPS "$(VCTargetsPath)\\Microsoft.Cpp.Default.props" @@ -189,20 +316,30 @@ void cmVisualStudio10TargetGenerator::Generate() this->GeneratorTarget->GetProperty("EXTERNAL_MSPROJECT")) { return; } - this->ProjectFileExtension = computeProjectFileExtension( + const std::string ProjectFileExtension = computeProjectFileExtension( this->GeneratorTarget, *this->Configurations.begin()); - if (this->ProjectFileExtension == ".vcxproj") { + if (ProjectFileExtension == ".vcxproj") { this->ProjectType = vcxproj; this->Managed = false; - } else if (this->ProjectFileExtension == ".csproj") { + } else if (ProjectFileExtension == ".csproj") { + if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY) { + std::string message = "The C# target \"" + + this->GeneratorTarget->GetName() + + "\" is of type STATIC_LIBRARY. This is discouraged (and may be " + "disabled in future). Make it a SHARED library instead."; + this->Makefile->IssueMessage(cmake::MessageType::DEPRECATION_WARNING, + message); + } this->ProjectType = csproj; this->Managed = true; } // Tell the global generator the name of the project file this->GeneratorTarget->Target->SetProperty("GENERATOR_FILE_NAME", this->Name.c_str()); - this->GeneratorTarget->Target->SetProperty( - "GENERATOR_FILE_NAME_EXT", this->ProjectFileExtension.c_str()); + this->GeneratorTarget->Target->SetProperty("GENERATOR_FILE_NAME_EXT", + ProjectFileExtension.c_str()); + this->DotNetHintReferences.clear(); + this->AdditionalUsingDirectories.clear(); if (this->GeneratorTarget->GetType() <= cmStateEnums::OBJECT_LIBRARY) { if (!this->ComputeClOptions()) { return; @@ -232,365 +369,348 @@ void cmVisualStudio10TargetGenerator::Generate() std::string path = this->LocalGenerator->GetCurrentBinaryDirectory(); path += "/"; path += this->Name; - path += this->ProjectFileExtension; - this->BuildFileStream = new cmGeneratedFileStream(path.c_str()); - this->PathToProjectFile = path; - this->BuildFileStream->SetCopyIfDifferent(true); + path += ProjectFileExtension; + cmGeneratedFileStream BuildFileStream(path.c_str()); + const std::string PathToProjectFile = path; + BuildFileStream.SetCopyIfDifferent(true); // Write the encoding header into the file char magic[] = { char(0xEF), char(0xBB), char(0xBF) }; - this->BuildFileStream->write(magic, 3); - - // get the tools version to use - const std::string toolsVer(this->GlobalGenerator->GetToolsVersion()); - std::string project_defaults = "<?xml version=\"1.0\" encoding=\"" + - this->GlobalGenerator->Encoding() + "\"?>\n"; - project_defaults.append("<Project DefaultTargets=\"Build\" ToolsVersion=\""); - project_defaults.append(toolsVer + "\" "); - project_defaults.append( - "xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n"); - this->WriteString(project_defaults.c_str(), 0); - - if (this->NsightTegra) { - this->WriteString("<PropertyGroup Label=\"NsightTegraProject\">\n", 1); - const int nsightTegraMajorVersion = this->NsightTegraVersion[0]; - const int nsightTegraMinorVersion = this->NsightTegraVersion[1]; - if (nsightTegraMajorVersion >= 2) { - this->WriteString("<NsightTegraProjectRevisionNumber>", 2); - if (nsightTegraMajorVersion > 3 || - (nsightTegraMajorVersion == 3 && nsightTegraMinorVersion >= 1)) { - (*this->BuildFileStream) << "11"; + BuildFileStream.write(magic, 3); + BuildFileStream << "<?xml version=\"1.0\" encoding=\"" + << this->GlobalGenerator->Encoding() << "\"?>" + << "\n"; + { + Elem e0(BuildFileStream); + e0.StartElement("Project"); + e0.Attribute("DefaultTargets", "Build"); + e0.Attribute("ToolsVersion", this->GlobalGenerator->GetToolsVersion()); + e0.Attribute("xmlns", + "http://schemas.microsoft.com/developer/msbuild/2003"); + + if (this->NsightTegra) { + Elem e1(e0, "PropertyGroup"); + e1.Attribute("Label", "NsightTegraProject"); + const unsigned int nsightTegraMajorVersion = this->NsightTegraVersion[0]; + const unsigned int nsightTegraMinorVersion = this->NsightTegraVersion[1]; + if (nsightTegraMajorVersion >= 2) { + if (nsightTegraMajorVersion > 3 || + (nsightTegraMajorVersion == 3 && nsightTegraMinorVersion >= 1)) { + e1.Element("NsightTegraProjectRevisionNumber", "11"); + } else { + // Nsight Tegra 2.0 uses project revision 9. + e1.Element("NsightTegraProjectRevisionNumber", "9"); + } + // Tell newer versions to upgrade silently when loading. + e1.Element("NsightTegraUpgradeOnceWithoutPrompt", "true"); } else { - // Nsight Tegra 2.0 uses project revision 9. - (*this->BuildFileStream) << "9"; + // Require Nsight Tegra 1.6 for JCompile support. + e1.Element("NsightTegraProjectRevisionNumber", "7"); } - (*this->BuildFileStream) << "</NsightTegraProjectRevisionNumber>\n"; - // Tell newer versions to upgrade silently when loading. - this->WriteString("<NsightTegraUpgradeOnceWithoutPrompt>" - "true" - "</NsightTegraUpgradeOnceWithoutPrompt>\n", - 2); - } else { - // Require Nsight Tegra 1.6 for JCompile support. - this->WriteString("<NsightTegraProjectRevisionNumber>" - "7" - "</NsightTegraProjectRevisionNumber>\n", - 2); } - this->WriteString("</PropertyGroup>\n", 1); - } - if (const char* hostArch = - this->GlobalGenerator->GetPlatformToolsetHostArchitecture()) { - this->WriteString("<PropertyGroup>\n", 1); - this->WriteString("<PreferredToolArchitecture>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(hostArch) - << "</PreferredToolArchitecture>\n"; - this->WriteString("</PropertyGroup>\n", 1); - } + if (const char* hostArch = + this->GlobalGenerator->GetPlatformToolsetHostArchitecture()) { + Elem e1(e0, "PropertyGroup"); + e1.Element("PreferredToolArchitecture", hostArch); + } - if (this->ProjectType != csproj) { - this->WriteProjectConfigurations(); - } - this->WriteString("<PropertyGroup Label=\"Globals\">\n", 1); - this->WriteString("<ProjectGuid>", 2); - (*this->BuildFileStream) << "{" << this->GUID << "}</ProjectGuid>\n"; + if (this->ProjectType != csproj) { + this->WriteProjectConfigurations(e0); + } - if (this->MSTools && - this->GeneratorTarget->GetType() <= cmStateEnums::GLOBAL_TARGET) { - this->WriteApplicationTypeSettings(); - this->VerifyNecessaryFiles(); - } + { + Elem e1(e0, "PropertyGroup"); + e1.Attribute("Label", "Globals"); + e1.Element("ProjectGuid", "{" + this->GUID + "}"); + + if (this->MSTools && + this->GeneratorTarget->GetType() <= cmStateEnums::GLOBAL_TARGET) { + this->WriteApplicationTypeSettings(e1); + this->VerifyNecessaryFiles(); + } - const char* vsProjectTypes = - this->GeneratorTarget->GetProperty("VS_GLOBAL_PROJECT_TYPES"); - if (vsProjectTypes) { - std::string tagName = "ProjectTypes"; - if (this->ProjectType == csproj) { - tagName = "ProjectTypeGuids"; - } - this->WriteString("", 2); - (*this->BuildFileStream) << "<" << tagName << ">" - << cmVS10EscapeXML(vsProjectTypes) << "</" - << tagName << ">\n"; - } + const char* vsProjectTypes = + this->GeneratorTarget->GetProperty("VS_GLOBAL_PROJECT_TYPES"); + if (vsProjectTypes) { + const char* tagName = "ProjectTypes"; + if (this->ProjectType == csproj) { + tagName = "ProjectTypeGuids"; + } + e1.Element(tagName, vsProjectTypes); + } - const char* vsProjectName = - this->GeneratorTarget->GetProperty("VS_SCC_PROJECTNAME"); - const char* vsLocalPath = - this->GeneratorTarget->GetProperty("VS_SCC_LOCALPATH"); - const char* vsProvider = - this->GeneratorTarget->GetProperty("VS_SCC_PROVIDER"); + const char* vsProjectName = + this->GeneratorTarget->GetProperty("VS_SCC_PROJECTNAME"); + const char* vsLocalPath = + this->GeneratorTarget->GetProperty("VS_SCC_LOCALPATH"); + const char* vsProvider = + this->GeneratorTarget->GetProperty("VS_SCC_PROVIDER"); + + if (vsProjectName && vsLocalPath && vsProvider) { + e1.Element("SccProjectName", vsProjectName); + e1.Element("SccLocalPath", vsLocalPath); + e1.Element("SccProvider", vsProvider); + + const char* vsAuxPath = + this->GeneratorTarget->GetProperty("VS_SCC_AUXPATH"); + if (vsAuxPath) { + e1.Element("SccAuxPath", vsAuxPath); + } + } - if (vsProjectName && vsLocalPath && vsProvider) { - this->WriteString("<SccProjectName>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(vsProjectName) - << "</SccProjectName>\n"; - this->WriteString("<SccLocalPath>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(vsLocalPath) - << "</SccLocalPath>\n"; - this->WriteString("<SccProvider>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(vsProvider) - << "</SccProvider>\n"; + if (this->GeneratorTarget->GetPropertyAsBool("VS_WINRT_COMPONENT")) { + e1.Element("WinMDAssembly", "true"); + } - const char* vsAuxPath = - this->GeneratorTarget->GetProperty("VS_SCC_AUXPATH"); - if (vsAuxPath) { - this->WriteString("<SccAuxPath>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(vsAuxPath) - << "</SccAuxPath>\n"; - } - } + const char* vsGlobalKeyword = + this->GeneratorTarget->GetProperty("VS_GLOBAL_KEYWORD"); + if (!vsGlobalKeyword) { + e1.Element("Keyword", "Win32Proj"); + } else { + e1.Element("Keyword", vsGlobalKeyword); + } - if (this->GeneratorTarget->GetPropertyAsBool("VS_WINRT_COMPONENT")) { - this->WriteString("<WinMDAssembly>true</WinMDAssembly>\n", 2); - } + const char* vsGlobalRootNamespace = + this->GeneratorTarget->GetProperty("VS_GLOBAL_ROOTNAMESPACE"); + if (vsGlobalRootNamespace) { + e1.Element("RootNamespace", vsGlobalRootNamespace); + } - const char* vsGlobalKeyword = - this->GeneratorTarget->GetProperty("VS_GLOBAL_KEYWORD"); - if (!vsGlobalKeyword) { - this->WriteString("<Keyword>Win32Proj</Keyword>\n", 2); - } else { - this->WriteString("<Keyword>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(vsGlobalKeyword) - << "</Keyword>\n"; - } - - const char* vsGlobalRootNamespace = - this->GeneratorTarget->GetProperty("VS_GLOBAL_ROOTNAMESPACE"); - if (vsGlobalRootNamespace) { - this->WriteString("<RootNamespace>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(vsGlobalRootNamespace) - << "</RootNamespace>\n"; - } - - this->WriteString("<Platform>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(this->Platform) - << "</Platform>\n"; - const char* projLabel = this->GeneratorTarget->GetProperty("PROJECT_LABEL"); - if (!projLabel) { - projLabel = this->Name.c_str(); - } - this->WriteString("<ProjectName>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(projLabel) << "</ProjectName>\n"; - if (const char* targetFrameworkVersion = this->GeneratorTarget->GetProperty( - "VS_DOTNET_TARGET_FRAMEWORK_VERSION")) { - this->WriteString("<TargetFrameworkVersion>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(targetFrameworkVersion) - << "</TargetFrameworkVersion>\n"; - } - - // Disable the project upgrade prompt that is displayed the first time a - // project using an older toolset version is opened in a newer version of - // the IDE (respected by VS 2013 and above). - if (this->GlobalGenerator->GetVersion() >= - cmGlobalVisualStudioGenerator::VS12) { - this->WriteString("<VCProjectUpgraderObjectName>NoUpgrade" - "</VCProjectUpgraderObjectName>\n", - 2); - } - - std::vector<std::string> keys = this->GeneratorTarget->GetPropertyKeys(); - for (std::string const& keyIt : keys) { - static const char* prefix = "VS_GLOBAL_"; - if (keyIt.find(prefix) != 0) - continue; - std::string globalKey = keyIt.substr(strlen(prefix)); - // Skip invalid or separately-handled properties. - if (globalKey.empty() || globalKey == "PROJECT_TYPES" || - globalKey == "ROOTNAMESPACE" || globalKey == "KEYWORD") { - continue; + e1.Element("Platform", this->Platform); + const char* projLabel = + this->GeneratorTarget->GetProperty("PROJECT_LABEL"); + if (!projLabel) { + projLabel = this->Name.c_str(); + } + e1.Element("ProjectName", projLabel); + { + // TODO: add deprecation warning for VS_* property? + const char* targetFrameworkVersion = + this->GeneratorTarget->GetProperty( + "VS_DOTNET_TARGET_FRAMEWORK_VERSION"); + if (!targetFrameworkVersion) { + targetFrameworkVersion = this->GeneratorTarget->GetProperty( + "DOTNET_TARGET_FRAMEWORK_VERSION"); + } + if (targetFrameworkVersion) { + e1.Element("TargetFrameworkVersion", targetFrameworkVersion); + } + } + + // Disable the project upgrade prompt that is displayed the first time a + // project using an older toolset version is opened in a newer version of + // the IDE (respected by VS 2013 and above). + if (this->GlobalGenerator->GetVersion() >= + cmGlobalVisualStudioGenerator::VS12) { + e1.Element("VCProjectUpgraderObjectName", "NoUpgrade"); + } + + std::vector<std::string> keys = this->GeneratorTarget->GetPropertyKeys(); + for (std::string const& keyIt : keys) { + static const char* prefix = "VS_GLOBAL_"; + if (keyIt.find(prefix) != 0) + continue; + std::string globalKey = keyIt.substr(strlen(prefix)); + // Skip invalid or separately-handled properties. + if (globalKey.empty() || globalKey == "PROJECT_TYPES" || + globalKey == "ROOTNAMESPACE" || globalKey == "KEYWORD") { + continue; + } + const char* value = this->GeneratorTarget->GetProperty(keyIt); + if (!value) + continue; + e1.Element(globalKey.c_str(), value); + } + + if (this->Managed) { + std::string outputType; + switch (this->GeneratorTarget->GetType()) { + case cmStateEnums::OBJECT_LIBRARY: + case cmStateEnums::STATIC_LIBRARY: + case cmStateEnums::SHARED_LIBRARY: + outputType = "Library"; + break; + case cmStateEnums::MODULE_LIBRARY: + outputType = "Module"; + break; + case cmStateEnums::EXECUTABLE: + if (this->GeneratorTarget->Target->GetPropertyAsBool( + "WIN32_EXECUTABLE")) { + outputType = "WinExe"; + } else { + outputType = "Exe"; + } + break; + case cmStateEnums::UTILITY: + case cmStateEnums::GLOBAL_TARGET: + outputType = "Utility"; + break; + case cmStateEnums::UNKNOWN_LIBRARY: + case cmStateEnums::INTERFACE_LIBRARY: + break; + } + e1.Element("OutputType", outputType); + e1.Element("AppDesignerFolder", "Properties"); + } } - const char* value = this->GeneratorTarget->GetProperty(keyIt); - if (!value) - continue; - this->WriteString("<", 2); - (*this->BuildFileStream) << globalKey << ">" << cmVS10EscapeXML(value) - << "</" << globalKey << ">\n"; - } - if (this->Managed) { - std::string outputType = "<OutputType>"; - switch (this->GeneratorTarget->GetType()) { - case cmStateEnums::OBJECT_LIBRARY: - case cmStateEnums::STATIC_LIBRARY: - case cmStateEnums::SHARED_LIBRARY: - outputType += "Library"; - break; - case cmStateEnums::MODULE_LIBRARY: - outputType += "Module"; - break; - case cmStateEnums::EXECUTABLE: - if (this->GeneratorTarget->Target->GetPropertyAsBool( - "WIN32_EXECUTABLE")) { - outputType += "WinExe"; - } else { - outputType += "Exe"; + switch (this->ProjectType) { + case vcxproj: + if (this->GlobalGenerator->GetPlatformToolsetVersion()) { + Elem(e0, "Import") + .Attribute("Project", + this->GlobalGenerator->GetAuxiliaryToolset()); } + Elem(e0, "Import").Attribute("Project", VS10_CXX_DEFAULT_PROPS); break; - case cmStateEnums::UTILITY: - case cmStateEnums::GLOBAL_TARGET: - outputType += "Utility"; - break; - case cmStateEnums::UNKNOWN_LIBRARY: - case cmStateEnums::INTERFACE_LIBRARY: + case csproj: + Elem(e0, "Import") + .Attribute("Project", VS10_CSharp_DEFAULT_PROPS) + .Attribute("Condition", "Exists('" VS10_CSharp_DEFAULT_PROPS "')"); break; } - outputType += "</OutputType>\n"; - this->WriteString(outputType.c_str(), 2); - this->WriteString("<AppDesignerFolder>Properties</AppDesignerFolder>\n", - 2); - } - this->WriteString("</PropertyGroup>\n", 1); + this->WriteProjectConfigurationValues(e0); - switch (this->ProjectType) { - case vcxproj: - this->WriteString("<Import Project=\"" VS10_CXX_DEFAULT_PROPS "\" />\n", - 1); - break; - case csproj: - this->WriteString("<Import Project=\"" VS10_CSharp_DEFAULT_PROPS "\" " - "Condition=\"Exists('" VS10_CSharp_DEFAULT_PROPS "')\"" - "/>\n", - 1); - break; - } + if (this->ProjectType == vcxproj) { + Elem(e0, "Import").Attribute("Project", VS10_CXX_PROPS); + } + { + Elem e1(e0, "ImportGroup"); + e1.Attribute("Label", "ExtensionSettings"); + e1.SetHasElements(); + + if (this->GlobalGenerator->IsCudaEnabled()) { + Elem(e1, "Import") + .Attribute("Project", + "$(VCTargetsPath)\\BuildCustomizations\\CUDA " + + this->GlobalGenerator->GetPlatformToolsetCudaString() + + ".props"); + } + if (this->GlobalGenerator->IsMasmEnabled()) { + Elem(e1, "Import") + .Attribute("Project", + "$(VCTargetsPath)\\BuildCustomizations\\masm.props"); + } + if (this->GlobalGenerator->IsNasmEnabled()) { + // Always search in the standard modules location. + std::string propsTemplate = + GetCMakeFilePath("Templates/MSBuild/nasm.props.in"); + + std::string propsLocal; + propsLocal += this->DefaultArtifactDir; + propsLocal += "\\nasm.props"; + ConvertToWindowsSlash(propsLocal); + this->Makefile->ConfigureFile(propsTemplate.c_str(), + propsLocal.c_str(), false, true, true); + Elem(e1, "Import").Attribute("Project", propsLocal); + } + } + { + Elem e1(e0, "ImportGroup"); + e1.Attribute("Label", "PropertySheets"); + std::string props; + switch (this->ProjectType) { + case vcxproj: + props = VS10_CXX_USER_PROPS; + break; + case csproj: + props = VS10_CSharp_USER_PROPS; + break; + } + if (const char* p = + this->GeneratorTarget->GetProperty("VS_USER_PROPS")) { + props = p; + } + if (!props.empty()) { + ConvertToWindowsSlash(props); + Elem(e1, "Import") + .Attribute("Project", props) + .Attribute("Condition", "exists('" + props + "')") + .Attribute("Label", "LocalAppDataPlatform"); + } - this->WriteProjectConfigurationValues(); - - if (this->ProjectType == vcxproj) { - this->WriteString("<Import Project=\"" VS10_CXX_PROPS "\" />\n", 1); - } - this->WriteString("<ImportGroup Label=\"ExtensionSettings\">\n", 1); - if (this->GlobalGenerator->IsCudaEnabled()) { - this->WriteString("<Import Project=\"$(VCTargetsPath)\\" - "BuildCustomizations\\CUDA ", - 2); - (*this->BuildFileStream) - << cmVS10EscapeXML(this->GlobalGenerator->GetPlatformToolsetCudaString()) - << ".props\" />\n"; - } - if (this->GlobalGenerator->IsMasmEnabled()) { - this->WriteString("<Import Project=\"$(VCTargetsPath)\\" - "BuildCustomizations\\masm.props\" />\n", - 2); - } - if (this->GlobalGenerator->IsNasmEnabled()) { - // Always search in the standard modules location. - std::string propsTemplate = - GetCMakeFilePath("Templates/MSBuild/nasm.props.in"); - - std::string propsLocal; - propsLocal += this->DefaultArtifactDir; - propsLocal += "\\nasm.props"; - ConvertToWindowsSlash(propsLocal); - this->Makefile->ConfigureFile(propsTemplate.c_str(), propsLocal.c_str(), - false, true, true); - std::string import = std::string("<Import Project=\"") + - cmVS10EscapeXML(propsLocal) + "\" />\n"; - this->WriteString(import.c_str(), 2); - } - this->WriteString("</ImportGroup>\n", 1); - this->WriteString("<ImportGroup Label=\"PropertySheets\">\n", 1); - { - std::string props; + this->WritePlatformExtensions(e1); + } + Elem(e0, "PropertyGroup").Attribute("Label", "UserMacros"); + this->WriteWinRTPackageCertificateKeyFile(e0); + this->WritePathAndIncrementalLinkOptions(e0); + this->WriteItemDefinitionGroups(e0); + this->WriteCustomCommands(e0); + this->WriteAllSources(e0); + this->WriteDotNetReferences(e0); + this->WriteEmbeddedResourceGroup(e0); + this->WriteXamlFilesGroup(e0); + this->WriteWinRTReferences(e0); + this->WriteProjectReferences(e0); + this->WriteSDKReferences(e0); switch (this->ProjectType) { case vcxproj: - props = VS10_CXX_USER_PROPS; + Elem(e0, "Import").Attribute("Project", VS10_CXX_TARGETS); break; case csproj: - props = VS10_CSharp_USER_PROPS; + Elem(e0, "Import").Attribute("Project", VS10_CSharp_TARGETS); break; } - if (const char* p = this->GeneratorTarget->GetProperty("VS_USER_PROPS")) { - props = p; - } - if (!props.empty()) { - ConvertToWindowsSlash(props); - this->WriteString("", 2); - (*this->BuildFileStream) - << "<Import Project=\"" << cmVS10EscapeXML(props) << "\"" - << " Condition=\"exists('" << cmVS10EscapeXML(props) << "')\"" - << " Label=\"LocalAppDataPlatform\" />\n"; - } - } - this->WritePlatformExtensions(); - this->WriteString("</ImportGroup>\n", 1); - this->WriteString("<PropertyGroup Label=\"UserMacros\" />\n", 1); - this->WriteWinRTPackageCertificateKeyFile(); - this->WritePathAndIncrementalLinkOptions(); - this->WriteItemDefinitionGroups(); - this->WriteCustomCommands(); - this->WriteAllSources(); - this->WriteDotNetReferences(); - this->WriteEmbeddedResourceGroup(); - this->WriteXamlFilesGroup(); - this->WriteWinRTReferences(); - this->WriteProjectReferences(); - this->WriteSDKReferences(); - switch (this->ProjectType) { - case vcxproj: - this->WriteString("<Import Project=\"" VS10_CXX_TARGETS "\" />\n", 1); - break; - case csproj: - this->WriteString("<Import Project=\"" VS10_CSharp_TARGETS "\" />\n", 1); - break; + + this->WriteTargetSpecificReferences(e0); + { + Elem e1(e0, "ImportGroup"); + e1.Attribute("Label", "ExtensionTargets"); + e1.SetHasElements(); + this->WriteTargetsFileReferences(e1); + if (this->GlobalGenerator->IsCudaEnabled()) { + Elem(e1, "Import") + .Attribute("Project", + "$(VCTargetsPath)\\BuildCustomizations\\CUDA " + + this->GlobalGenerator->GetPlatformToolsetCudaString() + + ".targets"); + } + if (this->GlobalGenerator->IsMasmEnabled()) { + Elem(e1, "Import") + .Attribute("Project", + "$(VCTargetsPath)\\BuildCustomizations\\masm.targets"); + } + if (this->GlobalGenerator->IsNasmEnabled()) { + std::string nasmTargets = + GetCMakeFilePath("Templates/MSBuild/nasm.targets"); + Elem(e1, "Import").Attribute("Project", nasmTargets); + } + } + if (this->ProjectType == csproj) { + for (std::string const& c : this->Configurations) { + Elem e1(e0, "PropertyGroup"); + e1.Attribute("Condition", "'$(Configuration)' == '" + c + "'"); + e1.SetHasElements(); + this->WriteEvents(e1, c); + } + // make sure custom commands are executed before build (if necessary) + { + Elem e1(e0, "PropertyGroup"); + std::ostringstream oss; + oss << "\n"; + for (std::string const& i : this->CSharpCustomCommandNames) { + oss << " " << i << ";\n"; + } + oss << " " + << "$(BuildDependsOn)\n"; + e1.Element("BuildDependsOn", oss.str()); + } + } + } + + if (BuildFileStream.Close()) { + this->GlobalGenerator->FileReplacedDuringGenerate(PathToProjectFile); } - this->WriteTargetSpecificReferences(); - this->WriteString("<ImportGroup Label=\"ExtensionTargets\">\n", 1); - this->WriteTargetsFileReferences(); - if (this->GlobalGenerator->IsCudaEnabled()) { - this->WriteString("<Import Project=\"$(VCTargetsPath)\\" - "BuildCustomizations\\CUDA ", - 2); - (*this->BuildFileStream) - << cmVS10EscapeXML(this->GlobalGenerator->GetPlatformToolsetCudaString()) - << ".targets\" />\n"; - } - if (this->GlobalGenerator->IsMasmEnabled()) { - this->WriteString("<Import Project=\"$(VCTargetsPath)\\" - "BuildCustomizations\\masm.targets\" />\n", - 2); - } - if (this->GlobalGenerator->IsNasmEnabled()) { - std::string nasmTargets = - GetCMakeFilePath("Templates/MSBuild/nasm.targets"); - std::string import = "<Import Project=\""; - import += cmVS10EscapeXML(nasmTargets) + "\" />\n"; - this->WriteString(import.c_str(), 2); - } - this->WriteString("</ImportGroup>\n", 1); - if (this->ProjectType == csproj) { - for (std::string const& i : this->Configurations) { - this->WriteString("<PropertyGroup Condition=\"'$(Configuration)' == '", - 1); - (*this->BuildFileStream) << i << "'\">\n"; - this->WriteEvents(i); - this->WriteString("</PropertyGroup>\n", 1); - } - // make sure custom commands are executed before build (if necessary) - this->WriteString("<PropertyGroup>\n", 1); - this->WriteString("<BuildDependsOn>\n", 2); - for (std::string const& i : this->CSharpCustomCommandNames) { - this->WriteString(i.c_str(), 3); - (*this->BuildFileStream) << ";\n"; - } - this->WriteString("$(BuildDependsOn)\n", 3); - this->WriteString("</BuildDependsOn>\n", 2); - this->WriteString("</PropertyGroup>\n", 1); - } - this->WriteString("</Project>", 0); // The groups are stored in a separate file for VS 10 this->WriteGroups(); } -void cmVisualStudio10TargetGenerator::WriteDotNetReferences() +void cmVisualStudio10TargetGenerator::WriteDotNetReferences(Elem& e0) { std::vector<std::string> references; - typedef std::pair<std::string, std::string> HintReference; - std::vector<HintReference> hintReferences; if (const char* vsDotNetReferences = this->GeneratorTarget->GetProperty("VS_DOTNET_REFERENCES")) { cmSystemTools::ExpandListArgument(vsDotNetReferences, references); @@ -602,17 +722,17 @@ void cmVisualStudio10TargetGenerator::WriteDotNetReferences() if (!name.empty()) { std::string path = i.second.GetValue(); if (!cmsys::SystemTools::FileIsFullPath(path)) { - path = std::string(this->GeneratorTarget->Target->GetMakefile() - ->GetCurrentSourceDirectory()) + + path = std::string(this->Makefile->GetCurrentSourceDirectory()) + "/" + path; } ConvertToWindowsSlash(path); - hintReferences.push_back(HintReference(name, path)); + this->DotNetHintReferences[""].push_back( + DotNetHintReference(name, path)); } } } - if (!references.empty() || !hintReferences.empty()) { - this->WriteString("<ItemGroup>\n", 1); + if (!references.empty() || !this->DotNetHintReferences.empty()) { + Elem e1(e0, "ItemGroup"); for (std::string const& ri : references) { // if the entry from VS_DOTNET_REFERENCES is an existing file, generate // a new hint-reference and name it from the filename @@ -620,29 +740,36 @@ void cmVisualStudio10TargetGenerator::WriteDotNetReferences() std::string name = cmsys::SystemTools::GetFilenameWithoutExtension(ri); std::string path = ri; ConvertToWindowsSlash(path); - hintReferences.push_back(HintReference(name, path)); + this->DotNetHintReferences[""].push_back( + DotNetHintReference(name, path)); } else { - this->WriteDotNetReference(ri, ""); + this->WriteDotNetReference(e1, ri, "", ""); } } - for (const auto& i : hintReferences) { - this->WriteDotNetReference(i.first, i.second); + for (const auto& h : this->DotNetHintReferences) { + // DotNetHintReferences is also populated from AddLibraries(). + // The configuration specific hint references are added there. + for (const auto& i : h.second) { + this->WriteDotNetReference(e1, i.first, i.second, h.first); + } } - this->WriteString("</ItemGroup>\n", 1); } } void cmVisualStudio10TargetGenerator::WriteDotNetReference( - std::string const& ref, std::string const& hint) + Elem& e1, std::string const& ref, std::string const& hint, + std::string const& config) { - this->WriteString("<Reference Include=\"", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(ref) << "\">\n"; - this->WriteString("<CopyLocalSatelliteAssemblies>true" - "</CopyLocalSatelliteAssemblies>\n", - 3); - this->WriteString("<ReferenceOutputAssembly>true" - "</ReferenceOutputAssembly>\n", - 3); + Elem e2(e1, "Reference"); + // If 'config' is not empty, the reference is only added for the given + // configuration. This is used when referencing imported managed assemblies. + // See also cmVisualStudio10TargetGenerator::AddLibraries(). + if (!config.empty()) { + e2.Attribute("Condition", this->CalcCondition(config)); + } + e2.Attribute("Include", ref); + e2.Element("CopyLocalSatelliteAssemblies", "true"); + e2.Element("ReferenceOutputAssembly", "true"); if (!hint.empty()) { const char* privateReference = "True"; if (const char* value = this->GeneratorTarget->GetProperty( @@ -651,17 +778,14 @@ void cmVisualStudio10TargetGenerator::WriteDotNetReference( privateReference = "False"; } } - this->WriteString("<Private>", 3); - (*this->BuildFileStream) << privateReference << "</Private>\n"; - this->WriteString("<HintPath>", 3); - (*this->BuildFileStream) << hint << "</HintPath>\n"; + e2.Element("Private", privateReference); + e2.Element("HintPath", hint); } - this->WriteDotNetReferenceCustomTags(ref); - this->WriteString("</Reference>\n", 2); + this->WriteDotNetReferenceCustomTags(e2, ref); } void cmVisualStudio10TargetGenerator::WriteDotNetReferenceCustomTags( - std::string const& ref) + Elem& e2, std::string const& ref) { static const std::string refpropPrefix = "VS_DOTNET_REFERENCEPROP_"; @@ -680,23 +804,20 @@ void cmVisualStudio10TargetGenerator::WriteDotNetReferenceCustomTags( } } for (auto const& tag : tags) { - this->WriteString("<", 3); - (*this->BuildFileStream) << tag.first << ">" << cmVS10EscapeXML(tag.second) - << "</" << tag.first << ">\n"; + e2.Element(tag.first.c_str(), tag.second); } } -void cmVisualStudio10TargetGenerator::WriteEmbeddedResourceGroup() +void cmVisualStudio10TargetGenerator::WriteEmbeddedResourceGroup(Elem& e0) { std::vector<cmSourceFile const*> resxObjs; this->GeneratorTarget->GetResxSources(resxObjs, ""); if (!resxObjs.empty()) { - this->WriteString("<ItemGroup>\n", 1); + Elem e1(e0, "ItemGroup"); std::string srcDir = this->Makefile->GetCurrentSourceDirectory(); ConvertToWindowsSlash(srcDir); for (cmSourceFile const* oi : resxObjs) { std::string obj = oi->GetFullPath(); - this->WriteString("<EmbeddedResource Include=\"", 2); ConvertToWindowsSlash(obj); bool useRelativePath = false; if (this->ProjectType == csproj && this->InSourceBuild) { @@ -710,23 +831,22 @@ void cmVisualStudio10TargetGenerator::WriteEmbeddedResourceGroup() useRelativePath = true; } } - (*this->BuildFileStream) << obj << "\">\n"; + Elem e2(e1, "EmbeddedResource"); + e2.Attribute("Include", obj); if (this->ProjectType != csproj) { - this->WriteString("<DependentUpon>", 3); std::string hFileName = obj.substr(0, obj.find_last_of(".")) + ".h"; - (*this->BuildFileStream) << hFileName << "</DependentUpon>\n"; + e2.Element("DependentUpon", hFileName); - for (std::string const& i : this->Configurations) { - this->WritePlatformConfigTag("LogicalName", i, 3); + for (std::string const& c : this->Configurations) { + std::string s; if (this->GeneratorTarget->GetProperty("VS_GLOBAL_ROOTNAMESPACE") || // Handle variant of VS_GLOBAL_<variable> for RootNamespace. this->GeneratorTarget->GetProperty("VS_GLOBAL_RootNamespace")) { - (*this->BuildFileStream) << "$(RootNamespace)."; + s = "$(RootNamespace)."; } - (*this->BuildFileStream) << "%(Filename)"; - (*this->BuildFileStream) << ".resources"; - (*this->BuildFileStream) << "</LogicalName>\n"; + s += "%(Filename).resources"; + e2.WritePlatformConfigTag("LogicalName", this->CalcCondition(c), s); } } else { std::string binDir = this->Makefile->GetCurrentBinaryDirectory(); @@ -743,8 +863,7 @@ void cmVisualStudio10TargetGenerator::WriteEmbeddedResourceGroup() link = cmsys::SystemTools::GetFilenameName(obj); } if (!link.empty()) { - this->WriteString("<Link>", 3); - (*this->BuildFileStream) << link << "</Link>\n"; + e2.Element("Link", link); } } // Determine if this is a generated resource from a .Designer.cs file @@ -758,9 +877,7 @@ void cmVisualStudio10TargetGenerator::WriteEmbeddedResourceGroup() generator = g; } if (!generator.empty()) { - this->WriteString("<Generator>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(generator) - << "</Generator>\n"; + e2.Element("Generator", generator); if (designerResource.find(srcDir) == 0) { designerResource = designerResource.substr(srcDir.length() + 1); } else if (designerResource.find(binDir) == 0) { @@ -770,9 +887,7 @@ void cmVisualStudio10TargetGenerator::WriteEmbeddedResourceGroup() cmsys::SystemTools::GetFilenameName(designerResource); } ConvertToWindowsSlash(designerResource); - this->WriteString("<LastGenOutput>", 3); - (*this->BuildFileStream) << designerResource - << "</LastGenOutput>\n"; + e2.Element("LastGenOutput", designerResource); } } const cmPropertyMap& props = oi->GetProperties(); @@ -783,31 +898,25 @@ void cmVisualStudio10TargetGenerator::WriteEmbeddedResourceGroup() if (!tagName.empty()) { std::string value = props.GetPropertyValue(p.first); if (!value.empty()) { - this->WriteString("<", 3); - (*this->BuildFileStream) << tagName << ">"; - (*this->BuildFileStream) << cmVS10EscapeXML(value); - (*this->BuildFileStream) << "</" << tagName << ">\n"; + e2.Element(tagName.c_str(), value); } } } } } - - this->WriteString("</EmbeddedResource>\n", 2); } - this->WriteString("</ItemGroup>\n", 1); } } -void cmVisualStudio10TargetGenerator::WriteXamlFilesGroup() +void cmVisualStudio10TargetGenerator::WriteXamlFilesGroup(Elem& e0) { std::vector<cmSourceFile const*> xamlObjs; this->GeneratorTarget->GetXamlSources(xamlObjs, ""); if (!xamlObjs.empty()) { - this->WriteString("<ItemGroup>\n", 1); + Elem e1(e0, "ItemGroup"); for (cmSourceFile const* oi : xamlObjs) { std::string obj = oi->GetFullPath(); - std::string xamlType; + const char* xamlType; const char* xamlTypeProperty = oi->GetProperty("VS_XAML_TYPE"); if (xamlTypeProperty) { xamlType = xamlTypeProperty; @@ -815,7 +924,9 @@ void cmVisualStudio10TargetGenerator::WriteXamlFilesGroup() xamlType = "Page"; } - this->WriteSource(xamlType, oi, ">\n"); + Elem e2(e1); + this->WriteSource(e2, xamlType, oi); + e2.SetHasElements(); if (this->ProjectType == csproj && !this->InSourceBuild) { // add <Link> tag to written XAML source if necessary const std::string srcDir = this->Makefile->GetCurrentSourceDirectory(); @@ -830,58 +941,51 @@ void cmVisualStudio10TargetGenerator::WriteXamlFilesGroup() } if (!link.empty()) { ConvertToWindowsSlash(link); - this->WriteString("<Link>", 3); - (*this->BuildFileStream) << link << "</Link>\n"; + e2.Element("Link", link); } } - this->WriteString("<SubType>Designer</SubType>\n", 3); - this->WriteString("</", 2); - (*this->BuildFileStream) << xamlType << ">\n"; + e2.Element("SubType", "Designer"); } - this->WriteString("</ItemGroup>\n", 1); } } -void cmVisualStudio10TargetGenerator::WriteTargetSpecificReferences() +void cmVisualStudio10TargetGenerator::WriteTargetSpecificReferences(Elem& e0) { if (this->MSTools) { if (this->GlobalGenerator->TargetsWindowsPhone() && this->GlobalGenerator->GetSystemVersion() == "8.0") { - this->WriteString("<Import Project=\"" - "$(MSBuildExtensionsPath)\\Microsoft\\WindowsPhone\\v" - "$(TargetPlatformVersion)\\Microsoft.Cpp.WindowsPhone." - "$(TargetPlatformVersion).targets\" />\n", - 1); + Elem(e0, "Import") + .Attribute("Project", + "$(MSBuildExtensionsPath)\\Microsoft\\WindowsPhone\\v" + "$(TargetPlatformVersion)\\Microsoft.Cpp.WindowsPhone." + "$(TargetPlatformVersion).targets"); } } } -void cmVisualStudio10TargetGenerator::WriteTargetsFileReferences() +void cmVisualStudio10TargetGenerator::WriteTargetsFileReferences(Elem& e1) { - for (std::vector<TargetsFileAndConfigs>::iterator i = - this->TargetsFileAndConfigsVec.begin(); - i != this->TargetsFileAndConfigsVec.end(); ++i) { - TargetsFileAndConfigs const& tac = *i; - this->WriteString("<Import Project=\"", 3); - (*this->BuildFileStream) << tac.File << "\" "; - (*this->BuildFileStream) << "Condition=\""; - (*this->BuildFileStream) << "Exists('" << tac.File << "')"; + for (TargetsFileAndConfigs const& tac : this->TargetsFileAndConfigsVec) { + std::ostringstream oss; + oss << "Exists('" << tac.File << "')"; if (!tac.Configs.empty()) { - (*this->BuildFileStream) << " And ("; + oss << " And ("; for (size_t j = 0; j < tac.Configs.size(); ++j) { if (j > 0) { - (*this->BuildFileStream) << " Or "; + oss << " Or "; } - (*this->BuildFileStream) << "'$(Configuration)'=='" << tac.Configs[j] - << "'"; + oss << "'$(Configuration)'=='" << tac.Configs[j] << "'"; } - (*this->BuildFileStream) << ")"; + oss << ")"; } - (*this->BuildFileStream) << "\" />\n"; + + Elem(e1, "Import") + .Attribute("Project", tac.File) + .Attribute("Condition", oss.str()); } } -void cmVisualStudio10TargetGenerator::WriteWinRTReferences() +void cmVisualStudio10TargetGenerator::WriteWinRTReferences(Elem& e0) { std::vector<std::string> references; if (const char* vsWinRTReferences = @@ -895,77 +999,67 @@ void cmVisualStudio10TargetGenerator::WriteWinRTReferences() references.push_back("platform.winmd"); } if (!references.empty()) { - this->WriteString("<ItemGroup>\n", 1); - for (std::vector<std::string>::iterator ri = references.begin(); - ri != references.end(); ++ri) { - this->WriteString("<Reference Include=\"", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(*ri) << "\">\n"; - this->WriteString("<IsWinMDFile>true</IsWinMDFile>\n", 3); - this->WriteString("</Reference>\n", 2); + Elem e1(e0, "ItemGroup"); + for (std::string const& ri : references) { + Elem e2(e1, "Reference"); + e2.Attribute("Include", ri); + e2.Element("IsWinMDFile", "true"); } - this->WriteString("</ItemGroup>\n", 1); } } // ConfigurationType Application, Utility StaticLibrary DynamicLibrary -void cmVisualStudio10TargetGenerator::WriteProjectConfigurations() +void cmVisualStudio10TargetGenerator::WriteProjectConfigurations(Elem& e0) { - this->WriteString("<ItemGroup Label=\"ProjectConfigurations\">\n", 1); - for (std::vector<std::string>::const_iterator i = - this->Configurations.begin(); - i != this->Configurations.end(); ++i) { - this->WriteString("<ProjectConfiguration Include=\"", 2); - (*this->BuildFileStream) << *i << "|" << this->Platform << "\">\n"; - this->WriteString("<Configuration>", 3); - (*this->BuildFileStream) << *i << "</Configuration>\n"; - this->WriteString("<Platform>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(this->Platform) - << "</Platform>\n"; - this->WriteString("</ProjectConfiguration>\n", 2); - } - this->WriteString("</ItemGroup>\n", 1); + Elem e1(e0, "ItemGroup"); + e1.Attribute("Label", "ProjectConfigurations"); + for (std::string const& c : this->Configurations) { + Elem e2(e1, "ProjectConfiguration"); + e2.Attribute("Include", c + "|" + this->Platform); + e2.Element("Configuration", c); + e2.Element("Platform", this->Platform); + } } -void cmVisualStudio10TargetGenerator::WriteProjectConfigurationValues() +void cmVisualStudio10TargetGenerator::WriteProjectConfigurationValues(Elem& e0) { - for (std::vector<std::string>::const_iterator i = - this->Configurations.begin(); - i != this->Configurations.end(); ++i) { - this->WritePlatformConfigTag("PropertyGroup", *i, 1, - " Label=\"Configuration\"", "\n"); + for (std::string const& c : this->Configurations) { + Elem e1(e0, "PropertyGroup"); + e1.Attribute("Condition", this->CalcCondition(c)); + e1.Attribute("Label", "Configuration"); if (this->ProjectType != csproj) { - std::string configType = "<ConfigurationType>"; + std::string configType; if (const char* vsConfigurationType = this->GeneratorTarget->GetProperty("VS_CONFIGURATION_TYPE")) { - configType += cmVS10EscapeXML(vsConfigurationType); + configType = vsConfigurationType; } else { switch (this->GeneratorTarget->GetType()) { case cmStateEnums::SHARED_LIBRARY: case cmStateEnums::MODULE_LIBRARY: - configType += "DynamicLibrary"; + configType = "DynamicLibrary"; break; case cmStateEnums::OBJECT_LIBRARY: case cmStateEnums::STATIC_LIBRARY: - configType += "StaticLibrary"; + configType = "StaticLibrary"; break; case cmStateEnums::EXECUTABLE: if (this->NsightTegra && !this->GeneratorTarget->GetPropertyAsBool("ANDROID_GUI")) { // Android executables are .so too. - configType += "DynamicLibrary"; + configType = "DynamicLibrary"; } else { - configType += "Application"; + configType = "Application"; } break; case cmStateEnums::UTILITY: case cmStateEnums::GLOBAL_TARGET: if (this->NsightTegra) { // Tegra-Android platform does not understand "Utility". - configType += "StaticLibrary"; + configType = "StaticLibrary"; } else { - configType += "Utility"; + configType = "Utility"; } break; case cmStateEnums::UNKNOWN_LIBRARY: @@ -973,32 +1067,26 @@ void cmVisualStudio10TargetGenerator::WriteProjectConfigurationValues() break; } } - configType += "</ConfigurationType>\n"; - this->WriteString(configType.c_str(), 2); + e1.Element("ConfigurationType", configType); } if (this->MSTools) { if (!this->Managed) { - this->WriteMSToolConfigurationValues(*i); + this->WriteMSToolConfigurationValues(e1, c); } else { - this->WriteMSToolConfigurationValuesManaged(*i); + this->WriteMSToolConfigurationValuesManaged(e1, c); } } else if (this->NsightTegra) { - this->WriteNsightTegraConfigurationValues(*i); + this->WriteNsightTegraConfigurationValues(e1, c); } - - this->WriteString("</PropertyGroup>\n", 1); } } void cmVisualStudio10TargetGenerator::WriteMSToolConfigurationValues( - std::string const& config) + Elem& e1, std::string const& config) { - cmGlobalVisualStudio10Generator* gg = - static_cast<cmGlobalVisualStudio10Generator*>(this->GlobalGenerator); - const char* mfcFlag = - this->GeneratorTarget->Target->GetMakefile()->GetDefinition( - "CMAKE_MFC_FLAG"); + cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator; + const char* mfcFlag = this->Makefile->GetDefinition("CMAKE_MFC_FLAG"); if (mfcFlag) { std::string const mfcFlagValue = mfcFlag; @@ -1010,9 +1098,7 @@ void cmVisualStudio10TargetGenerator::WriteMSToolConfigurationValues( useOfMfcValue = "Dynamic"; } } - std::string mfcLine = "<UseOfMfc>"; - mfcLine += useOfMfcValue + "</UseOfMfc>\n"; - this->WriteString(mfcLine.c_str(), 2); + e1.Element("UseOfMfc", useOfMfcValue); } if ((this->GeneratorTarget->GetType() <= cmStateEnums::OBJECT_LIBRARY && @@ -1021,57 +1107,46 @@ void cmVisualStudio10TargetGenerator::WriteMSToolConfigurationValues( this->GlobalGenerator->TargetsWindowsPhone() || this->GlobalGenerator->TargetsWindowsStore() || this->GeneratorTarget->GetPropertyAsBool("VS_WINRT_EXTENSIONS")) { - this->WriteString("<CharacterSet>Unicode</CharacterSet>\n", 2); + e1.Element("CharacterSet", "Unicode"); } else if (this->GeneratorTarget->GetType() <= cmStateEnums::MODULE_LIBRARY && this->ClOptions[config]->UsingSBCS()) { - this->WriteString("<CharacterSet>NotSet</CharacterSet>\n", 2); + e1.Element("CharacterSet", "NotSet"); } else { - this->WriteString("<CharacterSet>MultiByte</CharacterSet>\n", 2); + e1.Element("CharacterSet", "MultiByte"); } if (const char* toolset = gg->GetPlatformToolset()) { - std::string pts = "<PlatformToolset>"; - pts += toolset; - pts += "</PlatformToolset>\n"; - this->WriteString(pts.c_str(), 2); + e1.Element("PlatformToolset", toolset); } if (this->GeneratorTarget->GetPropertyAsBool("VS_WINRT_COMPONENT") || this->GeneratorTarget->GetPropertyAsBool("VS_WINRT_EXTENSIONS")) { - this->WriteString("<WindowsAppContainer>true" - "</WindowsAppContainer>\n", - 2); + e1.Element("WindowsAppContainer", "true"); } } void cmVisualStudio10TargetGenerator::WriteMSToolConfigurationValuesManaged( - std::string const& config) + Elem& e1, std::string const& config) { - cmGlobalVisualStudio10Generator* gg = - static_cast<cmGlobalVisualStudio10Generator*>(this->GlobalGenerator); + cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator; Options& o = *(this->ClOptions[config]); if (o.IsDebug()) { - this->WriteString("<DebugSymbols>true</DebugSymbols>\n", 2); - this->WriteString("<DefineDebug>true</DefineDebug>\n", 2); + e1.Element("DebugSymbols", "true"); + e1.Element("DefineDebug", "true"); } std::string outDir = this->GeneratorTarget->GetDirectory(config) + "/"; ConvertToWindowsSlash(outDir); - this->WriteString("<OutputPath>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(outDir) << "</OutputPath>\n"; + e1.Element("OutputPath", outDir); if (o.HasFlag("Platform")) { - this->WriteString("<PlatformTarget>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(o.GetFlag("Platform")) - << "</PlatformTarget>\n"; + e1.Element("PlatformTarget", o.GetFlag("Platform")); o.RemoveFlag("Platform"); } if (const char* toolset = gg->GetPlatformToolset()) { - this->WriteString("<PlatformToolset>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(toolset) - << "</PlatformToolset>\n"; + e1.Element("PlatformToolset", toolset); } std::string postfixName = cmSystemTools::UpperCase(config); @@ -1081,68 +1156,50 @@ void cmVisualStudio10TargetGenerator::WriteMSToolConfigurationValuesManaged( if (const char* postfix = this->GeneratorTarget->GetProperty(postfixName)) { assemblyName += postfix; } - this->WriteString("<AssemblyName>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(assemblyName) - << "</AssemblyName>\n"; + e1.Element("AssemblyName", assemblyName); if (cmStateEnums::EXECUTABLE == this->GeneratorTarget->GetType()) { - this->WriteString("<StartAction>Program</StartAction>\n", 2); - this->WriteString("<StartProgram>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(outDir) - << cmVS10EscapeXML(assemblyName) - << ".exe</StartProgram>\n"; + e1.Element("StartAction", "Program"); + e1.Element("StartProgram", outDir + assemblyName + ".exe"); } - o.OutputFlagMap(*this->BuildFileStream, " "); + OptionsHelper oh(o, e1); + oh.OutputFlagMap(); } //---------------------------------------------------------------------------- void cmVisualStudio10TargetGenerator::WriteNsightTegraConfigurationValues( - std::string const&) + Elem& e1, std::string const&) { - cmGlobalVisualStudio10Generator* gg = - static_cast<cmGlobalVisualStudio10Generator*>(this->GlobalGenerator); + cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator; const char* toolset = gg->GetPlatformToolset(); - std::string ntv = "<NdkToolchainVersion>"; - ntv += toolset ? toolset : "Default"; - ntv += "</NdkToolchainVersion>\n"; - this->WriteString(ntv.c_str(), 2); + e1.Element("NdkToolchainVersion", toolset ? toolset : "Default"); if (const char* minApi = this->GeneratorTarget->GetProperty("ANDROID_API_MIN")) { - this->WriteString("<AndroidMinAPI>", 2); - (*this->BuildFileStream) << "android-" << cmVS10EscapeXML(minApi) - << "</AndroidMinAPI>\n"; + e1.Element("AndroidMinAPI", "android-" + std::string(minApi)); } if (const char* api = this->GeneratorTarget->GetProperty("ANDROID_API")) { - this->WriteString("<AndroidTargetAPI>", 2); - (*this->BuildFileStream) << "android-" << cmVS10EscapeXML(api) - << "</AndroidTargetAPI>\n"; + e1.Element("AndroidTargetAPI", "android-" + std::string(api)); } if (const char* cpuArch = this->GeneratorTarget->GetProperty("ANDROID_ARCH")) { - this->WriteString("<AndroidArch>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(cpuArch) << "</AndroidArch>\n"; + e1.Element("AndroidArch", cpuArch); } if (const char* stlType = this->GeneratorTarget->GetProperty("ANDROID_STL_TYPE")) { - this->WriteString("<AndroidStlType>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(stlType) - << "</AndroidStlType>\n"; + e1.Element("AndroidStlType", stlType); } } -void cmVisualStudio10TargetGenerator::WriteCustomCommands() +void cmVisualStudio10TargetGenerator::WriteCustomCommands(Elem& e0) { - this->SourcesVisited.clear(); this->CSharpCustomCommandNames.clear(); std::vector<cmSourceFile const*> customCommands; this->GeneratorTarget->GetCustomCommands(customCommands, ""); - for (std::vector<cmSourceFile const*>::const_iterator si = - customCommands.begin(); - si != customCommands.end(); ++si) { - this->WriteCustomCommand(*si); + for (cmSourceFile const* si : customCommands) { + this->WriteCustomCommand(e0, si); } // Add CMakeLists.txt file with rule to re-run CMake for user convenience. @@ -1150,46 +1207,41 @@ void cmVisualStudio10TargetGenerator::WriteCustomCommands() this->GeneratorTarget->GetName() != CMAKE_CHECK_BUILD_SYSTEM_TARGET) { if (cmSourceFile const* sf = this->LocalGenerator->CreateVCProjBuildRule()) { - this->WriteCustomCommand(sf); + this->WriteCustomCommand(e0, sf); } } } void cmVisualStudio10TargetGenerator::WriteCustomCommand( - cmSourceFile const* sf) + Elem& e0, cmSourceFile const* sf) { - if (this->SourcesVisited.insert(sf).second) { + if (this->LocalGenerator->GetSourcesVisited(this->GeneratorTarget) + .insert(sf) + .second) { if (std::vector<cmSourceFile*> const* depends = this->GeneratorTarget->GetSourceDepends(sf)) { - for (std::vector<cmSourceFile*>::const_iterator di = depends->begin(); - di != depends->end(); ++di) { - this->WriteCustomCommand(*di); + for (cmSourceFile const* di : *depends) { + this->WriteCustomCommand(e0, di); } } if (cmCustomCommand const* command = sf->GetCustomCommand()) { // C# projects write their <Target> within WriteCustomRule() - if (this->ProjectType != csproj) { - this->WriteString("<ItemGroup>\n", 1); - } - this->WriteCustomRule(sf, *command); - if (this->ProjectType != csproj) { - this->WriteString("</ItemGroup>\n", 1); - } + this->WriteCustomRule(e0, sf, *command); } } } void cmVisualStudio10TargetGenerator::WriteCustomRule( - cmSourceFile const* source, cmCustomCommand const& command) + Elem& e0, cmSourceFile const* source, cmCustomCommand const& command) { std::string sourcePath = source->GetFullPath(); // VS 10 will always rebuild a custom command attached to a .rule // file that doesn't exist so create the file explicitly. if (source->GetPropertyAsBool("__CMAKE_RULE")) { - if (!cmSystemTools::FileExists(sourcePath.c_str())) { + if (!cmSystemTools::FileExists(sourcePath)) { // Make sure the path exists for the file std::string path = cmSystemTools::GetFilenamePath(sourcePath); - cmSystemTools::MakeDirectory(path.c_str()); + cmSystemTools::MakeDirectory(path); cmsys::ofstream fout(sourcePath.c_str()); if (fout) { fout << "# generated from CMake\n"; @@ -1209,111 +1261,93 @@ void cmVisualStudio10TargetGenerator::WriteCustomRule( } cmLocalVisualStudio7Generator* lg = this->LocalGenerator; + std::unique_ptr<Elem> spe1; + std::unique_ptr<Elem> spe2; if (this->ProjectType != csproj) { - this->WriteSource("CustomBuild", source, ">\n"); + spe1 = cm::make_unique<Elem>(e0, "ItemGroup"); + spe2 = cm::make_unique<Elem>(*spe1); + this->WriteSource(*spe2, "CustomBuild", source); + spe2->SetHasElements(); } else { - this->WriteString("<ItemGroup>\n", 1); + Elem e1(e0, "ItemGroup"); + Elem e2(e1); std::string link; this->GetCSharpSourceLink(source, link); - this->WriteSource("None", source, ">\n"); + this->WriteSource(e2, "None", source); + e2.SetHasElements(); if (!link.empty()) { - this->WriteString("<Link>", 3); - (*this->BuildFileStream) << link << "</Link>\n"; + e2.Element("Link", link); } - this->WriteString("</None>\n", 2); - this->WriteString("</ItemGroup>\n", 1); } - for (std::vector<std::string>::const_iterator i = - this->Configurations.begin(); - i != this->Configurations.end(); ++i) { - cmCustomCommandGenerator ccg(command, *i, this->LocalGenerator); + for (std::string const& c : this->Configurations) { + cmCustomCommandGenerator ccg(command, c, lg); std::string comment = lg->ConstructComment(ccg); comment = cmVS10EscapeComment(comment); - std::string script = cmVS10EscapeXML(lg->ConstructScript(ccg)); + std::string script = lg->ConstructScript(ccg); // input files for custom command std::stringstream inputs; - inputs << cmVS10EscapeXML(source->GetFullPath()); - for (std::vector<std::string>::const_iterator d = ccg.GetDepends().begin(); - d != ccg.GetDepends().end(); ++d) { + inputs << source->GetFullPath(); + for (std::string const& d : ccg.GetDepends()) { std::string dep; - if (this->LocalGenerator->GetRealDependency(*d, *i, dep)) { + if (lg->GetRealDependency(d, c, dep)) { ConvertToWindowsSlash(dep); - inputs << ";" << cmVS10EscapeXML(dep); + inputs << ";" << dep; } } // output files for custom command std::stringstream outputs; const char* sep = ""; - for (std::vector<std::string>::const_iterator o = ccg.GetOutputs().begin(); - o != ccg.GetOutputs().end(); ++o) { - std::string out = *o; + for (std::string const& o : ccg.GetOutputs()) { + std::string out = o; ConvertToWindowsSlash(out); - outputs << sep << cmVS10EscapeXML(out); + outputs << sep << out; sep = ";"; } if (this->ProjectType == csproj) { - std::string name = "CustomCommand_" + *i + "_" + + std::string name = "CustomCommand_" + c + "_" + cmSystemTools::ComputeStringMD5(sourcePath); - std::string inputs_s = inputs.str(); - std::string outputs_s = outputs.str(); - comment = cmVS10EscapeQuotes(comment); - script = cmVS10EscapeQuotes(script); - inputs_s = cmVS10EscapeQuotes(inputs_s); - outputs_s = cmVS10EscapeQuotes(outputs_s); - this->WriteCustomRuleCSharp(*i, name, script, inputs_s, outputs_s, - comment); + this->WriteCustomRuleCSharp(e0, c, name, script, inputs.str(), + outputs.str(), comment); } else { - this->WriteCustomRuleCpp(*i, script, inputs.str(), outputs.str(), + this->WriteCustomRuleCpp(*spe2, c, script, inputs.str(), outputs.str(), comment); } } - if (this->ProjectType != csproj) { - this->WriteString("</CustomBuild>\n", 2); - } } void cmVisualStudio10TargetGenerator::WriteCustomRuleCpp( - std::string const& config, std::string const& script, + Elem& e2, std::string const& config, std::string const& script, std::string const& inputs, std::string const& outputs, std::string const& comment) { - this->WritePlatformConfigTag("Message", config, 3); - (*this->BuildFileStream) << cmVS10EscapeXML(comment) << "</Message>\n"; - this->WritePlatformConfigTag("Command", config, 3); - (*this->BuildFileStream) << script << "</Command>\n"; - this->WritePlatformConfigTag("AdditionalInputs", config, 3); - (*this->BuildFileStream) << inputs; - (*this->BuildFileStream) << ";%(AdditionalInputs)</AdditionalInputs>\n"; - this->WritePlatformConfigTag("Outputs", config, 3); - (*this->BuildFileStream) << outputs << "</Outputs>\n"; + const std::string cond = this->CalcCondition(config); + e2.WritePlatformConfigTag("Message", cond, comment); + e2.WritePlatformConfigTag("Command", cond, script); + e2.WritePlatformConfigTag("AdditionalInputs", cond, + inputs + ";%(AdditionalInputs)"); + e2.WritePlatformConfigTag("Outputs", cond, outputs); if (this->LocalGenerator->GetVersion() > cmGlobalVisualStudioGenerator::VS10) { // VS >= 11 let us turn off linking of custom command outputs. - this->WritePlatformConfigTag("LinkObjects", config, 3); - (*this->BuildFileStream) << "false</LinkObjects>\n"; + e2.WritePlatformConfigTag("LinkObjects", cond, "false"); } } void cmVisualStudio10TargetGenerator::WriteCustomRuleCSharp( - std::string const& config, std::string const& name, + Elem& e0, std::string const& config, std::string const& name, std::string const& script, std::string const& inputs, std::string const& outputs, std::string const& comment) { this->CSharpCustomCommandNames.insert(name); - std::stringstream attributes; - attributes << "\n Name=\"" << name << "\""; - attributes << "\n Inputs=\"" << inputs << "\""; - attributes << "\n Outputs=\"" << outputs << "\""; - this->WritePlatformConfigTag("Target", config, 1, attributes.str().c_str(), - "\n"); + Elem e1(e0, "Target"); + e1.Attribute("Condition", this->CalcCondition(config)); + e1.S << "\n Name=\"" << name << "\""; + e1.S << "\n Inputs=\"" << cmVS10EscapeAttr(inputs) << "\""; + e1.S << "\n Outputs=\"" << cmVS10EscapeAttr(outputs) << "\""; if (!comment.empty()) { - this->WriteString("<Exec Command=\"", 2); - (*this->BuildFileStream) << "echo " << cmVS10EscapeXML(comment) - << "\" />\n"; + Elem(e1, "Exec").Attribute("Command", "echo " + comment); } - this->WriteString("<Exec Command=\"", 2); - (*this->BuildFileStream) << script << "\" />\n"; - this->WriteString("</Target>\n", 1); + Elem(e1, "Exec").Attribute("Command", script); } std::string cmVisualStudio10TargetGenerator::ConvertPath( @@ -1321,8 +1355,8 @@ std::string cmVisualStudio10TargetGenerator::ConvertPath( { return forceRelative ? cmSystemTools::RelativePath( - this->LocalGenerator->GetCurrentBinaryDirectory(), path.c_str()) - : path.c_str(); + this->LocalGenerator->GetCurrentBinaryDirectory(), path) + : path; } static void ConvertToWindowsSlash(std::string& s) @@ -1334,6 +1368,7 @@ static void ConvertToWindowsSlash(std::string& s) pos++; } } + void cmVisualStudio10TargetGenerator::WriteGroups() { if (this->ProjectType == csproj) { @@ -1347,10 +1382,8 @@ void cmVisualStudio10TargetGenerator::WriteGroups() this->GeneratorTarget->GetAllConfigSources(); std::set<cmSourceGroup*> groupsUsed; - for (std::vector<cmGeneratorTarget::AllConfigSource>::const_iterator si = - sources.begin(); - si != sources.end(); ++si) { - std::string const& source = si->Source->GetFullPath(); + for (cmGeneratorTarget::AllConfigSource const& si : sources) { + std::string const& source = si.Source->GetFullPath(); cmSourceGroup* sourceGroup = this->Makefile->FindSourceGroup(source, sourceGroups); groupsUsed.insert(sourceGroup); @@ -1369,109 +1402,94 @@ void cmVisualStudio10TargetGenerator::WriteGroups() fout.SetCopyIfDifferent(true); char magic[] = { char(0xEF), char(0xBB), char(0xBF) }; fout.write(magic, 3); - cmGeneratedFileStream* save = this->BuildFileStream; - this->BuildFileStream = &fout; - - // get the tools version to use - const std::string toolsVer(this->GlobalGenerator->GetToolsVersion()); - std::string project_defaults = "<?xml version=\"1.0\" encoding=\"" + - this->GlobalGenerator->Encoding() + "\"?>\n"; - project_defaults.append("<Project ToolsVersion=\""); - project_defaults.append(toolsVer + "\" "); - project_defaults.append( - "xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n"); - this->WriteString(project_defaults.c_str(), 0); - - for (ToolSourceMap::const_iterator ti = this->Tools.begin(); - ti != this->Tools.end(); ++ti) { - this->WriteGroupSources(ti->first.c_str(), ti->second, sourceGroups); - } - - // Added files are images and the manifest. - if (!this->AddedFiles.empty()) { - this->WriteString("<ItemGroup>\n", 1); - for (std::string const& oi : this->AddedFiles) { - std::string fileName = - cmSystemTools::LowerCase(cmSystemTools::GetFilenameName(oi)); - if (fileName == "wmappmanifest.xml") { - this->WriteString("<XML Include=\"", 2); - (*this->BuildFileStream) << oi << "\">\n"; - this->WriteString("<Filter>Resource Files</Filter>\n", 3); - this->WriteString("</XML>\n", 2); - } else if (cmSystemTools::GetFilenameExtension(fileName) == - ".appxmanifest") { - this->WriteString("<AppxManifest Include=\"", 2); - (*this->BuildFileStream) << oi << "\">\n"; - this->WriteString("<Filter>Resource Files</Filter>\n", 3); - this->WriteString("</AppxManifest>\n", 2); - } else if (cmSystemTools::GetFilenameExtension(fileName) == ".pfx") { - this->WriteString("<None Include=\"", 2); - (*this->BuildFileStream) << oi << "\">\n"; - this->WriteString("<Filter>Resource Files</Filter>\n", 3); - this->WriteString("</None>\n", 2); - } else { - this->WriteString("<Image Include=\"", 2); - (*this->BuildFileStream) << oi << "\">\n"; - this->WriteString("<Filter>Resource Files</Filter>\n", 3); - this->WriteString("</Image>\n", 2); + + fout << "<?xml version=\"1.0\" encoding=\"" + << this->GlobalGenerator->Encoding() << "\"?>" + << "\n"; + { + Elem e0(fout); + e0.StartElement("Project"); + e0.Attribute("ToolsVersion", this->GlobalGenerator->GetToolsVersion()); + e0.Attribute("xmlns", + "http://schemas.microsoft.com/developer/msbuild/2003"); + + for (auto const& ti : this->Tools) { + this->WriteGroupSources(e0, ti.first, ti.second, sourceGroups); + } + + // Added files are images and the manifest. + if (!this->AddedFiles.empty()) { + Elem e1(e0, "ItemGroup"); + e1.SetHasElements(); + for (std::string const& oi : this->AddedFiles) { + std::string fileName = + cmSystemTools::LowerCase(cmSystemTools::GetFilenameName(oi)); + if (fileName == "wmappmanifest.xml") { + Elem e2(e1, "XML"); + e2.Attribute("Include", oi); + e2.Element("Filter", "Resource Files"); + } else if (cmSystemTools::GetFilenameExtension(fileName) == + ".appxmanifest") { + Elem e2(e1, "AppxManifest"); + e2.Attribute("Include", oi); + e2.Element("Filter", "Resource Files"); + } else if (cmSystemTools::GetFilenameExtension(fileName) == ".pfx") { + Elem e2(e1, "None"); + e2.Attribute("Include", oi); + e2.Element("Filter", "Resource Files"); + } else { + Elem e2(e1, "Image"); + e2.Attribute("Include", oi); + e2.Element("Filter", "Resource Files"); + } } } - this->WriteString("</ItemGroup>\n", 1); - } - std::vector<cmSourceFile const*> resxObjs; - this->GeneratorTarget->GetResxSources(resxObjs, ""); - if (!resxObjs.empty()) { - this->WriteString("<ItemGroup>\n", 1); - for (cmSourceFile const* oi : resxObjs) { - std::string obj = oi->GetFullPath(); - this->WriteString("<EmbeddedResource Include=\"", 2); - ConvertToWindowsSlash(obj); - (*this->BuildFileStream) << cmVS10EscapeXML(obj) << "\">\n"; - this->WriteString("<Filter>Resource Files</Filter>\n", 3); - this->WriteString("</EmbeddedResource>\n", 2); - } - this->WriteString("</ItemGroup>\n", 1); - } - - this->WriteString("<ItemGroup>\n", 1); - std::vector<cmSourceGroup*> groupsVec(groupsUsed.begin(), groupsUsed.end()); - std::sort(groupsVec.begin(), groupsVec.end(), - [](cmSourceGroup* l, cmSourceGroup* r) { - return l->GetFullName() < r->GetFullName(); - }); - for (cmSourceGroup* sg : groupsVec) { - std::string const& name = sg->GetFullName(); - if (!name.empty()) { - this->WriteString("<Filter Include=\"", 2); - (*this->BuildFileStream) << name << "\">\n"; - std::string guidName = "SG_Filter_"; - guidName += name; - this->WriteString("<UniqueIdentifier>", 3); - std::string guid = this->GlobalGenerator->GetGUID(guidName); - (*this->BuildFileStream) << "{" << guid << "}" - << "</UniqueIdentifier>\n"; - this->WriteString("</Filter>\n", 2); - } - } - - if (!resxObjs.empty() || !this->AddedFiles.empty()) { - this->WriteString("<Filter Include=\"Resource Files\">\n", 2); - std::string guidName = "SG_Filter_Resource Files"; - this->WriteString("<UniqueIdentifier>", 3); - std::string guid = this->GlobalGenerator->GetGUID(guidName); - (*this->BuildFileStream) << "{" << guid << "}" - << "</UniqueIdentifier>\n"; - this->WriteString("<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;", 3); - (*this->BuildFileStream) << "gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;"; - (*this->BuildFileStream) << "mfcribbon-ms</Extensions>\n"; - this->WriteString("</Filter>\n", 2); - } - - this->WriteString("</ItemGroup>\n", 1); - this->WriteString("</Project>\n", 0); - // restore stream pointer - this->BuildFileStream = save; + std::vector<cmSourceFile const*> resxObjs; + this->GeneratorTarget->GetResxSources(resxObjs, ""); + if (!resxObjs.empty()) { + Elem e1(e0, "ItemGroup"); + for (cmSourceFile const* oi : resxObjs) { + std::string obj = oi->GetFullPath(); + ConvertToWindowsSlash(obj); + Elem e2(e1, "EmbeddedResource"); + e2.Attribute("Include", obj); + e2.Element("Filter", "Resource Files"); + } + } + { + Elem e1(e0, "ItemGroup"); + e1.SetHasElements(); + std::vector<cmSourceGroup*> groupsVec(groupsUsed.begin(), + groupsUsed.end()); + std::sort(groupsVec.begin(), groupsVec.end(), + [](cmSourceGroup* l, cmSourceGroup* r) { + return l->GetFullName() < r->GetFullName(); + }); + for (cmSourceGroup* sg : groupsVec) { + std::string const& name = sg->GetFullName(); + if (!name.empty()) { + std::string guidName = "SG_Filter_" + name; + std::string guid = this->GlobalGenerator->GetGUID(guidName); + Elem e2(e1, "Filter"); + e2.Attribute("Include", name); + e2.Element("UniqueIdentifier", "{" + guid + "}"); + } + } + + if (!resxObjs.empty() || !this->AddedFiles.empty()) { + std::string guidName = "SG_Filter_Resource Files"; + std::string guid = this->GlobalGenerator->GetGUID(guidName); + Elem e2(e1, "Filter"); + e2.Attribute("Include", "Resource Files"); + e2.Element("UniqueIdentifier", "{" + guid + "}"); + e2.Element("Extensions", + "rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;" + "gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms"); + } + } + } + fout << '\n'; if (fout.Close()) { this->GlobalGenerator->FileReplacedDuringGenerate(path); @@ -1516,61 +1534,53 @@ void cmVisualStudio10TargetGenerator::AddMissingSourceGroups( } void cmVisualStudio10TargetGenerator::WriteGroupSources( - const char* name, ToolSources const& sources, + Elem& e0, std::string const& name, ToolSources const& sources, std::vector<cmSourceGroup>& sourceGroups) { - this->WriteString("<ItemGroup>\n", 1); + Elem e1(e0, "ItemGroup"); + e1.SetHasElements(); for (ToolSource const& s : sources) { cmSourceFile const* sf = s.SourceFile; std::string const& source = sf->GetFullPath(); cmSourceGroup* sourceGroup = this->Makefile->FindSourceGroup(source, sourceGroups); std::string const& filter = sourceGroup->GetFullName(); - this->WriteString("<", 2); std::string path = this->ConvertPath(source, s.RelativePath); ConvertToWindowsSlash(path); - (*this->BuildFileStream) << name << " Include=\"" << cmVS10EscapeXML(path); + Elem e2(e1, name.c_str()); + e2.Attribute("Include", path); if (!filter.empty()) { - (*this->BuildFileStream) << "\">\n"; - this->WriteString("<Filter>", 3); - (*this->BuildFileStream) << filter << "</Filter>\n"; - this->WriteString("</", 2); - (*this->BuildFileStream) << name << ">\n"; - } else { - (*this->BuildFileStream) << "\" />\n"; + e2.Element("Filter", filter); } } - this->WriteString("</ItemGroup>\n", 1); } -void cmVisualStudio10TargetGenerator::WriteHeaderSource(cmSourceFile const* sf) +void cmVisualStudio10TargetGenerator::WriteHeaderSource(Elem& e1, + cmSourceFile const* sf) { std::string const& fileName = sf->GetFullPath(); + Elem e2(e1); + this->WriteSource(e2, "ClInclude", sf); if (this->IsResxHeader(fileName)) { - this->WriteSource("ClInclude", sf, ">\n"); - this->WriteString("<FileType>CppForm</FileType>\n", 3); - this->WriteString("</ClInclude>\n", 2); + e2.Element("FileType", "CppForm"); } else if (this->IsXamlHeader(fileName)) { - this->WriteSource("ClInclude", sf, ">\n"); - this->WriteString("<DependentUpon>", 3); std::string xamlFileName = fileName.substr(0, fileName.find_last_of(".")); - (*this->BuildFileStream) << xamlFileName << "</DependentUpon>\n"; - this->WriteString("</ClInclude>\n", 2); - } else { - this->WriteSource("ClInclude", sf); + e2.Element("DependentUpon", xamlFileName); } } -void cmVisualStudio10TargetGenerator::WriteExtraSource(cmSourceFile const* sf) +void cmVisualStudio10TargetGenerator::WriteExtraSource(Elem& e1, + cmSourceFile const* sf) { bool toolHasSettings = false; - std::string tool = "None"; + const char* tool = "None"; std::string shaderType; std::string shaderEntryPoint; std::string shaderModel; std::string shaderAdditionalFlags; std::string shaderDisableOptimizations; std::string shaderEnableDebug; + std::string shaderObjectFileName; std::string outputHeaderFile; std::string variableName; std::string settingsGenerator; @@ -1639,12 +1649,16 @@ void cmVisualStudio10TargetGenerator::WriteExtraSource(cmSourceFile const* sf) } // Figure out if debug information should be generated if (const char* sed = sf->GetProperty("VS_SHADER_ENABLE_DEBUG")) { - shaderEnableDebug = cmSystemTools::IsOn(sed) ? "true" : "false"; + shaderEnableDebug = sed; toolHasSettings = true; } // Figure out if optimizations should be disabled if (const char* sdo = sf->GetProperty("VS_SHADER_DISABLE_OPTIMIZATIONS")) { - shaderDisableOptimizations = cmSystemTools::IsOn(sdo) ? "true" : "false"; + shaderDisableOptimizations = sdo; + toolHasSettings = true; + } + if (const char* sofn = sf->GetProperty("VS_SHADER_OBJECT_FILE_NAME")) { + shaderObjectFileName = sofn; toolHasSettings = true; } } else if (ext == "jpg" || ext == "png") { @@ -1714,8 +1728,10 @@ void cmVisualStudio10TargetGenerator::WriteExtraSource(cmSourceFile const* sf) } } + Elem e2(e1); + this->WriteSource(e2, tool, sf); if (toolHasSettings) { - this->WriteSource(tool, sf, ">\n"); + e2.SetHasElements(); if (!deployContent.empty()) { cmGeneratorExpression ge; @@ -1723,123 +1739,113 @@ void cmVisualStudio10TargetGenerator::WriteExtraSource(cmSourceFile const* sf) ge.Parse(deployContent); // Deployment location cannot be set on a configuration basis if (!deployLocation.empty()) { - this->WriteString("<Link>", 3); - (*this->BuildFileStream) << deployLocation - << "\\%(FileName)%(Extension)"; - this->WriteString("</Link>\n", 0); + e2.Element("Link", deployLocation + "\\%(FileName)%(Extension)"); } for (size_t i = 0; i != this->Configurations.size(); ++i) { if (0 == strcmp(cge->Evaluate(this->LocalGenerator, this->Configurations[i]), "1")) { - this->WriteString("<DeploymentContent Condition=\"" - "'$(Configuration)|$(Platform)'=='", - 3); - (*this->BuildFileStream) << this->Configurations[i] << "|" - << this->Platform << "'\">true"; - this->WriteString("</DeploymentContent>\n", 0); + e2.WritePlatformConfigTag( + "DeploymentContent", "'$(Configuration)|$(Platform)'=='" + + this->Configurations[i] + "|" + this->Platform + "'", + "true"); } else { - this->WriteString("<ExcludedFromBuild Condition=\"" - "'$(Configuration)|$(Platform)'=='", - 3); - (*this->BuildFileStream) << this->Configurations[i] << "|" - << this->Platform << "'\">true"; - this->WriteString("</ExcludedFromBuild>\n", 0); + e2.WritePlatformConfigTag( + "ExcludedFromBuild", "'$(Configuration)|$(Platform)'=='" + + this->Configurations[i] + "|" + this->Platform + "'", + "true"); } } } if (!shaderType.empty()) { - this->WriteString("<ShaderType>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(shaderType) - << "</ShaderType>\n"; + e2.Element("ShaderType", shaderType); } if (!shaderEntryPoint.empty()) { - this->WriteString("<EntryPointName>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(shaderEntryPoint) - << "</EntryPointName>\n"; + e2.Element("EntryPointName", shaderEntryPoint); } if (!shaderModel.empty()) { - this->WriteString("<ShaderModel>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(shaderModel) - << "</ShaderModel>\n"; + e2.Element("ShaderModel", shaderModel); } if (!outputHeaderFile.empty()) { for (size_t i = 0; i != this->Configurations.size(); ++i) { - this->WriteString("<HeaderFileOutput Condition=\"" - "'$(Configuration)|$(Platform)'=='", - 3); - (*this->BuildFileStream) << this->Configurations[i] << "|" - << this->Platform << "'\">" - << cmVS10EscapeXML(outputHeaderFile); - this->WriteString("</HeaderFileOutput>\n", 0); + e2.WritePlatformConfigTag( + "HeaderFileOutput", "'$(Configuration)|$(Platform)'=='" + + this->Configurations[i] + "|" + this->Platform + "'", + outputHeaderFile); } } if (!variableName.empty()) { for (size_t i = 0; i != this->Configurations.size(); ++i) { - this->WriteString("<VariableName Condition=\"" - "'$(Configuration)|$(Platform)'=='", - 3); - (*this->BuildFileStream) << this->Configurations[i] << "|" - << this->Platform << "'\">" - << cmVS10EscapeXML(variableName); - this->WriteString("</VariableName>\n", 0); + e2.WritePlatformConfigTag( + "VariableName", "'$(Configuration)|$(Platform)'=='" + + this->Configurations[i] + "|" + this->Platform + "'", + variableName); } } if (!shaderEnableDebug.empty()) { - this->WriteString("<EnableDebuggingInformation>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(shaderEnableDebug) - << "</EnableDebuggingInformation>\n"; + cmGeneratorExpression ge; + std::unique_ptr<cmCompiledGeneratorExpression> cge = + ge.Parse(shaderEnableDebug); + + for (size_t i = 0; i != this->Configurations.size(); ++i) { + const char* enableDebug = + cge->Evaluate(this->LocalGenerator, this->Configurations[i]); + if (strlen(enableDebug) > 0) { + e2.WritePlatformConfigTag( + "EnableDebuggingInformation", "'$(Configuration)|$(Platform)'=='" + + this->Configurations[i] + "|" + this->Platform + "'", + cmSystemTools::IsOn(enableDebug) ? "true" : "false"); + } + } } if (!shaderDisableOptimizations.empty()) { - this->WriteString("<DisableOptimizations>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(shaderDisableOptimizations) - << "</DisableOptimizations>\n"; + cmGeneratorExpression ge; + std::unique_ptr<cmCompiledGeneratorExpression> cge = + ge.Parse(shaderDisableOptimizations); + + for (size_t i = 0; i != this->Configurations.size(); ++i) { + const char* disableOptimizations = + cge->Evaluate(this->LocalGenerator, this->Configurations[i]); + if (strlen(disableOptimizations) > 0) { + e2.WritePlatformConfigTag( + "DisableOptimizations", "'$(Configuration)|$(Platform)'=='" + + this->Configurations[i] + "|" + this->Platform + "'", + (cmSystemTools::IsOn(disableOptimizations) ? "true" : "false")); + } + } + } + if (!shaderObjectFileName.empty()) { + e2.Element("ObjectFileOutput", shaderObjectFileName); } if (!shaderAdditionalFlags.empty()) { - this->WriteString("<AdditionalOptions>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(shaderAdditionalFlags) - << "</AdditionalOptions>\n"; + e2.Element("AdditionalOptions", shaderAdditionalFlags); } if (!settingsGenerator.empty()) { - this->WriteString("<Generator>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(settingsGenerator) - << "</Generator>\n"; + e2.Element("Generator", settingsGenerator); } if (!settingsLastGenOutput.empty()) { - this->WriteString("<LastGenOutput>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(settingsLastGenOutput) - << "</LastGenOutput>\n"; + e2.Element("LastGenOutput", settingsLastGenOutput); } if (!sourceLink.empty()) { - this->WriteString("<Link>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(sourceLink) << "</Link>\n"; + e2.Element("Link", sourceLink); } if (!subType.empty()) { - this->WriteString("<SubType>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(subType) << "</SubType>\n"; + e2.Element("SubType", subType); } if (!copyToOutDir.empty()) { - this->WriteString("<CopyToOutputDirectory>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(copyToOutDir) - << "</CopyToOutputDirectory>\n"; + e2.Element("CopyToOutputDirectory", copyToOutDir); } if (!includeInVsix.empty()) { - this->WriteString("<IncludeInVSIX>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(includeInVsix) - << "</IncludeInVSIX>\n"; + e2.Element("IncludeInVSIX", includeInVsix); } // write source file specific tags - this->WriteCSharpSourceProperties(sourceFileTags); - this->WriteString("</", 2); - (*this->BuildFileStream) << tool << ">\n"; - } else { - this->WriteSource(tool, sf); + this->WriteCSharpSourceProperties(e2, sourceFileTags); } } -void cmVisualStudio10TargetGenerator::WriteSource(std::string const& tool, - cmSourceFile const* sf, - const char* end) +void cmVisualStudio10TargetGenerator::WriteSource(Elem& e2, + std::string const& tool, + cmSourceFile const* sf) { // Visual Studio tools append relative paths to the current dir, as in: // @@ -1853,7 +1859,7 @@ void cmVisualStudio10TargetGenerator::WriteSource(std::string const& tool, std::string sourceFile = this->ConvertPath(sf->GetFullPath(), forceRelative); if (this->LocalGenerator->GetVersion() == cmGlobalVisualStudioGenerator::VS10 && - cmSystemTools::FileIsFullPath(sourceFile.c_str())) { + cmSystemTools::FileIsFullPath(sourceFile)) { // Normal path conversion resulted in a full path. VS 10 (but not 11) // refuses to show the property page in the IDE for a source file with a // full path (not starting in a '.' or '/' AFAICT). CMake <= 2.8.4 used a @@ -1874,21 +1880,20 @@ void cmVisualStudio10TargetGenerator::WriteSource(std::string const& tool, } } ConvertToWindowsSlash(sourceFile); - this->WriteString("<", 2); - (*this->BuildFileStream) << tool << " Include=\"" - << cmVS10EscapeXML(sourceFile) << "\"" - << (end ? end : " />\n"); + e2.StartElement(tool.c_str()); + e2.Attribute("Include", sourceFile); ToolSource toolSource = { sf, forceRelative }; this->Tools[tool].push_back(toolSource); } -void cmVisualStudio10TargetGenerator::WriteAllSources() +void cmVisualStudio10TargetGenerator::WriteAllSources(Elem& e0) { if (this->GeneratorTarget->GetType() > cmStateEnums::UTILITY) { return; } - this->WriteString("<ItemGroup>\n", 1); + Elem e1(e0, "ItemGroup"); + e1.SetHasElements(); std::vector<size_t> all_configs; for (size_t ci = 0; ci < this->Configurations.size(); ++ci) { @@ -1906,7 +1911,7 @@ void cmVisualStudio10TargetGenerator::WriteAllSources() // Skip explicit reference to CMakeLists.txt source. continue; } - std::string tool; + const char* tool = nullptr; switch (si.Kind) { case cmGeneratorTarget::SourceKindAppManifest: tool = "AppxManifest"; @@ -1933,10 +1938,10 @@ void cmVisualStudio10TargetGenerator::WriteAllSources() } break; case cmGeneratorTarget::SourceKindExtra: - this->WriteExtraSource(si.Source); + this->WriteExtraSource(e1, si.Source); break; case cmGeneratorTarget::SourceKindHeader: - this->WriteHeaderSource(si.Source); + this->WriteHeaderSource(e1, si.Source); break; case cmGeneratorTarget::SourceKindIDL: tool = "Midl"; @@ -1975,7 +1980,7 @@ void cmVisualStudio10TargetGenerator::WriteAllSources() break; } - if (!tool.empty()) { + if (tool) { // Compute set of configurations to exclude, if any. std::vector<size_t> const& include_configs = si.Configs; std::vector<size_t> exclude_configs; @@ -1983,43 +1988,24 @@ void cmVisualStudio10TargetGenerator::WriteAllSources() include_configs.begin(), include_configs.end(), std::back_inserter(exclude_configs)); + Elem e2(e1); + this->WriteSource(e2, tool, si.Source); if (si.Kind == cmGeneratorTarget::SourceKindObjectSource) { - // FIXME: refactor generation to avoid tracking XML syntax state. - this->WriteSource(tool, si.Source, ""); - bool have_nested = this->OutputSourceSpecificFlags(si.Source); - if (!exclude_configs.empty()) { - if (!have_nested) { - (*this->BuildFileStream) << ">\n"; - } - this->WriteExcludeFromBuild(exclude_configs); - have_nested = true; - } - if (have_nested) { - this->WriteString("</", 2); - (*this->BuildFileStream) << tool << ">\n"; - } else { - (*this->BuildFileStream) << " />\n"; - } - } else if (!exclude_configs.empty()) { - this->WriteSource(tool, si.Source, ">\n"); - this->WriteExcludeFromBuild(exclude_configs); - this->WriteString("</", 2); - (*this->BuildFileStream) << tool << ">\n"; - } else { - this->WriteSource(tool, si.Source); + this->OutputSourceSpecificFlags(e2, si.Source); + } + if (!exclude_configs.empty()) { + this->WriteExcludeFromBuild(e2, exclude_configs); } } } if (this->IsMissingFiles) { - this->WriteMissingFiles(); + this->WriteMissingFiles(e1); } - - this->WriteString("</ItemGroup>\n", 1); } -bool cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags( - cmSourceFile const* source) +void cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags( + Elem& e2, cmSourceFile const* source) { cmSourceFile const& sf = *source; @@ -2079,22 +2065,13 @@ bool cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags( } } bool noWinRT = this->TargetCompileAsWinRT && lang == "C"; - bool hasFlags = false; // for the first time we need a new line if there is something // produced here. - const char* firstString = ">\n"; if (!objectName.empty()) { - (*this->BuildFileStream) << firstString; - firstString = ""; - hasFlags = true; if (lang == "CUDA") { - this->WriteString("<CompileOut>", 3); - (*this->BuildFileStream) << "$(IntDir)/" << objectName - << "</CompileOut>\n"; + e2.Element("CompileOut", "$(IntDir)/" + objectName); } else { - this->WriteString("<ObjectFileName>", 3); - (*this->BuildFileStream) << "$(IntDir)/" << objectName - << "</ObjectFileName>\n"; + e2.Element("ObjectFileName", "$(IntDir)/" + objectName); } } for (std::string const& config : this->Configurations) { @@ -2114,11 +2091,7 @@ bool cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags( // use them if (!flags.empty() || !options.empty() || !configDefines.empty() || !includes.empty() || compileAs || noWinRT) { - (*this->BuildFileStream) << firstString; - firstString = ""; // only do firstString once - hasFlags = true; - cmGlobalVisualStudio10Generator* gg = - static_cast<cmGlobalVisualStudio10Generator*>(this->GlobalGenerator); + cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator; cmIDEFlagTable const* flagtable = nullptr; const std::string& srclang = source->GetLanguage(); if (srclang == "C" || srclang == "CXX") { @@ -2137,9 +2110,9 @@ bool cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags( cmGeneratorExpressionInterpreter genexInterpreter( this->LocalGenerator, this->GeneratorTarget, config, this->GeneratorTarget->GetName(), lang); - cmVisualStudioGeneratorOptions clOptions( + cmVS10GeneratorOptions clOptions( this->LocalGenerator, cmVisualStudioGeneratorOptions::Compiler, - flagtable, 0, this); + flagtable, this); if (compileAs) { clOptions.AddFlag("CompileAs", compileAs); } @@ -2170,7 +2143,7 @@ bool cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags( clOptions.AddDefines( genexInterpreter.Evaluate(configDefines, "COMPILE_DEFINITIONS")); } else { - clOptions.AddDefines(configDefines.c_str()); + clOptions.AddDefines(configDefines); } std::vector<std::string> includeList; if (configDependentIncludes) { @@ -2182,23 +2155,18 @@ bool cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags( *source); } clOptions.AddIncludes(includeList); - clOptions.SetConfiguration(config.c_str()); - clOptions.PrependInheritedString("AdditionalOptions"); - clOptions.OutputAdditionalIncludeDirectories(*this->BuildFileStream, - " ", "\n", lang); - clOptions.OutputFlagMap(*this->BuildFileStream, " "); - clOptions.OutputPreprocessorDefinitions(*this->BuildFileStream, " ", - "\n", lang); + clOptions.SetConfiguration(config); + OptionsHelper oh(clOptions, e2); + oh.PrependInheritedString("AdditionalOptions"); + oh.OutputAdditionalIncludeDirectories(lang); + oh.OutputFlagMap(); + oh.OutputPreprocessorDefinitions(lang); } } if (this->IsXamlSource(source->GetFullPath())) { - (*this->BuildFileStream) << firstString; - firstString = ""; // only do firstString once - hasFlags = true; - this->WriteString("<DependentUpon>", 3); const std::string& fileName = source->GetFullPath(); std::string xamlFileName = fileName.substr(0, fileName.find_last_of(".")); - (*this->BuildFileStream) << xamlFileName << "</DependentUpon>\n"; + e2.Element("DependentUpon", xamlFileName); } if (this->ProjectType == csproj) { std::string f = source->GetFullPath(); @@ -2213,29 +2181,24 @@ bool cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags( this->GetCSharpSourceProperties(&sf, sourceFileTags); // write source file specific tags if (!sourceFileTags.empty()) { - hasFlags = true; - (*this->BuildFileStream) << firstString; - firstString = ""; - this->WriteCSharpSourceProperties(sourceFileTags); + this->WriteCSharpSourceProperties(e2, sourceFileTags); } } - - return hasFlags; } void cmVisualStudio10TargetGenerator::WriteExcludeFromBuild( - std::vector<size_t> const& exclude_configs) + Elem& e2, std::vector<size_t> const& exclude_configs) { for (size_t ci : exclude_configs) { - this->WriteString("", 3); - (*this->BuildFileStream) - << "<ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='" - << cmVS10EscapeXML(this->Configurations[ci]) << "|" - << cmVS10EscapeXML(this->Platform) << "'\">true</ExcludedFromBuild>\n"; + e2.WritePlatformConfigTag( + "ExcludedFromBuild", "'$(Configuration)|$(Platform)'=='" + + this->Configurations[ci] + "|" + this->Platform + "'", + "true"); } } -void cmVisualStudio10TargetGenerator::WritePathAndIncrementalLinkOptions() +void cmVisualStudio10TargetGenerator::WritePathAndIncrementalLinkOptions( + Elem& e0) { cmStateEnums::TargetType ttype = this->GeneratorTarget->GetType(); if (ttype > cmStateEnums::GLOBAL_TARGET) { @@ -2245,16 +2208,13 @@ void cmVisualStudio10TargetGenerator::WritePathAndIncrementalLinkOptions() return; } - this->WriteString("<PropertyGroup>\n", 1); - this->WriteString("<_ProjectFileVersion>10.0.20506.1" - "</_ProjectFileVersion>\n", - 2); + Elem e1(e0, "PropertyGroup"); + e1.Element("_ProjectFileVersion", "10.0.20506.1"); for (std::string const& config : this->Configurations) { + const std::string cond = this->CalcCondition(config); if (ttype >= cmStateEnums::UTILITY) { - this->WritePlatformConfigTag("IntDir", config, 2); - *this->BuildFileStream - << "$(Platform)\\$(Configuration)\\$(ProjectName)\\" - << "</IntDir>\n"; + e1.WritePlatformConfigTag( + "IntDir", cond, "$(Platform)\\$(Configuration)\\$(ProjectName)\\"); } else { std::string intermediateDir = this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget); @@ -2274,25 +2234,63 @@ void cmVisualStudio10TargetGenerator::WritePathAndIncrementalLinkOptions() ConvertToWindowsSlash(intermediateDir); ConvertToWindowsSlash(outDir); - this->WritePlatformConfigTag("OutDir", config, 2); - *this->BuildFileStream << cmVS10EscapeXML(outDir) << "</OutDir>\n"; + e1.WritePlatformConfigTag("OutDir", cond, outDir); - this->WritePlatformConfigTag("IntDir", config, 2); - *this->BuildFileStream << cmVS10EscapeXML(intermediateDir) - << "</IntDir>\n"; + e1.WritePlatformConfigTag("IntDir", cond, intermediateDir); + + if (const char* sdkExecutableDirectories = this->Makefile->GetDefinition( + "CMAKE_VS_SDK_EXECUTABLE_DIRECTORIES")) { + e1.WritePlatformConfigTag("ExecutablePath", cond, + sdkExecutableDirectories); + } + + if (const char* sdkIncludeDirectories = this->Makefile->GetDefinition( + "CMAKE_VS_SDK_INCLUDE_DIRECTORIES")) { + e1.WritePlatformConfigTag("IncludePath", cond, sdkIncludeDirectories); + } + + if (const char* sdkReferenceDirectories = this->Makefile->GetDefinition( + "CMAKE_VS_SDK_REFERENCE_DIRECTORIES")) { + e1.WritePlatformConfigTag("ReferencePath", cond, + sdkReferenceDirectories); + } + + if (const char* sdkLibraryDirectories = this->Makefile->GetDefinition( + "CMAKE_VS_SDK_LIBRARY_DIRECTORIES")) { + e1.WritePlatformConfigTag("LibraryPath", cond, sdkLibraryDirectories); + } + + if (const char* sdkLibraryWDirectories = this->Makefile->GetDefinition( + "CMAKE_VS_SDK_LIBRARY_WINRT_DIRECTORIES")) { + e1.WritePlatformConfigTag("LibraryWPath", cond, + sdkLibraryWDirectories); + } + + if (const char* sdkSourceDirectories = + this->Makefile->GetDefinition("CMAKE_VS_SDK_SOURCE_DIRECTORIES")) { + e1.WritePlatformConfigTag("SourcePath", cond, sdkSourceDirectories); + } + + if (const char* sdkExcludeDirectories = this->Makefile->GetDefinition( + "CMAKE_VS_SDK_EXCLUDE_DIRECTORIES")) { + e1.WritePlatformConfigTag("ExcludePath", cond, sdkExcludeDirectories); + } if (const char* workingDir = this->GeneratorTarget->GetProperty( "VS_DEBUGGER_WORKING_DIRECTORY")) { - this->WritePlatformConfigTag("LocalDebuggerWorkingDirectory", config, - 2); - *this->BuildFileStream << cmVS10EscapeXML(workingDir) - << "</LocalDebuggerWorkingDirectory>\n"; + e1.WritePlatformConfigTag("LocalDebuggerWorkingDirectory", cond, + workingDir); + } + + if (const char* debuggerCommand = + this->GeneratorTarget->GetProperty("VS_DEBUGGER_COMMAND")) { + e1.WritePlatformConfigTag("LocalDebuggerCommand", cond, + debuggerCommand); } std::string name = cmSystemTools::GetFilenameWithoutLastExtension(targetNameFull); - this->WritePlatformConfigTag("TargetName", config, 2); - *this->BuildFileStream << cmVS10EscapeXML(name) << "</TargetName>\n"; + e1.WritePlatformConfigTag("TargetName", cond, name); std::string ext = cmSystemTools::GetFilenameLastExtension(targetNameFull); @@ -2301,17 +2299,15 @@ void cmVisualStudio10TargetGenerator::WritePathAndIncrementalLinkOptions() // A single "." appears to be treated as an empty extension. ext = "."; } - this->WritePlatformConfigTag("TargetExt", config, 2); - *this->BuildFileStream << cmVS10EscapeXML(ext) << "</TargetExt>\n"; + e1.WritePlatformConfigTag("TargetExt", cond, ext); - this->OutputLinkIncremental(config); + this->OutputLinkIncremental(e1, config); } } - this->WriteString("</PropertyGroup>\n", 1); } void cmVisualStudio10TargetGenerator::OutputLinkIncremental( - std::string const& configName) + Elem& e1, std::string const& configName) { if (!this->MSTools) { return; @@ -2326,17 +2322,16 @@ void cmVisualStudio10TargetGenerator::OutputLinkIncremental( return; } Options& linkOptions = *(this->LinkOptions[configName]); + const std::string cond = this->CalcCondition(configName); const char* incremental = linkOptions.GetFlag("LinkIncremental"); - this->WritePlatformConfigTag("LinkIncremental", configName, 2); - *this->BuildFileStream << (incremental ? incremental : "true") - << "</LinkIncremental>\n"; + e1.WritePlatformConfigTag("LinkIncremental", cond, + (incremental ? incremental : "true")); linkOptions.RemoveFlag("LinkIncremental"); const char* manifest = linkOptions.GetFlag("GenerateManifest"); - this->WritePlatformConfigTag("GenerateManifest", configName, 2); - *this->BuildFileStream << (manifest ? manifest : "true") - << "</GenerateManifest>\n"; + e1.WritePlatformConfigTag("GenerateManifest", cond, + (manifest ? manifest : "true")); linkOptions.RemoveFlag("GenerateManifest"); // Some link options belong here. Use them now and remove them so that @@ -2345,8 +2340,7 @@ void cmVisualStudio10TargetGenerator::OutputLinkIncremental( for (const char** f = flags; *f; ++f) { const char* flag = *f; if (const char* value = linkOptions.GetFlag(flag)) { - this->WritePlatformConfigTag(flag, configName, 2); - *this->BuildFileStream << value << "</" << flag << ">\n"; + e1.WritePlatformConfigTag(flag, cond, value); linkOptions.RemoveFlag(flag); } } @@ -2366,8 +2360,8 @@ std::vector<std::string> cmVisualStudio10TargetGenerator::GetIncludes( bool cmVisualStudio10TargetGenerator::ComputeClOptions() { - for (std::string const& i : this->Configurations) { - if (!this->ComputeClOptions(i)) { + for (std::string const& c : this->Configurations) { + if (!this->ComputeClOptions(c)) { return false; } } @@ -2381,8 +2375,7 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( // copied from cmLocalVisualStudio7Generator.cxx 805 // TODO: Integrate code below with cmLocalVisualStudio7Generator. - cmGlobalVisualStudio10Generator* gg = - static_cast<cmGlobalVisualStudio10Generator*>(this->GlobalGenerator); + cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator; std::unique_ptr<Options> pOptions; switch (this->ProjectType) { case vcxproj: @@ -2429,15 +2422,11 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( std::string baseFlagVar = "CMAKE_"; baseFlagVar += langForClCompile; baseFlagVar += "_FLAGS"; - flags = - this->GeneratorTarget->Target->GetMakefile()->GetRequiredDefinition( - baseFlagVar); + flags = this->Makefile->GetRequiredDefinition(baseFlagVar); std::string flagVar = baseFlagVar + std::string("_") + cmSystemTools::UpperCase(configName); flags += " "; - flags += - this->GeneratorTarget->Target->GetMakefile()->GetRequiredDefinition( - flagVar); + flags += this->Makefile->GetRequiredDefinition(flagVar); this->LocalGenerator->AddCompileOptions(flags, this->GeneratorTarget, langForClCompile, configName); } @@ -2453,8 +2442,7 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( this->GeneratorTarget->IsIPOEnabled(linkLanguage, configName); // Get preprocessor definitions for this directory. - std::string defineFlags = - this->GeneratorTarget->Target->GetMakefile()->GetDefineFlags(); + std::string defineFlags = this->Makefile->GetDefineFlags(); if (this->MSTools) { if (this->ProjectType == vcxproj) { clOptions.FixExceptionHandlingDefault(); @@ -2463,6 +2451,34 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( clOptions.AddFlag("AssemblerListingLocation", asmLocation); } } + + // check for managed C++ assembly compiler flag. This overrides any + // /clr* compiler flags which may be defined in the flags variable(s). + if (this->ProjectType != csproj) { + // Warn if /clr was added manually. This should not be done + // 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) { + if (configName == this->Configurations[0]) { + std::string message = "For the target \"" + + this->GeneratorTarget->GetName() + + "\" the /clr compiler flag was added manually. " + + "Set usage of C++/CLI by setting COMMON_LANGUAGE_RUNTIME " + "target property."; + this->Makefile->IssueMessage(cmake::MessageType::WARNING, message); + } + } + if (auto* clr = + this->GeneratorTarget->GetProperty("COMMON_LANGUAGE_RUNTIME")) { + std::string clrString = clr; + if (!clrString.empty()) { + clrString = ":" + clrString; + } + flags += " /clr" + clrString; + } + } + clOptions.Parse(flags.c_str()); clOptions.Parse(defineFlags.c_str()); std::vector<std::string> targetDefines; @@ -2525,10 +2541,15 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( if (this->ProjectType != csproj && clOptions.IsManaged()) { this->Managed = true; std::string managedType = clOptions.GetFlag("CompileAsManaged"); - if (managedType == "Safe") { + if (managedType == "Safe" || managedType == "Pure") { // force empty calling convention if safe clr is used clOptions.AddFlag("CallingConvention", ""); } + // The default values of these flags are incompatible to + // managed assemblies. We have to force valid values if + // the target is a managed C++ target. + clOptions.AddFlag("ExceptionHandling", "Async"); + clOptions.AddFlag("BasicRuntimeChecks", "Default"); } if (this->ProjectType == csproj) { // /nowin32manifest overrides /win32manifest: parameter @@ -2542,26 +2563,23 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( } void cmVisualStudio10TargetGenerator::WriteClOptions( - std::string const& configName) + Elem& e1, std::string const& configName) { Options& clOptions = *(this->ClOptions[configName]); if (this->ProjectType == csproj) { return; } - this->WriteString("<ClCompile>\n", 2); - clOptions.PrependInheritedString("AdditionalOptions"); - clOptions.OutputAdditionalIncludeDirectories( - *this->BuildFileStream, " ", "\n", this->LangForClCompile); - clOptions.OutputFlagMap(*this->BuildFileStream, " "); - clOptions.OutputPreprocessorDefinitions(*this->BuildFileStream, " ", - "\n", this->LangForClCompile); + Elem e2(e1, "ClCompile"); + OptionsHelper oh(clOptions, e2); + oh.PrependInheritedString("AdditionalOptions"); + oh.OutputAdditionalIncludeDirectories(this->LangForClCompile); + oh.OutputFlagMap(); + oh.OutputPreprocessorDefinitions(this->LangForClCompile); if (this->NsightTegra) { if (const char* processMax = this->GeneratorTarget->GetProperty("ANDROID_PROCESS_MAX")) { - this->WriteString("<ProcessMax>", 3); - *this->BuildFileStream << cmVS10EscapeXML(processMax) - << "</ProcessMax>\n"; + e2.Element("ProcessMax", processMax); } } @@ -2569,12 +2587,9 @@ void cmVisualStudio10TargetGenerator::WriteClOptions( cmsys::RegularExpression clangToolset("v[0-9]+_clang_.*"); const char* toolset = this->GlobalGenerator->GetPlatformToolset(); if (toolset && clangToolset.find(toolset)) { - this->WriteString("<ObjectFileName>" - "$(IntDir)%(filename).obj" - "</ObjectFileName>\n", - 3); + e2.Element("ObjectFileName", "$(IntDir)%(filename).obj"); } else { - this->WriteString("<ObjectFileName>$(IntDir)</ObjectFileName>\n", 3); + e2.Element("ObjectFileName", "$(IntDir)"); } // If not in debug mode, write the DebugInformationFormat field @@ -2582,27 +2597,35 @@ void cmVisualStudio10TargetGenerator::WriteClOptions( // goes on its own line because Visual Studio corrects it this // way when saving the project after CMake generates it. if (!clOptions.IsDebug()) { - this->WriteString("<DebugInformationFormat>\n", 3); - this->WriteString("</DebugInformationFormat>\n", 3); + Elem e3(e2, "DebugInformationFormat"); + e3.SetHasElements(); } // Specify the compiler program database file if configured. std::string pdb = this->GeneratorTarget->GetCompilePDBPath(configName); if (!pdb.empty()) { ConvertToWindowsSlash(pdb); - this->WriteString("<ProgramDataBaseFileName>", 3); - *this->BuildFileStream << cmVS10EscapeXML(pdb) - << "</ProgramDataBaseFileName>\n"; + e2.Element("ProgramDataBaseFileName", pdb); } - } - this->WriteString("</ClCompile>\n", 2); + // add AdditionalUsingDirectories + if (this->AdditionalUsingDirectories.count(configName) > 0) { + std::string dirs; + for (auto u : this->AdditionalUsingDirectories[configName]) { + if (!dirs.empty()) { + dirs.append(";"); + } + dirs.append(u); + } + e2.Element("AdditionalUsingDirectories", dirs); + } + } } bool cmVisualStudio10TargetGenerator::ComputeRcOptions() { - for (std::string const& i : this->Configurations) { - if (!this->ComputeRcOptions(i)) { + for (std::string const& c : this->Configurations) { + if (!this->ComputeRcOptions(c)) { return false; } } @@ -2612,8 +2635,7 @@ bool cmVisualStudio10TargetGenerator::ComputeRcOptions() bool cmVisualStudio10TargetGenerator::ComputeRcOptions( std::string const& configName) { - cmGlobalVisualStudio10Generator* gg = - static_cast<cmGlobalVisualStudio10Generator*>(this->GlobalGenerator); + cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator; auto pOptions = cm::make_unique<Options>( this->LocalGenerator, Options::ResourceCompiler, gg->GetRcFlagTable()); Options& rcOptions = *pOptions; @@ -2639,22 +2661,18 @@ bool cmVisualStudio10TargetGenerator::ComputeRcOptions( } void cmVisualStudio10TargetGenerator::WriteRCOptions( - std::string const& configName) + Elem& e1, std::string const& configName) { if (!this->MSTools) { return; } - this->WriteString("<ResourceCompile>\n", 2); + Elem e2(e1, "ResourceCompile"); - Options& rcOptions = *(this->RcOptions[configName]); - rcOptions.OutputPreprocessorDefinitions(*this->BuildFileStream, " ", - "\n", "RC"); - rcOptions.OutputAdditionalIncludeDirectories(*this->BuildFileStream, - " ", "\n", "RC"); + OptionsHelper rcOptions(*(this->RcOptions[configName]), e2); + rcOptions.OutputPreprocessorDefinitions("RC"); + rcOptions.OutputAdditionalIncludeDirectories("RC"); rcOptions.PrependInheritedString("AdditionalOptions"); - rcOptions.OutputFlagMap(*this->BuildFileStream, " "); - - this->WriteString("</ResourceCompile>\n", 2); + rcOptions.OutputFlagMap(); } bool cmVisualStudio10TargetGenerator::ComputeCudaOptions() @@ -2662,8 +2680,8 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaOptions() if (!this->GlobalGenerator->IsCudaEnabled()) { return true; } - for (std::string const& i : this->Configurations) { - if (!this->ComputeCudaOptions(i)) { + for (std::string const& c : this->Configurations) { + if (!this->ComputeCudaOptions(c)) { return false; } } @@ -2673,8 +2691,7 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaOptions() bool cmVisualStudio10TargetGenerator::ComputeCudaOptions( std::string const& configName) { - cmGlobalVisualStudio10Generator* gg = - static_cast<cmGlobalVisualStudio10Generator*>(this->GlobalGenerator); + cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator; auto pOptions = cm::make_unique<Options>( this->LocalGenerator, Options::CudaCompiler, gg->GetCudaFlagTable()); Options& cudaOptions = *pOptions; @@ -2690,8 +2707,7 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaOptions( configName); // Get preprocessor definitions for this directory. - std::string defineFlags = - this->GeneratorTarget->Target->GetMakefile()->GetDefineFlags(); + std::string defineFlags = this->Makefile->GetDefineFlags(); cudaOptions.Parse(flags.c_str()); cudaOptions.Parse(defineFlags.c_str()); @@ -2729,6 +2745,20 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaOptions( cudaOptions.AppendFlagString("AdditionalOptions", "-x cu"); } + // Specify the compiler program database file if configured. + std::string pdb = this->GeneratorTarget->GetCompilePDBPath(configName); + if (!pdb.empty()) { + // CUDA does not have a field for this and does not honor the + // ProgramDataBaseFileName field in ClCompile. Work around this + // limitation by creating the directory and passing the flag ourselves. + std::string const pdbDir = cmSystemTools::GetFilenamePath(pdb); + cmSystemTools::MakeDirectory(pdbDir); + pdb = this->ConvertPath(pdb, true); + ConvertToWindowsSlash(pdb); + std::string const clFd = "-Xcompiler=\"-Fd\\\"" + pdb + "\\\"\""; + cudaOptions.AppendFlagString("AdditionalOptions", clFd); + } + // CUDA automatically passes the proper '--machine' flag to nvcc // for the current architecture, but does not reflect this default // in the user-visible IDE settings. Set it explicitly. @@ -2777,22 +2807,18 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaOptions( } void cmVisualStudio10TargetGenerator::WriteCudaOptions( - std::string const& configName) + Elem& e1, std::string const& configName) { if (!this->MSTools || !this->GlobalGenerator->IsCudaEnabled()) { return; } - this->WriteString("<CudaCompile>\n", 2); + Elem e2(e1, "CudaCompile"); - Options& cudaOptions = *(this->CudaOptions[configName]); - cudaOptions.OutputAdditionalIncludeDirectories(*this->BuildFileStream, - " ", "\n", "CUDA"); - cudaOptions.OutputPreprocessorDefinitions(*this->BuildFileStream, " ", - "\n", "CUDA"); + OptionsHelper cudaOptions(*(this->CudaOptions[configName]), e2); + cudaOptions.OutputAdditionalIncludeDirectories("CUDA"); + cudaOptions.OutputPreprocessorDefinitions("CUDA"); cudaOptions.PrependInheritedString("AdditionalOptions"); - cudaOptions.OutputFlagMap(*this->BuildFileStream, " "); - - this->WriteString("</CudaCompile>\n", 2); + cudaOptions.OutputFlagMap(); } bool cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions() @@ -2800,8 +2826,8 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions() if (!this->GlobalGenerator->IsCudaEnabled()) { return true; } - for (std::string const& i : this->Configurations) { - if (!this->ComputeCudaLinkOptions(i)) { + for (std::string const& c : this->Configurations) { + if (!this->ComputeCudaLinkOptions(c)) { return false; } } @@ -2811,8 +2837,7 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions() bool cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions( std::string const& configName) { - cmGlobalVisualStudio10Generator* gg = - static_cast<cmGlobalVisualStudio10Generator*>(this->GlobalGenerator); + cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator; auto pOptions = cm::make_unique<Options>( this->LocalGenerator, Options::CudaCompiler, gg->GetCudaFlagTable()); Options& cudaLinkOptions = *pOptions; @@ -2848,7 +2873,7 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions( } void cmVisualStudio10TargetGenerator::WriteCudaLinkOptions( - std::string const& configName) + Elem& e1, std::string const& configName) { if (this->GeneratorTarget->GetType() > cmStateEnums::MODULE_LIBRARY) { return; @@ -2858,10 +2883,9 @@ void cmVisualStudio10TargetGenerator::WriteCudaLinkOptions( return; } - this->WriteString("<CudaLink>\n", 2); - Options& cudaLinkOptions = *(this->CudaLinkOptions[configName]); - cudaLinkOptions.OutputFlagMap(*this->BuildFileStream, " "); - this->WriteString("</CudaLink>\n", 2); + Elem e2(e1, "CudaLink"); + OptionsHelper cudaLinkOptions(*(this->CudaLinkOptions[configName]), e2); + cudaLinkOptions.OutputFlagMap(); } bool cmVisualStudio10TargetGenerator::ComputeMasmOptions() @@ -2869,8 +2893,8 @@ bool cmVisualStudio10TargetGenerator::ComputeMasmOptions() if (!this->GlobalGenerator->IsMasmEnabled()) { return true; } - for (std::string const& i : this->Configurations) { - if (!this->ComputeMasmOptions(i)) { + for (std::string const& c : this->Configurations) { + if (!this->ComputeMasmOptions(c)) { return false; } } @@ -2880,8 +2904,7 @@ bool cmVisualStudio10TargetGenerator::ComputeMasmOptions() bool cmVisualStudio10TargetGenerator::ComputeMasmOptions( std::string const& configName) { - cmGlobalVisualStudio10Generator* gg = - static_cast<cmGlobalVisualStudio10Generator*>(this->GlobalGenerator); + cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator; auto pOptions = cm::make_unique<Options>( this->LocalGenerator, Options::MasmCompiler, gg->GetMasmFlagTable()); Options& masmOptions = *pOptions; @@ -2903,25 +2926,21 @@ bool cmVisualStudio10TargetGenerator::ComputeMasmOptions( } void cmVisualStudio10TargetGenerator::WriteMasmOptions( - std::string const& configName) + Elem& e1, std::string const& configName) { if (!this->MSTools || !this->GlobalGenerator->IsMasmEnabled()) { return; } - this->WriteString("<MASM>\n", 2); + Elem e2(e1, "MASM"); // Preprocessor definitions and includes are shared with clOptions. - Options& clOptions = *(this->ClOptions[configName]); - clOptions.OutputPreprocessorDefinitions(*this->BuildFileStream, " ", - "\n", "ASM_MASM"); + OptionsHelper clOptions(*(this->ClOptions[configName]), e2); + clOptions.OutputPreprocessorDefinitions("ASM_MASM"); - Options& masmOptions = *(this->MasmOptions[configName]); - masmOptions.OutputAdditionalIncludeDirectories(*this->BuildFileStream, - " ", "\n", "ASM_MASM"); + OptionsHelper masmOptions(*(this->MasmOptions[configName]), e2); + masmOptions.OutputAdditionalIncludeDirectories("ASM_MASM"); masmOptions.PrependInheritedString("AdditionalOptions"); - masmOptions.OutputFlagMap(*this->BuildFileStream, " "); - - this->WriteString("</MASM>\n", 2); + masmOptions.OutputFlagMap(); } bool cmVisualStudio10TargetGenerator::ComputeNasmOptions() @@ -2929,8 +2948,8 @@ bool cmVisualStudio10TargetGenerator::ComputeNasmOptions() if (!this->GlobalGenerator->IsNasmEnabled()) { return true; } - for (std::string const& i : this->Configurations) { - if (!this->ComputeNasmOptions(i)) { + for (std::string const& c : this->Configurations) { + if (!this->ComputeNasmOptions(c)) { return false; } } @@ -2940,8 +2959,7 @@ bool cmVisualStudio10TargetGenerator::ComputeNasmOptions() bool cmVisualStudio10TargetGenerator::ComputeNasmOptions( std::string const& configName) { - cmGlobalVisualStudio10Generator* gg = - static_cast<cmGlobalVisualStudio10Generator*>(this->GlobalGenerator); + cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator; auto pOptions = cm::make_unique<Options>( this->LocalGenerator, Options::NasmCompiler, gg->GetNasmFlagTable()); Options& nasmOptions = *pOptions; @@ -2964,33 +2982,28 @@ bool cmVisualStudio10TargetGenerator::ComputeNasmOptions( } void cmVisualStudio10TargetGenerator::WriteNasmOptions( - std::string const& configName) + Elem& e1, std::string const& configName) { if (!this->GlobalGenerator->IsNasmEnabled()) { return; } - this->WriteString("<NASM>\n", 2); + Elem e2(e1, "NASM"); std::vector<std::string> includes = this->GetIncludes(configName, "ASM_NASM"); - Options& nasmOptions = *(this->NasmOptions[configName]); - nasmOptions.OutputAdditionalIncludeDirectories(*this->BuildFileStream, - " ", "\n", "ASM_NASM"); - nasmOptions.OutputFlagMap(*this->BuildFileStream, " "); + OptionsHelper nasmOptions(*(this->NasmOptions[configName]), e2); + nasmOptions.OutputAdditionalIncludeDirectories("ASM_NASM"); + nasmOptions.OutputFlagMap(); nasmOptions.PrependInheritedString("AdditionalOptions"); - nasmOptions.OutputPreprocessorDefinitions(*this->BuildFileStream, " ", - "\n", "ASM_NASM"); + nasmOptions.OutputPreprocessorDefinitions("ASM_NASM"); // Preprocessor definitions and includes are shared with clOptions. - Options& clOptions = *(this->ClOptions[configName]); - clOptions.OutputPreprocessorDefinitions(*this->BuildFileStream, " ", - "\n", "ASM_NASM"); - - this->WriteString("</NASM>\n", 2); + OptionsHelper clOptions(*(this->ClOptions[configName]), e2); + clOptions.OutputPreprocessorDefinitions("ASM_NASM"); } void cmVisualStudio10TargetGenerator::WriteLibOptions( - std::string const& config) + Elem& e1, std::string const& config) { if (this->GeneratorTarget->GetType() != cmStateEnums::STATIC_LIBRARY && this->GeneratorTarget->GetType() != cmStateEnums::OBJECT_LIBRARY) { @@ -3000,16 +3013,15 @@ void cmVisualStudio10TargetGenerator::WriteLibOptions( this->LocalGenerator->GetStaticLibraryFlags( libflags, cmSystemTools::UpperCase(config), this->GeneratorTarget); if (!libflags.empty()) { - this->WriteString("<Lib>\n", 2); - cmGlobalVisualStudio10Generator* gg = - static_cast<cmGlobalVisualStudio10Generator*>(this->GlobalGenerator); - cmVisualStudioGeneratorOptions libOptions( - this->LocalGenerator, cmVisualStudioGeneratorOptions::Linker, - gg->GetLibFlagTable(), 0, this); + Elem e2(e1, "Lib"); + cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator; + cmVS10GeneratorOptions libOptions(this->LocalGenerator, + cmVisualStudioGeneratorOptions::Linker, + gg->GetLibFlagTable(), this); libOptions.Parse(libflags.c_str()); - libOptions.PrependInheritedString("AdditionalOptions"); - libOptions.OutputFlagMap(*this->BuildFileStream, " "); - this->WriteString("</Lib>\n", 2); + OptionsHelper oh(libOptions, e2); + oh.PrependInheritedString("AdditionalOptions"); + oh.OutputFlagMap(); } // We cannot generate metadata for static libraries. WindowsPhone @@ -3017,16 +3029,13 @@ void cmVisualStudio10TargetGenerator::WriteLibOptions( // Link tool options even for static libraries. if (this->GlobalGenerator->TargetsWindowsPhone() || this->GlobalGenerator->TargetsWindowsStore()) { - this->WriteString("<Link>\n", 2); - this->WriteString("<GenerateWindowsMetadata>false" - "</GenerateWindowsMetadata>\n", - 3); - this->WriteString("</Link>\n", 2); + Elem e2(e1, "Link"); + e2.Element("GenerateWindowsMetadata", "false"); } } void cmVisualStudio10TargetGenerator::WriteManifestOptions( - std::string const& config) + Elem& e1, std::string const& config) { if (this->GeneratorTarget->GetType() != cmStateEnums::EXECUTABLE && this->GeneratorTarget->GetType() != cmStateEnums::SHARED_LIBRARY && @@ -3037,20 +3046,19 @@ void cmVisualStudio10TargetGenerator::WriteManifestOptions( std::vector<cmSourceFile const*> manifest_srcs; this->GeneratorTarget->GetManifests(manifest_srcs, config); if (!manifest_srcs.empty()) { - this->WriteString("<Manifest>\n", 2); - this->WriteString("<AdditionalManifestFiles>", 3); + std::ostringstream oss; for (cmSourceFile const* mi : manifest_srcs) { std::string m = this->ConvertPath(mi->GetFullPath(), false); ConvertToWindowsSlash(m); - (*this->BuildFileStream) << m << ";"; + oss << m << ";"; } - (*this->BuildFileStream) << "</AdditionalManifestFiles>\n"; - this->WriteString("</Manifest>\n", 2); + Elem e2(e1, "Manifest"); + e2.Element("AdditionalManifestFiles", oss.str()); } } void cmVisualStudio10TargetGenerator::WriteAntBuildOptions( - std::string const& configName) + Elem& e1, std::string const& configName) { // Look through the sources for AndroidManifest.xml and use // its location as the root source directory. @@ -3068,35 +3076,29 @@ void cmVisualStudio10TargetGenerator::WriteAntBuildOptions( } // Tell MSBuild to launch Ant. + Elem e2(e1, "AntBuild"); { std::string antBuildPath = rootDir; - this->WriteString("<AntBuild>\n", 2); - this->WriteString("<AntBuildPath>", 3); ConvertToWindowsSlash(antBuildPath); - (*this->BuildFileStream) << cmVS10EscapeXML(antBuildPath) - << "</AntBuildPath>\n"; + e2.Element("AntBuildPath", antBuildPath); } if (this->GeneratorTarget->GetPropertyAsBool("ANDROID_SKIP_ANT_STEP")) { - this->WriteString("<SkipAntStep>true</SkipAntStep>\n", 3); + e2.Element("SkipAntStep", "true"); } if (this->GeneratorTarget->GetPropertyAsBool("ANDROID_PROGUARD")) { - this->WriteString("<EnableProGuard>true</EnableProGuard>\n", 3); + e2.Element("EnableProGuard", "true"); } if (const char* proGuardConfigLocation = this->GeneratorTarget->GetProperty("ANDROID_PROGUARD_CONFIG_PATH")) { - this->WriteString("<ProGuardConfigLocation>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(proGuardConfigLocation) - << "</ProGuardConfigLocation>\n"; + e2.Element("ProGuardConfigLocation", proGuardConfigLocation); } if (const char* securePropertiesLocation = this->GeneratorTarget->GetProperty("ANDROID_SECURE_PROPS_PATH")) { - this->WriteString("<SecurePropertiesLocation>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(securePropertiesLocation) - << "</SecurePropertiesLocation>\n"; + e2.Element("SecurePropertiesLocation", securePropertiesLocation); } if (const char* nativeLibDirectoriesExpression = @@ -3106,9 +3108,7 @@ void cmVisualStudio10TargetGenerator::WriteAntBuildOptions( ge.Parse(nativeLibDirectoriesExpression); std::string nativeLibDirs = cge->Evaluate(this->LocalGenerator, configName); - this->WriteString("<NativeLibDirectories>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(nativeLibDirs) - << "</NativeLibDirectories>\n"; + e2.Element("NativeLibDirectories", nativeLibDirs); } if (const char* nativeLibDependenciesExpression = @@ -3119,16 +3119,12 @@ void cmVisualStudio10TargetGenerator::WriteAntBuildOptions( ge.Parse(nativeLibDependenciesExpression); std::string nativeLibDeps = cge->Evaluate(this->LocalGenerator, configName); - this->WriteString("<NativeLibDependencies>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(nativeLibDeps) - << "</NativeLibDependencies>\n"; + e2.Element("NativeLibDependencies", nativeLibDeps); } if (const char* javaSourceDir = this->GeneratorTarget->GetProperty("ANDROID_JAVA_SOURCE_DIR")) { - this->WriteString("<JavaSourceDir>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(javaSourceDir) - << "</JavaSourceDir>\n"; + e2.Element("JavaSourceDir", javaSourceDir); } if (const char* jarDirectoriesExpression = @@ -3138,41 +3134,30 @@ void cmVisualStudio10TargetGenerator::WriteAntBuildOptions( ge.Parse(jarDirectoriesExpression); std::string jarDirectories = cge->Evaluate(this->LocalGenerator, configName); - this->WriteString("<JarDirectories>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(jarDirectories) - << "</JarDirectories>\n"; + e2.Element("JarDirectories", jarDirectories); } if (const char* jarDeps = this->GeneratorTarget->GetProperty("ANDROID_JAR_DEPENDENCIES")) { - this->WriteString("<JarDependencies>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(jarDeps) - << "</JarDependencies>\n"; + e2.Element("JarDependencies", jarDeps); } if (const char* assetsDirectories = this->GeneratorTarget->GetProperty("ANDROID_ASSETS_DIRECTORIES")) { - this->WriteString("<AssetsDirectories>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(assetsDirectories) - << "</AssetsDirectories>\n"; + e2.Element("AssetsDirectories", assetsDirectories); } { std::string manifest_xml = rootDir + "/AndroidManifest.xml"; ConvertToWindowsSlash(manifest_xml); - this->WriteString("<AndroidManifestLocation>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(manifest_xml) - << "</AndroidManifestLocation>\n"; + e2.Element("AndroidManifestLocation", manifest_xml); } if (const char* antAdditionalOptions = this->GeneratorTarget->GetProperty("ANDROID_ANT_ADDITIONAL_OPTIONS")) { - this->WriteString("<AdditionalOptions>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(antAdditionalOptions) - << " %(AdditionalOptions)</AdditionalOptions>\n"; + e2.Element("AdditionalOptions", + std::string(antAdditionalOptions) + " %(AdditionalOptions)"); } - - this->WriteString("</AntBuild>\n", 2); } bool cmVisualStudio10TargetGenerator::ComputeLinkOptions() @@ -3180,8 +3165,8 @@ bool cmVisualStudio10TargetGenerator::ComputeLinkOptions() if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE || this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY || this->GeneratorTarget->GetType() == cmStateEnums::MODULE_LIBRARY) { - for (std::string const& i : this->Configurations) { - if (!this->ComputeLinkOptions(i)) { + for (std::string const& c : this->Configurations) { + if (!this->ComputeLinkOptions(c)) { return false; } } @@ -3192,11 +3177,9 @@ bool cmVisualStudio10TargetGenerator::ComputeLinkOptions() bool cmVisualStudio10TargetGenerator::ComputeLinkOptions( std::string const& config) { - cmGlobalVisualStudio10Generator* gg = - static_cast<cmGlobalVisualStudio10Generator*>(this->GlobalGenerator); - auto pOptions = - cm::make_unique<Options>(this->LocalGenerator, Options::Linker, - gg->GetLinkFlagTable(), nullptr, this); + cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator; + auto pOptions = cm::make_unique<Options>( + this->LocalGenerator, Options::Linker, gg->GetLinkFlagTable(), this); Options& linkOptions = *pOptions; cmGeneratorTarget::LinkClosure const* linkClosure = @@ -3224,12 +3207,10 @@ bool cmVisualStudio10TargetGenerator::ComputeLinkOptions( linkFlagVarBase += linkType; linkFlagVarBase += "_LINKER_FLAGS"; flags += " "; - flags += this->GeneratorTarget->Target->GetMakefile()->GetRequiredDefinition( - linkFlagVarBase); + flags += this->Makefile->GetRequiredDefinition(linkFlagVarBase); std::string linkFlagVar = linkFlagVarBase + "_" + CONFIG; flags += " "; - flags += this->GeneratorTarget->Target->GetMakefile()->GetRequiredDefinition( - linkFlagVar); + flags += this->Makefile->GetRequiredDefinition(linkFlagVar); const char* targetLinkFlags = this->GeneratorTarget->GetProperty("LINK_FLAGS"); if (targetLinkFlags) { @@ -3256,14 +3237,16 @@ bool cmVisualStudio10TargetGenerator::ComputeLinkOptions( std::vector<std::string> libVec; std::vector<std::string> vsTargetVec; - this->AddLibraries(cli, libVec, vsTargetVec); + this->AddLibraries(cli, libVec, vsTargetVec, config); if (std::find(linkClosure->Languages.begin(), linkClosure->Languages.end(), "CUDA") != linkClosure->Languages.end()) { switch (this->CudaOptions[config]->GetCudaRuntime()) { case cmVisualStudioGeneratorOptions::CudaRuntimeStatic: + libVec.push_back("cudadevrt.lib"); libVec.push_back("cudart_static.lib"); break; case cmVisualStudioGeneratorOptions::CudaRuntimeShared: + libVec.push_back("cudadevrt.lib"); libVec.push_back("cudart.lib"); break; case cmVisualStudioGeneratorOptions::CudaRuntimeNone: @@ -3383,7 +3366,7 @@ bool cmVisualStudio10TargetGenerator::ComputeLinkOptions( cmGeneratorTarget::ModuleDefinitionInfo const* mdi = this->GeneratorTarget->GetModuleDefinitionInfo(config); if (mdi && !mdi->DefFile.empty()) { - linkOptions.AddFlag("ModuleDefinitionFile", mdi->DefFile.c_str()); + linkOptions.AddFlag("ModuleDefinitionFile", mdi->DefFile); } linkOptions.AppendFlag("IgnoreSpecificDefaultLibraries", "%(IgnoreSpecificDefaultLibraries)"); @@ -3401,6 +3384,15 @@ bool cmVisualStudio10TargetGenerator::ComputeLinkOptions( } } + // Managed code cannot be linked with /DEBUG:FASTLINK + if (this->Managed) { + if (const char* debug = linkOptions.GetFlag("GenerateDebugInformation")) { + if (strcmp(debug, "DebugFastLink") == 0) { + linkOptions.AddFlag("GenerateDebugInformation", "Debug"); + } + } + } + this->LinkOptions[config] = std::move(pOptions); return true; } @@ -3408,8 +3400,8 @@ bool cmVisualStudio10TargetGenerator::ComputeLinkOptions( bool cmVisualStudio10TargetGenerator::ComputeLibOptions() { if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY) { - for (std::string const& i : this->Configurations) { - if (!this->ComputeLibOptions(i)) { + for (std::string const& c : this->Configurations) { + if (!this->ComputeLibOptions(c)) { return false; } } @@ -3447,7 +3439,7 @@ bool cmVisualStudio10TargetGenerator::ComputeLibOptions( } void cmVisualStudio10TargetGenerator::WriteLinkOptions( - std::string const& config) + Elem& e1, std::string const& config) { if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY || this->GeneratorTarget->GetType() > cmStateEnums::MODULE_LIBRARY) { @@ -3456,31 +3448,69 @@ void cmVisualStudio10TargetGenerator::WriteLinkOptions( if (this->ProjectType == csproj) { return; } - Options& linkOptions = *(this->LinkOptions[config]); - this->WriteString("<Link>\n", 2); - linkOptions.PrependInheritedString("AdditionalOptions"); - linkOptions.OutputFlagMap(*this->BuildFileStream, " "); + { + Elem e2(e1, "Link"); + OptionsHelper linkOptions(*(this->LinkOptions[config]), e2); + linkOptions.PrependInheritedString("AdditionalOptions"); + linkOptions.OutputFlagMap(); + } - this->WriteString("</Link>\n", 2); if (!this->GlobalGenerator->NeedLinkLibraryDependencies( this->GeneratorTarget)) { - this->WriteString("<ProjectReference>\n", 2); - this->WriteString( - "<LinkLibraryDependencies>false</LinkLibraryDependencies>\n", 3); - this->WriteString("</ProjectReference>\n", 2); + Elem e2(e1, "ProjectReference"); + e2.Element("LinkLibraryDependencies", "false"); } } void cmVisualStudio10TargetGenerator::AddLibraries( - cmComputeLinkInformation& cli, std::vector<std::string>& libVec, - std::vector<std::string>& vsTargetVec) + const cmComputeLinkInformation& cli, std::vector<std::string>& libVec, + std::vector<std::string>& vsTargetVec, const std::string& config) { typedef cmComputeLinkInformation::ItemVector ItemVector; ItemVector const& libs = cli.GetItems(); std::string currentBinDir = this->LocalGenerator->GetCurrentBinaryDirectory(); for (cmComputeLinkInformation::Item const& l : libs) { + if (l.Target) { + auto managedType = l.Target->GetManagedType(config); + if (managedType != cmGeneratorTarget::ManagedType::Native && + this->GeneratorTarget->GetManagedType(config) != + cmGeneratorTarget::ManagedType::Native && + l.Target->IsImported()) { + auto location = l.Target->GetFullPath(config); + if (!location.empty()) { + ConvertToWindowsSlash(location); + switch (this->ProjectType) { + case csproj: + // If the target we want to "link" to is an imported managed + // target and this is a C# project, we add a hint reference. This + // reference is written to project file in + // WriteDotNetReferences(). + this->DotNetHintReferences[config].push_back( + DotNetHintReference(l.Target->GetName(), location)); + break; + case vcxproj: + // Add path of assembly to list of using-directories, so the + // managed assembly can be used by '#using <assembly.dll>' in + // code. + this->AdditionalUsingDirectories[config].insert( + cmSystemTools::GetFilenamePath(location)); + break; + } + } + } + // Do not allow C# targets to be added to the LIB listing. LIB files are + // used for linking C++ dependencies. C# libraries do not have lib files. + // Instead, they compile down to C# reference libraries (DLL files). The + // `<ProjectReference>` elements added to the vcxproj are enough for the + // IDE to deduce the DLL file required by other C# projects that need its + // reference library. + if (managedType == cmGeneratorTarget::ManagedType::Managed) { + continue; + } + } + if (l.IsPath) { std::string path = this->LocalGenerator->ConvertToRelativePath(currentBinDir, l.Value); @@ -3516,7 +3546,7 @@ void cmVisualStudio10TargetGenerator::AddTargetsFileAndConfigPair( } void cmVisualStudio10TargetGenerator::WriteMidlOptions( - std::string const& configName) + Elem& e1, std::string const& configName) { if (!this->MSTools) { return; @@ -3540,69 +3570,64 @@ void cmVisualStudio10TargetGenerator::WriteMidlOptions( // up (for non-directory form, it ends up looking in project binary dir // only). Perhaps there's something to be done to make this more automatic // on the CMake side? - this->WriteString("<Midl>\n", 2); - this->WriteString("<AdditionalIncludeDirectories>", 3); std::vector<std::string> const includes = this->GetIncludes(configName, "MIDL"); + std::ostringstream oss; for (std::string const& i : includes) { - *this->BuildFileStream << cmVS10EscapeXML(i) << ";"; - } - this->WriteString("%(AdditionalIncludeDirectories)" - "</AdditionalIncludeDirectories>\n", - 0); - this->WriteString("<OutputDirectory>$(ProjectDir)/$(IntDir)" - "</OutputDirectory>\n", - 3); - this->WriteString("<HeaderFileName>%(Filename).h</HeaderFileName>\n", 3); - this->WriteString("<TypeLibraryName>%(Filename).tlb</TypeLibraryName>\n", 3); - this->WriteString("<InterfaceIdentifierFileName>" - "%(Filename)_i.c</InterfaceIdentifierFileName>\n", - 3); - this->WriteString("<ProxyFileName>%(Filename)_p.c</ProxyFileName>\n", 3); - this->WriteString("</Midl>\n", 2); + oss << i << ";"; + } + oss << "%(AdditionalIncludeDirectories)"; + + Elem e2(e1, "Midl"); + e2.Element("AdditionalIncludeDirectories", oss.str()); + e2.Element("OutputDirectory", "$(ProjectDir)/$(IntDir)"); + e2.Element("HeaderFileName", "%(Filename).h"); + e2.Element("TypeLibraryName", "%(Filename).tlb"); + e2.Element("InterfaceIdentifierFileName", "%(Filename)_i.c"); + e2.Element("ProxyFileName", "%(Filename)_p.c"); } -void cmVisualStudio10TargetGenerator::WriteItemDefinitionGroups() +void cmVisualStudio10TargetGenerator::WriteItemDefinitionGroups(Elem& e0) { if (this->ProjectType == csproj) { return; } - for (const auto& i : this->Configurations) { - this->WritePlatformConfigTag("ItemDefinitionGroup", i, 1); - *this->BuildFileStream << "\n"; + for (const std::string& c : this->Configurations) { + Elem e1(e0, "ItemDefinitionGroup"); + e1.Attribute("Condition", this->CalcCondition(c)); + // output cl compile flags <ClCompile></ClCompile> if (this->GeneratorTarget->GetType() <= cmStateEnums::OBJECT_LIBRARY) { - this->WriteClOptions(i); + this->WriteClOptions(e1, c); // output rc compile flags <ResourceCompile></ResourceCompile> - this->WriteRCOptions(i); - this->WriteCudaOptions(i); - this->WriteMasmOptions(i); - this->WriteNasmOptions(i); + this->WriteRCOptions(e1, c); + this->WriteCudaOptions(e1, c); + this->WriteMasmOptions(e1, c); + this->WriteNasmOptions(e1, c); } // output midl flags <Midl></Midl> - this->WriteMidlOptions(i); + this->WriteMidlOptions(e1, c); // write events if (this->ProjectType != csproj) { - this->WriteEvents(i); + this->WriteEvents(e1, c); } // output link flags <Link></Link> - this->WriteLinkOptions(i); - this->WriteCudaLinkOptions(i); + this->WriteLinkOptions(e1, c); + this->WriteCudaLinkOptions(e1, c); // output lib flags <Lib></Lib> - this->WriteLibOptions(i); + this->WriteLibOptions(e1, c); // output manifest flags <Manifest></Manifest> - this->WriteManifestOptions(i); + this->WriteManifestOptions(e1, c); if (this->NsightTegra && this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE && this->GeneratorTarget->GetPropertyAsBool("ANDROID_GUI")) { - this->WriteAntBuildOptions(i); + this->WriteAntBuildOptions(e1, c); } - this->WriteString("</ItemDefinitionGroup>\n", 1); } } void cmVisualStudio10TargetGenerator::WriteEvents( - std::string const& configName) + Elem& e1, std::string const& configName) { bool addedPrelink = false; cmGeneratorTarget::ModuleDefinitionInfo const* mdi = @@ -3613,84 +3638,76 @@ void cmVisualStudio10TargetGenerator::WriteEvents( this->GeneratorTarget->GetPreLinkCommands(); this->GlobalGenerator->AddSymbolExportCommand(this->GeneratorTarget, commands, configName); - this->WriteEvent("PreLinkEvent", commands, configName); + this->WriteEvent(e1, "PreLinkEvent", commands, configName); } if (!addedPrelink) { - this->WriteEvent("PreLinkEvent", + this->WriteEvent(e1, "PreLinkEvent", this->GeneratorTarget->GetPreLinkCommands(), configName); } - this->WriteEvent("PreBuildEvent", + this->WriteEvent(e1, "PreBuildEvent", this->GeneratorTarget->GetPreBuildCommands(), configName); - this->WriteEvent("PostBuildEvent", + this->WriteEvent(e1, "PostBuildEvent", this->GeneratorTarget->GetPostBuildCommands(), configName); } void cmVisualStudio10TargetGenerator::WriteEvent( - const char* name, std::vector<cmCustomCommand> const& commands, + Elem& e1, const char* name, std::vector<cmCustomCommand> const& commands, std::string const& configName) { if (commands.empty()) { return; } - this->WriteString("<", 2); - (*this->BuildFileStream) << name << ">\n"; cmLocalVisualStudio7Generator* lg = this->LocalGenerator; std::string script; const char* pre = ""; std::string comment; - for (cmCustomCommand const& i : commands) { - cmCustomCommandGenerator ccg(i, configName, this->LocalGenerator); + for (cmCustomCommand const& cc : commands) { + cmCustomCommandGenerator ccg(cc, configName, lg); if (!ccg.HasOnlyEmptyCommandLines()) { comment += pre; comment += lg->ConstructComment(ccg); script += pre; pre = "\n"; - script += cmVS10EscapeXML(lg->ConstructScript(ccg)); + script += lg->ConstructScript(ccg); } } comment = cmVS10EscapeComment(comment); if (this->ProjectType != csproj) { - this->WriteString("<Message>", 3); - (*this->BuildFileStream) << cmVS10EscapeXML(comment) << "</Message>\n"; - this->WriteString("<Command>", 3); + Elem e2(e1, name); + e2.Element("Message", comment); + e2.Element("Command", script); } else { std::string strippedComment = comment; strippedComment.erase( std::remove(strippedComment.begin(), strippedComment.end(), '\t'), strippedComment.end()); + std::ostringstream oss; if (!comment.empty() && !strippedComment.empty()) { - (*this->BuildFileStream) << "echo " << cmVS10EscapeXML(comment) << "\n"; + oss << "echo " << comment << "\n"; } + oss << script << "\n"; + e1.Element(name, oss.str()); } - (*this->BuildFileStream) << script; - if (this->ProjectType != csproj) { - (*this->BuildFileStream) << "</Command>"; - } - (*this->BuildFileStream) << "\n"; - this->WriteString("</", 2); - (*this->BuildFileStream) << name << ">\n"; } -void cmVisualStudio10TargetGenerator::WriteProjectReferences() +void cmVisualStudio10TargetGenerator::WriteProjectReferences(Elem& e0) { cmGlobalGenerator::TargetDependSet const& unordered = this->GlobalGenerator->GetTargetDirectDepends(this->GeneratorTarget); typedef cmGlobalVisualStudioGenerator::OrderedTargetDependSet OrderedTargetDependSet; OrderedTargetDependSet depends(unordered, CMAKE_CHECK_BUILD_SYSTEM_TARGET); - this->WriteString("<ItemGroup>\n", 1); - for (cmTargetDepend const& i : depends) { - cmGeneratorTarget const* dt = i; + Elem e1(e0, "ItemGroup"); + e1.SetHasElements(); + for (cmGeneratorTarget const* dt : depends) { if (dt->GetType() == cmStateEnums::INTERFACE_LIBRARY) { continue; } // skip fortran targets as they can not be processed by MSBuild // the only reference will be in the .sln file - if (static_cast<cmGlobalVisualStudioGenerator*>(this->GlobalGenerator) - ->TargetIsFortranOnly(dt)) { + if (this->GlobalGenerator->TargetIsFortranOnly(dt)) { continue; } - this->WriteString("<ProjectReference Include=\"", 2); cmLocalGenerator* lg = dt->GetLocalGenerator(); std::string name = dt->GetName(); std::string path; @@ -3704,27 +3721,41 @@ void cmVisualStudio10TargetGenerator::WriteProjectReferences() path += computeProjectFileExtension(dt, *this->Configurations.begin()); } ConvertToWindowsSlash(path); - (*this->BuildFileStream) << cmVS10EscapeXML(path) << "\">\n"; - this->WriteString("<Project>", 3); - (*this->BuildFileStream) << "{" << this->GlobalGenerator->GetGUID(name) - << "}"; - (*this->BuildFileStream) << "</Project>\n"; - this->WriteString("<Name>", 3); - (*this->BuildFileStream) << name << "</Name>\n"; - this->WriteDotNetReferenceCustomTags(name); - if (csproj == this->ProjectType) { - if (!static_cast<cmGlobalVisualStudioGenerator*>(this->GlobalGenerator) - ->TargetCanBeReferenced(dt)) { - this->WriteString( - "<ReferenceOutputAssembly>false</ReferenceOutputAssembly>\n", 3); + Elem e2(e1, "ProjectReference"); + e2.Attribute("Include", path); + e2.Element("Project", "{" + this->GlobalGenerator->GetGUID(name) + "}"); + e2.Element("Name", name); + this->WriteDotNetReferenceCustomTags(e2, name); + if (this->Managed) { + // If the dependency target is not managed (compiled with /clr or + // C# target) we cannot reference it and have to set + // 'ReferenceOutputAssembly' to false. + auto referenceNotManaged = + dt->GetManagedType("") < cmGeneratorTarget::ManagedType::Mixed; + // Workaround to check for manually set /clr flags. + if (referenceNotManaged) { + if (const auto* flags = dt->GetProperty("COMPILE_OPTIONS")) { + std::string flagsStr = flags; + if (flagsStr.find("clr") != std::string::npos) { + // There is a warning already issued when building the flags. + referenceNotManaged = false; + } + } + } + // Workaround for static library C# targets + if (referenceNotManaged && + dt->GetType() == cmStateEnums::STATIC_LIBRARY) { + referenceNotManaged = !dt->HasLanguage("CSharp", ""); + } + if (referenceNotManaged) { + e2.Element("ReferenceOutputAssembly", "false"); + e2.Element("CopyToOutputDirectory", "Never"); } } - this->WriteString("</ProjectReference>\n", 2); } - this->WriteString("</ItemGroup>\n", 1); } -void cmVisualStudio10TargetGenerator::WritePlatformExtensions() +void cmVisualStudio10TargetGenerator::WritePlatformExtensions(Elem& e1) { // This only applies to Windows 10 apps if (this->GlobalGenerator->TargetsWindowsStore() && @@ -3732,52 +3763,46 @@ void cmVisualStudio10TargetGenerator::WritePlatformExtensions() const char* desktopExtensionsVersion = this->GeneratorTarget->GetProperty("VS_DESKTOP_EXTENSIONS_VERSION"); if (desktopExtensionsVersion) { - this->WriteSinglePlatformExtension("WindowsDesktop", + this->WriteSinglePlatformExtension(e1, "WindowsDesktop", desktopExtensionsVersion); } const char* mobileExtensionsVersion = this->GeneratorTarget->GetProperty("VS_MOBILE_EXTENSIONS_VERSION"); if (mobileExtensionsVersion) { - this->WriteSinglePlatformExtension("WindowsMobile", + this->WriteSinglePlatformExtension(e1, "WindowsMobile", mobileExtensionsVersion); } } } void cmVisualStudio10TargetGenerator::WriteSinglePlatformExtension( - std::string const& extension, std::string const& version) + Elem& e1, std::string const& extension, std::string const& version) { - this->WriteString("<Import Project=", 2); - (*this->BuildFileStream) - << "\"$([Microsoft.Build.Utilities.ToolLocationHelper]" - << "::GetPlatformExtensionSDKLocation(`" << extension - << ", Version=" << version - << "`, $(TargetPlatformIdentifier), $(TargetPlatformVersion), null, " - << "$(ExtensionSDKDirectoryRoot), null))" - << "\\DesignTime\\CommonConfiguration\\Neutral\\" << extension - << ".props\" " - << "Condition=\"exists('$(" - << "[Microsoft.Build.Utilities.ToolLocationHelper]" - << "::GetPlatformExtensionSDKLocation(`" << extension - << ", Version=" << version - << "`, $(TargetPlatformIdentifier), $(TargetPlatformVersion), null, " - << "$(ExtensionSDKDirectoryRoot), null))" - << "\\DesignTime\\CommonConfiguration\\Neutral\\" << extension - << ".props')\" />\n"; + const std::string s = "$([Microsoft.Build.Utilities.ToolLocationHelper]" + "::GetPlatformExtensionSDKLocation(`" + + extension + ", Version=" + version + + "`, $(TargetPlatformIdentifier), $(TargetPlatformVersion), null, " + "$(ExtensionSDKDirectoryRoot), null))" + "\\DesignTime\\CommonConfiguration\\Neutral\\" + + extension + ".props"; + + Elem e2(e1, "Import"); + e2.Attribute("Project", s); + e2.Attribute("Condition", "exists('" + s + "')"); } -void cmVisualStudio10TargetGenerator::WriteSDKReferences() +void cmVisualStudio10TargetGenerator::WriteSDKReferences(Elem& e0) { std::vector<std::string> sdkReferences; + Elem e1(e0); bool hasWrittenItemGroup = false; if (const char* vsSDKReferences = this->GeneratorTarget->GetProperty("VS_SDK_REFERENCES")) { cmSystemTools::ExpandListArgument(vsSDKReferences, sdkReferences); - this->WriteString("<ItemGroup>\n", 1); + e1.StartElement("ItemGroup"); hasWrittenItemGroup = true; for (std::string const& ri : sdkReferences) { - this->WriteString("<SDKReference Include=\"", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(ri) << "\"/>\n"; + Elem(e1, "SDKReference").Attribute("Include", ri); } } @@ -3794,37 +3819,32 @@ void cmVisualStudio10TargetGenerator::WriteSDKReferences() if (desktopExtensionsVersion || mobileExtensionsVersion || iotExtensionsVersion) { if (!hasWrittenItemGroup) { - this->WriteString("<ItemGroup>\n", 1); - hasWrittenItemGroup = true; + e1.StartElement("ItemGroup"); } if (desktopExtensionsVersion) { - this->WriteSingleSDKReference("WindowsDesktop", + this->WriteSingleSDKReference(e1, "WindowsDesktop", desktopExtensionsVersion); } if (mobileExtensionsVersion) { - this->WriteSingleSDKReference("WindowsMobile", + this->WriteSingleSDKReference(e1, "WindowsMobile", mobileExtensionsVersion); } if (iotExtensionsVersion) { - this->WriteSingleSDKReference("WindowsIoT", iotExtensionsVersion); + this->WriteSingleSDKReference(e1, "WindowsIoT", iotExtensionsVersion); } } } - - if (hasWrittenItemGroup) { - this->WriteString("</ItemGroup>\n", 1); - } } void cmVisualStudio10TargetGenerator::WriteSingleSDKReference( - std::string const& extension, std::string const& version) + Elem& e1, std::string const& extension, std::string const& version) { - this->WriteString("<SDKReference Include=\"", 2); - (*this->BuildFileStream) << extension << ", Version=" << version - << "\" />\n"; + Elem(e1, "SDKReference") + .Attribute("Include", extension + ", Version=" + version); } -void cmVisualStudio10TargetGenerator::WriteWinRTPackageCertificateKeyFile() +void cmVisualStudio10TargetGenerator::WriteWinRTPackageCertificateKeyFile( + Elem& e0) { if ((this->GlobalGenerator->TargetsWindowsStore() || this->GlobalGenerator->TargetsWindowsPhone()) && @@ -3845,15 +3865,12 @@ void cmVisualStudio10TargetGenerator::WriteWinRTPackageCertificateKeyFile() std::string artifactDir = this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget); ConvertToWindowsSlash(artifactDir); - this->WriteString("<PropertyGroup>\n", 1); - this->WriteString("<AppxPackageArtifactsDir>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(artifactDir) - << "\\</AppxPackageArtifactsDir>\n"; - this->WriteString("<ProjectPriFullPath>", 2); + Elem e1(e0, "PropertyGroup"); + e1.Element("AppxPackageArtifactsDir", artifactDir + "\\"); std::string resourcePriFile = this->DefaultArtifactDir + "/resources.pri"; ConvertToWindowsSlash(resourcePriFile); - (*this->BuildFileStream) << resourcePriFile << "</ProjectPriFullPath>\n"; + e1.Element("ProjectPriFullPath", resourcePriFile); // If we are missing files and we don't have a certificate and // aren't targeting WP8.0, add a default certificate @@ -3865,30 +3882,21 @@ void cmVisualStudio10TargetGenerator::WriteWinRTPackageCertificateKeyFile() pfxFile, false); ConvertToWindowsSlash(pfxFile); this->AddedFiles.push_back(pfxFile); + this->AddedDefaultCertificate = true; } - this->WriteString("<", 2); - (*this->BuildFileStream) << "PackageCertificateKeyFile>" << pfxFile - << "</PackageCertificateKeyFile>\n"; + e1.Element("PackageCertificateKeyFile", pfxFile); std::string thumb = cmSystemTools::ComputeCertificateThumbprint(pfxFile); if (!thumb.empty()) { - this->WriteString("<PackageCertificateThumbprint>", 2); - (*this->BuildFileStream) << thumb - << "</PackageCertificateThumbprint>\n"; + e1.Element("PackageCertificateThumbprint", thumb); } - this->WriteString("</PropertyGroup>\n", 1); } else if (!pfxFile.empty()) { - this->WriteString("<PropertyGroup>\n", 1); - this->WriteString("<", 2); - (*this->BuildFileStream) << "PackageCertificateKeyFile>" << pfxFile - << "</PackageCertificateKeyFile>\n"; + Elem e1(e0, "PropertyGroup"); + e1.Element("PackageCertificateKeyFile", pfxFile); std::string thumb = cmSystemTools::ComputeCertificateThumbprint(pfxFile); if (!thumb.empty()) { - this->WriteString("<PackageCertificateThumbprint>", 2); - (*this->BuildFileStream) << thumb - << "</PackageCertificateThumbprint>\n"; + e1.Element("PackageCertificateThumbprint", thumb); } - this->WriteString("</PropertyGroup>\n", 1); } } } @@ -3926,54 +3934,37 @@ bool cmVisualStudio10TargetGenerator::IsXamlSource( return it != expectedXamlSources.end(); } -void cmVisualStudio10TargetGenerator::WriteApplicationTypeSettings() +void cmVisualStudio10TargetGenerator::WriteApplicationTypeSettings(Elem& e1) { - cmGlobalVisualStudio10Generator* gg = - static_cast<cmGlobalVisualStudio10Generator*>(this->GlobalGenerator); + cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator; bool isAppContainer = false; bool const isWindowsPhone = this->GlobalGenerator->TargetsWindowsPhone(); bool const isWindowsStore = this->GlobalGenerator->TargetsWindowsStore(); std::string const& v = this->GlobalGenerator->GetSystemVersion(); if (isWindowsPhone || isWindowsStore) { - this->WriteString("<ApplicationType>", 2); - (*this->BuildFileStream) - << (isWindowsPhone ? "Windows Phone" : "Windows Store") - << "</ApplicationType>\n"; - this->WriteString("<DefaultLanguage>en-US" - "</DefaultLanguage>\n", - 2); + e1.Element("ApplicationType", + (isWindowsPhone ? "Windows Phone" : "Windows Store")); + e1.Element("DefaultLanguage", "en-US"); if (cmHasLiteralPrefix(v, "10.0")) { - this->WriteString("<ApplicationTypeRevision>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML("10.0") - << "</ApplicationTypeRevision>\n"; + e1.Element("ApplicationTypeRevision", "10.0"); // Visual Studio 14.0 is necessary for building 10.0 apps - this->WriteString("<MinimumVisualStudioVersion>14.0" - "</MinimumVisualStudioVersion>\n", - 2); + e1.Element("MinimumVisualStudioVersion", "14.0"); if (this->GeneratorTarget->GetType() < cmStateEnums::UTILITY) { isAppContainer = true; } } else if (v == "8.1") { - this->WriteString("<ApplicationTypeRevision>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(v) - << "</ApplicationTypeRevision>\n"; + e1.Element("ApplicationTypeRevision", v); // Visual Studio 12.0 is necessary for building 8.1 apps - this->WriteString("<MinimumVisualStudioVersion>12.0" - "</MinimumVisualStudioVersion>\n", - 2); + e1.Element("MinimumVisualStudioVersion", "12.0"); if (this->GeneratorTarget->GetType() < cmStateEnums::UTILITY) { isAppContainer = true; } } else if (v == "8.0") { - this->WriteString("<ApplicationTypeRevision>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(v) - << "</ApplicationTypeRevision>\n"; + e1.Element("ApplicationTypeRevision", v); // Visual Studio 11.0 is necessary for building 8.0 apps - this->WriteString("<MinimumVisualStudioVersion>11.0" - "</MinimumVisualStudioVersion>\n", - 2); + e1.Element("MinimumVisualStudioVersion", "11.0"); if (isWindowsStore && this->GeneratorTarget->GetType() < cmStateEnums::UTILITY) { @@ -3981,52 +3972,38 @@ void cmVisualStudio10TargetGenerator::WriteApplicationTypeSettings() } else if (isWindowsPhone && this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE) { - this->WriteString("<XapOutputs>true</XapOutputs>\n", 2); - this->WriteString("<XapFilename>", 2); - (*this->BuildFileStream) - << cmVS10EscapeXML(this->Name) - << "_$(Configuration)_$(Platform).xap</XapFilename>\n"; + e1.Element("XapOutputs", "true"); + e1.Element("XapFilename", + this->Name + "_$(Configuration)_$(Platform).xap"); } } } if (isAppContainer) { - this->WriteString("<AppContainerApplication>true" - "</AppContainerApplication>\n", - 2); + e1.Element("AppContainerApplication", "true"); } else if (this->Platform == "ARM64") { - this->WriteString("<WindowsSDKDesktopARM64Support>true" - "</WindowsSDKDesktopARM64Support>\n", - 2); + e1.Element("WindowsSDKDesktopARM64Support", "true"); } else if (this->Platform == "ARM") { - this->WriteString("<WindowsSDKDesktopARMSupport>true" - "</WindowsSDKDesktopARMSupport>\n", - 2); + e1.Element("WindowsSDKDesktopARMSupport", "true"); } std::string const& targetPlatformVersion = gg->GetWindowsTargetPlatformVersion(); if (!targetPlatformVersion.empty()) { - this->WriteString("<WindowsTargetPlatformVersion>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(targetPlatformVersion) - << "</WindowsTargetPlatformVersion>\n"; + e1.Element("WindowsTargetPlatformVersion", targetPlatformVersion); } const char* targetPlatformMinVersion = this->GeneratorTarget->GetProperty( "VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION"); if (targetPlatformMinVersion) { - this->WriteString("<WindowsTargetPlatformMinVersion>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(targetPlatformMinVersion) - << "</WindowsTargetPlatformMinVersion>\n"; + e1.Element("WindowsTargetPlatformMinVersion", targetPlatformMinVersion); } else if (isWindowsStore && cmHasLiteralPrefix(v, "10.0")) { // If the min version is not set, then use the TargetPlatformVersion if (!targetPlatformVersion.empty()) { - this->WriteString("<WindowsTargetPlatformMinVersion>", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(targetPlatformVersion) - << "</WindowsTargetPlatformMinVersion>\n"; + e1.Element("WindowsTargetPlatformMinVersion", targetPlatformVersion); } } // Added IoT Startup Task support if (this->GeneratorTarget->GetPropertyAsBool("VS_IOT_STARTUP_TASK")) { - this->WriteString("<ContainsStartupTask>true</ContainsStartupTask>\n", 2); + e1.Element("ContainsStartupTask", "true"); } } @@ -4074,27 +4051,27 @@ void cmVisualStudio10TargetGenerator::VerifyNecessaryFiles() } } -void cmVisualStudio10TargetGenerator::WriteMissingFiles() +void cmVisualStudio10TargetGenerator::WriteMissingFiles(Elem& e1) { std::string const& v = this->GlobalGenerator->GetSystemVersion(); if (this->GlobalGenerator->TargetsWindowsPhone()) { if (v == "8.0") { - this->WriteMissingFilesWP80(); + this->WriteMissingFilesWP80(e1); } else if (v == "8.1") { - this->WriteMissingFilesWP81(); + this->WriteMissingFilesWP81(e1); } } else if (this->GlobalGenerator->TargetsWindowsStore()) { if (v == "8.0") { - this->WriteMissingFilesWS80(); + this->WriteMissingFilesWS80(e1); } else if (v == "8.1") { - this->WriteMissingFilesWS81(); + this->WriteMissingFilesWS81(e1); } else if (cmHasLiteralPrefix(v, "10.0")) { - this->WriteMissingFilesWS10_0(); + this->WriteMissingFilesWS10_0(e1); } } } -void cmVisualStudio10TargetGenerator::WriteMissingFilesWP80() +void cmVisualStudio10TargetGenerator::WriteMissingFilesWP80(Elem& e1) { std::string templateFolder = cmSystemTools::GetCMakeRoot() + "/Templates/Windows"; @@ -4155,25 +4132,24 @@ void cmVisualStudio10TargetGenerator::WriteMissingFilesWP80() std::string sourceFile = this->ConvertPath(manifestFile, false); ConvertToWindowsSlash(sourceFile); - this->WriteString("<Xml Include=\"", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(sourceFile) << "\">\n"; - this->WriteString("<SubType>Designer</SubType>\n", 3); - this->WriteString("</Xml>\n", 2); + { + Elem e2(e1, "Xml"); + e2.Attribute("Include", sourceFile); + e2.Element("SubType", "Designer"); + } this->AddedFiles.push_back(sourceFile); std::string smallLogo = this->DefaultArtifactDir + "/SmallLogo.png"; cmSystemTools::CopyAFile(templateFolder + "/SmallLogo.png", smallLogo, false); ConvertToWindowsSlash(smallLogo); - this->WriteString("<Image Include=\"", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(smallLogo) << "\" />\n"; + Elem(e1, "Image").Attribute("Include", smallLogo); this->AddedFiles.push_back(smallLogo); std::string logo = this->DefaultArtifactDir + "/Logo.png"; cmSystemTools::CopyAFile(templateFolder + "/Logo.png", logo, false); ConvertToWindowsSlash(logo); - this->WriteString("<Image Include=\"", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(logo) << "\" />\n"; + Elem(e1, "Image").Attribute("Include", logo); this->AddedFiles.push_back(logo); std::string applicationIcon = @@ -4181,12 +4157,11 @@ void cmVisualStudio10TargetGenerator::WriteMissingFilesWP80() cmSystemTools::CopyAFile(templateFolder + "/ApplicationIcon.png", applicationIcon, false); ConvertToWindowsSlash(applicationIcon); - this->WriteString("<Image Include=\"", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(applicationIcon) << "\" />\n"; + Elem(e1, "Image").Attribute("Include", applicationIcon); this->AddedFiles.push_back(applicationIcon); } -void cmVisualStudio10TargetGenerator::WriteMissingFilesWP81() +void cmVisualStudio10TargetGenerator::WriteMissingFilesWP81(Elem& e1) { std::string manifestFile = this->DefaultArtifactDir + "/package.appxManifest"; @@ -4246,10 +4221,10 @@ void cmVisualStudio10TargetGenerator::WriteMissingFilesWP81() "</Package>\n"; /* clang-format on */ - this->WriteCommonMissingFiles(manifestFile); + this->WriteCommonMissingFiles(e1, manifestFile); } -void cmVisualStudio10TargetGenerator::WriteMissingFilesWS80() +void cmVisualStudio10TargetGenerator::WriteMissingFilesWS80(Elem& e1) { std::string manifestFile = this->DefaultArtifactDir + "/package.appxManifest"; @@ -4301,10 +4276,10 @@ void cmVisualStudio10TargetGenerator::WriteMissingFilesWS80() "</Package>\n"; /* clang-format on */ - this->WriteCommonMissingFiles(manifestFile); + this->WriteCommonMissingFiles(e1, manifestFile); } -void cmVisualStudio10TargetGenerator::WriteMissingFilesWS81() +void cmVisualStudio10TargetGenerator::WriteMissingFilesWS81(Elem& e1) { std::string manifestFile = this->DefaultArtifactDir + "/package.appxManifest"; @@ -4361,10 +4336,10 @@ void cmVisualStudio10TargetGenerator::WriteMissingFilesWS81() "</Package>\n"; /* clang-format on */ - this->WriteCommonMissingFiles(manifestFile); + this->WriteCommonMissingFiles(e1, manifestFile); } -void cmVisualStudio10TargetGenerator::WriteMissingFilesWS10_0() +void cmVisualStudio10TargetGenerator::WriteMissingFilesWS10_0(Elem& e1) { std::string manifestFile = this->DefaultArtifactDir + "/package.appxManifest"; @@ -4422,67 +4397,65 @@ void cmVisualStudio10TargetGenerator::WriteMissingFilesWS10_0() "</Package>\n"; /* clang-format on */ - this->WriteCommonMissingFiles(manifestFile); + this->WriteCommonMissingFiles(e1, manifestFile); } void cmVisualStudio10TargetGenerator::WriteCommonMissingFiles( - const std::string& manifestFile) + Elem& e1, const std::string& manifestFile) { std::string templateFolder = cmSystemTools::GetCMakeRoot() + "/Templates/Windows"; std::string sourceFile = this->ConvertPath(manifestFile, false); ConvertToWindowsSlash(sourceFile); - this->WriteString("<AppxManifest Include=\"", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(sourceFile) << "\">\n"; - this->WriteString("<SubType>Designer</SubType>\n", 3); - this->WriteString("</AppxManifest>\n", 2); + { + Elem e2(e1, "AppxManifest"); + e2.Attribute("Include", sourceFile); + e2.Element("SubType", "Designer"); + } this->AddedFiles.push_back(sourceFile); std::string smallLogo = this->DefaultArtifactDir + "/SmallLogo.png"; cmSystemTools::CopyAFile(templateFolder + "/SmallLogo.png", smallLogo, false); ConvertToWindowsSlash(smallLogo); - this->WriteString("<Image Include=\"", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(smallLogo) << "\" />\n"; + Elem(e1, "Image").Attribute("Include", smallLogo); this->AddedFiles.push_back(smallLogo); std::string smallLogo44 = this->DefaultArtifactDir + "/SmallLogo44x44.png"; cmSystemTools::CopyAFile(templateFolder + "/SmallLogo44x44.png", smallLogo44, false); ConvertToWindowsSlash(smallLogo44); - this->WriteString("<Image Include=\"", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(smallLogo44) << "\" />\n"; + Elem(e1, "Image").Attribute("Include", smallLogo44); this->AddedFiles.push_back(smallLogo44); std::string logo = this->DefaultArtifactDir + "/Logo.png"; cmSystemTools::CopyAFile(templateFolder + "/Logo.png", logo, false); ConvertToWindowsSlash(logo); - this->WriteString("<Image Include=\"", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(logo) << "\" />\n"; + Elem(e1, "Image").Attribute("Include", logo); this->AddedFiles.push_back(logo); std::string storeLogo = this->DefaultArtifactDir + "/StoreLogo.png"; cmSystemTools::CopyAFile(templateFolder + "/StoreLogo.png", storeLogo, false); ConvertToWindowsSlash(storeLogo); - this->WriteString("<Image Include=\"", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(storeLogo) << "\" />\n"; + Elem(e1, "Image").Attribute("Include", storeLogo); this->AddedFiles.push_back(storeLogo); std::string splashScreen = this->DefaultArtifactDir + "/SplashScreen.png"; cmSystemTools::CopyAFile(templateFolder + "/SplashScreen.png", splashScreen, false); ConvertToWindowsSlash(splashScreen); - this->WriteString("<Image Include=\"", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(splashScreen) << "\" />\n"; + Elem(e1, "Image").Attribute("Include", splashScreen); this->AddedFiles.push_back(splashScreen); - // This file has already been added to the build so don't copy it - std::string keyFile = this->DefaultArtifactDir + "/Windows_TemporaryKey.pfx"; - ConvertToWindowsSlash(keyFile); - this->WriteString("<None Include=\"", 2); - (*this->BuildFileStream) << cmVS10EscapeXML(keyFile) << "\" />\n"; + if (this->AddedDefaultCertificate) { + // This file has already been added to the build so don't copy it + std::string keyFile = + this->DefaultArtifactDir + "/Windows_TemporaryKey.pfx"; + ConvertToWindowsSlash(keyFile); + Elem(e1, "None").Attribute("Include", keyFile); + } } bool cmVisualStudio10TargetGenerator::ForceOld(const std::string& source) const @@ -4528,13 +4501,11 @@ void cmVisualStudio10TargetGenerator::GetCSharpSourceProperties( } void cmVisualStudio10TargetGenerator::WriteCSharpSourceProperties( - const std::map<std::string, std::string>& tags) + Elem& e2, const std::map<std::string, std::string>& tags) { if (!tags.empty()) { for (const auto& i : tags) { - this->WriteString("<", 3); - (*this->BuildFileStream) << i.first << ">" << cmVS10EscapeXML(i.second) - << "</" << i.first << ">\n"; + e2.Element(i.first.c_str(), i.second); } } } diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index 33d4fb7..15e47b4 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -17,11 +17,11 @@ class cmCustomCommand; class cmGeneratedFileStream; class cmGeneratorTarget; class cmGlobalVisualStudio10Generator; -class cmLocalVisualStudio7Generator; +class cmLocalVisualStudio10Generator; class cmMakefile; class cmSourceFile; class cmSourceGroup; -class cmVisualStudioGeneratorOptions; +class cmVS10GeneratorOptions; class cmVisualStudio10TargetGenerator { @@ -32,10 +32,6 @@ public: cmGlobalVisualStudio10Generator* gg); ~cmVisualStudio10TargetGenerator(); void Generate(); - // used by cmVisualStudioGeneratorOptions - void WritePlatformConfigTag(const char* tag, const std::string& config, - int indentLevel, const char* attribute = 0, - const char* end = 0, std::ostream* strm = 0); private: struct ToolSource @@ -53,107 +49,116 @@ private: std::vector<std::string> Configs; }; + struct Elem; + struct OptionsHelper; + std::string ConvertPath(std::string const& path, bool forceRelative); - void WriteString(const char* line, int indentLevel); - void WriteProjectConfigurations(); - void WriteProjectConfigurationValues(); - void WriteMSToolConfigurationValues(std::string const& config); - void WriteMSToolConfigurationValuesManaged(std::string const& config); - void WriteHeaderSource(cmSourceFile const* sf); - void WriteExtraSource(cmSourceFile const* sf); - void WriteNsightTegraConfigurationValues(std::string const& config); - void WriteSource(std::string const& tool, cmSourceFile const* sf, - const char* end = 0); - void WriteExcludeFromBuild(std::vector<size_t> const& exclude_configs); - void WriteAllSources(); - void WriteDotNetReferences(); - void WriteDotNetReference(std::string const& ref, std::string const& hint); - void WriteDotNetReferenceCustomTags(std::string const& ref); - void WriteEmbeddedResourceGroup(); - void WriteWinRTReferences(); - void WriteWinRTPackageCertificateKeyFile(); - void WriteXamlFilesGroup(); - void WritePathAndIncrementalLinkOptions(); - void WriteItemDefinitionGroups(); + std::string CalcCondition(const std::string& config) const; + void WriteProjectConfigurations(Elem& e0); + void WriteProjectConfigurationValues(Elem& e0); + void WriteMSToolConfigurationValues(Elem& e1, std::string const& config); + void WriteMSToolConfigurationValuesManaged(Elem& e1, + std::string const& config); + void WriteHeaderSource(Elem& e1, cmSourceFile const* sf); + void WriteExtraSource(Elem& e1, cmSourceFile const* sf); + void WriteNsightTegraConfigurationValues(Elem& e1, + std::string const& config); + void WriteSource(Elem& e2, std::string const& tool, cmSourceFile const* sf); + void WriteExcludeFromBuild(Elem& e2, + std::vector<size_t> const& exclude_configs); + void WriteAllSources(Elem& e0); + void WriteDotNetReferences(Elem& e0); + void WriteDotNetReference(Elem& e1, std::string const& ref, + std::string const& hint, + std::string const& config); + void WriteDotNetReferenceCustomTags(Elem& e2, std::string const& ref); + void WriteEmbeddedResourceGroup(Elem& e0); + void WriteWinRTReferences(Elem& e0); + void WriteWinRTPackageCertificateKeyFile(Elem& e0); + void WriteXamlFilesGroup(Elem& e0); + void WritePathAndIncrementalLinkOptions(Elem& e0); + void WriteItemDefinitionGroups(Elem& e0); void VerifyNecessaryFiles(); - void WriteMissingFiles(); - void WriteMissingFilesWP80(); - void WriteMissingFilesWP81(); - void WriteMissingFilesWS80(); - void WriteMissingFilesWS81(); - void WriteMissingFilesWS10_0(); - void WritePlatformExtensions(); - void WriteSinglePlatformExtension(std::string const& extension, + void WriteMissingFiles(Elem& e1); + void WriteMissingFilesWP80(Elem& e1); + void WriteMissingFilesWP81(Elem& e1); + void WriteMissingFilesWS80(Elem& e1); + void WriteMissingFilesWS81(Elem& e1); + void WriteMissingFilesWS10_0(Elem& e1); + void WritePlatformExtensions(Elem& e1); + void WriteSinglePlatformExtension(Elem& e1, std::string const& extension, std::string const& version); - void WriteSDKReferences(); - void WriteSingleSDKReference(std::string const& extension, + void WriteSDKReferences(Elem& e0); + void WriteSingleSDKReference(Elem& e1, std::string const& extension, std::string const& version); - void WriteCommonMissingFiles(const std::string& manifestFile); - void WriteTargetSpecificReferences(); - void WriteTargetsFileReferences(); + void WriteCommonMissingFiles(Elem& e1, const std::string& manifestFile); + void WriteTargetSpecificReferences(Elem& e0); + void WriteTargetsFileReferences(Elem& e1); std::vector<std::string> GetIncludes(std::string const& config, std::string const& lang) const; bool ComputeClOptions(); bool ComputeClOptions(std::string const& configName); - void WriteClOptions(std::string const& config); + void WriteClOptions(Elem& e1, std::string const& config); bool ComputeRcOptions(); bool ComputeRcOptions(std::string const& config); - void WriteRCOptions(std::string const& config); + void WriteRCOptions(Elem& e1, std::string const& config); bool ComputeCudaOptions(); bool ComputeCudaOptions(std::string const& config); - void WriteCudaOptions(std::string const& config); + void WriteCudaOptions(Elem& e1, std::string const& config); bool ComputeCudaLinkOptions(); bool ComputeCudaLinkOptions(std::string const& config); - void WriteCudaLinkOptions(std::string const& config); + void WriteCudaLinkOptions(Elem& e1, std::string const& config); bool ComputeMasmOptions(); bool ComputeMasmOptions(std::string const& config); - void WriteMasmOptions(std::string const& config); + void WriteMasmOptions(Elem& e1, std::string const& config); bool ComputeNasmOptions(); bool ComputeNasmOptions(std::string const& config); - void WriteNasmOptions(std::string const& config); + void WriteNasmOptions(Elem& e1, std::string const& config); bool ComputeLinkOptions(); bool ComputeLinkOptions(std::string const& config); bool ComputeLibOptions(); bool ComputeLibOptions(std::string const& config); - void WriteLinkOptions(std::string const& config); - void WriteMidlOptions(std::string const& config); - void WriteAntBuildOptions(std::string const& config); - void OutputLinkIncremental(std::string const& configName); - void WriteCustomRule(cmSourceFile const* source, + void WriteLinkOptions(Elem& e1, std::string const& config); + void WriteMidlOptions(Elem& e1, std::string const& config); + void WriteAntBuildOptions(Elem& e1, std::string const& config); + void OutputLinkIncremental(Elem& e1, std::string const& configName); + void WriteCustomRule(Elem& e0, cmSourceFile const* source, cmCustomCommand const& command); - void WriteCustomRuleCpp(std::string const& config, std::string const& script, - std::string const& inputs, + void WriteCustomRuleCpp(Elem& e2, std::string const& config, + std::string const& script, std::string const& inputs, std::string const& outputs, std::string const& comment); - void WriteCustomRuleCSharp(std::string const& config, + void WriteCustomRuleCSharp(Elem& e0, std::string const& config, std::string const& commandName, std::string const& script, std::string const& inputs, std::string const& outputs, std::string const& comment); - void WriteCustomCommands(); - void WriteCustomCommand(cmSourceFile const* sf); + void WriteCustomCommands(Elem& e0); + void WriteCustomCommand(Elem& e0, cmSourceFile const* sf); void WriteGroups(); - void WriteProjectReferences(); - void WriteApplicationTypeSettings(); - bool OutputSourceSpecificFlags(cmSourceFile const* source); - void AddLibraries(cmComputeLinkInformation& cli, + void WriteProjectReferences(Elem& e0); + void WriteApplicationTypeSettings(Elem& e1); + void OutputSourceSpecificFlags(Elem& e2, cmSourceFile const* source); + void AddLibraries(const cmComputeLinkInformation& cli, std::vector<std::string>& libVec, - std::vector<std::string>& vsTargetVec); + std::vector<std::string>& vsTargetVec, + const std::string& config); void AddTargetsFileAndConfigPair(std::string const& targetsFile, std::string const& config); - void WriteLibOptions(std::string const& config); - void WriteManifestOptions(std::string const& config); - void WriteEvents(std::string const& configName); - void WriteEvent(const char* name, + void WriteLibOptions(Elem& e1, std::string const& config); + void WriteManifestOptions(Elem& e1, std::string const& config); + void WriteEvents(Elem& e1, std::string const& configName); + void WriteEvent(Elem& e1, const char* name, std::vector<cmCustomCommand> const& commands, std::string const& configName); - void WriteGroupSources(const char* name, ToolSources const& sources, + void WriteGroupSources(Elem& e0, std::string const& name, + ToolSources const& sources, std::vector<cmSourceGroup>&); void AddMissingSourceGroups(std::set<cmSourceGroup*>& groupsUsed, const std::vector<cmSourceGroup>& allGroups); @@ -166,11 +171,12 @@ private: void GetCSharpSourceProperties(cmSourceFile const* sf, std::map<std::string, std::string>& tags); void WriteCSharpSourceProperties( - const std::map<std::string, std::string>& tags); + Elem& e2, const std::map<std::string, std::string>& tags); void GetCSharpSourceLink(cmSourceFile const* sf, std::string& link); private: - typedef cmVisualStudioGeneratorOptions Options; + friend class cmVS10GeneratorOptions; + typedef cmVS10GeneratorOptions Options; typedef std::map<std::string, std::unique_ptr<Options>> OptionsMap; OptionsMap ClOptions; OptionsMap RcOptions; @@ -180,8 +186,6 @@ private: OptionsMap NasmOptions; OptionsMap LinkOptions; std::string LangForClCompile; - std::string PathToProjectFile; - std::string ProjectFileExtension; enum VsProjectType { vcxproj, @@ -190,24 +194,32 @@ private: bool InSourceBuild; std::vector<std::string> Configurations; std::vector<TargetsFileAndConfigs> TargetsFileAndConfigsVec; - cmGeneratorTarget* GeneratorTarget; - cmMakefile* Makefile; - std::string Platform; - std::string GUID; - std::string Name; + cmGeneratorTarget* const GeneratorTarget; + cmMakefile* const Makefile; + std::string const Platform; + std::string const Name; + std::string const GUID; bool MSTools; bool Managed; bool NsightTegra; - int NsightTegraVersion[4]; + unsigned int NsightTegraVersion[4]; bool TargetCompileAsWinRT; - cmGlobalVisualStudio10Generator* GlobalGenerator; - cmGeneratedFileStream* BuildFileStream; - cmLocalVisualStudio7Generator* LocalGenerator; - std::set<cmSourceFile const*> SourcesVisited; + cmGlobalVisualStudio10Generator* const GlobalGenerator; + cmLocalVisualStudio10Generator* const LocalGenerator; std::set<std::string> CSharpCustomCommandNames; bool IsMissingFiles; std::vector<std::string> AddedFiles; std::string DefaultArtifactDir; + bool AddedDefaultCertificate = false; + // managed C++/C# relevant members + typedef std::pair<std::string, std::string> DotNetHintReference; + typedef std::vector<DotNetHintReference> DotNetHintReferenceList; + typedef std::map<std::string, DotNetHintReferenceList> + DotNetHintReferenceMap; + DotNetHintReferenceMap DotNetHintReferences; + typedef std::set<std::string> UsingDirectories; + typedef std::map<std::string, UsingDirectories> UsingDirectoriesMap; + UsingDirectoriesMap AdditionalUsingDirectories; typedef std::map<std::string, ToolSources> ToolSourceMap; ToolSourceMap Tools; diff --git a/Source/cmVisualStudioGeneratorOptions.cxx b/Source/cmVisualStudioGeneratorOptions.cxx index ccbff83..3ec3355 100644 --- a/Source/cmVisualStudioGeneratorOptions.cxx +++ b/Source/cmVisualStudioGeneratorOptions.cxx @@ -4,43 +4,19 @@ #include "cmLocalVisualStudioGenerator.h" #include "cmOutputConverter.h" #include "cmSystemTools.h" -#include "cmVisualStudio10TargetGenerator.h" -static std::string cmVisualStudio10GeneratorOptionsEscapeForXML( - std::string ret) +static void cmVS10EscapeForMSBuild(std::string& ret) { cmSystemTools::ReplaceString(ret, ";", "%3B"); - cmSystemTools::ReplaceString(ret, "&", "&"); - cmSystemTools::ReplaceString(ret, "<", "<"); - cmSystemTools::ReplaceString(ret, ">", ">"); - return ret; -} - -static std::string cmVisualStudioGeneratorOptionsEscapeForXML(std::string ret) -{ - cmSystemTools::ReplaceString(ret, "&", "&"); - cmSystemTools::ReplaceString(ret, "\"", """); - cmSystemTools::ReplaceString(ret, "<", "<"); - cmSystemTools::ReplaceString(ret, ">", ">"); - cmSystemTools::ReplaceString(ret, "\n", "
"); - return ret; -} - -cmVisualStudioGeneratorOptions::cmVisualStudioGeneratorOptions( - cmLocalVisualStudioGenerator* lg, Tool tool, - cmVisualStudio10TargetGenerator* g) - : cmVisualStudioGeneratorOptions(lg, tool, nullptr, nullptr, g) -{ } cmVisualStudioGeneratorOptions::cmVisualStudioGeneratorOptions( cmLocalVisualStudioGenerator* lg, Tool tool, cmVS7FlagTable const* table, - cmVS7FlagTable const* extraTable, cmVisualStudio10TargetGenerator* g) + cmVS7FlagTable const* extraTable) : cmIDEOptions() , LocalGenerator(lg) , Version(lg->GetVersion()) , CurrentTool(tool) - , TargetGenerator(g) { // Store the given flag tables. this->AddTable(table); @@ -151,10 +127,9 @@ bool cmVisualStudioGeneratorOptions::IsManaged() const bool cmVisualStudioGeneratorOptions::UsingUnicode() const { - // Look for the a _UNICODE definition. - for (std::vector<std::string>::const_iterator di = this->Defines.begin(); - di != this->Defines.end(); ++di) { - if (*di == "_UNICODE") { + // Look for a _UNICODE definition. + for (std::string const& di : this->Defines) { + if (di == "_UNICODE") { return true; } } @@ -162,10 +137,9 @@ bool cmVisualStudioGeneratorOptions::UsingUnicode() const } bool cmVisualStudioGeneratorOptions::UsingSBCS() const { - // Look for the a _SBCS definition. - for (std::vector<std::string>::const_iterator di = this->Defines.begin(); - di != this->Defines.end(); ++di) { - if (*di == "_SBCS") { + // Look for a _SBCS definition. + for (std::string const& di : this->Defines) { + if (di == "_SBCS") { return true; } } @@ -227,7 +201,7 @@ void cmVisualStudioGeneratorOptions::FixCudaCodeGeneration() // It translates to -arch=<virtual> -code=<real>. cmSystemTools::ReplaceString(arch_name, "sm_", "compute_"); } - for (auto const& c : codes) { + for (std::string const& c : codes) { std::string entry = arch_name + "," + c; result.push_back(entry); } @@ -237,7 +211,7 @@ void cmVisualStudioGeneratorOptions::FixCudaCodeGeneration() // -gencode=<arch>,<code> // -gencode=<arch>,[<code1>,<code2>] // -gencode=<arch>,"<code1>,<code2>" - for (auto const& e : gencode) { + for (std::string const& e : gencode) { std::string entry = e; cmSystemTools::ReplaceString(entry, "arch=", ""); cmSystemTools::ReplaceString(entry, "code=", ""); @@ -285,7 +259,7 @@ void cmVisualStudioGeneratorOptions::FixManifestUACFlags() uacExecuteLevelMap["highestAvailable"] = "HighestAvailable"; uacExecuteLevelMap["requireAdministrator"] = "RequireAdministrator"; - for (auto const& subopt : subOptions) { + for (std::string const& subopt : subOptions) { std::vector<std::string> keyValue; cmsys::SystemTools::Split(subopt, keyValue, '='); if (keyValue.size() != 2 || (uacMap.find(keyValue[0]) == uacMap.end())) { @@ -332,9 +306,8 @@ void cmVisualStudioGeneratorOptions::Parse(const char* flags) // Process flags that need to be represented specially in the IDE // project file. - for (std::vector<std::string>::iterator ai = args.begin(); ai != args.end(); - ++ai) { - this->HandleFlag(ai->c_str()); + for (std::string const& ai : args) { + this->HandleFlag(ai); } } @@ -396,23 +369,23 @@ void cmVisualStudioGeneratorOptions::Reparse(std::string const& key) this->Parse(original.c_str()); } -void cmVisualStudioGeneratorOptions::StoreUnknownFlag(const char* flag) +void cmVisualStudioGeneratorOptions::StoreUnknownFlag(std::string const& flag) { // Look for Intel Fortran flags that do not map well in the flag table. if (this->CurrentTool == FortranCompiler) { - if (strcmp(flag, "/dbglibs") == 0) { + if (flag == "/dbglibs") { this->FortranRuntimeDebug = true; return; } - if (strcmp(flag, "/threads") == 0) { + if (flag == "/threads") { this->FortranRuntimeMT = true; return; } - if (strcmp(flag, "/libs:dll") == 0) { + if (flag == "/libs:dll") { this->FortranRuntimeDLL = true; return; } - if (strcmp(flag, "/libs:static") == 0) { + if (flag == "/libs:static") { this->FortranRuntimeDLL = false; return; } @@ -420,7 +393,7 @@ void cmVisualStudioGeneratorOptions::StoreUnknownFlag(const char* flag) // This option is not known. Store it in the output flags. std::string const opts = cmOutputConverter::EscapeWindowsShellArgument( - flag, cmOutputConverter::Shell_Flag_AllowMakeVariables | + flag.c_str(), cmOutputConverter::Shell_Flag_AllowMakeVariables | cmOutputConverter::Shell_Flag_VSIDE); this->AppendFlagString(this->UnknownFlagField, opts); } @@ -437,14 +410,19 @@ cmIDEOptions::FlagValue cmVisualStudioGeneratorOptions::TakeFlag( return value; } -void cmVisualStudioGeneratorOptions::SetConfiguration(const char* config) +void cmVisualStudioGeneratorOptions::SetConfiguration( + const std::string& config) { this->Configuration = config; } +const std::string& cmVisualStudioGeneratorOptions::GetConfiguration() const +{ + return this->Configuration; +} + void cmVisualStudioGeneratorOptions::OutputPreprocessorDefinitions( - std::ostream& fout, const char* prefix, const char* suffix, - const std::string& lang) + std::ostream& fout, int indent, const std::string& lang) { if (this->Defines.empty()) { return; @@ -453,19 +431,8 @@ void cmVisualStudioGeneratorOptions::OutputPreprocessorDefinitions( if (lang == "CUDA") { tag = "Defines"; } - if (this->Version >= cmGlobalVisualStudioGenerator::VS10) { - // if there are configuration specific flags, then - // use the configuration specific tag for PreprocessorDefinitions - if (!this->Configuration.empty()) { - fout << prefix; - this->TargetGenerator->WritePlatformConfigTag( - tag, this->Configuration.c_str(), 0, 0, 0, &fout); - } else { - fout << prefix << "<" << tag << ">"; - } - } else { - fout << prefix << tag << "=\""; - } + + std::ostringstream oss; const char* sep = ""; std::vector<std::string>::const_iterator de = cmRemoveDuplicates(this->Defines); @@ -474,34 +441,30 @@ void cmVisualStudioGeneratorOptions::OutputPreprocessorDefinitions( // Escape the definition for the compiler. std::string define; if (this->Version < cmGlobalVisualStudioGenerator::VS10) { - define = this->LocalGenerator->EscapeForShell(di->c_str(), true); + define = this->LocalGenerator->EscapeForShell(*di, true); } else { define = *di; } - // Escape this flag for the IDE. + // Escape this flag for the MSBuild. if (this->Version >= cmGlobalVisualStudioGenerator::VS10) { - define = cmVisualStudio10GeneratorOptionsEscapeForXML(define); - + cmVS10EscapeForMSBuild(define); if (lang == "RC") { cmSystemTools::ReplaceString(define, "\"", "\\\""); } - } else { - define = cmVisualStudioGeneratorOptionsEscapeForXML(define); } // Store the flag in the project file. - fout << sep << define; + oss << sep << define; sep = ";"; } if (this->Version >= cmGlobalVisualStudioGenerator::VS10) { - fout << ";%(" << tag << ")</" << tag << ">" << suffix; - } else { - fout << "\"" << suffix; + oss << ";%(" << tag << ")"; } + + this->OutputFlag(fout, indent, tag, oss.str()); } void cmVisualStudioGeneratorOptions::OutputAdditionalIncludeDirectories( - std::ostream& fout, const char* prefix, const char* suffix, - const std::string& lang) + std::ostream& fout, int indent, const std::string& lang) { if (this->Includes.empty()) { return; @@ -514,20 +477,7 @@ void cmVisualStudioGeneratorOptions::OutputAdditionalIncludeDirectories( tag = "IncludePaths"; } - if (this->Version >= cmGlobalVisualStudioGenerator::VS10) { - // if there are configuration specific flags, then - // use the configuration specific tag for PreprocessorDefinitions - if (!this->Configuration.empty()) { - fout << prefix; - this->TargetGenerator->WritePlatformConfigTag( - tag, this->Configuration.c_str(), 0, 0, 0, &fout); - } else { - fout << prefix << "<" << tag << ">"; - } - } else { - fout << prefix << tag << "=\""; - } - + std::ostringstream oss; const char* sep = ""; for (std::string include : this->Includes) { // first convert all of the slashes @@ -541,59 +491,40 @@ void cmVisualStudioGeneratorOptions::OutputAdditionalIncludeDirectories( include += "\\"; } - // Escape this include for the IDE. - fout << sep << (this->Version >= cmGlobalVisualStudioGenerator::VS10 - ? cmVisualStudio10GeneratorOptionsEscapeForXML(include) - : cmVisualStudioGeneratorOptionsEscapeForXML(include)); + // Escape this include for the MSBuild. + if (this->Version >= cmGlobalVisualStudioGenerator::VS10) { + cmVS10EscapeForMSBuild(include); + } + oss << sep << include; sep = ";"; if (lang == "Fortran") { include += "/$(ConfigurationName)"; - fout << sep << (this->Version >= cmGlobalVisualStudioGenerator::VS10 - ? cmVisualStudio10GeneratorOptionsEscapeForXML(include) - : cmVisualStudioGeneratorOptionsEscapeForXML(include)); + oss << sep << include; } } if (this->Version >= cmGlobalVisualStudioGenerator::VS10) { - fout << sep << "%(" << tag << ")</" << tag << ">" << suffix; - } else { - fout << "\"" << suffix; + oss << sep << "%(" << tag << ")"; } + + this->OutputFlag(fout, indent, tag, oss.str()); } void cmVisualStudioGeneratorOptions::OutputFlagMap(std::ostream& fout, - const char* indent) + int indent) { - if (this->Version >= cmGlobalVisualStudioGenerator::VS10) { - for (std::map<std::string, FlagValue>::iterator m = this->FlagMap.begin(); - m != this->FlagMap.end(); ++m) { - fout << indent; - if (!this->Configuration.empty()) { - this->TargetGenerator->WritePlatformConfigTag( - m->first.c_str(), this->Configuration.c_str(), 0, 0, 0, &fout); - } else { - fout << "<" << m->first << ">"; + for (auto const& m : this->FlagMap) { + std::ostringstream oss; + const char* sep = ""; + for (std::string i : m.second) { + if (this->Version >= cmGlobalVisualStudioGenerator::VS10) { + cmVS10EscapeForMSBuild(i); } - const char* sep = ""; - for (std::vector<std::string>::iterator i = m->second.begin(); - i != m->second.end(); ++i) { - fout << sep << cmVisualStudio10GeneratorOptionsEscapeForXML(*i); - sep = ";"; - } - fout << "</" << m->first << ">\n"; - } - } else { - for (std::map<std::string, FlagValue>::iterator m = this->FlagMap.begin(); - m != this->FlagMap.end(); ++m) { - fout << indent << m->first << "=\""; - const char* sep = ""; - for (std::vector<std::string>::iterator i = m->second.begin(); - i != m->second.end(); ++i) { - fout << sep << cmVisualStudioGeneratorOptionsEscapeForXML(*i); - sep = ";"; - } - fout << "\"\n"; + oss << sep << i; + sep = ";"; } + + this->OutputFlag(fout, indent, m.first.c_str(), oss.str()); } } diff --git a/Source/cmVisualStudioGeneratorOptions.h b/Source/cmVisualStudioGeneratorOptions.h index 2dffe9b..c6b594d 100644 --- a/Source/cmVisualStudioGeneratorOptions.h +++ b/Source/cmVisualStudioGeneratorOptions.h @@ -16,8 +16,6 @@ class cmLocalVisualStudioGenerator; typedef cmIDEFlagTable cmVS7FlagTable; -class cmVisualStudio10TargetGenerator; - class cmVisualStudioGeneratorOptions : public cmIDEOptions { public: @@ -34,12 +32,8 @@ public: CSharpCompiler }; cmVisualStudioGeneratorOptions(cmLocalVisualStudioGenerator* lg, Tool tool, - cmVS7FlagTable const* table, - cmVS7FlagTable const* extraTable = 0, - cmVisualStudio10TargetGenerator* g = 0); - - cmVisualStudioGeneratorOptions(cmLocalVisualStudioGenerator* lg, Tool tool, - cmVisualStudio10TargetGenerator* g = 0); + cmVS7FlagTable const* table = nullptr, + cmVS7FlagTable const* extraTable = nullptr); // Add a table of flags. void AddTable(cmVS7FlagTable const* table); @@ -83,15 +77,17 @@ public: bool IsWinRt() const; bool IsManaged() const; // Write options to output. - void OutputPreprocessorDefinitions(std::ostream& fout, const char* prefix, - const char* suffix, + void OutputPreprocessorDefinitions(std::ostream& fout, int indent, const std::string& lang); - void OutputAdditionalIncludeDirectories(std::ostream& fout, - const char* prefix, - const char* suffix, + void OutputAdditionalIncludeDirectories(std::ostream& fout, int indent, const std::string& lang); - void OutputFlagMap(std::ostream& fout, const char* indent); - void SetConfiguration(const char* config); + void OutputFlagMap(std::ostream& fout, int indent); + void SetConfiguration(const std::string& config); + const std::string& GetConfiguration() const; + +protected: + virtual void OutputFlag(std::ostream& fout, int indent, const char* tag, + const std::string& content) = 0; private: cmLocalVisualStudioGenerator* LocalGenerator; @@ -99,7 +95,6 @@ private: std::string Configuration; Tool CurrentTool; - cmVisualStudio10TargetGenerator* TargetGenerator; bool FortranRuntimeDebug; bool FortranRuntimeDLL; @@ -107,7 +102,7 @@ private: std::string UnknownFlagField; - virtual void StoreUnknownFlag(const char* flag); + void StoreUnknownFlag(std::string const& flag) override; FlagValue TakeFlag(std::string const& key); }; diff --git a/Source/cmWhileCommand.cxx b/Source/cmWhileCommand.cxx index a080034..d5bcfc2 100644 --- a/Source/cmWhileCommand.cxx +++ b/Source/cmWhileCommand.cxx @@ -28,10 +28,10 @@ bool cmWhileFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff, cmExecutionStatus& inStatus) { // at end of for each execute recorded commands - if (!cmSystemTools::Strucmp(lff.Name.c_str(), "while")) { + if (lff.Name.Lower == "while") { // record the number of while commands past this one this->Depth++; - } else if (!cmSystemTools::Strucmp(lff.Name.c_str(), "endwhile")) { + } else if (lff.Name.Lower == "endwhile") { // if this is the endwhile for this while loop then execute if (!this->Depth) { // Remove the function blocker for this scope or bail. @@ -117,7 +117,7 @@ bool cmWhileFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff, bool cmWhileFunctionBlocker::ShouldRemove(const cmListFileFunction& lff, cmMakefile&) { - if (!cmSystemTools::Strucmp(lff.Name.c_str(), "endwhile")) { + if (lff.Name.Lower == "endwhile") { // if the endwhile has arguments, then make sure // they match the arguments of the matching while if (lff.Arguments.empty() || lff.Arguments == this->Args) { diff --git a/Source/cmWorkingDirectory.cxx b/Source/cmWorkingDirectory.cxx index 99c9ba8..816f104 100644 --- a/Source/cmWorkingDirectory.cxx +++ b/Source/cmWorkingDirectory.cxx @@ -4,10 +4,12 @@ #include "cmSystemTools.h" +#include <cerrno> + cmWorkingDirectory::cmWorkingDirectory(std::string const& newdir) { this->OldDir = cmSystemTools::GetCurrentWorkingDirectory(); - cmSystemTools::ChangeDirectory(newdir); + this->SetDirectory(newdir); } cmWorkingDirectory::~cmWorkingDirectory() @@ -15,10 +17,20 @@ cmWorkingDirectory::~cmWorkingDirectory() this->Pop(); } +bool cmWorkingDirectory::SetDirectory(std::string const& newdir) +{ + if (cmSystemTools::ChangeDirectory(newdir) == 0) { + this->ResultCode = 0; + return true; + } + this->ResultCode = errno; + return false; +} + void cmWorkingDirectory::Pop() { if (!this->OldDir.empty()) { - cmSystemTools::ChangeDirectory(this->OldDir); + this->SetDirectory(this->OldDir); this->OldDir.clear(); } } diff --git a/Source/cmWorkingDirectory.h b/Source/cmWorkingDirectory.h index aff9267..1f18ce7 100644 --- a/Source/cmWorkingDirectory.h +++ b/Source/cmWorkingDirectory.h @@ -9,6 +9,12 @@ /** \class cmWorkingDirectory * \brief An RAII class to manipulate the working directory. + * + * The current working directory is set to the location given to the + * constructor. The working directory can be changed again as needed + * by calling SetDirectory(). When the object is destroyed, the destructor + * will restore the working directory to what it was when the object was + * created, regardless of any calls to SetDirectory() in the meantime. */ class cmWorkingDirectory { @@ -16,10 +22,21 @@ public: cmWorkingDirectory(std::string const& newdir); ~cmWorkingDirectory(); + bool SetDirectory(std::string const& newdir); void Pop(); + bool Failed() const { return ResultCode != 0; } + + /** \return 0 if the last attempt to set the working directory was + * successful. If it failed, the value returned will be the + * \c errno value associated with the failure. A description + * of the error code can be obtained by passing the result + * to \c std::strerror(). + */ + int GetLastResult() const { return ResultCode; } private: std::string OldDir; + int ResultCode; }; #endif diff --git a/Source/cmXMLWriter.cxx b/Source/cmXMLWriter.cxx index 3cbc70d..9d2a3c4 100644 --- a/Source/cmXMLWriter.cxx +++ b/Source/cmXMLWriter.cxx @@ -9,6 +9,7 @@ cmXMLWriter::cmXMLWriter(std::ostream& output, std::size_t level) : Output(output) , IndentationElement(1, '\t') , Level(level) + , Indent(0) , ElementOpen(false) , BreakAttrib(false) , IsContent(false) @@ -17,7 +18,7 @@ cmXMLWriter::cmXMLWriter(std::ostream& output, std::size_t level) cmXMLWriter::~cmXMLWriter() { - assert(this->Elements.empty()); + assert(this->Indent == 0); } void cmXMLWriter::StartDocument(const char* encoding) @@ -27,27 +28,29 @@ void cmXMLWriter::StartDocument(const char* encoding) void cmXMLWriter::EndDocument() { - assert(this->Elements.empty()); + assert(this->Indent == 0); this->Output << '\n'; } void cmXMLWriter::StartElement(std::string const& name) { this->CloseStartElement(); - this->ConditionalLineBreak(!this->IsContent, this->Elements.size()); + this->ConditionalLineBreak(!this->IsContent); this->Output << '<' << name; this->Elements.push(name); + ++this->Indent; this->ElementOpen = true; this->BreakAttrib = false; } void cmXMLWriter::EndElement() { - assert(!this->Elements.empty()); + assert(this->Indent > 0); + --this->Indent; if (this->ElementOpen) { this->Output << "/>"; } else { - this->ConditionalLineBreak(!this->IsContent, this->Elements.size() - 1); + this->ConditionalLineBreak(!this->IsContent); this->IsContent = false; this->Output << "</" << this->Elements.top() << '>'; } @@ -58,7 +61,7 @@ void cmXMLWriter::EndElement() void cmXMLWriter::Element(const char* name) { this->CloseStartElement(); - this->ConditionalLineBreak(!this->IsContent, this->Elements.size()); + this->ConditionalLineBreak(!this->IsContent); this->Output << '<' << name << "/>"; } @@ -70,7 +73,7 @@ void cmXMLWriter::BreakAttributes() void cmXMLWriter::Comment(const char* comment) { this->CloseStartElement(); - this->ConditionalLineBreak(!this->IsContent, this->Elements.size()); + this->ConditionalLineBreak(!this->IsContent); this->Output << "<!-- " << comment << " -->"; } @@ -83,14 +86,14 @@ void cmXMLWriter::CData(std::string const& data) void cmXMLWriter::Doctype(const char* doctype) { this->CloseStartElement(); - this->ConditionalLineBreak(!this->IsContent, this->Elements.size()); + this->ConditionalLineBreak(!this->IsContent); this->Output << "<!DOCTYPE " << doctype << ">"; } void cmXMLWriter::ProcessingInstruction(const char* target, const char* data) { this->CloseStartElement(); - this->ConditionalLineBreak(!this->IsContent, this->Elements.size()); + this->ConditionalLineBreak(!this->IsContent); this->Output << "<?" << target << ' ' << data << "?>"; } @@ -106,11 +109,11 @@ void cmXMLWriter::SetIndentationElement(std::string const& element) this->IndentationElement = element; } -void cmXMLWriter::ConditionalLineBreak(bool condition, std::size_t indent) +void cmXMLWriter::ConditionalLineBreak(bool condition) { if (condition) { this->Output << '\n'; - for (std::size_t i = 0; i < indent + this->Level; ++i) { + for (std::size_t i = 0; i < this->Indent + this->Level; ++i) { this->Output << this->IndentationElement; } } @@ -119,7 +122,7 @@ void cmXMLWriter::ConditionalLineBreak(bool condition, std::size_t indent) void cmXMLWriter::PreAttribute() { assert(this->ElementOpen); - this->ConditionalLineBreak(this->BreakAttrib, this->Elements.size()); + this->ConditionalLineBreak(this->BreakAttrib); if (!this->BreakAttrib) { this->Output << ' '; } @@ -134,7 +137,7 @@ void cmXMLWriter::PreContent() void cmXMLWriter::CloseStartElement() { if (this->ElementOpen) { - this->ConditionalLineBreak(this->BreakAttrib, this->Elements.size()); + this->ConditionalLineBreak(this->BreakAttrib); this->Output << '>'; this->ElementOpen = false; } diff --git a/Source/cmXMLWriter.h b/Source/cmXMLWriter.h index 7bae21e..d124907 100644 --- a/Source/cmXMLWriter.h +++ b/Source/cmXMLWriter.h @@ -67,7 +67,7 @@ public: void SetIndentationElement(std::string const& element); private: - void ConditionalLineBreak(bool condition, std::size_t indent); + void ConditionalLineBreak(bool condition); void PreAttribute(); void PreContent(); @@ -128,9 +128,68 @@ private: std::stack<std::string, std::vector<std::string>> Elements; std::string IndentationElement; std::size_t Level; + std::size_t Indent; bool ElementOpen; bool BreakAttrib; bool IsContent; }; +class cmXMLElement; // IWYU pragma: keep + +class cmXMLDocument +{ +public: + cmXMLDocument(cmXMLWriter& xml) + : xmlwr(xml) + { + xmlwr.StartDocument(); + } + ~cmXMLDocument() { xmlwr.EndDocument(); } +private: + friend class cmXMLElement; + cmXMLWriter& xmlwr; +}; + +class cmXMLElement +{ +public: + cmXMLElement(cmXMLWriter& xml, const char* tag) + : xmlwr(xml) + { + xmlwr.StartElement(tag); + } + cmXMLElement(cmXMLElement& par, const char* tag) + : xmlwr(par.xmlwr) + { + xmlwr.StartElement(tag); + } + cmXMLElement(cmXMLDocument& doc, const char* tag) + : xmlwr(doc.xmlwr) + { + xmlwr.StartElement(tag); + } + ~cmXMLElement() { xmlwr.EndElement(); } + + template <typename T> + cmXMLElement& Attribute(const char* name, T const& value) + { + xmlwr.Attribute(name, value); + return *this; + } + template <typename T> + void Content(T const& content) + { + xmlwr.Content(content); + } + template <typename T> + void Element(std::string const& name, T const& value) + { + xmlwr.Element(name, value); + } + void Comment(const char* comment) { xmlwr.Comment(comment); } + +private: + cmXMLWriter& xmlwr; +}; + #endif diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 5620723..801d52d 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -55,7 +55,6 @@ #include "cmGlobalVisualStudio12Generator.h" #include "cmGlobalVisualStudio14Generator.h" #include "cmGlobalVisualStudio15Generator.h" -#include "cmGlobalVisualStudio8Generator.h" #include "cmGlobalVisualStudio9Generator.h" #include "cmVSSetupHelper.h" @@ -98,13 +97,13 @@ #include "cmsys/Glob.hxx" #include "cmsys/RegularExpression.hxx" #include <algorithm> +#include <cstring> #include <iostream> #include <iterator> #include <memory> // IWYU pragma: keep #include <sstream> #include <stdio.h> #include <stdlib.h> -#include <string.h> #include <utility> namespace { @@ -1434,6 +1433,7 @@ int cmake::ActualConfigure() // only save the cache if there were no fatal errors if (this->GetWorkingMode() == NORMAL_MODE) { + this->State->SaveVerificationScript(this->GetHomeOutputDirectory()); this->SaveCache(this->GetHomeOutputDirectory()); } if (cmSystemTools::GetErrorOccuredFlag()) { @@ -1464,8 +1464,7 @@ void cmake::CreateDefaultGlobalGenerator() { "12.0", "Visual Studio 12 2013" }, // { "11.0", "Visual Studio 11 2012" }, // { "10.0", "Visual Studio 10 2010" }, // - { "9.0", "Visual Studio 9 2008" }, // - { "8.0", "Visual Studio 8 2005" } + { "9.0", "Visual Studio 9 2008" } }; static const char* const vsEntries[] = { "\\Setup\\VC;ProductDir", // @@ -1649,6 +1648,33 @@ void cmake::AddCacheEntry(const std::string& key, const char* value, this->UnwatchUnusedCli(key); } +bool cmake::DoWriteGlobVerifyTarget() const +{ + return this->State->DoWriteGlobVerifyTarget(); +} + +std::string const& cmake::GetGlobVerifyScript() const +{ + return this->State->GetGlobVerifyScript(); +} + +std::string const& cmake::GetGlobVerifyStamp() const +{ + return this->State->GetGlobVerifyStamp(); +} + +void cmake::AddGlobCacheEntry(bool recurse, bool listDirectories, + bool followSymlinks, const std::string& relative, + const std::string& expression, + const std::vector<std::string>& files, + const std::string& variable, + cmListFileBacktrace const& backtrace) +{ + this->State->AddGlobCacheEntry(recurse, listDirectories, followSymlinks, + relative, expression, files, variable, + backtrace); +} + std::string cmake::StripExtension(const std::string& file) const { auto dotpos = file.rfind('.'); @@ -1689,7 +1715,6 @@ void cmake::AddDefaultGenerators() this->Generators.push_back(cmGlobalVisualStudio11Generator::NewFactory()); this->Generators.push_back(cmGlobalVisualStudio10Generator::NewFactory()); this->Generators.push_back(cmGlobalVisualStudio9Generator::NewFactory()); - this->Generators.push_back(cmGlobalVisualStudio8Generator::NewFactory()); this->Generators.push_back(cmGlobalBorlandMakefileGenerator::NewFactory()); this->Generators.push_back(cmGlobalNMakeMakefileGenerator::NewFactory()); this->Generators.push_back(cmGlobalJOMMakefileGenerator::NewFactory()); @@ -2209,6 +2234,15 @@ int cmake::GetSystemInformation(std::vector<std::string>& args) { // now run cmake on the CMakeLists file cmWorkingDirectory workdir(destPath); + if (workdir.Failed()) { + // We created the directory and we were able to copy the CMakeLists.txt + // file to it, so we wouldn't expect to get here unless the default + // permissions are questionable or some other process has deleted the + // directory + std::cerr << "Failed to change to directory " << destPath << " : " + << std::strerror(workdir.GetLastResult()) << std::endl; + return 1; + } std::vector<std::string> args2; args2.push_back(args[0]); args2.push_back(destPath); @@ -2364,7 +2398,7 @@ cmMessenger* cmake::GetMessenger() const return this->Messenger; } -int cmake::Build(const std::string& dir, const std::string& target, +int cmake::Build(int jobs, const std::string& dir, const std::string& target, const std::string& config, const std::vector<std::string>& nativeOptions, bool clean) { @@ -2387,17 +2421,17 @@ int cmake::Build(const std::string& dir, const std::string& target, std::cerr << "Error: could not find CMAKE_GENERATOR in Cache\n"; return 1; } - std::unique_ptr<cmGlobalGenerator> gen( - this->CreateGlobalGenerator(cachedGenerator)); - if (!gen.get()) { + cmGlobalGenerator* gen = this->CreateGlobalGenerator(cachedGenerator); + if (!gen) { std::cerr << "Error: could create CMAKE_GENERATOR \"" << cachedGenerator << "\"\n"; return 1; } + this->SetGlobalGenerator(gen); const char* cachedGeneratorInstance = this->State->GetCacheEntryValue("CMAKE_GENERATOR_INSTANCE"); if (cachedGeneratorInstance) { - cmMakefile mf(gen.get(), this->GetCurrentSnapshot()); + cmMakefile mf(gen, this->GetCurrentSnapshot()); if (!gen->SetGeneratorInstance(cachedGeneratorInstance, &mf)) { return 1; } @@ -2425,44 +2459,58 @@ int cmake::Build(const std::string& dir, const std::string& target, // to limitations of the underlying build system. std::string const stampList = cachePath + "/" + GetCMakeFilesDirectoryPostSlash() + - cmGlobalVisualStudio8Generator::GetGenerateStampList(); + cmGlobalVisualStudio9Generator::GetGenerateStampList(); // Note that the stampList file only exists for VS generators. - if (cmSystemTools::FileExists(stampList) && - !cmakeCheckStampList(stampList.c_str(), false)) { - - // Correctly initialize the home (=source) and home output (=binary) - // directories, which is required for running the generation step. - std::string homeOrig = this->GetHomeDirectory(); - std::string homeOutputOrig = this->GetHomeOutputDirectory(); - this->SetDirectoriesFromFile(cachePath.c_str()); + if (cmSystemTools::FileExists(stampList)) { + // Check if running for Visual Studio 9 - we need to explicitly run + // the glob verification script before starting the build this->AddScriptingCommands(); - this->AddProjectCommands(); - - int ret = this->Configure(); - if (ret) { - cmSystemTools::Message("CMake Configure step failed. " - "Build files cannot be regenerated correctly."); - return ret; - } - ret = this->Generate(); - if (ret) { - cmSystemTools::Message("CMake Generate step failed. " - "Build files cannot be regenerated correctly."); - return ret; + if (this->GlobalGenerator->MatchesGeneratorName("Visual Studio 9 2008")) { + std::string const globVerifyScript = cachePath + "/" + + GetCMakeFilesDirectoryPostSlash() + "VerifyGlobs.cmake"; + if (cmSystemTools::FileExists(globVerifyScript)) { + std::vector<std::string> args; + this->ReadListFile(args, globVerifyScript.c_str()); + } } - std::string message = "Build files have been written to: "; - message += this->GetHomeOutputDirectory(); - this->UpdateProgress(message.c_str(), -1); - // Restore the previously set directories to their original value. - this->SetHomeDirectory(homeOrig); - this->SetHomeOutputDirectory(homeOutputOrig); + if (!cmakeCheckStampList(stampList.c_str(), false)) { + // Correctly initialize the home (=source) and home output (=binary) + // directories, which is required for running the generation step. + std::string homeOrig = this->GetHomeDirectory(); + std::string homeOutputOrig = this->GetHomeOutputDirectory(); + this->SetDirectoriesFromFile(cachePath.c_str()); + + this->AddProjectCommands(); + + int ret = this->Configure(); + if (ret) { + cmSystemTools::Message("CMake Configure step failed. " + "Build files cannot be regenerated correctly."); + return ret; + } + ret = this->Generate(); + if (ret) { + cmSystemTools::Message("CMake Generate step failed. " + "Build files cannot be regenerated correctly."); + return ret; + } + std::string message = "Build files have been written to: "; + message += this->GetHomeOutputDirectory(); + this->UpdateProgress(message.c_str(), -1); + + // Restore the previously set directories to their original value. + this->SetHomeDirectory(homeOrig); + this->SetHomeOutputDirectory(homeOutputOrig); + } } #endif - return gen->Build("", dir, projName, target, output, "", config, clean, + gen->PrintBuildCommandAdvice(std::cerr, jobs); + + return gen->Build(jobs, "", dir, projName, target, output, "", config, clean, false, verbose, cmDuration::zero(), cmSystemTools::OUTPUT_PASSTHROUGH, nativeOptions); } diff --git a/Source/cmake.h b/Source/cmake.h index 1ac549b..53c2f45 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -119,6 +119,9 @@ public: typedef std::map<std::string, cmInstalledFile> InstalledFilesMap; + static const int NO_BUILD_PARALLEL_LEVEL = -1; + static const int DEFAULT_BUILD_PARALLEL_LEVEL = 0; + /// Default constructor cmake(Role role); /// Destructor @@ -255,6 +258,16 @@ public: void AddCacheEntry(const std::string& key, const char* value, const char* helpString, int type); + bool DoWriteGlobVerifyTarget() const; + std::string const& GetGlobVerifyScript() const; + std::string const& GetGlobVerifyStamp() const; + void AddGlobCacheEntry(bool recurse, bool listDirectories, + bool followSymlinks, const std::string& relative, + const std::string& expression, + const std::vector<std::string>& files, + const std::string& variable, + cmListFileBacktrace const& bt); + /** * Get the system information and write it to the file specified */ @@ -420,7 +433,7 @@ public: cmListFileBacktrace const& backtrace = cmListFileBacktrace()) const; ///! run the --build option - int Build(const std::string& dir, const std::string& target, + int Build(int jobs, const std::string& dir, const std::string& target, const std::string& config, const std::vector<std::string>& nativeOptions, bool clean); @@ -540,7 +553,7 @@ private: #define CMAKE_STANDARD_OPTIONS_TABLE \ { "-C <initial-cache>", "Pre-load a script to populate the cache." }, \ - { "-D <var>[:<type>]=<value>", "Create a cmake cache entry." }, \ + { "-D <var>[:<type>]=<value>", "Create or update a cmake cache entry." }, \ { "-U <globbing_expr>", "Remove matching entries from CMake cache." }, \ { "-G <generator-name>", "Specify a build system generator." }, \ { "-T <toolset-name>", \ @@ -574,6 +587,7 @@ private: F(cxx_std_11) \ F(cxx_std_14) \ F(cxx_std_17) \ + F(cxx_std_20) \ F(cxx_aggregate_default_initializers) \ F(cxx_alias_templates) \ F(cxx_alignas) \ diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx index b185a1b..9c9f65c 100644 --- a/Source/cmakemain.cxx +++ b/Source/cmakemain.cxx @@ -22,6 +22,8 @@ #if defined(_WIN32) && defined(CMAKE_BUILD_WITH_CMAKE) #include "cmsys/ConsoleBuf.hxx" #endif + +#include <ctype.h> #include <iostream> #include <string.h> #include <string> @@ -49,6 +51,12 @@ static const char* cmDocumentationUsageNote[][2] = { #define CMAKE_BUILD_OPTIONS \ " <dir> = Project binary directory to be built.\n" \ + " -j [<jobs>] --parallel [<jobs>] = Build in parallel using\n" \ + " the given number of jobs. If <jobs> is omitted\n" \ + " the native build tool's default number is used.\n" \ + " The CMAKE_BUILD_PARALLEL_LEVEL environment variable\n" \ + " specifies a default parallel level when this option\n" \ + " is not given.\n" \ " --target <tgt> = Build <tgt> instead of default targets.\n" \ " May only be specified once.\n" \ " --config <cfg> = For multi-configuration tools, choose <cfg>.\n" \ @@ -338,6 +346,7 @@ static int do_build(int ac, char const* const* av) std::cerr << "This cmake does not support --build\n"; return -1; #else + int jobs = cmake::NO_BUILD_PARALLEL_LEVEL; std::string target; std::string config = "Debug"; std::string dir; @@ -348,6 +357,7 @@ static int do_build(int ac, char const* const* av) enum Doing { DoingNone, + DoingJobs, DoingDir, DoingTarget, DoingConfig, @@ -357,6 +367,13 @@ static int do_build(int ac, char const* const* av) for (int i = 2; i < ac; ++i) { if (doing == DoingNative) { nativeOptions.push_back(av[i]); + } else if ((strcmp(av[i], "-j") == 0) || + (strcmp(av[i], "--parallel") == 0)) { + jobs = cmake::DEFAULT_BUILD_PARALLEL_LEVEL; + /* does the next argument start with a number? */ + if ((i + 1 < ac) && (isdigit(*av[i + 1]))) { + doing = DoingJobs; + } } else if (strcmp(av[i], "--target") == 0) { if (!hasTarget) { doing = DoingTarget; @@ -377,6 +394,18 @@ static int do_build(int ac, char const* const* av) doing = DoingNative; } else { switch (doing) { + case DoingJobs: { + unsigned long numJobs = 0; + if (cmSystemTools::StringToULong(av[i], &numJobs)) { + jobs = int(numJobs); + doing = DoingNone; + } else { + std::cerr << "'" << av[i - 1] << "' invalid number '" << av[i] + << "' given.\n\n"; + dir.clear(); + break; + } + } break; case DoingDir: dir = cmSystemTools::CollapseFullPath(av[i]); doing = DoingNone; @@ -396,6 +425,25 @@ static int do_build(int ac, char const* const* av) } } } + + if (jobs == cmake::NO_BUILD_PARALLEL_LEVEL) { + std::string parallel; + if (cmSystemTools::GetEnv("CMAKE_BUILD_PARALLEL_LEVEL", parallel)) { + if (parallel.empty()) { + jobs = cmake::DEFAULT_BUILD_PARALLEL_LEVEL; + } else { + unsigned long numJobs = 0; + if (cmSystemTools::StringToULong(parallel.c_str(), &numJobs)) { + jobs = int(numJobs); + } else { + std::cerr << "'CMAKE_BUILD_PARALLEL_LEVEL' environment variable\n" + << "invalid number '" << parallel << "' given.\n\n"; + dir.clear(); + } + } + } + } + if (dir.empty()) { /* clang-format off */ std::cerr << @@ -410,7 +458,7 @@ static int do_build(int ac, char const* const* av) cmake cm(cmake::RoleInternal); cmSystemTools::SetMessageCallback(cmakemainMessageCallback, &cm); cm.SetProgressCallback(cmakemainProgressCallback, &cm); - return cm.Build(dir, target, config, nativeOptions, clean); + return cm.Build(jobs, dir, target, config, nativeOptions, clean); #endif } diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 0988c3c..6f3a90f 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -359,7 +359,8 @@ struct CoCompileJob int cmcmd::HandleCoCompileCommands(std::vector<std::string>& args) { std::vector<CoCompileJob> jobs; - std::string sourceFile; // store --source= + std::string sourceFile; // store --source= + std::vector<std::string> launchers; // store --launcher= // Default is to run the original command found after -- if the option // does not need to do that, it should be specified here, currently only @@ -390,15 +391,17 @@ int cmcmd::HandleCoCompileCommands(std::vector<std::string>& args) } } } - if (cmHasLiteralPrefix(arg, "--source=")) { - sourceFile = arg.substr(9); - optionFound = true; - } - // if it was not a co-compiler or --source then error if (!optionFound) { - std::cerr << "__run_co_compile given unknown argument: " << arg - << "\n"; - return 1; + if (cmHasLiteralPrefix(arg, "--source=")) { + sourceFile = arg.substr(9); + } else if (cmHasLiteralPrefix(arg, "--launcher=")) { + cmSystemTools::ExpandListArgument(arg.substr(11), launchers, true); + } else { + // if it was not a co-compiler or --source/--launcher then error + std::cerr << "__run_co_compile given unknown argument: " << arg + << "\n"; + return 1; + } } } else { // if not doing_options then push to orig_cmd orig_cmd.push_back(arg); @@ -436,6 +439,11 @@ int cmcmd::HandleCoCompileCommands(std::vector<std::string>& args) return 0; } + // Prepend launcher argument(s), if any + if (!launchers.empty()) { + orig_cmd.insert(orig_cmd.begin(), launchers.begin(), launchers.end()); + } + // Now run the real compiler command and return its result value int ret; if (!cmSystemTools::RunSingleCommand(orig_cmd, nullptr, nullptr, &ret, @@ -689,8 +697,6 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) // Touch file if (args[1] == "touch_nocreate" && args.size() > 2) { for (std::string::size_type cc = 2; cc < args.size(); cc++) { - // Complain if the file could not be removed, still exists, - // and the -f option was not given. if (!cmSystemTools::Touch(args[cc], false)) { return 1; } diff --git a/Source/kwsys/CMakeLists.txt b/Source/kwsys/CMakeLists.txt index 4fc176b..96088c8 100644 --- a/Source/kwsys/CMakeLists.txt +++ b/Source/kwsys/CMakeLists.txt @@ -196,6 +196,11 @@ IF(KWSYS_STANDALONE) ENDIF() ENDIF() +# Choose default shared/static build if not specified. +IF(NOT DEFINED KWSYS_BUILD_SHARED) + SET(KWSYS_BUILD_SHARED ${BUILD_SHARED_LIBS}) +ENDIF() + # Include helper macros. INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/kwsysPlatformTests.cmake) INCLUDE(CheckTypeSize) @@ -230,13 +235,14 @@ ENDIF() # Setup library install rules. SET(KWSYS_INSTALL_LIBRARY_RULE) +SET(KWSYS_INSTALL_NAMELINK_RULE) IF(KWSYS_INSTALL_LIB_DIR) IF(KWSYS_INSTALL_EXPORT_NAME) LIST(APPEND KWSYS_INSTALL_LIBRARY_RULE EXPORT ${KWSYS_INSTALL_EXPORT_NAME}) ENDIF() # Install the shared library to the lib directory. SET(KWSYS_INSTALL_LIBRARY_RULE ${KWSYS_INSTALL_LIBRARY_RULE} - LIBRARY DESTINATION ${KWSYS_INSTALL_LIB_DIR} + LIBRARY DESTINATION ${KWSYS_INSTALL_LIB_DIR} NAMELINK_SKIP ) # Assign the shared library to the runtime component. IF(KWSYS_INSTALL_COMPONENT_NAME_RUNTIME) @@ -244,6 +250,17 @@ IF(KWSYS_INSTALL_LIB_DIR) COMPONENT ${KWSYS_INSTALL_COMPONENT_NAME_RUNTIME} ) ENDIF() + IF(KWSYS_BUILD_SHARED) + SET(KWSYS_INSTALL_NAMELINK_RULE ${KWSYS_INSTALL_NAMELINK_RULE} + LIBRARY DESTINATION ${KWSYS_INSTALL_LIB_DIR} NAMELINK_ONLY + ) + # Assign the namelink to the development component. + IF(KWSYS_INSTALL_COMPONENT_NAME_DEVELOPMENT) + SET(KWSYS_INSTALL_NAMELINK_RULE ${KWSYS_INSTALL_NAMELINK_RULE} + COMPONENT ${KWSYS_INSTALL_COMPONENT_NAME_DEVELOPMENT} + ) + ENDIF() + ENDIF() # Install the archive to the lib directory. SET(KWSYS_INSTALL_LIBRARY_RULE ${KWSYS_INSTALL_LIBRARY_RULE} @@ -427,11 +444,6 @@ ELSE() SET(KWSYS_NAME_IS_KWSYS 0) ENDIF() -# Choose default shared/static build if not specified. -IF(KWSYS_BUILD_SHARED MATCHES "^KWSYS_BUILD_SHARED$") - SET(KWSYS_BUILD_SHARED ${BUILD_SHARED_LIBS}) -ENDIF() - IF(KWSYS_BUILD_SHARED) SET(KWSYS_BUILD_SHARED 1) SET(KWSYS_LIBRARY_TYPE SHARED) @@ -933,6 +945,9 @@ IF(KWSYS_C_SRCS OR KWSYS_CXX_SRCS) IF(KWSYS_INSTALL_LIBRARY_RULE) INSTALL(TARGETS ${KWSYS_TARGET_INSTALL} ${KWSYS_INSTALL_LIBRARY_RULE}) ENDIF() + IF(KWSYS_INSTALL_NAMELINK_RULE) + INSTALL(TARGETS ${KWSYS_TARGET_INSTALL} ${KWSYS_INSTALL_NAMELINK_RULE}) + ENDIF() ENDIF() # Add a C-only library if requested. diff --git a/Source/kwsys/RegularExpression.hxx.in b/Source/kwsys/RegularExpression.hxx.in index 3cbbeb8..92e4b36 100644 --- a/Source/kwsys/RegularExpression.hxx.in +++ b/Source/kwsys/RegularExpression.hxx.in @@ -218,7 +218,7 @@ inline std::string RegularExpressionMatch::match(int n) const * object as an argument and creates an object initialized with the * information from the given RegularExpression object. * - * The find member function finds the first occurrence of the regular + * The find member function finds the first occurrence of the regular * expression of that object in the string given to find as an argument. Find * returns a boolean, and if true, mutates the private data appropriately. * Find sets pointers to the beginning and end of the thing last found, they @@ -230,9 +230,9 @@ inline std::string RegularExpressionMatch::match(int n) const * the to see if the compiled regular expression is the same, and the * deep_equal functions also checks to see if the start and end pointers are * the same. The is_valid function returns false if program is set to NULL, - * (i.e. there is no valid compiled exression). The set_invalid function sets - * the program to NULL (Warning: this deletes the compiled expression). The - * following examples may help clarify regular expression usage: + * (i.e. there is no valid compiled expression). The set_invalid function + * sets the program to NULL (Warning: this deletes the compiled expression). + * The following examples may help clarify regular expression usage: * * * The regular expression "^hello" matches a "hello" only at the * beginning of a line. It would match "hello there" but not "hi, @@ -270,13 +270,13 @@ inline std::string RegularExpressionMatch::match(int n) const * * * The regular expression "(..p)b" matches something ending with pb * and beginning with whatever the two characters before the first p - * encounterd in the line were. It would find "repb" in "rep drepa + * encountered in the line were. It would find "repb" in "rep drepa * qrepb". The regular expression "(..p)a" would find "repa qrepb" * in "rep drepa qrepb" * * * The regular expression "d(..p)" matches something ending with p, * beginning with d, and having two characters in between that are - * the same as the two characters before the first p encounterd in + * the same as the two characters before the first p encountered in * the line. It would match "drepa qrepb" in "rep drepa qrepb". * * All methods of RegularExpression can be called simultaneously from diff --git a/Source/kwsys/SystemInformation.cxx b/Source/kwsys/SystemInformation.cxx index 2b9d7b1..37dec13 100644 --- a/Source/kwsys/SystemInformation.cxx +++ b/Source/kwsys/SystemInformation.cxx @@ -94,7 +94,6 @@ typedef int siginfo_t; #endif #ifdef __APPLE__ -#include <fenv.h> #include <mach/host_info.h> #include <mach/mach.h> #include <mach/mach_types.h> @@ -114,7 +113,6 @@ typedef int siginfo_t; #endif #if defined(__linux) || defined(__sun) || defined(_SCO_DS) -#include <fenv.h> #include <netdb.h> #include <netinet/in.h> #include <sys/socket.h> @@ -1272,7 +1270,7 @@ public: long GetLineNumber() const { return this->LineNumber; } // Description: - // Set the address where the biinary image is mapped + // Set the address where the binary image is mapped // into memory. void SetBinaryBaseAddress(void* address) { @@ -4063,7 +4061,7 @@ bool SystemInformationImplementation::QueryLinuxMemory() if (linuxMajor >= 3 || ((linuxMajor >= 2) && (linuxMinor >= 6))) { // new /proc/meminfo format since kernel 2.6.x - // Rigorously, this test should check from the developping version 2.5.x + // Rigorously, this test should check from the developing version 2.5.x // that introduced the new format... enum diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx index 106afe5..7743e4e 100644 --- a/Source/kwsys/SystemTools.cxx +++ b/Source/kwsys/SystemTools.cxx @@ -2282,7 +2282,9 @@ bool SystemTools::CopyADirectory(const std::string& source, const std::string& destination, bool always) { Directory dir; - dir.Load(source); + if (dir.Load(source) == 0) { + return false; + } size_t fileNum; if (!SystemTools::MakeDirectory(destination)) { return false; @@ -3344,15 +3346,20 @@ std::string SystemTools::RelativePath(const std::string& local, static std::string GetCasePathName(std::string const& pathIn) { std::string casePath; - std::vector<std::string> path_components; - SystemTools::SplitPath(pathIn, path_components); - if (path_components[0].empty()) // First component always exists. - { - // Relative paths cannot be converted. + + // First check if the file is relative. We don't fix relative paths since the + // real case depends on the root directory and the given path fragment may + // have meaning elsewhere in the project. + if (!SystemTools::FileIsFullPath(pathIn)) { + // This looks unnecessary, but it allows for the return value optimization + // since all return paths return the same local variable. casePath = pathIn; return casePath; } + std::vector<std::string> path_components; + SystemTools::SplitPath(pathIn, path_components); + // Start with root component. std::vector<std::string>::size_type idx = 0; casePath = path_components[idx++]; @@ -4025,23 +4032,16 @@ std::string SystemTools::MakeCidentifier(const std::string& s) return str; } -// Due to a buggy stream library on the HP and another on Mac OS X, we -// need this very carefully written version of getline. Returns true +// Convenience function around std::getline which removes a trailing carriage +// return and can truncate the buffer as needed. Returns true // if any data were read before the end-of-file was reached. bool SystemTools::GetLineFromStream(std::istream& is, std::string& line, bool* has_newline /* = 0 */, long sizeLimit /* = -1 */) { - const int bufferSize = 1024; - char buffer[bufferSize]; - bool haveData = false; - bool haveNewline = false; - // Start with an empty line. line = ""; - long leftToRead = sizeLimit; - // Early short circuit return if stream is no good. Just return // false and the empty line. (Probably means caller tried to // create a file stream with a non-existent file name...) @@ -4053,44 +4053,23 @@ bool SystemTools::GetLineFromStream(std::istream& is, std::string& line, return false; } - // If no characters are read from the stream, the end of file has - // been reached. Clear the fail bit just before reading. - while (!haveNewline && leftToRead != 0 && - (static_cast<void>(is.clear(is.rdstate() & ~std::ios::failbit)), - static_cast<void>(is.getline(buffer, bufferSize)), - is.gcount() > 0)) { - // We have read at least one byte. - haveData = true; - - // If newline character was read the gcount includes the character - // but the buffer does not: the end of line has been reached. - size_t length = strlen(buffer); - if (length < static_cast<size_t>(is.gcount())) { - haveNewline = true; - } - + std::getline(is, line); + bool haveData = !line.empty() || !is.eof(); + if (!line.empty()) { // Avoid storing a carriage return character. - if (length > 0 && buffer[length - 1] == '\r') { - buffer[length - 1] = 0; + if (*line.rbegin() == '\r') { + line.resize(line.size() - 1); } // if we read too much then truncate the buffer - if (leftToRead > 0) { - if (static_cast<long>(length) > leftToRead) { - buffer[leftToRead] = 0; - leftToRead = 0; - } else { - leftToRead -= static_cast<long>(length); - } + if (sizeLimit >= 0 && line.size() >= static_cast<size_t>(sizeLimit)) { + line.resize(sizeLimit); } - - // Append the data read to the line. - line.append(buffer); } // Return the results. if (has_newline) { - *has_newline = haveNewline; + *has_newline = !is.eof(); } return haveData; } diff --git a/Source/kwsys/SystemTools.hxx.in b/Source/kwsys/SystemTools.hxx.in index e79e3fc..3898e3a 100644 --- a/Source/kwsys/SystemTools.hxx.in +++ b/Source/kwsys/SystemTools.hxx.in @@ -523,9 +523,8 @@ public: static bool GetShortPath(const std::string& path, std::string& result); /** - * Read line from file. Make sure to get everything. Due to a buggy stream - * library on the HP and another on Mac OS X, we need this very carefully - * written version of getline. Returns true if any data were read before the + * Read line from file. Make sure to read a full line and truncates it if + * requested via sizeLimit. Returns true if any data were read before the * end-of-file was reached. If the has_newline argument is specified, it will * be true when the line read had a newline character. */ diff --git a/Source/kwsys/Terminal.c b/Source/kwsys/Terminal.c index eaa5c7d..c9f9dc5 100644 --- a/Source/kwsys/Terminal.c +++ b/Source/kwsys/Terminal.c @@ -153,6 +153,7 @@ static const char* kwsysTerminalVT100Names[] = { "Eterm", "xterm-88color", "xterm-color", "xterm-debian", + "xterm-kitty", "xterm-termite", 0 }; diff --git a/Source/kwsys/testCommandLineArguments.cxx b/Source/kwsys/testCommandLineArguments.cxx index 78baed9..58f861c 100644 --- a/Source/kwsys/testCommandLineArguments.cxx +++ b/Source/kwsys/testCommandLineArguments.cxx @@ -136,7 +136,7 @@ int testCommandLineArguments(int argc, char* argv[]) arg.AddCallback("-C", argT::EQUAL_ARGUMENT, argument, random_ptr, "Option -C takes argument after ="); arg.AddCallback("-D", argT::CONCAT_ARGUMENT, argument, random_ptr, - "This option takes concatinated argument"); + "This option takes concatenated argument"); arg.AddCallback("--long1", argT::NO_ARGUMENT, argument, random_ptr, "-A"); arg.AddCallback("--long2", argT::SPACE_ARGUMENT, argument, random_ptr, "-B"); arg.AddCallback("--long3", argT::EQUAL_ARGUMENT, argument, random_ptr, diff --git a/Source/kwsys/testDirectory.cxx b/Source/kwsys/testDirectory.cxx index 983f2c6..62a0986 100644 --- a/Source/kwsys/testDirectory.cxx +++ b/Source/kwsys/testDirectory.cxx @@ -73,7 +73,38 @@ int _doLongPathTest() return res; } +int _copyDirectoryTest() +{ + using namespace kwsys; + const std::string source(TEST_SYSTEMTOOLS_BINARY_DIR + "/directory_testing/copyDirectoryTestSrc"); + if (SystemTools::PathExists(source)) { + std::cerr << source << " shouldn't exist before test" << std::endl; + return 1; + } + const std::string destination(TEST_SYSTEMTOOLS_BINARY_DIR + "/directory_testing/copyDirectoryTestDst"); + if (SystemTools::PathExists(destination)) { + std::cerr << destination << " shouldn't exist before test" << std::endl; + return 2; + } + const bool copysuccess = SystemTools::CopyADirectory(source, destination); + const bool destinationexists = SystemTools::PathExists(destination); + if (copysuccess) { + std::cerr << "CopyADirectory should have returned false" << std::endl; + SystemTools::RemoveADirectory(destination); + return 3; + } + if (destinationexists) { + std::cerr << "CopyADirectory returned false, but destination directory" + << " has been created" << std::endl; + SystemTools::RemoveADirectory(destination); + return 4; + } + return 0; +} + int testDirectory(int, char* []) { - return _doLongPathTest(); + return _doLongPathTest() + _copyDirectoryTest(); } diff --git a/Source/kwsys/testSystemTools.cxx b/Source/kwsys/testSystemTools.cxx index e436a2b..8c928d4 100644 --- a/Source/kwsys/testSystemTools.cxx +++ b/Source/kwsys/testSystemTools.cxx @@ -738,29 +738,29 @@ static bool CheckGetPath() #endif const char* registryPath = "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MyApp; MyKey]"; - std::vector<std::string> originalPathes; - originalPathes.push_back(registryPath); + std::vector<std::string> originalPaths; + originalPaths.push_back(registryPath); - std::vector<std::string> expectedPathes; - expectedPathes.push_back(registryPath); + std::vector<std::string> expectedPaths; + expectedPaths.push_back(registryPath); #ifdef _WIN32 - expectedPathes.push_back("C:/Somewhere/something"); - expectedPathes.push_back("D:/Temp"); + expectedPaths.push_back("C:/Somewhere/something"); + expectedPaths.push_back("D:/Temp"); #else - expectedPathes.push_back("/Somewhere/something"); - expectedPathes.push_back("/tmp"); + expectedPaths.push_back("/Somewhere/something"); + expectedPaths.push_back("/tmp"); #endif bool res = true; res &= CheckPutEnv(std::string(envName) + "=" + envValue, envName, envValue); - std::vector<std::string> pathes = originalPathes; - kwsys::SystemTools::GetPath(pathes, envName); + std::vector<std::string> paths = originalPaths; + kwsys::SystemTools::GetPath(paths, envName); - if (pathes != expectedPathes) { - std::cerr << "GetPath(" << StringVectorToString(originalPathes) << ", " - << envName << ") yielded " << StringVectorToString(pathes) - << " instead of " << StringVectorToString(expectedPathes) + if (paths != expectedPaths) { + std::cerr << "GetPath(" << StringVectorToString(originalPaths) << ", " + << envName << ") yielded " << StringVectorToString(paths) + << " instead of " << StringVectorToString(expectedPaths) << std::endl; res = false; } @@ -912,6 +912,78 @@ static bool CheckGetLineFromStream() return ret; } +static bool CheckGetLineFromStreamLongLine() +{ + const std::string fileWithLongLine("longlines.txt"); + std::string firstLine, secondLine; + // First line: large buffer, containing a carriage return for some reason. + firstLine.assign(2050, ' '); + firstLine += "\rfirst"; + secondLine.assign(2050, 'y'); + secondLine += "second"; + + // Create file with long lines. + { + kwsys::ofstream out(fileWithLongLine.c_str(), std::ios::binary); + if (!out) { + std::cerr << "Problem opening for write: " << fileWithLongLine + << std::endl; + return false; + } + out << firstLine << "\r\n\n" << secondLine << "\n"; + } + + kwsys::ifstream file(fileWithLongLine.c_str(), std::ios::binary); + if (!file) { + std::cerr << "Problem opening: " << fileWithLongLine << std::endl; + return false; + } + + std::string line; + bool has_newline = false; + bool result; + + // Read first line. + result = kwsys::SystemTools::GetLineFromStream(file, line, &has_newline, -1); + if (!result || line != firstLine) { + std::cerr << "First line does not match, expected " << firstLine.size() + << " characters, got " << line.size() << std::endl; + return false; + } + if (!has_newline) { + std::cerr << "Expected new line to be read from first line" << std::endl; + return false; + } + + // Read empty line. + has_newline = false; + result = kwsys::SystemTools::GetLineFromStream(file, line, &has_newline, -1); + if (!result || !line.empty()) { + std::cerr << "Expected successful read with an empty line, got " + << line.size() << " characters" << std::endl; + return false; + } + if (!has_newline) { + std::cerr << "Expected new line to be read for an empty line" << std::endl; + return false; + } + + // Read second line. + has_newline = false; + result = kwsys::SystemTools::GetLineFromStream(file, line, &has_newline, -1); + if (!result || line != secondLine) { + std::cerr << "Second line does not match, expected " << secondLine.size() + << " characters, got " << line.size() << std::endl; + return false; + } + if (!has_newline) { + std::cerr << "Expected new line to be read from second line" << std::endl; + return false; + } + + return true; +} + int testSystemTools(int, char* []) { bool res = true; @@ -951,6 +1023,8 @@ int testSystemTools(int, char* []) res &= CheckGetLineFromStream(); + res &= CheckGetLineFromStreamLongLine(); + res &= CheckGetFilenameName(); return res ? 0 : 1; |