summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
Diffstat (limited to 'Source')
-rw-r--r--Source/CMakeLists.txt6
-rw-r--r--Source/CMakeVersion.cmake2
-rw-r--r--Source/CPack/WiX/cmCPackWIXGenerator.cxx1
-rw-r--r--Source/CPack/cmCPackArchiveGenerator.cxx153
-rw-r--r--Source/CPack/cmCPackArchiveGenerator.h6
-rw-r--r--Source/CPack/cmCPackInnoSetupGenerator.cxx8
-rw-r--r--Source/CPack/cmCPackLog.h2
-rw-r--r--Source/CTest/cmCTestBZR.cxx2
-rw-r--r--Source/CTest/cmCTestCVS.cxx4
-rw-r--r--Source/CTest/cmCTestEmptyBinaryDirectoryCommand.cxx22
-rw-r--r--Source/CTest/cmCTestGIT.cxx2
-rw-r--r--Source/CTest/cmCTestHG.cxx6
-rw-r--r--Source/CTest/cmCTestMultiProcessHandler.cxx429
-rw-r--r--Source/CTest/cmCTestMultiProcessHandler.h59
-rw-r--r--Source/CTest/cmCTestP4.cxx26
-rw-r--r--Source/CTest/cmCTestRunTest.cxx19
-rw-r--r--Source/CTest/cmCTestRunTest.h21
-rw-r--r--Source/CTest/cmCTestSVN.cxx22
-rw-r--r--Source/CTest/cmCTestScriptHandler.cxx32
-rw-r--r--Source/CTest/cmCTestScriptHandler.h7
-rw-r--r--Source/CTest/cmCTestTestHandler.cxx8
-rw-r--r--Source/CTest/cmCTestTestHandler.h2
-rw-r--r--Source/CTest/cmUVJobServerClient.cxx518
-rw-r--r--Source/CTest/cmUVJobServerClient.h96
-rw-r--r--Source/Checks/Curses/CMakeLists.txt2
-rw-r--r--Source/Checks/Curses/CheckCurses.c2
-rw-r--r--Source/Checks/cm_cxx_filesystem.cxx11
-rw-r--r--Source/Modules/CMakeBuildUtilities.cmake2
-rw-r--r--Source/cmAddCustomCommandCommand.cxx4
-rw-r--r--Source/cmCMakeHostSystemInformationCommand.cxx2
-rw-r--r--Source/cmComputeLinkDepends.cxx359
-rw-r--r--Source/cmComputeLinkDepends.h2
-rw-r--r--Source/cmConditionEvaluator.cxx21
-rw-r--r--Source/cmCoreTryCompile.cxx62
-rw-r--r--Source/cmCoreTryCompile.h1
-rw-r--r--Source/cmCreateTestSourceList.cxx16
-rw-r--r--Source/cmDebuggerStackFrame.h4
-rw-r--r--Source/cmDebuggerThread.cxx4
-rw-r--r--Source/cmDyndepCollation.cxx6
-rw-r--r--Source/cmExportBuildAndroidMKGenerator.cxx4
-rw-r--r--Source/cmExportBuildAndroidMKGenerator.h7
-rw-r--r--Source/cmExportBuildFileGenerator.cxx46
-rw-r--r--Source/cmExportBuildFileGenerator.h36
-rw-r--r--Source/cmExportCommand.cxx106
-rw-r--r--Source/cmExportFileGenerator.cxx214
-rw-r--r--Source/cmExportFileGenerator.h34
-rw-r--r--Source/cmExportInstallAndroidMKGenerator.cxx11
-rw-r--r--Source/cmExportInstallAndroidMKGenerator.h12
-rw-r--r--Source/cmExportInstallFileGenerator.cxx40
-rw-r--r--Source/cmExportInstallFileGenerator.h8
-rw-r--r--Source/cmExportSet.cxx25
-rw-r--r--Source/cmExportSet.h31
-rw-r--r--Source/cmFileAPI.cxx2
-rw-r--r--Source/cmFileAPICodemodel.cxx51
-rw-r--r--Source/cmFileCommand.cxx16
-rw-r--r--Source/cmFindPackageCommand.cxx2
-rw-r--r--Source/cmFindPackageStack.cxx7
-rw-r--r--Source/cmFindPackageStack.h33
-rw-r--r--Source/cmGeneratorExpressionDAGChecker.cxx5
-rw-r--r--Source/cmGeneratorTarget.cxx140
-rw-r--r--Source/cmGeneratorTarget.h17
-rw-r--r--Source/cmGhsMultiTargetGenerator.cxx13
-rw-r--r--Source/cmGlobalGenerator.cxx63
-rw-r--r--Source/cmGlobalGenerator.h2
-rw-r--r--Source/cmGlobalNinjaGenerator.cxx36
-rw-r--r--Source/cmGlobalNinjaGenerator.h5
-rw-r--r--Source/cmGlobalVisualStudio10Generator.cxx24
-rw-r--r--Source/cmGlobalVisualStudio10Generator.h7
-rw-r--r--Source/cmGlobalVisualStudio7Generator.cxx4
-rw-r--r--Source/cmGlobalVisualStudio7Generator.h6
-rw-r--r--Source/cmGlobalXCodeGenerator.cxx49
-rw-r--r--Source/cmGlobalXCodeGenerator.h1
-rw-r--r--Source/cmInstallCommand.cxx7
-rw-r--r--Source/cmInstallExportGenerator.cxx4
-rw-r--r--Source/cmInstallExportGenerator.h4
-rw-r--r--Source/cmJSONState.cxx6
-rw-r--r--Source/cmLinkItem.cxx30
-rw-r--r--Source/cmLinkItem.h14
-rw-r--r--Source/cmLinkItemGraphVisitor.cxx2
-rw-r--r--Source/cmLocalGenerator.cxx129
-rw-r--r--Source/cmLocalGenerator.h20
-rw-r--r--Source/cmLocalVisualStudio7Generator.cxx24
-rw-r--r--Source/cmMakefile.cxx56
-rw-r--r--Source/cmMakefile.h18
-rw-r--r--Source/cmMakefileExecutableTargetGenerator.cxx3
-rw-r--r--Source/cmMakefileLibraryTargetGenerator.cxx6
-rw-r--r--Source/cmMakefileTargetGenerator.cxx2
-rw-r--r--Source/cmNinjaNormalTargetGenerator.cxx23
-rw-r--r--Source/cmNinjaTargetGenerator.cxx256
-rw-r--r--Source/cmNinjaTargetGenerator.h4
-rw-r--r--Source/cmPolicies.h15
-rw-r--r--Source/cmProjectCommand.cxx65
-rw-r--r--Source/cmQtAutoGen.cxx7
-rw-r--r--Source/cmQtAutoGen.h5
-rw-r--r--Source/cmQtAutoGenInitializer.cxx36
-rw-r--r--Source/cmQtAutoGenInitializer.h3
-rw-r--r--Source/cmQtAutoMocUic.cxx116
-rw-r--r--Source/cmRulePlaceholderExpander.cxx25
-rw-r--r--Source/cmRulePlaceholderExpander.h1
-rw-r--r--Source/cmTarget.cxx15
-rw-r--r--Source/cmTarget.h4
-rw-r--r--Source/cmTargetExport.h2
-rw-r--r--Source/cmTest.cxx1
-rw-r--r--Source/cmTest.h8
-rw-r--r--Source/cmTestGenerator.cxx16
-rw-r--r--Source/cmTransformDepfile.cxx7
-rw-r--r--Source/cmUVHandlePtr.cxx78
-rw-r--r--Source/cmUVHandlePtr.h54
-rw-r--r--Source/cmUVProcessChain.cxx29
-rw-r--r--Source/cmUVProcessChain.h5
-rw-r--r--Source/cmUVSignalHackRAII.h45
-rw-r--r--Source/cmVisualStudio10TargetGenerator.cxx10
-rw-r--r--Source/cmVisualStudioGeneratorOptions.cxx12
-rw-r--r--Source/cmWorkerPool.cxx7
-rw-r--r--Source/cmake.cxx26
-rw-r--r--Source/cmcmd.cxx18
-rw-r--r--Source/kwsys/CMakeLists.txt8
-rw-r--r--Source/kwsys/SystemTools.cxx34
118 files changed, 3413 insertions, 855 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 1bc855e..8c57762 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -450,7 +450,6 @@ add_library(
cmUVProcessChain.h
cmUVStream.h
cmUVStreambuf.h
- cmUVSignalHackRAII.h
cmVariableWatch.cxx
cmVariableWatch.h
cmVersion.cxx
@@ -558,6 +557,8 @@ add_library(
cmFindLibraryCommand.h
cmFindPackageCommand.cxx
cmFindPackageCommand.h
+ cmFindPackageStack.cxx
+ cmFindPackageStack.h
cmFindPathCommand.cxx
cmFindPathCommand.h
cmFindProgramCommand.cxx
@@ -1090,6 +1091,9 @@ add_library(
CTest/cmCTestP4.cxx
CTest/cmCTestP4.h
+ CTest/cmUVJobServerClient.cxx
+ CTest/cmUVJobServerClient.h
+
LexerParser/cmCTestResourceGroupsLexer.cxx
LexerParser/cmCTestResourceGroupsLexer.h
LexerParser/cmCTestResourceGroupsLexer.in.l
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index a51ba2e..eda95c1 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,7 +1,7 @@
# CMake version number components.
set(CMake_VERSION_MAJOR 3)
set(CMake_VERSION_MINOR 28)
-set(CMake_VERSION_PATCH 1)
+set(CMake_VERSION_PATCH 20240104)
#set(CMake_VERSION_RC 0)
set(CMake_VERSION_IS_DIRTY 0)
diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.cxx b/Source/CPack/WiX/cmCPackWIXGenerator.cxx
index 5077596..6918b5e 100644
--- a/Source/CPack/WiX/cmCPackWIXGenerator.cxx
+++ b/Source/CPack/WiX/cmCPackWIXGenerator.cxx
@@ -359,6 +359,7 @@ void cmCPackWIXGenerator::CreateWiXVariablesIncludeFile()
GetOption("CPACK_PACKAGE_NAME"));
CopyDefinition(includeFile, "CPACK_WIX_PROGRAM_MENU_FOLDER");
CopyDefinition(includeFile, "CPACK_WIX_UI_REF");
+ CopyDefinition(includeFile, "CPACK_WIX_INSTALL_SCOPE");
}
void cmCPackWIXGenerator::CreateWiXPropertiesIncludeFile()
diff --git a/Source/CPack/cmCPackArchiveGenerator.cxx b/Source/CPack/cmCPackArchiveGenerator.cxx
index c9c069c..b7b6785 100644
--- a/Source/CPack/cmCPackArchiveGenerator.cxx
+++ b/Source/CPack/cmCPackArchiveGenerator.cxx
@@ -5,6 +5,8 @@
#include <cstring>
#include <map>
#include <ostream>
+#include <unordered_map>
+#include <unordered_set>
#include <utility>
#include <vector>
@@ -17,6 +19,121 @@
#include "cmValue.h"
#include "cmWorkingDirectory.h"
+enum class DeduplicateStatus
+{
+ Skip,
+ Add,
+ Error
+};
+
+/**
+ * @class cmCPackArchiveGenerator::Deduplicator
+ * @brief A utility class for deduplicating files, folders, and symlinks.
+ *
+ * This class is responsible for identifying duplicate files, folders, and
+ * symlinks when generating an archive. It keeps track of the paths that have
+ * been processed and helps in deciding whether a new path should be added,
+ * skipped, or flagged as an error.
+ */
+class cmCPackArchiveGenerator::Deduplicator
+{
+private:
+ /**
+ * @brief Compares a file with already processed files.
+ *
+ * @param path The path of the file to compare.
+ * @param localTopLevel The top-level directory for the file.
+ * @return DeduplicateStatus indicating whether to add, skip, or flag an
+ * error for the file.
+ */
+ DeduplicateStatus CompareFile(const std::string& path,
+ const std::string& localTopLevel)
+ {
+ auto fileItr = this->Files.find(path);
+ if (fileItr != this->Files.end()) {
+ return cmSystemTools::FilesDiffer(path, fileItr->second)
+ ? DeduplicateStatus::Error
+ : DeduplicateStatus::Skip;
+ }
+
+ this->Files[path] = cmStrCat(localTopLevel, "/", path);
+ return DeduplicateStatus::Add;
+ }
+
+ /**
+ * @brief Compares a folder with already processed folders.
+ *
+ * @param path The path of the folder to compare.
+ * @return DeduplicateStatus indicating whether to add or skip the folder.
+ */
+ DeduplicateStatus CompareFolder(const std::string& path)
+ {
+ if (this->Folders.find(path) != this->Folders.end()) {
+ return DeduplicateStatus::Skip;
+ }
+
+ this->Folders.emplace(path);
+ return DeduplicateStatus::Add;
+ }
+
+ /**
+ * @brief Compares a symlink with already processed symlinks.
+ *
+ * @param path The path of the symlink to compare.
+ * @return DeduplicateStatus indicating whether to add, skip, or flag an
+ * error for the symlink.
+ */
+ DeduplicateStatus CompareSymlink(const std::string& path)
+ {
+ auto symlinkItr = this->Symlink.find(path);
+ std::string symlinkValue;
+ auto status = cmSystemTools::ReadSymlink(path, symlinkValue);
+ if (!status.IsSuccess()) {
+ return DeduplicateStatus::Error;
+ }
+
+ if (symlinkItr != this->Symlink.end()) {
+ return symlinkValue == symlinkItr->second ? DeduplicateStatus::Skip
+ : DeduplicateStatus::Error;
+ }
+
+ this->Symlink[path] = symlinkValue;
+ return DeduplicateStatus::Add;
+ }
+
+public:
+ /**
+ * @brief Determines the deduplication status of a given path.
+ *
+ * This method identifies whether the given path is a file, folder, or
+ * symlink and then delegates to the appropriate comparison method.
+ *
+ * @param path The path to check for deduplication.
+ * @param localTopLevel The top-level directory for the path.
+ * @return DeduplicateStatus indicating the action to take for the given
+ * path.
+ */
+ DeduplicateStatus IsDeduplicate(const std::string& path,
+ const std::string& localTopLevel)
+ {
+ DeduplicateStatus status;
+ if (cmSystemTools::FileIsDirectory(path)) {
+ status = this->CompareFolder(path);
+ } else if (cmSystemTools::FileIsSymlink(path)) {
+ status = this->CompareSymlink(path);
+ } else {
+ status = this->CompareFile(path, localTopLevel);
+ }
+
+ return status;
+ }
+
+private:
+ std::unordered_map<std::string, std::string> Symlink;
+ std::unordered_set<std::string> Folders;
+ std::unordered_map<std::string, std::string> Files;
+};
+
cmCPackGenerator* cmCPackArchiveGenerator::Create7ZGenerator()
{
return new cmCPackArchiveGenerator(cmArchiveWrite::CompressNone, "7zip",
@@ -110,7 +227,8 @@ int cmCPackArchiveGenerator::InitializeInternal()
}
int cmCPackArchiveGenerator::addOneComponentToArchive(
- cmArchiveWrite& archive, cmCPackComponent* component)
+ cmArchiveWrite& archive, cmCPackComponent* component,
+ Deduplicator* deduplicator)
{
cmCPackLogger(cmCPackLog::LOG_VERBOSE,
" - packaging component: " << component->Name << std::endl);
@@ -139,8 +257,25 @@ int cmCPackArchiveGenerator::addOneComponentToArchive(
}
for (std::string const& file : component->Files) {
std::string rp = filePrefix + file;
- cmCPackLogger(cmCPackLog::LOG_DEBUG, "Adding file: " << rp << std::endl);
- archive.Add(rp, 0, nullptr, false);
+
+ DeduplicateStatus status = DeduplicateStatus::Add;
+ if (deduplicator != nullptr) {
+ status = deduplicator->IsDeduplicate(rp, localToplevel);
+ }
+
+ if (deduplicator == nullptr || status == DeduplicateStatus::Add) {
+ cmCPackLogger(cmCPackLog::LOG_DEBUG, "Adding file: " << rp << std::endl);
+ archive.Add(rp, 0, nullptr, false);
+ } else if (status == DeduplicateStatus::Error) {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "ERROR The data in files with the "
+ "same filename is different.");
+ return 0;
+ } else {
+ cmCPackLogger(cmCPackLog::LOG_DEBUG,
+ "Passing file: " << rp << std::endl);
+ }
+
if (!archive) {
cmCPackLogger(cmCPackLog::LOG_ERROR,
"ERROR while packaging files: " << archive.GetError()
@@ -197,6 +332,8 @@ int cmCPackArchiveGenerator::PackageComponents(bool ignoreGroup)
std::string packageFileName = std::string(this->toplevel) + "/" +
this->GetArchiveComponentFileName(compG.first, true);
+ Deduplicator deduplicator;
+
// open a block in order to automatically close archive
// at the end of the block
{
@@ -204,7 +341,7 @@ int cmCPackArchiveGenerator::PackageComponents(bool ignoreGroup)
// now iterate over the component of this group
for (cmCPackComponent* comp : (compG.second).Components) {
// Add the files of this component to the archive
- this->addOneComponentToArchive(archive, comp);
+ this->addOneComponentToArchive(archive, comp, &deduplicator);
}
}
// add the generated package to package file names list
@@ -231,7 +368,7 @@ int cmCPackArchiveGenerator::PackageComponents(bool ignoreGroup)
{
DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive);
// Add the files of this component to the archive
- this->addOneComponentToArchive(archive, &(comp.second));
+ this->addOneComponentToArchive(archive, &(comp.second), nullptr);
}
// add the generated package to package file names list
this->packageFileNames.push_back(std::move(packageFileName));
@@ -252,7 +389,7 @@ int cmCPackArchiveGenerator::PackageComponents(bool ignoreGroup)
{
DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive);
// Add the files of this component to the archive
- this->addOneComponentToArchive(archive, &(comp.second));
+ this->addOneComponentToArchive(archive, &(comp.second), nullptr);
}
// add the generated package to package file names list
this->packageFileNames.push_back(std::move(packageFileName));
@@ -282,10 +419,12 @@ int cmCPackArchiveGenerator::PackageComponentsAllInOne()
<< std::endl);
DECLARE_AND_OPEN_ARCHIVE(packageFileNames[0], archive);
+ Deduplicator deduplicator;
+
// The ALL COMPONENTS in ONE package case
for (auto& comp : this->Components) {
// Add the files of this component to the archive
- this->addOneComponentToArchive(archive, &(comp.second));
+ this->addOneComponentToArchive(archive, &(comp.second), &deduplicator);
}
// archive goes out of scope so it will finalized and closed.
diff --git a/Source/CPack/cmCPackArchiveGenerator.h b/Source/CPack/cmCPackArchiveGenerator.h
index 8a9bbc6..b8a1afa 100644
--- a/Source/CPack/cmCPackArchiveGenerator.h
+++ b/Source/CPack/cmCPackArchiveGenerator.h
@@ -47,6 +47,8 @@ private:
std::string GetArchiveComponentFileName(const std::string& component,
bool isGroupName);
+ class Deduplicator;
+
protected:
int InitializeInternal() override;
/**
@@ -54,9 +56,11 @@ protected:
* to the provided (already opened) archive.
* @param[in,out] archive the archive object
* @param[in] component the component whose file will be added to archive
+ * @param[in] deduplicator file deduplicator utility.
*/
int addOneComponentToArchive(cmArchiveWrite& archive,
- cmCPackComponent* component);
+ cmCPackComponent* component,
+ Deduplicator* deduplicator);
/**
* The main package file method.
diff --git a/Source/CPack/cmCPackInnoSetupGenerator.cxx b/Source/CPack/cmCPackInnoSetupGenerator.cxx
index b8bf070..bf90b06 100644
--- a/Source/CPack/cmCPackInnoSetupGenerator.cxx
+++ b/Source/CPack/cmCPackInnoSetupGenerator.cxx
@@ -579,8 +579,9 @@ bool cmCPackInnoSetupGenerator::ProcessFiles()
bool cmCPackInnoSetupGenerator::ProcessComponents()
{
- codeIncludes.push_back("{ The following lines are required by CPack because "
- "this script uses components }");
+ codeIncludes.emplace_back(
+ "{ The following lines are required by CPack because "
+ "this script uses components }");
// Installation types
std::vector<cmCPackInstallationType*> types(InstallationTypes.size());
@@ -607,7 +608,7 @@ bool cmCPackInnoSetupGenerator::ProcessComponents()
"\"{code:CPackGetCustomInstallationMessage}\"";
customTypeParams["Flags"] = "iscustom";
- allTypes.push_back("custom");
+ allTypes.emplace_back("custom");
typeInstructions.push_back(ISKeyValueLine(customTypeParams));
// Components
@@ -633,6 +634,7 @@ bool cmCPackInnoSetupGenerator::ProcessComponents()
} else if (!component->InstallationTypes.empty()) {
std::vector<std::string> installationTypes;
+ installationTypes.reserve(component->InstallationTypes.size());
for (cmCPackInstallationType* j : component->InstallationTypes) {
installationTypes.push_back(j->Name);
}
diff --git a/Source/CPack/cmCPackLog.h b/Source/CPack/cmCPackLog.h
index 2ab2f8e..347b0f7 100644
--- a/Source/CPack/cmCPackLog.h
+++ b/Source/CPack/cmCPackLog.h
@@ -29,7 +29,7 @@ public:
cmCPackLog(const cmCPackLog&) = delete;
cmCPackLog& operator=(const cmCPackLog&) = delete;
- enum __log_tags
+ enum cm_log_tags
{
NOTAG = 0,
LOG_OUTPUT = 0x1,
diff --git a/Source/CTest/cmCTestBZR.cxx b/Source/CTest/cmCTestBZR.cxx
index 36df344..87081f0 100644
--- a/Source/CTest/cmCTestBZR.cxx
+++ b/Source/CTest/cmCTestBZR.cxx
@@ -374,7 +374,7 @@ bool cmCTestBZR::UpdateImpl()
// Use "bzr pull" to update the working tree.
std::vector<std::string> bzr_update;
bzr_update.push_back(this->CommandLineTool);
- bzr_update.push_back("pull");
+ bzr_update.emplace_back("pull");
cm::append(bzr_update, args);
diff --git a/Source/CTest/cmCTestCVS.cxx b/Source/CTest/cmCTestCVS.cxx
index ef95b25..badd43e 100644
--- a/Source/CTest/cmCTestCVS.cxx
+++ b/Source/CTest/cmCTestCVS.cxx
@@ -92,8 +92,8 @@ bool cmCTestCVS::UpdateImpl()
// Run "cvs update" to update the work tree.
std::vector<std::string> cvs_update;
cvs_update.push_back(this->CommandLineTool);
- cvs_update.push_back("-z3");
- cvs_update.push_back("update");
+ cvs_update.emplace_back("-z3");
+ cvs_update.emplace_back("update");
cm::append(cvs_update, args);
UpdateParser out(this, "up-out> ");
diff --git a/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.cxx b/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.cxx
index af495bb..2c92d77 100644
--- a/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.cxx
+++ b/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.cxx
@@ -2,25 +2,27 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmCTestEmptyBinaryDirectoryCommand.h"
-#include <sstream>
-
#include "cmCTestScriptHandler.h"
-
-class cmExecutionStatus;
+#include "cmExecutionStatus.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
bool cmCTestEmptyBinaryDirectoryCommand::InitialPass(
- std::vector<std::string> const& args, cmExecutionStatus& /*unused*/)
+ std::vector<std::string> const& args, cmExecutionStatus& status)
{
if (args.size() != 1) {
this->SetError("called with incorrect number of arguments");
return false;
}
- if (!cmCTestScriptHandler::EmptyBinaryDirectory(args[0])) {
- std::ostringstream ostr;
- ostr << "problem removing the binary directory: " << args[0];
- this->SetError(ostr.str());
- return false;
+ std::string err;
+ if (!cmCTestScriptHandler::EmptyBinaryDirectory(args[0], err)) {
+ status.GetMakefile().IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("Did not remove the binary directory:\n ", args[0],
+ "\nbecause:\n ", err));
+ return true;
}
return true;
diff --git a/Source/CTest/cmCTestGIT.cxx b/Source/CTest/cmCTestGIT.cxx
index ca8659e..984c837 100644
--- a/Source/CTest/cmCTestGIT.cxx
+++ b/Source/CTest/cmCTestGIT.cxx
@@ -159,7 +159,7 @@ bool cmCTestGIT::UpdateByFetchAndReset()
// Use "git fetch" to get remote commits.
std::vector<std::string> git_fetch;
git_fetch.push_back(git);
- git_fetch.push_back("fetch");
+ git_fetch.emplace_back("fetch");
// Add user-specified update options.
std::string opts = this->CTest->GetCTestConfiguration("UpdateOptions");
diff --git a/Source/CTest/cmCTestHG.cxx b/Source/CTest/cmCTestHG.cxx
index e1a945d..3d56be0 100644
--- a/Source/CTest/cmCTestHG.cxx
+++ b/Source/CTest/cmCTestHG.cxx
@@ -137,9 +137,9 @@ bool cmCTestHG::UpdateImpl()
// TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY)
std::vector<std::string> hg_update;
- hg_update.push_back(this->CommandLineTool.c_str());
- hg_update.push_back("update");
- hg_update.push_back("-v");
+ hg_update.emplace_back(this->CommandLineTool);
+ hg_update.emplace_back("update");
+ hg_update.emplace_back("-v");
// Add user-specified update options.
std::string opts = this->CTest->GetCTestConfiguration("UpdateOptions");
diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx
index ca07a08..7b72f30 100644
--- a/Source/CTest/cmCTestMultiProcessHandler.cxx
+++ b/Source/CTest/cmCTestMultiProcessHandler.cxx
@@ -40,7 +40,7 @@
#include "cmRange.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
-#include "cmUVSignalHackRAII.h" // IWYU pragma: keep
+#include "cmUVJobServerClient.h"
#include "cmWorkingDirectory.h"
namespace cmsys {
@@ -83,17 +83,12 @@ cmCTestMultiProcessHandler::cmCTestMultiProcessHandler()
cmCTestMultiProcessHandler::~cmCTestMultiProcessHandler() = default;
// Set the tests
-void cmCTestMultiProcessHandler::SetTests(TestMap& tests,
- PropertiesMap& properties)
+void cmCTestMultiProcessHandler::SetTests(TestMap tests,
+ PropertiesMap properties)
{
- this->Tests = tests;
- this->Properties = properties;
- this->Total = this->Tests.size();
- // set test run map to false for all
- for (auto const& t : this->Tests) {
- this->TestRunningMap[t.first] = false;
- this->TestFinishMap[t.first] = false;
- }
+ this->PendingTests = std::move(tests);
+ this->Properties = std::move(properties);
+ this->Total = this->PendingTests.size();
if (!this->CTest->GetShowOnly()) {
this->ReadCostData();
this->HasCycles = !this->CheckCycles();
@@ -126,25 +121,50 @@ void cmCTestMultiProcessHandler::SetTestLoad(unsigned long load)
}
}
+bool cmCTestMultiProcessHandler::Complete()
+{
+ return this->Completed == this->Total;
+}
+
+void cmCTestMultiProcessHandler::InitializeLoop()
+{
+ this->Loop.init();
+ this->StartNextTestsOnIdle_.init(*this->Loop, this);
+ this->StartNextTestsOnTimer_.init(*this->Loop, this);
+
+ this->JobServerClient = cmUVJobServerClient::Connect(
+ *this->Loop, /*onToken=*/[this]() { this->JobServerReceivedToken(); },
+ /*onDisconnect=*/nullptr);
+ if (this->JobServerClient) {
+ cmCTestLog(this->CTest, OUTPUT,
+ "Connected to MAKE jobserver" << std::endl);
+ }
+}
+
+void cmCTestMultiProcessHandler::FinalizeLoop()
+{
+ this->JobServerClient.reset();
+ this->StartNextTestsOnTimer_.reset();
+ this->StartNextTestsOnIdle_.reset();
+ this->Loop.reset();
+}
+
void cmCTestMultiProcessHandler::RunTests()
{
this->CheckResume();
if (this->HasCycles || this->HasInvalidGeneratedResourceSpec) {
return;
}
-#ifdef CMAKE_UV_SIGNAL_HACK
- cmUVSignalHackRAII hackRAII;
-#endif
this->TestHandler->SetMaxIndex(this->FindMaxIndex());
- uv_loop_init(&this->Loop);
- this->StartNextTests();
- uv_run(&this->Loop, UV_RUN_DEFAULT);
- uv_loop_close(&this->Loop);
+ this->InitializeLoop();
+ this->StartNextTestsOnIdle();
+ uv_run(this->Loop, UV_RUN_DEFAULT);
+ this->FinalizeLoop();
if (!this->StopTimePassed && !this->CheckStopOnFailure()) {
- assert(this->Completed == this->Total);
- assert(this->Tests.empty());
+ assert(this->Complete());
+ assert(this->PendingTests.empty());
}
assert(this->AllResourcesAvailable());
@@ -152,38 +172,17 @@ void cmCTestMultiProcessHandler::RunTests()
this->UpdateCostData();
}
-bool cmCTestMultiProcessHandler::StartTestProcess(int test)
+void cmCTestMultiProcessHandler::StartTestProcess(int test)
{
- 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
- // now remove the test itself
- this->EraseTest(test);
- this->RunningCount += this->GetProcessorsUsed(test);
- auto testRun = cm::make_unique<cmCTestRunTest>(*this);
+ auto testRun = cm::make_unique<cmCTestRunTest>(*this, test);
if (this->RepeatMode != cmCTest::Repeat::Never) {
testRun->SetRepeatMode(this->RepeatMode);
testRun->SetNumberOfRuns(this->RepeatCount);
}
- testRun->SetIndex(test);
- testRun->SetTestProperties(this->Properties[test]);
if (this->UseResourceSpec) {
testRun->SetUseAllocatedResources(true);
testRun->SetAllocatedResources(this->AllocatedResources[test]);
@@ -197,22 +196,18 @@ bool cmCTestMultiProcessHandler::StartTestProcess(int test)
}
}
- // 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 (!this->ResourceAllocationErrors[test].empty()) {
+ if (!this->ResourceAvailabilityErrors[test].empty()) {
std::ostringstream e;
e << "Insufficient resources for test " << this->Properties[test]->Name
<< ":\n\n";
- for (auto const& it : this->ResourceAllocationErrors[test]) {
+ for (auto const& it : this->ResourceAvailabilityErrors[test]) {
switch (it.second) {
- case ResourceAllocationError::NoResourceType:
+ case ResourceAvailabilityError::NoResourceType:
e << " Test requested resources of type '" << it.first
<< "' which does not exist\n";
break;
- case ResourceAllocationError::InsufficientResources:
+ case ResourceAvailabilityError::InsufficientResources:
e << " Test requested resources of type '" << it.first
<< "' in the following amounts:\n";
for (auto const& group : this->Properties[test]->ResourceGroups) {
@@ -236,7 +231,7 @@ bool cmCTestMultiProcessHandler::StartTestProcess(int test)
e << "Resource spec file:\n\n " << this->ResourceSpecFile;
cmCTestRunTest::StartFailure(std::move(testRun), this->Total, e.str(),
"Insufficient resources");
- return false;
+ return;
}
cmWorkingDirectory workdir(this->Properties[test]->Directory);
@@ -246,13 +241,12 @@ bool cmCTestMultiProcessHandler::StartTestProcess(int test)
this->Properties[test]->Directory + " : " +
std::strerror(workdir.GetLastResult()),
"Failed to change working directory");
- return false;
+ return;
}
// Ownership of 'testRun' has moved to another structure.
// When the test finishes, FinishTestProcess will be called.
- return cmCTestRunTest::StartTest(std::move(testRun), this->Completed,
- this->Total);
+ cmCTestRunTest::StartTest(std::move(testRun), this->Completed, this->Total);
}
bool cmCTestMultiProcessHandler::AllocateResources(int index)
@@ -261,6 +255,12 @@ bool cmCTestMultiProcessHandler::AllocateResources(int index)
return true;
}
+ // If the test needs unavailable resources then do not allocate anything
+ // because it will never run. We will issue the recorded errors instead.
+ if (!this->ResourceAvailabilityErrors[index].empty()) {
+ return true;
+ }
+
std::map<std::string, std::vector<cmCTestBinPackerAllocation>> allocations;
if (!this->TryAllocateResources(index, allocations)) {
return false;
@@ -285,7 +285,7 @@ bool cmCTestMultiProcessHandler::AllocateResources(int index)
bool cmCTestMultiProcessHandler::TryAllocateResources(
int index,
std::map<std::string, std::vector<cmCTestBinPackerAllocation>>& allocations,
- std::map<std::string, ResourceAllocationError>* errors)
+ std::map<std::string, ResourceAvailabilityError>* errors)
{
allocations.clear();
@@ -305,7 +305,7 @@ bool cmCTestMultiProcessHandler::TryAllocateResources(
for (auto& it : allocations) {
if (!availableResources.count(it.first)) {
if (errors) {
- (*errors)[it.first] = ResourceAllocationError::NoResourceType;
+ (*errors)[it.first] = ResourceAvailabilityError::NoResourceType;
result = false;
} else {
return false;
@@ -313,7 +313,7 @@ bool cmCTestMultiProcessHandler::TryAllocateResources(
} else if (!cmAllocateCTestResourcesRoundRobin(
availableResources.at(it.first), it.second)) {
if (errors) {
- (*errors)[it.first] = ResourceAllocationError::InsufficientResources;
+ (*errors)[it.first] = ResourceAvailabilityError::InsufficientResources;
result = false;
} else {
return false;
@@ -360,14 +360,14 @@ bool cmCTestMultiProcessHandler::AllResourcesAvailable()
return true;
}
-void cmCTestMultiProcessHandler::CheckResourcesAvailable()
+void cmCTestMultiProcessHandler::CheckResourceAvailability()
{
if (this->UseResourceSpec) {
- for (auto test : this->SortedTests) {
+ for (auto const& t : this->PendingTests) {
std::map<std::string, std::vector<cmCTestBinPackerAllocation>>
allocations;
- this->TryAllocateResources(test, allocations,
- &this->ResourceAllocationErrors[test]);
+ this->TryAllocateResources(t.first, allocations,
+ &this->ResourceAvailabilityErrors[t.first]);
}
}
}
@@ -403,30 +403,49 @@ void cmCTestMultiProcessHandler::SetStopTimePassed()
void cmCTestMultiProcessHandler::LockResources(int index)
{
- this->LockedResources.insert(
- this->Properties[index]->LockedResources.begin(),
- this->Properties[index]->LockedResources.end());
+ this->RunningCount += this->GetProcessorsUsed(index);
+
+ auto* properties = this->Properties[index];
+
+ this->ProjectResourcesLocked.insert(properties->ProjectResources.begin(),
+ properties->ProjectResources.end());
- if (this->Properties[index]->RunSerial) {
+ if (properties->RunSerial) {
this->SerialTestRunning = true;
}
+
+ if (this->HaveAffinity && properties->WantAffinity) {
+ size_t needProcessors = this->GetProcessorsUsed(index);
+ assert(needProcessors <= this->ProcessorsAvailable.size());
+ 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);
+ }
+ properties->Affinity = std::move(affinity);
+ }
}
void cmCTestMultiProcessHandler::UnlockResources(int index)
{
- for (std::string const& i : this->Properties[index]->LockedResources) {
- this->LockedResources.erase(i);
+ auto* properties = this->Properties[index];
+
+ for (auto p : properties->Affinity) {
+ this->ProcessorsAvailable.insert(p);
}
- if (this->Properties[index]->RunSerial) {
+ properties->Affinity.clear();
+
+ for (std::string const& i : properties->ProjectResources) {
+ this->ProjectResourcesLocked.erase(i);
+ }
+
+ if (properties->RunSerial) {
this->SerialTestRunning = false;
}
-}
-void cmCTestMultiProcessHandler::EraseTest(int test)
-{
- this->Tests.erase(test);
- this->SortedTests.erase(
- std::find(this->SortedTests.begin(), this->SortedTests.end(), test));
+ this->RunningCount -= this->GetProcessorsUsed(index);
}
inline size_t cmCTestMultiProcessHandler::GetProcessorsUsed(int test)
@@ -450,50 +469,40 @@ std::string cmCTestMultiProcessHandler::GetName(int test)
return this->Properties[test]->Name;
}
-bool cmCTestMultiProcessHandler::StartTest(int test)
+void cmCTestMultiProcessHandler::StartTest(int test)
{
- // Check for locked resources
- for (std::string const& i : this->Properties[test]->LockedResources) {
- if (cm::contains(this->LockedResources, i)) {
- return false;
- }
- }
-
- // Allocate resources
- if (this->ResourceAllocationErrors[test].empty() &&
- !this->AllocateResources(test)) {
- this->DeallocateResources(test);
- return false;
+ if (this->JobServerClient) {
+ // There is a job server. Request a token and queue the test to run
+ // when a token is received. Note that if we do not get a token right
+ // away it's possible that the system load will be higher when the
+ // token is received and we may violate the test-load limit. However,
+ // this is unlikely because if we do not get a token right away, some
+ // other job that's currently running must finish before we get one.
+ this->JobServerClient->RequestToken();
+ this->JobServerQueuedTests.emplace_back(test);
+ } else {
+ // There is no job server. Start the test now.
+ this->StartTestProcess(test);
}
+}
- // if there are no depends left then run this test
- if (this->Tests[test].empty()) {
- return this->StartTestProcess(test);
- }
- // This test was not able to start because it is waiting
- // on depends to run
- this->DeallocateResources(test);
- return false;
+void cmCTestMultiProcessHandler::JobServerReceivedToken()
+{
+ assert(!this->JobServerQueuedTests.empty());
+ int test = this->JobServerQueuedTests.front();
+ this->JobServerQueuedTests.pop_front();
+ this->StartTestProcess(test);
}
void cmCTestMultiProcessHandler::StartNextTests()
{
- if (this->TestLoadRetryTimer.get() != nullptr) {
- // This timer may be waiting to call StartNextTests again.
- // Since we have been called it is no longer needed.
- uv_timer_stop(this->TestLoadRetryTimer);
- }
-
- if (this->Tests.empty()) {
- this->TestLoadRetryTimer.reset();
- return;
- }
+ // One or more events may be scheduled to call this method again.
+ // Since this method has been called they are no longer needed.
+ this->StartNextTestsOnIdle_.stop();
+ this->StartNextTestsOnTimer_.stop();
- if (this->CheckStopTimePassed()) {
- return;
- }
-
- if (this->CheckStopOnFailure() && !this->Failed->empty()) {
+ if (this->PendingTests.empty() || this->CheckStopTimePassed() ||
+ (this->CheckStopOnFailure() && !this->Failed->empty())) {
return;
}
@@ -545,50 +554,79 @@ void cmCTestMultiProcessHandler::StartNextTests()
}
}
- TestList copy = this->SortedTests;
- for (auto const& test : copy) {
- // Take a nap if we're currently performing a RUN_SERIAL test.
- if (this->SerialTestRunning) {
- break;
- }
+ // Start tests in the preferred order, each subject to readiness checks.
+ auto ti = this->OrderedTests.begin();
+ while (numToStart > 0 && !this->SerialTestRunning &&
+ ti != this->OrderedTests.end()) {
+ // Increment the test iterator now because the current list
+ // entry may be deleted below.
+ auto cti = ti++;
+ int test = *cti;
+
// We can only start a RUN_SERIAL test if no other tests are also
// running.
if (this->Properties[test]->RunSerial && this->RunningCount > 0) {
continue;
}
+ // Exclude tests that depend on unfinished tests.
+ if (!this->PendingTests[test].Depends.empty()) {
+ continue;
+ }
+
size_t processors = this->GetProcessorsUsed(test);
- bool testLoadOk = true;
if (this->TestLoad > 0) {
- if (processors <= spareLoad) {
- cmCTestLog(this->CTest, DEBUG,
- "OK to run " << this->GetName(test) << ", it requires "
- << processors << " procs & system load is: "
- << systemLoad << std::endl);
- allTestsFailedTestLoadCheck = false;
- } else {
- testLoadOk = false;
+ // Exclude tests that are too big to fit in the spare load.
+ if (processors > spareLoad) {
+ // Keep track of the smallest excluded test to report in message below.
+ if (processors <= minProcessorsRequired) {
+ minProcessorsRequired = processors;
+ testWithMinProcessors = this->GetName(test);
+ }
+ continue;
}
+
+ // We found a test that fits in the spare load.
+ allTestsFailedTestLoadCheck = false;
+ cmCTestLog(this->CTest, DEBUG,
+ "OK to run "
+ << this->GetName(test) << ", it requires " << processors
+ << " procs & system load is: " << systemLoad << std::endl);
}
- if (processors <= minProcessorsRequired) {
- minProcessorsRequired = processors;
- testWithMinProcessors = this->GetName(test);
+ // Exclude tests that are too big to fit in the concurrency limit.
+ if (processors > numToStart) {
+ continue;
}
- if (testLoadOk && processors <= numToStart && this->StartTest(test)) {
- numToStart -= processors;
- } else if (numToStart == 0) {
- break;
+ // Exclude tests that depend on currently-locked project resources.
+ for (std::string const& i : this->Properties[test]->ProjectResources) {
+ if (cm::contains(this->ProjectResourcesLocked, i)) {
+ continue;
+ }
}
+
+ // Allocate system resources needed by this test.
+ if (!this->AllocateResources(test)) {
+ continue;
+ }
+
+ // Lock resources needed by this test.
+ this->LockResources(test);
+
+ // The test is ready to run.
+ numToStart -= processors;
+ this->OrderedTests.erase(cti);
+ this->PendingTests.erase(test);
+ this->StartTest(test);
}
if (allTestsFailedTestLoadCheck) {
// Find out whether there are any non RUN_SERIAL tests left, so that the
// correct warning may be displayed.
bool onlyRunSerialTestsLeft = true;
- for (auto const& test : copy) {
- if (!this->Properties[test]->RunSerial) {
+ for (auto const& t : this->PendingTests) {
+ if (!this->Properties[t.first]->RunSerial) {
onlyRunSerialTestsLeft = false;
}
}
@@ -600,7 +638,7 @@ void cmCTestMultiProcessHandler::StartNextTests()
} else if (onlyRunSerialTestsLeft) {
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Only RUN_SERIAL tests remain, awaiting available slot.");
- } else {
+ } else if (!testWithMinProcessors.empty()) {
/* clang-format off */
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"System Load: " << systemLoad << ", "
@@ -608,26 +646,43 @@ void cmCTestMultiProcessHandler::StartNextTests()
"Smallest test " << testWithMinProcessors <<
" requires " << minProcessorsRequired);
/* clang-format on */
+ } else {
+ /* clang-format off */
+ cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+ "System Load: " << systemLoad << ", "
+ "Max Allowed Load: " << this->TestLoad);
+ /* clang-format on */
}
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "*****" << std::endl);
- // Wait between 1 and 5 seconds before trying again.
- unsigned int milliseconds = (cmSystemTools::RandomSeed() % 5 + 1) * 1000;
- if (this->FakeLoadForTesting) {
- milliseconds = 10;
- }
- if (this->TestLoadRetryTimer.get() == nullptr) {
- this->TestLoadRetryTimer.init(this->Loop, this);
- }
- this->TestLoadRetryTimer.start(
- &cmCTestMultiProcessHandler::OnTestLoadRetryCB, milliseconds, 0);
+ // Try again later when the load might be lower.
+ this->StartNextTestsOnTimer();
}
}
-void cmCTestMultiProcessHandler::OnTestLoadRetryCB(uv_timer_t* timer)
+void cmCTestMultiProcessHandler::StartNextTestsOnIdle()
{
- auto* self = static_cast<cmCTestMultiProcessHandler*>(timer->data);
- self->StartNextTests();
+ // Start more tests on the next loop iteration.
+ this->StartNextTestsOnIdle_.start([](uv_idle_t* idle) {
+ uv_idle_stop(idle);
+ auto* self = static_cast<cmCTestMultiProcessHandler*>(idle->data);
+ self->StartNextTests();
+ });
+}
+
+void cmCTestMultiProcessHandler::StartNextTestsOnTimer()
+{
+ // Wait between 1 and 5 seconds before trying again.
+ unsigned int const milliseconds = this->FakeLoadForTesting
+ ? 10
+ : (cmSystemTools::RandomSeed() % 5 + 1) * 1000;
+ this->StartNextTestsOnTimer_.start(
+ [](uv_timer_t* timer) {
+ uv_timer_stop(timer);
+ auto* self = static_cast<cmCTestMultiProcessHandler*>(timer->data);
+ self->StartNextTests();
+ },
+ milliseconds, 0);
}
void cmCTestMultiProcessHandler::FinishTestProcess(
@@ -657,26 +712,20 @@ void cmCTestMultiProcessHandler::FinishTestProcess(
this->Failed->push_back(properties->Name);
}
- for (auto& t : this->Tests) {
- t.second.erase(test);
+ for (auto& t : this->PendingTests) {
+ t.second.Depends.erase(test);
}
- this->TestFinishMap[test] = true;
- this->TestRunningMap[test] = false;
this->WriteCheckpoint(test);
this->DeallocateResources(test);
this->UnlockResources(test);
- this->RunningCount -= this->GetProcessorsUsed(test);
-
- for (auto p : properties->Affinity) {
- this->ProcessorsAvailable.insert(p);
- }
- properties->Affinity.clear();
runner.reset();
- if (started) {
- this->StartNextTests();
+
+ if (this->JobServerClient) {
+ this->JobServerClient->ReleaseToken();
}
+ this->StartNextTestsOnIdle();
}
void cmCTestMultiProcessHandler::UpdateCostData()
@@ -807,7 +856,7 @@ void cmCTestMultiProcessHandler::CreateTestCostList()
void cmCTestMultiProcessHandler::CreateParallelTestCostList()
{
- TestSet alreadySortedTests;
+ TestSet alreadyOrderedTests;
std::list<TestSet> priorityStack;
priorityStack.emplace_back();
@@ -815,11 +864,11 @@ void cmCTestMultiProcessHandler::CreateParallelTestCostList()
// In parallel test runs add previously failed tests to the front
// of the cost list and queue other tests for further sorting
- for (auto const& t : this->Tests) {
+ for (auto const& t : this->PendingTests) {
if (cm::contains(this->LastTestsFailed, this->Properties[t.first]->Name)) {
// If the test failed last time, it should be run first.
- this->SortedTests.push_back(t.first);
- alreadySortedTests.insert(t.first);
+ this->OrderedTests.push_back(t.first);
+ alreadyOrderedTests.insert(t.first);
} else {
topLevel.insert(t.first);
}
@@ -834,7 +883,7 @@ void cmCTestMultiProcessHandler::CreateParallelTestCostList()
TestSet& currentSet = priorityStack.back();
for (auto const& i : previousSet) {
- TestSet const& dependencies = this->Tests[i];
+ TestSet const& dependencies = this->PendingTests[i].Depends;
currentSet.insert(dependencies.begin(), dependencies.end());
}
@@ -855,9 +904,9 @@ void cmCTestMultiProcessHandler::CreateParallelTestCostList()
TestComparator(this));
for (auto const& j : sortedCopy) {
- if (!cm::contains(alreadySortedTests, j)) {
- this->SortedTests.push_back(j);
- alreadySortedTests.insert(j);
+ if (!cm::contains(alreadyOrderedTests, j)) {
+ this->OrderedTests.push_back(j);
+ alreadyOrderedTests.insert(j);
}
}
}
@@ -866,7 +915,7 @@ void cmCTestMultiProcessHandler::CreateParallelTestCostList()
void cmCTestMultiProcessHandler::GetAllTestDependencies(int test,
TestList& dependencies)
{
- TestSet const& dependencySet = this->Tests[test];
+ TestSet const& dependencySet = this->PendingTests[test].Depends;
for (int i : dependencySet) {
this->GetAllTestDependencies(i, dependencies);
dependencies.push_back(i);
@@ -877,17 +926,17 @@ void cmCTestMultiProcessHandler::CreateSerialTestCostList()
{
TestList presortedList;
- for (auto const& i : this->Tests) {
+ for (auto const& i : this->PendingTests) {
presortedList.push_back(i.first);
}
std::stable_sort(presortedList.begin(), presortedList.end(),
TestComparator(this));
- TestSet alreadySortedTests;
+ TestSet alreadyOrderedTests;
for (int test : presortedList) {
- if (cm::contains(alreadySortedTests, test)) {
+ if (cm::contains(alreadyOrderedTests, test)) {
continue;
}
@@ -895,14 +944,14 @@ void cmCTestMultiProcessHandler::CreateSerialTestCostList()
this->GetAllTestDependencies(test, dependencies);
for (int testDependency : dependencies) {
- if (!cm::contains(alreadySortedTests, testDependency)) {
- alreadySortedTests.insert(testDependency);
- this->SortedTests.push_back(testDependency);
+ if (!cm::contains(alreadyOrderedTests, testDependency)) {
+ alreadyOrderedTests.insert(testDependency);
+ this->OrderedTests.push_back(testDependency);
}
}
- alreadySortedTests.insert(test);
- this->SortedTests.push_back(test);
+ alreadyOrderedTests.insert(test);
+ this->OrderedTests.push_back(test);
}
}
@@ -1089,9 +1138,9 @@ static Json::Value DumpCTestProperties(
properties.append(DumpCTestProperty(
"REQUIRED_FILES", DumpToJsonArray(testProperties.RequiredFiles)));
}
- if (!testProperties.LockedResources.empty()) {
+ if (!testProperties.ProjectResources.empty()) {
properties.append(DumpCTestProperty(
- "RESOURCE_LOCK", DumpToJsonArray(testProperties.LockedResources)));
+ "RESOURCE_LOCK", DumpToJsonArray(testProperties.ProjectResources)));
}
if (testProperties.RunSerial) {
properties.append(
@@ -1259,9 +1308,7 @@ void cmCTestMultiProcessHandler::PrintOutputAsJson()
// Don't worry if this fails, we are only showing the test list, not
// running the tests
cmWorkingDirectory workdir(p.Directory);
- cmCTestRunTest testRun(*this);
- testRun.SetIndex(p.Index);
- testRun.SetTestProperties(&p);
+ cmCTestRunTest testRun(*this, p.Index);
testRun.ComputeArguments();
// Skip tests not available in this configuration.
@@ -1298,9 +1345,7 @@ void cmCTestMultiProcessHandler::PrintTestList()
// running the tests
cmWorkingDirectory workdir(p.Directory);
- cmCTestRunTest testRun(*this);
- testRun.SetIndex(p.Index);
- testRun.SetTestProperties(&p);
+ cmCTestRunTest testRun(*this, p.Index);
testRun.ComputeArguments(); // logs the command in verbose mode
if (!p.Labels.empty()) // print the labels
@@ -1394,17 +1439,17 @@ void cmCTestMultiProcessHandler::CheckResume()
void cmCTestMultiProcessHandler::RemoveTest(int index)
{
- this->EraseTest(index);
+ this->OrderedTests.erase(
+ std::find(this->OrderedTests.begin(), this->OrderedTests.end(), index));
+ this->PendingTests.erase(index);
this->Properties.erase(index);
- this->TestRunningMap[index] = false;
- this->TestFinishMap[index] = true;
this->Completed++;
}
int cmCTestMultiProcessHandler::FindMaxIndex()
{
int max = 0;
- for (auto const& i : this->Tests) {
+ for (auto const& i : this->PendingTests) {
if (i.first > max) {
max = i.first;
}
@@ -1418,7 +1463,7 @@ bool cmCTestMultiProcessHandler::CheckCycles()
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Checking test dependency graph..." << std::endl,
this->Quiet);
- for (auto const& it : this->Tests) {
+ for (auto const& it : this->PendingTests) {
// DFS from each element to itself
int root = it.first;
std::set<int> visited;
@@ -1428,7 +1473,7 @@ bool cmCTestMultiProcessHandler::CheckCycles()
int test = s.top();
s.pop();
if (visited.insert(test).second) {
- for (auto const& d : this->Tests[test]) {
+ for (auto const& d : this->PendingTests[test].Depends) {
if (d == root) {
// cycle exists
cmCTestLog(
diff --git a/Source/CTest/cmCTestMultiProcessHandler.h b/Source/CTest/cmCTestMultiProcessHandler.h
index 3b4e9c5..02589ca 100644
--- a/Source/CTest/cmCTestMultiProcessHandler.h
+++ b/Source/CTest/cmCTestMultiProcessHandler.h
@@ -5,6 +5,7 @@
#include "cmConfigure.h" // IWYU pragma: keep
#include <cstddef>
+#include <list>
#include <map>
#include <memory>
#include <set>
@@ -13,13 +14,12 @@
#include <cm/optional>
-#include <cm3p/uv.h>
-
#include "cmCTest.h"
#include "cmCTestResourceAllocator.h"
#include "cmCTestResourceSpec.h"
#include "cmCTestTestHandler.h"
#include "cmUVHandlePtr.h"
+#include "cmUVJobServerClient.h"
struct cmCTestBinPackerAllocation;
class cmCTestRunTest;
@@ -38,7 +38,11 @@ public:
struct TestSet : public std::set<int>
{
};
- struct TestMap : public std::map<int, TestSet>
+ struct TestInfo
+ {
+ TestSet Depends;
+ };
+ struct TestMap : public std::map<int, TestInfo>
{
};
struct TestList : public std::vector<int>
@@ -57,7 +61,7 @@ public:
cmCTestMultiProcessHandler();
virtual ~cmCTestMultiProcessHandler();
// Set the tests
- void SetTests(TestMap& tests, PropertiesMap& properties);
+ void SetTests(TestMap tests, PropertiesMap properties);
// Set the max number of tests that can be run at the same time.
void SetParallelLevel(size_t);
void SetTestLoad(unsigned long load);
@@ -99,14 +103,14 @@ public:
void SetQuiet(bool b) { this->Quiet = b; }
- void CheckResourcesAvailable();
+ void CheckResourceAvailability();
protected:
// Start the next test or tests as many as are allowed by
// ParallelLevel
void StartNextTests();
- bool StartTestProcess(int test);
- bool StartTest(int test);
+ void StartTestProcess(int test);
+ void StartTest(int test);
// Mark the checkpoint for the given test
void WriteCheckpoint(int index);
@@ -124,10 +128,10 @@ protected:
// Removes the checkpoint file
void MarkFinished();
- void EraseTest(int index);
void FinishTestProcess(std::unique_ptr<cmCTestRunTest> runner, bool started);
- static void OnTestLoadRetryCB(uv_timer_t* timer);
+ void StartNextTestsOnIdle();
+ void StartNextTestsOnTimer();
void RemoveTest(int index);
// Check if we need to resume an interrupted test set
@@ -143,21 +147,25 @@ protected:
bool CheckStopTimePassed();
void SetStopTimePassed();
+ void InitializeLoop();
+ void FinalizeLoop();
+
void LockResources(int index);
void UnlockResources(int index);
- enum class ResourceAllocationError
+ enum class ResourceAvailabilityError
{
NoResourceType,
InsufficientResources,
};
+ bool Complete();
bool AllocateResources(int index);
bool TryAllocateResources(
int index,
std::map<std::string, std::vector<cmCTestBinPackerAllocation>>&
allocations,
- std::map<std::string, ResourceAllocationError>* errors = nullptr);
+ std::map<std::string, ResourceAvailabilityError>* errors = nullptr);
void DeallocateResources(int index);
bool AllResourcesAvailable();
bool InitResourceAllocator(std::string& error);
@@ -170,9 +178,10 @@ protected:
cm::optional<std::size_t> ResourceSpecSetupTest;
bool HasInvalidGeneratedResourceSpec;
- // map from test number to set of depend tests
- TestMap Tests;
- TestList SortedTests;
+ // Tests pending selection to start. They may have dependencies.
+ TestMap PendingTests;
+ // List of pending test indexes, ordered by cost.
+ std::list<int> OrderedTests;
// Total number of tests we'll be running
size_t Total;
// Number of tests that are complete
@@ -183,25 +192,33 @@ protected:
bool StopTimePassed = false;
// list of test properties (indices concurrent to the test map)
PropertiesMap Properties;
- std::map<int, bool> TestRunningMap;
- std::map<int, bool> TestFinishMap;
std::map<int, std::string> TestOutput;
std::vector<std::string>* Passed;
std::vector<std::string>* Failed;
std::vector<std::string> LastTestsFailed;
- std::set<std::string> LockedResources;
+ std::set<std::string> ProjectResourcesLocked;
std::map<int,
std::vector<std::map<std::string, std::vector<ResourceAllocation>>>>
AllocatedResources;
- std::map<int, std::map<std::string, ResourceAllocationError>>
- ResourceAllocationErrors;
+ std::map<int, std::map<std::string, ResourceAvailabilityError>>
+ ResourceAvailabilityErrors;
cmCTestResourceAllocator ResourceAllocator;
std::vector<cmCTestTestHandler::cmCTestTestResult>* TestResults;
size_t ParallelLevel; // max number of process that can be run at once
+
+ // 'make' jobserver client. If connected, we acquire a token
+ // for each test before running its process.
+ cm::optional<cmUVJobServerClient> JobServerClient;
+ // List of tests that are queued to run when a token is available.
+ std::list<int> JobServerQueuedTests;
+ // Callback invoked when a token is received.
+ void JobServerReceivedToken();
+
unsigned long TestLoad;
unsigned long FakeLoadForTesting;
- uv_loop_t Loop;
- cm::uv_timer_ptr TestLoadRetryTimer;
+ cm::uv_loop_ptr Loop;
+ cm::uv_idle_ptr StartNextTestsOnIdle_;
+ cm::uv_timer_ptr StartNextTestsOnTimer_;
cmCTestTestHandler* TestHandler;
cmCTest* CTest;
bool HasCycles;
diff --git a/Source/CTest/cmCTestP4.cxx b/Source/CTest/cmCTestP4.cxx
index 5d71b84..20bd0ec 100644
--- a/Source/CTest/cmCTestP4.cxx
+++ b/Source/CTest/cmCTestP4.cxx
@@ -151,9 +151,9 @@ cmCTestP4::User cmCTestP4::GetUserData(const std::string& username)
if (it == this->Users.end()) {
std::vector<std::string> p4_users;
this->SetP4Options(p4_users);
- p4_users.push_back("users");
- p4_users.push_back("-m");
- p4_users.push_back("1");
+ p4_users.emplace_back("users");
+ p4_users.emplace_back("-m");
+ p4_users.emplace_back("1");
p4_users.push_back(username);
UserParser out(this, "users-out> ");
@@ -335,10 +335,10 @@ std::string cmCTestP4::GetWorkingRevision()
std::vector<std::string> p4_identify;
this->SetP4Options(p4_identify);
- p4_identify.push_back("changes");
- p4_identify.push_back("-m");
- p4_identify.push_back("1");
- p4_identify.push_back("-t");
+ p4_identify.emplace_back("changes");
+ p4_identify.emplace_back("-m");
+ p4_identify.emplace_back("1");
+ p4_identify.emplace_back("-t");
std::string source = this->SourceDirectory + "/...#have";
p4_identify.push_back(source);
@@ -403,7 +403,7 @@ bool cmCTestP4::LoadRevisions()
.append(",")
.append(this->NewRevision);
- p4_changes.push_back("changes");
+ p4_changes.emplace_back("changes");
p4_changes.push_back(range);
ChangesParser out(this, "p4_changes-out> ");
@@ -420,8 +420,8 @@ bool cmCTestP4::LoadRevisions()
std::vector<std::string> p4_describe;
for (std::string const& i : cmReverseRange(this->ChangeLists)) {
this->SetP4Options(p4_describe);
- p4_describe.push_back("describe");
- p4_describe.push_back("-s");
+ p4_describe.emplace_back("describe");
+ p4_describe.emplace_back("-s");
p4_describe.push_back(i);
DescribeParser outDescribe(this, "p4_describe-out> ");
@@ -436,10 +436,10 @@ bool cmCTestP4::LoadModifications()
std::vector<std::string> p4_diff;
this->SetP4Options(p4_diff);
- p4_diff.push_back("diff");
+ p4_diff.emplace_back("diff");
// Ideally we would use -Od but not all clients support it
- p4_diff.push_back("-dn");
+ p4_diff.emplace_back("-dn");
std::string source = this->SourceDirectory + "/...";
p4_diff.push_back(source);
@@ -480,7 +480,7 @@ bool cmCTestP4::UpdateImpl()
std::vector<std::string> p4_sync;
this->SetP4Options(p4_sync);
- p4_sync.push_back("sync");
+ p4_sync.emplace_back("sync");
// Get user-specified update options.
std::string opts = this->CTest->GetCTestConfiguration("UpdateOptions");
diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx
index 8ceb9db..483b3b4 100644
--- a/Source/CTest/cmCTestRunTest.cxx
+++ b/Source/CTest/cmCTestRunTest.cxx
@@ -25,13 +25,17 @@
#include "cmProcess.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
+#include "cmUVHandlePtr.h"
#include "cmWorkingDirectory.h"
-cmCTestRunTest::cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler)
+cmCTestRunTest::cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler,
+ int index)
: MultiTestHandler(multiHandler)
+ , Index(index)
+ , CTest(MultiTestHandler.CTest)
+ , TestHandler(MultiTestHandler.TestHandler)
+ , TestProperties(MultiTestHandler.Properties[Index])
{
- this->CTest = multiHandler.CTest;
- this->TestHandler = multiHandler.TestHandler;
}
void cmCTestRunTest::CheckOutput(std::string const& line)
@@ -161,7 +165,7 @@ cmCTestRunTest::EndTestResult cmCTestRunTest::EndTest(size_t completed,
reason = "Invalid resource spec file";
forceFail = true;
} else {
- this->MultiTestHandler.CheckResourcesAvailable();
+ this->MultiTestHandler.CheckResourceAvailability();
}
}
std::ostringstream outputStream;
@@ -526,7 +530,7 @@ std::string cmCTestRunTest::GetTestPrefix(size_t completed, size_t total) const
return outputStream.str();
}
-bool cmCTestRunTest::StartTest(std::unique_ptr<cmCTestRunTest> runner,
+void cmCTestRunTest::StartTest(std::unique_ptr<cmCTestRunTest> runner,
size_t completed, size_t total)
{
auto* testRun = runner.get();
@@ -535,10 +539,7 @@ bool cmCTestRunTest::StartTest(std::unique_ptr<cmCTestRunTest> runner,
if (!testRun->StartTest(completed, total)) {
testRun->FinalizeTest(false);
- return false;
}
-
- return true;
}
// Starts the execution of a test. Returns once it has started
@@ -887,7 +888,7 @@ bool cmCTestRunTest::ForkProcess()
this->TestResult.Environment.erase(this->TestResult.Environment.length() -
1);
- return this->TestProcess->StartProcess(this->MultiTestHandler.Loop,
+ return this->TestProcess->StartProcess(*this->MultiTestHandler.Loop,
&this->TestProperties->Affinity);
}
diff --git a/Source/CTest/cmCTestRunTest.h b/Source/CTest/cmCTestRunTest.h
index 34f23c4..71d0865 100644
--- a/Source/CTest/cmCTestRunTest.h
+++ b/Source/CTest/cmCTestRunTest.h
@@ -24,7 +24,7 @@
class cmCTestRunTest
{
public:
- explicit cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler);
+ explicit cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler, int index);
void SetNumberOfRuns(int n)
{
@@ -33,18 +33,12 @@ public:
}
void SetRepeatMode(cmCTest::Repeat r) { this->RepeatMode = r; }
- void SetTestProperties(cmCTestTestHandler::cmCTestTestProperties* prop)
- {
- this->TestProperties = prop;
- }
cmCTestTestHandler::cmCTestTestProperties* GetTestProperties()
{
return this->TestProperties;
}
- void SetIndex(int i) { this->Index = i; }
-
int GetIndex() { return this->Index; }
void AddFailedDependency(const std::string& failedTest)
@@ -62,7 +56,7 @@ public:
// Read and store output. Returns true if it must be called again.
void CheckOutput(std::string const& line);
- static bool StartTest(std::unique_ptr<cmCTestRunTest> runner,
+ static void StartTest(std::unique_ptr<cmCTestRunTest> runner,
size_t completed, size_t total);
static bool StartAgain(std::unique_ptr<cmCTestRunTest> runner,
size_t completed);
@@ -124,16 +118,15 @@ private:
// Returns "completed/total Test #Index: "
std::string GetTestPrefix(size_t completed, size_t total) const;
- cmCTestTestHandler::cmCTestTestProperties* TestProperties;
- // Pointer back to the "parent"; the handler that invoked this test run
- cmCTestTestHandler* TestHandler;
+ cmCTestMultiProcessHandler& MultiTestHandler;
+ int Index;
cmCTest* CTest;
+ cmCTestTestHandler* TestHandler;
+ cmCTestTestHandler::cmCTestTestProperties* TestProperties;
+
std::unique_ptr<cmProcess> TestProcess;
std::string ProcessOutput;
- // The test results
cmCTestTestHandler::cmCTestTestResult TestResult;
- cmCTestMultiProcessHandler& MultiTestHandler;
- int Index;
std::set<std::string> FailedDependencies;
std::string StartTime;
std::string ActualCommand;
diff --git a/Source/CTest/cmCTestSVN.cxx b/Source/CTest/cmCTestSVN.cxx
index 14bc510..fc7051c 100644
--- a/Source/CTest/cmCTestSVN.cxx
+++ b/Source/CTest/cmCTestSVN.cxx
@@ -34,7 +34,7 @@ cmCTestSVN::~cmCTestSVN() = default;
void cmCTestSVN::CleanupImpl()
{
std::vector<std::string> svn_cleanup;
- svn_cleanup.push_back("cleanup");
+ svn_cleanup.emplace_back("cleanup");
OutputLogger out(this->Log, "cleanup-out> ");
OutputLogger err(this->Log, "cleanup-err> ");
this->RunSVNCommand(svn_cleanup, &out, &err);
@@ -89,7 +89,7 @@ std::string cmCTestSVN::LoadInfo(SVNInfo& svninfo)
{
// Run "svn info" to get the repository info from the work tree.
std::vector<std::string> svn_info;
- svn_info.push_back("info");
+ svn_info.emplace_back("info");
svn_info.push_back(svninfo.LocalPath);
std::string rev;
InfoParser out(this, "info-out> ", rev, svninfo);
@@ -252,7 +252,7 @@ bool cmCTestSVN::UpdateImpl()
}
std::vector<std::string> svn_update;
- svn_update.push_back("update");
+ svn_update.emplace_back("update");
cm::append(svn_update, args);
UpdateParser out(this, "up-out> ");
@@ -270,7 +270,7 @@ bool cmCTestSVN::RunSVNCommand(std::vector<std::string> const& parameters,
std::vector<std::string> args;
args.push_back(this->CommandLineTool);
cm::append(args, parameters);
- args.push_back("--non-interactive");
+ args.emplace_back("--non-interactive");
std::string userOptions = this->CTest->GetCTestConfiguration("SVNOptions");
@@ -388,11 +388,11 @@ bool cmCTestSVN::LoadRevisions(SVNInfo& svninfo)
// Run "svn log" to get all global revisions of interest.
std::vector<std::string> svn_log;
- svn_log.push_back("log");
- svn_log.push_back("--xml");
- svn_log.push_back("-v");
- svn_log.push_back(revs.c_str());
- svn_log.push_back(svninfo.LocalPath.c_str());
+ svn_log.emplace_back("log");
+ svn_log.emplace_back("--xml");
+ svn_log.emplace_back("-v");
+ svn_log.emplace_back(revs);
+ svn_log.emplace_back(svninfo.LocalPath);
LogParser out(this, "log-out> ", svninfo);
OutputLogger err(this->Log, "log-err> ");
return this->RunSVNCommand(svn_log, &out, &err);
@@ -467,7 +467,7 @@ bool cmCTestSVN::LoadModifications()
{
// Run "svn status" which reports local modifications.
std::vector<std::string> svn_status;
- svn_status.push_back("status");
+ svn_status.emplace_back("status");
StatusParser out(this, "status-out> ");
OutputLogger err(this->Log, "status-err> ");
this->RunSVNCommand(svn_status, &out, &err);
@@ -529,7 +529,7 @@ bool cmCTestSVN::LoadRepositories()
// Run "svn status" to get the list of external repositories
std::vector<std::string> svn_status;
- svn_status.push_back("status");
+ svn_status.emplace_back("status");
ExternalParser out(this, "external-out> ");
OutputLogger err(this->Log, "external-err> ");
return this->RunSVNCommand(svn_status, &out, &err);
diff --git a/Source/CTest/cmCTestScriptHandler.cxx b/Source/CTest/cmCTestScriptHandler.cxx
index 48f8f6d..0beee67 100644
--- a/Source/CTest/cmCTestScriptHandler.cxx
+++ b/Source/CTest/cmCTestScriptHandler.cxx
@@ -672,9 +672,11 @@ int cmCTestScriptHandler::RunConfigurationDashboard()
// clear the binary directory?
if (this->EmptyBinDir) {
- if (!cmCTestScriptHandler::EmptyBinaryDirectory(this->BinaryDir)) {
+ std::string err;
+ if (!cmCTestScriptHandler::EmptyBinaryDirectory(this->BinaryDir, err)) {
cmCTestLog(this->CTest, ERROR_MESSAGE,
- "Problem removing the binary directory" << std::endl);
+ "Problem removing the binary directory ("
+ << err << "): " << this->BinaryDir << std::endl);
}
}
@@ -860,10 +862,12 @@ bool cmCTestScriptHandler::RunScript(cmCTest* ctest, cmMakefile* mf,
return true;
}
-bool cmCTestScriptHandler::EmptyBinaryDirectory(const std::string& sname)
+bool cmCTestScriptHandler::EmptyBinaryDirectory(const std::string& sname,
+ std::string& err)
{
// try to avoid deleting root
if (sname.size() < 2) {
+ err = "path too short";
return false;
}
@@ -876,20 +880,24 @@ bool cmCTestScriptHandler::EmptyBinaryDirectory(const std::string& sname)
std::string check = cmStrCat(sname, "/CMakeCache.txt");
if (!cmSystemTools::FileExists(check)) {
+ err = "path does not contain an existing CMakeCache.txt file";
return false;
}
+ cmsys::Status status;
for (int i = 0; i < 5; ++i) {
- if (TryToRemoveBinaryDirectoryOnce(sname)) {
+ status = TryToRemoveBinaryDirectoryOnce(sname);
+ if (status) {
return true;
}
cmSystemTools::Delay(100);
}
+ err = status.GetString();
return false;
}
-bool cmCTestScriptHandler::TryToRemoveBinaryDirectoryOnce(
+cmsys::Status cmCTestScriptHandler::TryToRemoveBinaryDirectoryOnce(
const std::string& directoryPath)
{
cmsys::Directory directory;
@@ -907,18 +915,18 @@ bool cmCTestScriptHandler::TryToRemoveBinaryDirectoryOnce(
bool isDirectory = cmSystemTools::FileIsDirectory(fullPath) &&
!cmSystemTools::FileIsSymlink(fullPath);
+ cmsys::Status status;
if (isDirectory) {
- if (!cmSystemTools::RemoveADirectory(fullPath)) {
- return false;
- }
+ status = cmSystemTools::RemoveADirectory(fullPath);
} else {
- if (!cmSystemTools::RemoveFile(fullPath)) {
- return false;
- }
+ status = cmSystemTools::RemoveFile(fullPath);
+ }
+ if (!status) {
+ return status;
}
}
- return static_cast<bool>(cmSystemTools::RemoveADirectory(directoryPath));
+ return cmSystemTools::RemoveADirectory(directoryPath);
}
cmDuration cmCTestScriptHandler::GetRemainingTimeAllowed()
diff --git a/Source/CTest/cmCTestScriptHandler.h b/Source/CTest/cmCTestScriptHandler.h
index b7764b2..8aa07e7 100644
--- a/Source/CTest/cmCTestScriptHandler.h
+++ b/Source/CTest/cmCTestScriptHandler.h
@@ -9,6 +9,8 @@
#include <string>
#include <vector>
+#include "cmsys/Status.hxx"
+
#include "cmCTestGenericHandler.h"
#include "cmDuration.h"
@@ -80,7 +82,7 @@ public:
/*
* Empty Binary Directory
*/
- static bool EmptyBinaryDirectory(const std::string& dir);
+ static bool EmptyBinaryDirectory(const std::string& dir, std::string& err);
/*
* Write an initial CMakeCache.txt from the given contents.
@@ -139,7 +141,8 @@ private:
std::unique_ptr<cmCTestCommand> command);
// Try to remove the binary directory once
- static bool TryToRemoveBinaryDirectoryOnce(const std::string& directoryPath);
+ static cmsys::Status TryToRemoveBinaryDirectoryOnce(
+ const std::string& directoryPath);
std::vector<std::string> ConfigurationScripts;
std::vector<bool> ScriptProcessScope;
diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx
index eb3b4dd..1918b2c 100644
--- a/Source/CTest/cmCTestTestHandler.cxx
+++ b/Source/CTest/cmCTestTestHandler.cxx
@@ -1381,15 +1381,15 @@ bool cmCTestTestHandler::ProcessDirectory(std::vector<std::string>& passed,
}
}
}
- tests[p.Index] = depends;
+ tests[p.Index].Depends = depends;
properties[p.Index] = &p;
}
parallel->SetResourceSpecFile(this->ResourceSpecFile);
- parallel->SetTests(tests, properties);
+ parallel->SetTests(std::move(tests), std::move(properties));
parallel->SetPassFailVectors(&passed, &failed);
this->TestResults.clear();
parallel->SetTestResults(&this->TestResults);
- parallel->CheckResourcesAvailable();
+ parallel->CheckResourceAvailability();
if (this->CTest->ShouldPrintLabels()) {
parallel->PrintLabels();
@@ -2233,7 +2233,7 @@ bool cmCTestTestHandler::SetTestsProperties(
} else if (key == "RESOURCE_LOCK"_s) {
cmList lval{ val };
- rt.LockedResources.insert(lval.begin(), lval.end());
+ rt.ProjectResources.insert(lval.begin(), lval.end());
} else if (key == "FIXTURES_SETUP"_s) {
cmList lval{ val };
diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h
index 23f0a76..b81fcd5 100644
--- a/Source/CTest/cmCTestTestHandler.h
+++ b/Source/CTest/cmCTestTestHandler.h
@@ -165,7 +165,7 @@ public:
std::vector<std::string> Environment;
std::vector<std::string> EnvironmentModification;
std::vector<std::string> Labels;
- std::set<std::string> LockedResources;
+ std::set<std::string> ProjectResources; // RESOURCE_LOCK
std::set<std::string> FixturesSetup;
std::set<std::string> FixturesCleanup;
std::set<std::string> FixturesRequired;
diff --git a/Source/CTest/cmUVJobServerClient.cxx b/Source/CTest/cmUVJobServerClient.cxx
new file mode 100644
index 0000000..d7d76c9
--- /dev/null
+++ b/Source/CTest/cmUVJobServerClient.cxx
@@ -0,0 +1,518 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmUVJobServerClient.h"
+
+#include <cassert>
+#include <utility>
+
+#ifndef _WIN32
+# include <cstdio>
+# include <string>
+# include <vector>
+
+# include <fcntl.h>
+# include <unistd.h>
+#endif
+
+#include <cm/memory>
+#include <cm/optional>
+#include <cm/string_view>
+
+#include "cmRange.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmUVHandlePtr.h"
+
+class cmUVJobServerClient::Impl
+{
+public:
+ uv_loop_t& Loop;
+
+ cm::uv_idle_ptr ImplicitToken;
+ std::function<void()> OnToken;
+ std::function<void(int)> OnDisconnect;
+
+ // The number of tokens held by this client.
+ unsigned int HeldTokens = 0;
+
+ // The number of tokens we need to receive from the job server.
+ unsigned int NeedTokens = 0;
+
+ Impl(uv_loop_t& loop);
+ virtual ~Impl();
+
+ virtual void SendToken() = 0;
+ virtual void StartReceivingTokens() = 0;
+ virtual void StopReceivingTokens() = 0;
+
+ void RequestToken();
+ void ReleaseToken();
+ void RequestExplicitToken();
+ void DecrementNeedTokens();
+ void HoldToken();
+ void RequestImplicitToken();
+ void ReleaseImplicitToken();
+ void ReceivedToken();
+ void Disconnected(int status);
+};
+
+cmUVJobServerClient::Impl::Impl(uv_loop_t& loop)
+ : Loop(loop)
+{
+ this->ImplicitToken.init(this->Loop, this);
+}
+
+cmUVJobServerClient::Impl::~Impl() = default;
+
+void cmUVJobServerClient::Impl::RequestToken()
+{
+ if (this->HeldTokens == 0 && !uv_is_active(this->ImplicitToken)) {
+ this->RequestImplicitToken();
+ } else {
+ this->RequestExplicitToken();
+ }
+}
+
+void cmUVJobServerClient::Impl::ReleaseToken()
+{
+ assert(this->HeldTokens > 0);
+ --this->HeldTokens;
+ if (this->HeldTokens == 0) {
+ // This was the token implicitly owned by our process.
+ this->ReleaseImplicitToken();
+ } else {
+ // This was a token we received from the job server. Send it back.
+ this->SendToken();
+ }
+}
+
+void cmUVJobServerClient::Impl::RequestExplicitToken()
+{
+ ++this->NeedTokens;
+ this->StartReceivingTokens();
+}
+
+void cmUVJobServerClient::Impl::DecrementNeedTokens()
+{
+ assert(this->NeedTokens > 0);
+ --this->NeedTokens;
+ if (this->NeedTokens == 0) {
+ this->StopReceivingTokens();
+ }
+}
+
+void cmUVJobServerClient::Impl::HoldToken()
+{
+ ++this->HeldTokens;
+ if (this->OnToken) {
+ this->OnToken();
+ } else {
+ this->ReleaseToken();
+ }
+}
+
+void cmUVJobServerClient::Impl::RequestImplicitToken()
+{
+ assert(this->HeldTokens == 0);
+ this->ImplicitToken.start([](uv_idle_t* handle) {
+ uv_idle_stop(handle);
+ auto* self = static_cast<Impl*>(handle->data);
+ self->HoldToken();
+ });
+}
+
+void cmUVJobServerClient::Impl::ReleaseImplicitToken()
+{
+ assert(this->HeldTokens == 0);
+ // Use the implicit token in place of receiving one from the job server.
+ if (this->NeedTokens > 0) {
+ this->DecrementNeedTokens();
+ this->RequestImplicitToken();
+ }
+}
+
+void cmUVJobServerClient::Impl::ReceivedToken()
+{
+ this->DecrementNeedTokens();
+ this->HoldToken();
+}
+
+void cmUVJobServerClient::Impl::Disconnected(int status)
+{
+ if (this->OnDisconnect) {
+ this->OnDisconnect(status);
+ }
+}
+
+//---------------------------------------------------------------------------
+// Implementation on POSIX platforms.
+// https://www.gnu.org/software/make/manual/html_node/POSIX-Jobserver.html
+
+#ifndef _WIN32
+namespace {
+class ImplPosix : public cmUVJobServerClient::Impl
+{
+public:
+ enum class Connection
+ {
+ None,
+ FDs,
+ FIFO,
+ };
+ Connection Conn = Connection::None;
+
+ cm::uv_pipe_ptr ConnRead;
+ cm::uv_pipe_ptr ConnWrite;
+ cm::uv_pipe_ptr ConnFIFO;
+
+ std::shared_ptr<std::function<void(int)>> OnWrite;
+
+ void Connect();
+ void ConnectFDs(int rfd, int wfd);
+ void ConnectFIFO(const char* path);
+ void Disconnect(int status);
+
+ cm::uv_pipe_ptr OpenFD(int fd);
+
+ uv_stream_t* GetWriter() const;
+ uv_stream_t* GetReader() const;
+
+ static void OnAllocateCB(uv_handle_t* handle, size_t suggested_size,
+ uv_buf_t* buf);
+ static void OnReadCB(uv_stream_t* stream, ssize_t nread,
+ const uv_buf_t* buf);
+
+ void OnAllocate(size_t suggested_size, uv_buf_t* buf);
+ void OnRead(ssize_t nread, const uv_buf_t* buf);
+
+ char ReadBuf = '.';
+
+ bool ReceivingTokens = false;
+
+ bool IsConnected() const;
+
+ void SendToken() override;
+ void StartReceivingTokens() override;
+ void StopReceivingTokens() override;
+
+ ImplPosix(uv_loop_t& loop);
+ ~ImplPosix() override;
+};
+
+ImplPosix::ImplPosix(uv_loop_t& loop)
+ : Impl(loop)
+ , OnWrite(std::make_shared<std::function<void(int)>>([this](int status) {
+ if (status != 0) {
+ this->Disconnect(status);
+ }
+ }))
+{
+ this->Connect();
+}
+
+ImplPosix::~ImplPosix()
+{
+ this->Disconnect(0);
+}
+
+void ImplPosix::Connect()
+{
+ // --jobserver-auth= for gnu make versions >= 4.2
+ // --jobserver-fds= for gnu make versions < 4.2
+ // -J for bsd make
+ static const std::vector<cm::string_view> prefixes = {
+ "--jobserver-auth=", "--jobserver-fds=", "-J"
+ };
+
+ cm::optional<std::string> makeflags = cmSystemTools::GetEnvVar("MAKEFLAGS");
+ if (!makeflags) {
+ return;
+ }
+
+ // Look for the *last* occurrence of jobserver flags.
+ cm::optional<std::string> auth;
+ std::vector<std::string> args;
+ cmSystemTools::ParseUnixCommandLine(makeflags->c_str(), args);
+ for (cm::string_view arg : cmReverseRange(args)) {
+ for (cm::string_view prefix : prefixes) {
+ if (cmHasPrefix(arg, prefix)) {
+ auth = cmTrimWhitespace(arg.substr(prefix.length()));
+ break;
+ }
+ }
+ if (auth) {
+ break;
+ }
+ }
+
+ if (!auth) {
+ return;
+ }
+
+ // fifo:PATH
+ if (cmHasLiteralPrefix(*auth, "fifo:")) {
+ ConnectFIFO(auth->substr(cmStrLen("fifo:")).c_str());
+ return;
+ }
+
+ // reader,writer
+ int reader;
+ int writer;
+ if (std::sscanf(auth->c_str(), "%d,%d", &reader, &writer) == 2) {
+ ConnectFDs(reader, writer);
+ }
+}
+
+cm::uv_pipe_ptr ImplPosix::OpenFD(int fd)
+{
+ // Create a CLOEXEC duplicate so `uv_pipe_ptr` can close it
+ // without closing the original file descriptor, which our
+ // child processes might want to use too.
+ cm::uv_pipe_ptr p;
+ int fd_dup = dup(fd);
+ if (fd_dup < 0) {
+ return p;
+ }
+ if (fcntl(fd_dup, F_SETFD, FD_CLOEXEC) == -1) {
+ close(fd_dup);
+ return p;
+ }
+ p.init(this->Loop, 0, this);
+ if (uv_pipe_open(p, fd_dup) < 0) {
+ close(fd_dup);
+ }
+ return p;
+}
+
+void ImplPosix::ConnectFDs(int rfd, int wfd)
+{
+ cm::uv_pipe_ptr connRead = this->OpenFD(rfd);
+ cm::uv_pipe_ptr connWrite = this->OpenFD(wfd);
+
+ // Verify that the read end is readable and the write end is writable.
+ if (!connRead || !uv_is_readable(connRead) || //
+ !connWrite || !uv_is_writable(connWrite)) {
+ return;
+ }
+
+ this->ConnRead = std::move(connRead);
+ this->ConnWrite = std::move(connWrite);
+ this->Conn = Connection::FDs;
+}
+
+void ImplPosix::ConnectFIFO(const char* path)
+{
+ int fd = open(path, O_RDWR);
+ if (fd < 0) {
+ return;
+ }
+
+ cm::uv_pipe_ptr connFIFO;
+ connFIFO.init(this->Loop, 0, this);
+ if (uv_pipe_open(connFIFO, fd) != 0) {
+ close(fd);
+ return;
+ }
+
+ // Verify that the fifo is both readable and writable.
+ if (!connFIFO || !uv_is_readable(connFIFO) || !uv_is_writable(connFIFO)) {
+ return;
+ }
+
+ this->ConnFIFO = std::move(connFIFO);
+ this->Conn = Connection::FIFO;
+}
+
+void ImplPosix::Disconnect(int status)
+{
+ if (this->Conn == Connection::None) {
+ return;
+ }
+
+ this->StopReceivingTokens();
+
+ switch (this->Conn) {
+ case Connection::FDs:
+ this->ConnRead.reset();
+ this->ConnWrite.reset();
+ break;
+ case Connection::FIFO:
+ this->ConnFIFO.reset();
+ break;
+ default:
+ break;
+ }
+
+ this->Conn = Connection::None;
+ if (status != 0) {
+ this->Disconnected(status);
+ }
+}
+
+uv_stream_t* ImplPosix::GetWriter() const
+{
+ switch (this->Conn) {
+ case Connection::FDs:
+ return this->ConnWrite;
+ case Connection::FIFO:
+ return this->ConnFIFO;
+ default:
+ return nullptr;
+ }
+}
+
+uv_stream_t* ImplPosix::GetReader() const
+{
+ switch (this->Conn) {
+ case Connection::FDs:
+ return this->ConnRead;
+ case Connection::FIFO:
+ return this->ConnFIFO;
+ default:
+ return nullptr;
+ }
+}
+
+void ImplPosix::OnAllocateCB(uv_handle_t* handle, size_t suggested_size,
+ uv_buf_t* buf)
+{
+ auto* self = static_cast<ImplPosix*>(handle->data);
+ self->OnAllocate(suggested_size, buf);
+}
+
+void ImplPosix::OnReadCB(uv_stream_t* stream, ssize_t nread,
+ const uv_buf_t* buf)
+{
+ auto* self = static_cast<ImplPosix*>(stream->data);
+ self->OnRead(nread, buf);
+}
+
+void ImplPosix::OnAllocate(size_t /*suggested_size*/, uv_buf_t* buf)
+{
+ *buf = uv_buf_init(&this->ReadBuf, 1);
+}
+
+void ImplPosix::OnRead(ssize_t nread, const uv_buf_t* /*buf*/)
+{
+ if (nread == 0) {
+ return;
+ }
+
+ if (nread < 0) {
+ auto status = static_cast<int>(nread);
+ this->Disconnect(status);
+ return;
+ }
+
+ assert(nread == 1);
+ this->ReceivedToken();
+}
+
+bool ImplPosix::IsConnected() const
+{
+ return this->Conn != Connection::None;
+}
+
+void ImplPosix::SendToken()
+{
+ if (this->Conn == Connection::None) {
+ return;
+ }
+
+ static char token = '.';
+
+ uv_buf_t const buf = uv_buf_init(&token, sizeof(token));
+ int status = cm::uv_write(this->GetWriter(), &buf, 1, this->OnWrite);
+ if (status != 0) {
+ this->Disconnect(status);
+ }
+}
+
+void ImplPosix::StartReceivingTokens()
+{
+ if (this->Conn == Connection::None) {
+ return;
+ }
+ if (this->ReceivingTokens) {
+ return;
+ }
+
+ int status = uv_read_start(this->GetReader(), &ImplPosix::OnAllocateCB,
+ &ImplPosix::OnReadCB);
+ if (status != 0) {
+ this->Disconnect(status);
+ return;
+ }
+
+ this->ReceivingTokens = true;
+}
+
+void ImplPosix::StopReceivingTokens()
+{
+ if (this->Conn == Connection::None) {
+ return;
+ }
+ if (!this->ReceivingTokens) {
+ return;
+ }
+
+ this->ReceivingTokens = false;
+ uv_read_stop(this->GetReader());
+}
+}
+#endif
+
+//---------------------------------------------------------------------------
+// Implementation of public interface.
+
+cmUVJobServerClient::cmUVJobServerClient(std::unique_ptr<Impl> impl)
+ : Impl_(std::move(impl))
+{
+}
+
+cmUVJobServerClient::~cmUVJobServerClient() = default;
+
+cmUVJobServerClient::cmUVJobServerClient(cmUVJobServerClient&&) noexcept =
+ default;
+cmUVJobServerClient& cmUVJobServerClient::operator=(
+ cmUVJobServerClient&&) noexcept = default;
+
+void cmUVJobServerClient::RequestToken()
+{
+ this->Impl_->RequestToken();
+}
+
+void cmUVJobServerClient::ReleaseToken()
+{
+ this->Impl_->ReleaseToken();
+}
+
+int cmUVJobServerClient::GetHeldTokens() const
+{
+ return this->Impl_->HeldTokens;
+}
+
+int cmUVJobServerClient::GetNeedTokens() const
+{
+ return this->Impl_->NeedTokens;
+}
+
+cm::optional<cmUVJobServerClient> cmUVJobServerClient::Connect(
+ uv_loop_t& loop, std::function<void()> onToken,
+ std::function<void(int)> onDisconnect)
+{
+#if defined(_WIN32)
+ // FIXME: Windows job server client not yet implemented.
+ static_cast<void>(loop);
+ static_cast<void>(onToken);
+ static_cast<void>(onDisconnect);
+#else
+ auto impl = cm::make_unique<ImplPosix>(loop);
+ if (impl && impl->IsConnected()) {
+ impl->OnToken = std::move(onToken);
+ impl->OnDisconnect = std::move(onDisconnect);
+ return cmUVJobServerClient(std::move(impl));
+ }
+#endif
+ return cm::nullopt;
+}
diff --git a/Source/CTest/cmUVJobServerClient.h b/Source/CTest/cmUVJobServerClient.h
new file mode 100644
index 0000000..bbb5f08
--- /dev/null
+++ b/Source/CTest/cmUVJobServerClient.h
@@ -0,0 +1,96 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <functional>
+#include <memory>
+
+#include <cm/optional>
+
+#include <cm3p/uv.h>
+
+/** \class cmUVJobServerClient
+ * \brief Job server client that can integrate with a libuv event loop.
+ *
+ * Use the \a Connect method to connect to an ambient job server as
+ * described by the MAKEFLAGS environment variable, if any. Request
+ * a token using the \a RequestToken method. The \a onToken callback
+ * will be invoked asynchronously when the token is received. Act
+ * on the token, and then use \a ReleaseToken to release it.
+ *
+ * The job server protocol states that a client process implicitly
+ * has one free token available, corresponding to the token its
+ * parent used to start it. \a cmUVJobServerClient will use the
+ * implicit token whenever it is available instead of requesting
+ * an explicit token from the job server. However, clients of
+ * this class must still request and receive the token before
+ * acting on it, and cannot assume that it is always held.
+ *
+ * If the job server connection breaks, \a onDisconnect will be
+ * called with the libuv error. No further tokens can be received
+ * from the job server, but progress can still be made serially
+ * using the implicit token.
+ */
+class cmUVJobServerClient
+{
+public:
+ class Impl;
+
+private:
+ std::unique_ptr<Impl> Impl_;
+
+ cmUVJobServerClient(std::unique_ptr<Impl> impl);
+
+public:
+ /**
+ * Disconnect from the job server.
+ */
+ ~cmUVJobServerClient();
+
+ cmUVJobServerClient(cmUVJobServerClient&&) noexcept;
+ cmUVJobServerClient(cmUVJobServerClient const&) = delete;
+ cmUVJobServerClient& operator=(cmUVJobServerClient&&) noexcept;
+ cmUVJobServerClient& operator=(cmUVJobServerClient const&) = delete;
+
+ /**
+ * Request a token from the job server.
+ * When the token is held, the \a onToken callback will be invoked.
+ */
+ void RequestToken();
+
+ /**
+ * Release a token to the job server.
+ * This may be called only after a corresponding \a onToken callback.
+ */
+ void ReleaseToken();
+
+ /**
+ * Get the number of implicit and explicit tokens currently held.
+ * This is the number of times \a onToken has been called but not
+ * yet followed by a call to \a ReleaseToken.
+ * This is meant for testing and debugging.
+ */
+ int GetHeldTokens() const;
+
+ /**
+ * Get the number of explicit tokens currently requested from the
+ * job server but not yet received. If the implicit token becomes
+ * available, it is used in place of a requested token, and this
+ * is decremented without receiving an explicit token.
+ * This is meant for testing and debugging.
+ */
+ int GetNeedTokens() const;
+
+ /**
+ * Connect to an ambient job server, if any.
+ * \param loop The libuv event loop on which to schedule events.
+ * \param onToken Function to call when a new token is held.
+ * \param onDisconnect Function to call on disconnect, with libuv error.
+ * \returns Connected instance, or cm::nullopt.
+ */
+ static cm::optional<cmUVJobServerClient> Connect(
+ uv_loop_t& loop, std::function<void()> onToken,
+ std::function<void(int)> onDisconnect);
+};
diff --git a/Source/Checks/Curses/CMakeLists.txt b/Source/Checks/Curses/CMakeLists.txt
index bc6b906..6f5f145 100644
--- a/Source/Checks/Curses/CMakeLists.txt
+++ b/Source/Checks/Curses/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.13...3.26 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.13...3.27 FATAL_ERROR)
project(CheckCurses C)
set(CURSES_NEED_NCURSES TRUE)
diff --git a/Source/Checks/Curses/CheckCurses.c b/Source/Checks/Curses/CheckCurses.c
index 7d827e6..3264fa0 100644
--- a/Source/Checks/Curses/CheckCurses.c
+++ b/Source/Checks/Curses/CheckCurses.c
@@ -8,7 +8,7 @@
# include <curses.h>
#endif
-int main()
+int main(void)
{
curses_version();
return 0;
diff --git a/Source/Checks/cm_cxx_filesystem.cxx b/Source/Checks/cm_cxx_filesystem.cxx
index c8df589..bdc7c6d 100644
--- a/Source/Checks/cm_cxx_filesystem.cxx
+++ b/Source/Checks/cm_cxx_filesystem.cxx
@@ -9,12 +9,13 @@ int main()
std::filesystem::path p0(L"/a/b/c");
std::filesystem::path p1("/a/b/c");
- std::filesystem::path p2("/a/b/c");
- if (p1 != p2) {
+ std::filesystem::path p2("/a/b//c");
+ if (p1 != p2.lexically_normal()) {
return 1;
}
#if defined(_WIN32)
+ // "//host/" is not preserved in some environments like GNU under MinGW.
std::filesystem::path p3("//host/a/b/../c");
if (p3.lexically_normal().generic_string() != "//host/a/c") {
return 1;
@@ -24,6 +25,12 @@ int main()
if (p4.lexically_normal().generic_string() != "c:/a/") {
return 1;
}
+
+ std::filesystem::path b1("C:\\path\\y\\..\\");
+ if (std::filesystem::weakly_canonical("\\\\?\\C:\\path\\x\\..") !=
+ b1.lexically_normal()) {
+ return 1;
+ }
#endif
// If std::string is copy-on-write, the std::filesystem::path
diff --git a/Source/Modules/CMakeBuildUtilities.cmake b/Source/Modules/CMakeBuildUtilities.cmake
index 21f04e6..4e7f0fe 100644
--- a/Source/Modules/CMakeBuildUtilities.cmake
+++ b/Source/Modules/CMakeBuildUtilities.cmake
@@ -287,6 +287,8 @@ else()
set(ENABLE_CPIO_SHARED OFF)
set(ENABLE_CAT OFF)
set(ENABLE_CAT_SHARED OFF)
+ set(ENABLE_UNZIP OFF)
+ set(ENABLE_UNZIP_SHARED OFF)
set(ENABLE_XATTR OFF)
set(ENABLE_ACL OFF)
set(ENABLE_ICONV OFF)
diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx
index b1589ff..ea97287 100644
--- a/Source/cmAddCustomCommandCommand.cxx
+++ b/Source/cmAddCustomCommandCommand.cxx
@@ -189,8 +189,8 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
} else if (copy == keyDEPFILE) {
doing = doing_depfile;
if (!mf.GetGlobalGenerator()->SupportsCustomCommandDepfile()) {
- status.SetError("Option DEPFILE not supported by " +
- mf.GetGlobalGenerator()->GetName());
+ status.SetError(cmStrCat("Option DEPFILE not supported by ",
+ mf.GetGlobalGenerator()->GetName()));
return false;
}
} else if (copy == keyJOB_POOL) {
diff --git a/Source/cmCMakeHostSystemInformationCommand.cxx b/Source/cmCMakeHostSystemInformationCommand.cxx
index 0efb9a4..699e23b 100644
--- a/Source/cmCMakeHostSystemInformationCommand.cxx
+++ b/Source/cmCMakeHostSystemInformationCommand.cxx
@@ -270,7 +270,7 @@ std::map<std::string, std::string> GetOSReleaseVariables(
std::map<std::string, std::string> data;
// Based on
- // https://www.freedesktop.org/software/systemd/man/os-release.html
+ // https://www.freedesktop.org/software/systemd/man/latest/os-release.html
for (auto name : { "/etc/os-release"_s, "/usr/lib/os-release"_s }) {
const auto& filename = cmStrCat(sysroot, name);
if (cmSystemTools::FileExists(filename)) {
diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx
index 320c57c..f4b26f3 100644
--- a/Source/cmComputeLinkDepends.cxx
+++ b/Source/cmComputeLinkDepends.cxx
@@ -16,6 +16,8 @@
#include <cm/string_view>
#include <cmext/string_view>
+#include "cmsys/RegularExpression.hxx"
+
#include "cmComputeComponentGraph.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorExpressionDAGChecker.h"
@@ -26,6 +28,7 @@
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
+#include "cmPolicies.h"
#include "cmRange.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
@@ -184,15 +187,6 @@ items that we know the linker will reuse automatically (shared libs).
namespace {
// LINK_LIBRARY helpers
-const auto LL_BEGIN = "<LINK_LIBRARY:"_s;
-const auto LL_END = "</LINK_LIBRARY:"_s;
-
-inline std::string ExtractFeature(std::string const& item)
-{
- return item.substr(LL_BEGIN.length(),
- item.find('>', LL_BEGIN.length()) - LL_BEGIN.length());
-}
-
bool IsFeatureSupported(cmMakefile* makefile, std::string const& linkLanguage,
std::string const& feature)
{
@@ -231,9 +225,208 @@ bool IsGroupFeatureSupported(cmMakefile* makefile,
cmStrCat("CMAKE_LINK_GROUP_USING_", feature, "_SUPPORTED");
return makefile->GetDefinition(featureSupported).IsOn();
}
+
+class EntriesProcessing
+{
+public:
+ using LinkEntry = cmComputeLinkDepends::LinkEntry;
+ using EntryVector = cmComputeLinkDepends::EntryVector;
+
+ EntriesProcessing(const cmGeneratorTarget* target,
+ const std::string& linkLanguage, EntryVector& entries,
+ EntryVector& finalEntries)
+ : Entries(entries)
+ , FinalEntries(finalEntries)
+ {
+ const auto* makefile = target->Makefile;
+
+ switch (target->GetPolicyStatusCMP0156()) {
+ case cmPolicies::WARN:
+ if (!makefile->GetCMakeInstance()->GetIsInTryCompile() &&
+ makefile->PolicyOptionalWarningEnabled(
+ "CMAKE_POLICY_WARNING_CMP0156")) {
+ makefile->GetCMakeInstance()->IssueMessage(
+ MessageType::AUTHOR_WARNING,
+ cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0156),
+ "\nSince the policy is not set, legacy libraries "
+ "de-duplication strategy will be applied."),
+ target->GetBacktrace());
+ }
+ CM_FALLTHROUGH;
+ case cmPolicies::OLD:
+ // rely on default initialization of the class
+ break;
+ case cmPolicies::REQUIRED_IF_USED:
+ case cmPolicies::REQUIRED_ALWAYS:
+ makefile->GetCMakeInstance()->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0156),
+ target->GetBacktrace());
+ CM_FALLTHROUGH;
+ case cmPolicies::NEW: {
+ if (auto libProcessing = makefile->GetDefinition(cmStrCat(
+ "CMAKE_", linkLanguage, "_LINK_LIBRARIES_PROCESSING"))) {
+ cmsys::RegularExpression processingOption{
+ "^(ORDER|UNICITY)=(FORWARD|REVERSE|ALL|NONE|SHARED)$"
+ };
+ std::string errorMessage;
+ for (auto const& option : cmList{ libProcessing }) {
+ if (processingOption.find(option)) {
+ if (processingOption.match(1) == "ORDER") {
+ if (processingOption.match(2) == "FORWARD") {
+ this->Order = Forward;
+ } else if (processingOption.match(2) == "REVERSE") {
+ this->Order = Reverse;
+ } else {
+ errorMessage += cmStrCat(" ", option, '\n');
+ }
+ } else if (processingOption.match(1) == "UNICITY") {
+ if (processingOption.match(2) == "ALL") {
+ this->Unicity = All;
+ } else if (processingOption.match(2) == "NONE") {
+ this->Unicity = None;
+ } else if (processingOption.match(2) == "SHARED") {
+ this->Unicity = Shared;
+ } else {
+ errorMessage += cmStrCat(" ", option, '\n');
+ }
+ }
+ } else {
+ errorMessage += cmStrCat(" ", option, '\n');
+ }
+ }
+ if (!errorMessage.empty()) {
+ makefile->GetCMakeInstance()->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("Erroneous option(s) for 'CMAKE_", linkLanguage,
+ "_LINK_LIBRARIES_PROCESSING':\n", errorMessage),
+ target->GetBacktrace());
+ }
+ }
+ }
+ }
+ }
+
+ void AddGroups(const std::map<size_t, std::vector<size_t>>& groups)
+ {
+ if (!groups.empty()) {
+ this->Groups = &groups;
+ // record all libraries as part of groups to ensure correct
+ // deduplication: libraries as part of groups are always kept.
+ for (const auto& group : groups) {
+ for (auto index : group.second) {
+ this->Emitted.insert(index);
+ }
+ }
+ }
+ }
+
+ void AddLibraries(const std::vector<size_t>& libEntries)
+ {
+ if (this->Order == Reverse) {
+ // Iterate in reverse order so we can keep only the last occurrence
+ // of a library.
+ this->AddLibraries(cmReverseRange(libEntries));
+ } else {
+ this->AddLibraries(cmMakeRange(libEntries));
+ }
+ }
+
+ void AddObjects(const std::vector<size_t>& objectEntries)
+ {
+ // Place explicitly linked object files in the front. The linker will
+ // always use them anyway, and they may depend on symbols from libraries.
+ if (this->Order == Reverse) {
+ // Append in reverse order at the end since we reverse the final order.
+ for (auto index : cmReverseRange(objectEntries)) {
+ this->FinalEntries.emplace_back(this->Entries[index]);
+ }
+ } else {
+ // Append in reverse order to ensure correct final order
+ for (auto index : cmReverseRange(objectEntries)) {
+ this->FinalEntries.emplace(this->FinalEntries.begin(),
+ this->Entries[index]);
+ }
+ }
+ }
+
+ void Finalize()
+ {
+ if (this->Order == Reverse) {
+ // Reverse the resulting order since we iterated in reverse.
+ std::reverse(this->FinalEntries.begin(), this->FinalEntries.end());
+ }
+
+ // expand groups
+ if (this->Groups != nullptr) {
+ for (const auto& group : *this->Groups) {
+ const LinkEntry& groupEntry = this->Entries[group.first];
+ auto it = this->FinalEntries.begin();
+ while (true) {
+ it = std::find_if(it, this->FinalEntries.end(),
+ [&groupEntry](const LinkEntry& entry) -> bool {
+ return groupEntry.Item == entry.Item;
+ });
+ if (it == this->FinalEntries.end()) {
+ break;
+ }
+ it->Item.Value = "</LINK_GROUP>";
+ for (auto index = group.second.rbegin();
+ index != group.second.rend(); ++index) {
+ it = this->FinalEntries.insert(it, this->Entries[*index]);
+ }
+ it = this->FinalEntries.insert(it, groupEntry);
+ it->Item.Value = "<LINK_GROUP>";
+ }
+ }
+ }
+ }
+
+private:
+ enum OrderKind
+ {
+ Forward,
+ Reverse
+ };
+
+ enum UnicityKind
+ {
+ None,
+ Shared,
+ All
+ };
+
+ bool IncludeEntry(LinkEntry const& entry) const
+ {
+ return this->Unicity == None ||
+ (this->Unicity == Shared &&
+ (entry.Target == nullptr ||
+ entry.Target->GetType() != cmStateEnums::SHARED_LIBRARY)) ||
+ (this->Unicity == All && entry.Kind != LinkEntry::Library);
+ }
+
+ template <typename Range>
+ void AddLibraries(const Range& libEntries)
+ {
+ for (auto index : libEntries) {
+ LinkEntry const& entry = this->Entries[index];
+ if (this->IncludeEntry(entry) || this->Emitted.insert(index).second) {
+ this->FinalEntries.emplace_back(entry);
+ }
+ }
+ }
+
+ OrderKind Order = Reverse;
+ UnicityKind Unicity = Shared;
+ EntryVector& Entries;
+ EntryVector& FinalEntries;
+ std::set<size_t> Emitted;
+ const std::map<size_t, std::vector<size_t>>* Groups = nullptr;
+};
}
-const std::string cmComputeLinkDepends::LinkEntry::DEFAULT = "DEFAULT";
+std::string const& cmComputeLinkDepends::LinkEntry::DEFAULT =
+ cmLinkItem::DEFAULT;
cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target,
const std::string& config,
@@ -376,49 +569,14 @@ cmComputeLinkDepends::Compute()
this->OrderLinkEntries();
// Compute the final set of link entries.
- // Iterate in reverse order so we can keep only the last occurrence
- // of a shared library.
- std::set<size_t> emitted;
- for (size_t i : cmReverseRange(this->FinalLinkOrder)) {
- LinkEntry const& e = this->EntryList[i];
- cmGeneratorTarget const* t = e.Target;
- // Entries that we know the linker will reuse do not need to be repeated.
- bool uniquify = t && t->GetType() == cmStateEnums::SHARED_LIBRARY;
- if (!uniquify || emitted.insert(i).second) {
- this->FinalLinkEntries.push_back(e);
- }
- }
- // Place explicitly linked object files in the front. The linker will
- // always use them anyway, and they may depend on symbols from libraries.
- // Append in reverse order since we reverse the final order below.
- for (size_t i : cmReverseRange(this->ObjectEntries)) {
- this->FinalLinkEntries.emplace_back(this->EntryList[i]);
- }
- // Reverse the resulting order since we iterated in reverse.
- std::reverse(this->FinalLinkEntries.begin(), this->FinalLinkEntries.end());
-
- // Expand group items
- if (!this->GroupItems.empty()) {
- for (const auto& group : this->GroupItems) {
- const LinkEntry& groupEntry = this->EntryList[group.first];
- auto it = this->FinalLinkEntries.begin();
- while (true) {
- it = std::find_if(it, this->FinalLinkEntries.end(),
- [&groupEntry](const LinkEntry& entry) -> bool {
- return groupEntry.Item == entry.Item;
- });
- if (it == this->FinalLinkEntries.end()) {
- break;
- }
- it->Item.Value = "</LINK_GROUP>";
- for (auto i = group.second.rbegin(); i != group.second.rend(); ++i) {
- it = this->FinalLinkEntries.insert(it, this->EntryList[*i]);
- }
- it = this->FinalLinkEntries.insert(it, groupEntry);
- it->Item.Value = "<LINK_GROUP>";
- }
- }
- }
+ EntriesProcessing entriesProcessing{ this->Target, this->LinkLanguage,
+ this->EntryList,
+ this->FinalLinkEntries };
+ // Add groups first, to ensure that libraries of the groups are always kept.
+ entriesProcessing.AddGroups(this->GroupItems);
+ entriesProcessing.AddLibraries(this->FinalLinkOrder);
+ entriesProcessing.AddObjects(this->ObjectEntries);
+ entriesProcessing.Finalize();
// Display the final set.
if (this->DebugMode) {
@@ -466,10 +624,12 @@ std::pair<size_t, bool> cmComputeLinkDepends::AddLinkEntry(
LinkEntry& entry = this->EntryList[index];
entry.Item = BT<std::string>(item.AsStr(), item.Backtrace);
entry.Target = item.Target;
+ entry.Feature = item.Feature;
if (!entry.Target && entry.Item.Value[0] == '-' &&
entry.Item.Value[1] != 'l' &&
entry.Item.Value.substr(0, 10) != "-framework") {
entry.Kind = LinkEntry::Flag;
+ entry.Feature = LinkEntry::DEFAULT;
} else if (cmHasPrefix(entry.Item.Value, LG_BEGIN) &&
cmHasSuffix(entry.Item.Value, '>')) {
entry.Kind = LinkEntry::Group;
@@ -710,7 +870,6 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index,
{
// Track inferred dependency sets implied by this list.
std::map<size_t, DependSet> dependSets;
- std::string feature = LinkEntry::DEFAULT;
bool inGroup = false;
std::pair<size_t, bool> groupIndex{
@@ -727,34 +886,27 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index,
continue;
}
- if (cmHasPrefix(item.AsStr(), LL_BEGIN) &&
- cmHasSuffix(item.AsStr(), '>')) {
- feature = ExtractFeature(item.AsStr());
- // emit a warning if an undefined feature is used as part of
- // an imported target
- if (depender_index != cmComputeComponentGraph::INVALID_COMPONENT) {
- const auto& depender = this->EntryList[depender_index];
- if (depender.Target != nullptr && depender.Target->IsImported() &&
- !IsFeatureSupported(this->Makefile, this->LinkLanguage, feature)) {
- this->CMakeInstance->IssueMessage(
- MessageType::AUTHOR_ERROR,
- cmStrCat("The 'IMPORTED' target '", depender.Target->GetName(),
- "' uses the generator-expression '$<LINK_LIBRARY>' with "
- "the feature '",
- feature,
- "', which is undefined or unsupported.\nDid you miss to "
- "define it by setting variables \"CMAKE_",
- this->LinkLanguage, "_LINK_LIBRARY_USING_", feature,
- "\" and \"CMAKE_", this->LinkLanguage,
- "_LINK_LIBRARY_USING_", feature, "_SUPPORTED\"?"),
- this->Target->GetBacktrace());
- }
+ // emit a warning if an undefined feature is used as part of
+ // an imported target
+ if (item.Feature != LinkEntry::DEFAULT &&
+ depender_index != cmComputeComponentGraph::INVALID_COMPONENT) {
+ const auto& depender = this->EntryList[depender_index];
+ if (depender.Target != nullptr && depender.Target->IsImported() &&
+ !IsFeatureSupported(this->Makefile, this->LinkLanguage,
+ item.Feature)) {
+ this->CMakeInstance->IssueMessage(
+ MessageType::AUTHOR_ERROR,
+ cmStrCat("The 'IMPORTED' target '", depender.Target->GetName(),
+ "' uses the generator-expression '$<LINK_LIBRARY>' with "
+ "the feature '",
+ item.Feature,
+ "', which is undefined or unsupported.\nDid you miss to "
+ "define it by setting variables \"CMAKE_",
+ this->LinkLanguage, "_LINK_LIBRARY_USING_", item.Feature,
+ "\" and \"CMAKE_", this->LinkLanguage,
+ "_LINK_LIBRARY_USING_", item.Feature, "_SUPPORTED\"?"),
+ this->Target->GetBacktrace());
}
- continue;
- }
- if (cmHasPrefix(item.AsStr(), LL_END) && cmHasSuffix(item.AsStr(), '>')) {
- feature = LinkEntry::DEFAULT;
- continue;
}
if (cmHasPrefix(item.AsStr(), LG_BEGIN) &&
@@ -815,7 +967,7 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index,
dependee_index = ale.first;
LinkEntry& entry = this->EntryList[dependee_index];
auto const& itemFeature =
- this->GetCurrentFeature(entry.Item.Value, feature);
+ this->GetCurrentFeature(entry.Item.Value, item.Feature);
if (inGroup && ale.second && entry.Target != nullptr &&
(entry.Target->GetType() == cmStateEnums::TargetType::OBJECT_LIBRARY ||
entry.Target->GetType() ==
@@ -834,30 +986,27 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index,
" library '", entry.Item.Value, "'."),
this->Target->GetBacktrace());
}
- if (itemFeature != LinkEntry::DEFAULT) {
- if (ale.second) {
- // current item not yet defined
- if (entry.Target != nullptr &&
- (entry.Target->GetType() ==
- cmStateEnums::TargetType::OBJECT_LIBRARY ||
- entry.Target->GetType() ==
- cmStateEnums::TargetType::INTERFACE_LIBRARY)) {
- this->CMakeInstance->IssueMessage(
- MessageType::AUTHOR_WARNING,
- cmStrCat("The feature '", feature,
- "', specified as part of a generator-expression "
- "'$",
- LL_BEGIN, feature, ">', will not be applied to the ",
- (entry.Target->GetType() ==
- cmStateEnums::TargetType::OBJECT_LIBRARY
- ? "OBJECT"
- : "INTERFACE"),
- " library '", entry.Item.Value, "'."),
- this->Target->GetBacktrace());
- } else {
- entry.Feature = itemFeature;
- }
+ if (ale.second) {
+ // current item not yet defined
+ if (itemFeature != LinkEntry::DEFAULT && entry.Target != nullptr &&
+ (entry.Target->GetType() ==
+ cmStateEnums::TargetType::OBJECT_LIBRARY ||
+ entry.Target->GetType() ==
+ cmStateEnums::TargetType::INTERFACE_LIBRARY)) {
+ this->CMakeInstance->IssueMessage(
+ MessageType::AUTHOR_WARNING,
+ cmStrCat("The feature '", itemFeature,
+ "', specified as part of a generator-expression "
+ "'$<LINK_LIBRARY:",
+ itemFeature, ">', will not be applied to the ",
+ (entry.Target->GetType() ==
+ cmStateEnums::TargetType::OBJECT_LIBRARY
+ ? "OBJECT"
+ : "INTERFACE"),
+ " library '", entry.Item.Value, "'."),
+ this->Target->GetBacktrace());
}
+ entry.Feature = itemFeature;
}
bool supportedItem = entry.Target == nullptr ||
diff --git a/Source/cmComputeLinkDepends.h b/Source/cmComputeLinkDepends.h
index 3233217..66daa07 100644
--- a/Source/cmComputeLinkDepends.h
+++ b/Source/cmComputeLinkDepends.h
@@ -49,7 +49,7 @@ public:
{
}
- static const std::string DEFAULT;
+ static std::string const& DEFAULT;
enum EntryKind
{
diff --git a/Source/cmConditionEvaluator.cxx b/Source/cmConditionEvaluator.cxx
index 6f9f541..eba4c57 100644
--- a/Source/cmConditionEvaluator.cxx
+++ b/Source/cmConditionEvaluator.cxx
@@ -33,6 +33,9 @@ auto const keyCOMMAND = "COMMAND"_s;
auto const keyDEFINED = "DEFINED"_s;
auto const keyEQUAL = "EQUAL"_s;
auto const keyEXISTS = "EXISTS"_s;
+auto const keyIS_READABLE = "IS_READABLE"_s;
+auto const keyIS_WRITABLE = "IS_WRITABLE"_s;
+auto const keyIS_EXECUTABLE = "IS_EXECUTABLE"_s;
auto const keyGREATER = "GREATER"_s;
auto const keyGREATER_EQUAL = "GREATER_EQUAL"_s;
auto const keyIN_LIST = "IN_LIST"_s;
@@ -568,6 +571,24 @@ bool cmConditionEvaluator::HandleLevel1(cmArgumentList& newArgs, std::string&,
newArgs.ReduceOneArg(cmSystemTools::FileExists(args.next->GetValue()),
args);
}
+ // check if a file is readable
+ else if (this->IsKeyword(keyIS_READABLE, *args.current)) {
+ newArgs.ReduceOneArg(cmSystemTools::TestFileAccess(
+ args.next->GetValue(), cmsys::TEST_FILE_READ),
+ args);
+ }
+ // check if a file is writable
+ else if (this->IsKeyword(keyIS_WRITABLE, *args.current)) {
+ newArgs.ReduceOneArg(cmSystemTools::TestFileAccess(
+ args.next->GetValue(), cmsys::TEST_FILE_WRITE),
+ args);
+ }
+ // check if a file is executable
+ else if (this->IsKeyword(keyIS_EXECUTABLE, *args.current)) {
+ newArgs.ReduceOneArg(cmSystemTools::TestFileAccess(
+ args.next->GetValue(), cmsys::TEST_FILE_EXECUTE),
+ args);
+ }
// does a directory with this name exist
else if (this->IsKeyword(keyIS_DIRECTORY, *args.current)) {
newArgs.ReduceOneArg(
diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx
index 8c84ace..a4f36cc 100644
--- a/Source/cmCoreTryCompile.cxx
+++ b/Source/cmCoreTryCompile.cxx
@@ -14,6 +14,7 @@
#include "cmsys/Directory.hxx"
#include "cmsys/FStream.hxx"
+#include "cmsys/RegularExpression.hxx"
#include "cmArgumentParser.h"
#include "cmConfigureLog.h"
@@ -83,6 +84,7 @@ std::string const kCMAKE_HIP_PLATFORM = "CMAKE_HIP_PLATFORM";
std::string const kCMAKE_HIP_RUNTIME_LIBRARY = "CMAKE_HIP_RUNTIME_LIBRARY";
std::string const kCMAKE_ISPC_INSTRUCTION_SETS = "CMAKE_ISPC_INSTRUCTION_SETS";
std::string const kCMAKE_ISPC_HEADER_SUFFIX = "CMAKE_ISPC_HEADER_SUFFIX";
+std::string const kCMAKE_LINKER_TYPE = "CMAKE_LINKER_TYPE";
std::string const kCMAKE_LINK_SEARCH_END_STATIC =
"CMAKE_LINK_SEARCH_END_STATIC";
std::string const kCMAKE_LINK_SEARCH_START_STATIC =
@@ -176,6 +178,7 @@ auto const TryCompileBaseSourcesArgParser =
ArgumentParser::ExpectAtLeast{ 0 })
.Bind("LINK_LIBRARIES"_s, &Arguments::LinkLibraries)
.Bind("LINK_OPTIONS"_s, &Arguments::LinkOptions)
+ .Bind("LINKER_LANGUAGE"_s, &Arguments::LinkerLanguage)
.Bind("COPY_FILE"_s, &Arguments::CopyFileTo)
.Bind("COPY_FILE_ERROR"_s, &Arguments::CopyFileError)
.BIND_LANG_PROPS(C)
@@ -852,8 +855,30 @@ cm::optional<cmTryCompileResult> cmCoreTryCompile::TryCompileCode(
fclose(fout);
return cm::nullopt;
}
- fprintf(fout, "\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/%s\")\n\n",
+ fprintf(fout, "\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/%s\")\n",
fname.c_str());
+ // Create all relevant alias targets
+ if (arguments.LinkLibraries) {
+ const auto& aliasTargets = this->Makefile->GetAliasTargets();
+ for (std::string const& i : *arguments.LinkLibraries) {
+ auto alias = aliasTargets.find(i);
+ if (alias != aliasTargets.end()) {
+ const auto& aliasTarget =
+ this->Makefile->FindTargetToUse(alias->second);
+ // Create equivalent library/executable alias
+ if (aliasTarget->GetType() == cmStateEnums::EXECUTABLE) {
+ fprintf(fout, "add_executable(\"%s\" ALIAS \"%s\")\n", i.c_str(),
+ alias->second.c_str());
+ } else {
+ // Other cases like UTILITY and GLOBAL_TARGET are excluded when
+ // arguments.LinkLibraries is initially parsed in this function.
+ fprintf(fout, "add_library(\"%s\" ALIAS \"%s\")\n", i.c_str(),
+ alias->second.c_str());
+ }
+ }
+ }
+ }
+ fprintf(fout, "\n");
}
/* Set the appropriate policy information for ENABLE_EXPORTS */
@@ -877,6 +902,14 @@ cm::optional<cmTryCompileResult> cmCoreTryCompile::TryCompileCode(
? "NEW"
: "OLD");
+ /* Set the appropriate policy information for Swift compilation mode */
+ fprintf(
+ fout, "cmake_policy(SET CMP0157 %s)\n",
+ this->Makefile->GetDefinition("CMAKE_Swift_COMPILATION_MODE_DEFAULT")
+ .IsEmpty()
+ ? "OLD"
+ : "NEW");
+
// Workaround for -Wl,-headerpad_max_install_names issue until we can avoid
// adding that flag in the platform and compiler language files
fprintf(fout,
@@ -1041,6 +1074,19 @@ cm::optional<cmTryCompileResult> cmCoreTryCompile::TryCompileCode(
}
}
+ if (arguments.LinkerLanguage) {
+ std::string LinkerLanguage = *arguments.LinkerLanguage;
+ if (testLangs.find(LinkerLanguage) == testLangs.end()) {
+ this->Makefile->IssueMessage(
+ MessageType::FATAL_ERROR,
+ "Linker language '" + LinkerLanguage +
+ "' must be enabled in project(LANGUAGES).");
+ }
+
+ fprintf(fout, "set_property(TARGET %s PROPERTY LINKER_LANGUAGE %s)\n",
+ targetName.c_str(), LinkerLanguage.c_str());
+ }
+
if (arguments.LinkLibraries) {
std::string libsToLink = " ";
for (std::string const& i : *arguments.LinkLibraries) {
@@ -1112,6 +1158,20 @@ cm::optional<cmTryCompileResult> cmCoreTryCompile::TryCompileCode(
vars.insert(varList.begin(), varList.end());
}
+ if (this->Makefile->GetDefinition(kCMAKE_LINKER_TYPE)) {
+ // propagate various variables to support linker selection
+ vars.insert(kCMAKE_LINKER_TYPE);
+ auto defs = this->Makefile->GetDefinitions();
+ cmsys::RegularExpression linkerTypeDef{
+ "^CMAKE_[A-Za-z]+_USING_LINKER_"
+ };
+ for (auto const& def : defs) {
+ if (linkerTypeDef.find(def)) {
+ vars.insert(def);
+ }
+ }
+ }
+
if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0083) ==
cmPolicies::NEW) {
// To ensure full support of PIE, propagate cache variables
diff --git a/Source/cmCoreTryCompile.h b/Source/cmCoreTryCompile.h
index 3217a1b..6a26e88 100644
--- a/Source/cmCoreTryCompile.h
+++ b/Source/cmCoreTryCompile.h
@@ -91,6 +91,7 @@ public:
cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>>
LinkLibraries;
ArgumentParser::MaybeEmpty<std::vector<std::string>> LinkOptions;
+ cm::optional<std::string> LinkerLanguage;
std::map<std::string, std::string> LangProps;
std::string CMakeInternal;
cm::optional<std::string> OutputVariable;
diff --git a/Source/cmCreateTestSourceList.cxx b/Source/cmCreateTestSourceList.cxx
index 75c25e3..9edbafe 100644
--- a/Source/cmCreateTestSourceList.cxx
+++ b/Source/cmCreateTestSourceList.cxx
@@ -81,8 +81,8 @@ bool cmCreateTestSourceList(std::vector<std::string> const& args,
}
std::string func_name;
if (!cmSystemTools::GetFilenamePath(*i).empty()) {
- func_name = cmSystemTools::GetFilenamePath(*i) + "/" +
- cmSystemTools::GetFilenameWithoutLastExtension(*i);
+ func_name = cmStrCat(cmSystemTools::GetFilenamePath(*i), '/',
+ cmSystemTools::GetFilenameWithoutLastExtension(*i));
} else {
func_name = cmSystemTools::GetFilenameWithoutLastExtension(*i);
}
@@ -93,9 +93,7 @@ bool cmCreateTestSourceList(std::vector<std::string> const& args,
tests_func_name.end();
tests_func_name.push_back(func_name);
if (!already_declared) {
- forwardDeclareCode += "int ";
- forwardDeclareCode += func_name;
- forwardDeclareCode += "(int, char*[]);\n";
+ forwardDeclareCode += cmStrCat("int ", func_name, "(int, char*[]);\n");
}
}
@@ -105,8 +103,8 @@ bool cmCreateTestSourceList(std::vector<std::string> const& args,
++i, ++j) {
std::string func_name;
if (!cmSystemTools::GetFilenamePath(*i).empty()) {
- func_name = cmSystemTools::GetFilenamePath(*i) + "/" +
- cmSystemTools::GetFilenameWithoutLastExtension(*i);
+ func_name = cmStrCat(cmSystemTools::GetFilenamePath(*i), '/',
+ cmSystemTools::GetFilenameWithoutLastExtension(*i));
} else {
func_name = cmSystemTools::GetFilenameWithoutLastExtension(*i);
}
@@ -137,12 +135,12 @@ bool cmCreateTestSourceList(std::vector<std::string> const& args,
{
cmSourceFile* sf = mf.GetOrCreateSource(driver);
sf->SetProperty("ABSTRACT", "0");
- sourceListValue = args[1];
+ sourceListValue = driver;
}
for (i = testsBegin; i != tests.end(); ++i) {
cmSourceFile* sf = mf.GetOrCreateSource(*i);
sf->SetProperty("ABSTRACT", "0");
- sourceListValue += ";";
+ sourceListValue += ';';
sourceListValue += *i;
}
diff --git a/Source/cmDebuggerStackFrame.h b/Source/cmDebuggerStackFrame.h
index dc3b2ab..f4e6612 100644
--- a/Source/cmDebuggerStackFrame.h
+++ b/Source/cmDebuggerStackFrame.h
@@ -28,6 +28,10 @@ public:
std::string const& GetFileName() const noexcept { return this->FileName; }
int64_t GetLine() const noexcept;
cmMakefile* GetMakefile() const noexcept { return this->Makefile; }
+ cmListFileFunction const& GetFunction() const noexcept
+ {
+ return this->Function;
+ }
};
} // namespace cmDebugger
diff --git a/Source/cmDebuggerThread.cxx b/Source/cmDebuggerThread.cxx
index fd52f5a..f7a1778 100644
--- a/Source/cmDebuggerThread.cxx
+++ b/Source/cmDebuggerThread.cxx
@@ -13,6 +13,7 @@
#include "cmDebuggerVariables.h"
#include "cmDebuggerVariablesHelper.h"
#include "cmDebuggerVariablesManager.h"
+#include "cmListFileCache.h"
namespace cmDebugger {
@@ -135,8 +136,7 @@ dap::StackTraceResponse GetStackTraceResponse(
#endif
stackFrame.line = thread->Frames[i]->GetLine();
stackFrame.column = 1;
- stackFrame.name = thread->Frames[i]->GetFileName() + " Line " +
- std::to_string(stackFrame.line);
+ stackFrame.name = thread->Frames[i]->GetFunction().OriginalName();
stackFrame.id = thread->Frames[i]->GetId();
stackFrame.source = source;
diff --git a/Source/cmDyndepCollation.cxx b/Source/cmDyndepCollation.cxx
index edf56ff..ea46f03 100644
--- a/Source/cmDyndepCollation.cxx
+++ b/Source/cmDyndepCollation.cxx
@@ -242,14 +242,16 @@ Json::Value CollationInformationExports(cmGeneratorTarget const* gt)
auto const& all_build_exports = gt->Makefile->GetExportBuildFileGenerators();
for (auto const& exp : all_build_exports) {
- std::vector<std::string> targets;
+ std::vector<cmExportBuildFileGenerator::TargetExport> targets;
exp->GetTargets(targets);
// Ignore exports sets which are not for this target.
auto const& name = gt->GetName();
bool has_current_target =
std::any_of(targets.begin(), targets.end(),
- [name](std::string const& tname) { return tname == name; });
+ [name](cmExportBuildFileGenerator::TargetExport const& te) {
+ return te.Name == name;
+ });
if (!has_current_target) {
continue;
}
diff --git a/Source/cmExportBuildAndroidMKGenerator.cxx b/Source/cmExportBuildAndroidMKGenerator.cxx
index c54e6ac..34bda1b 100644
--- a/Source/cmExportBuildAndroidMKGenerator.cxx
+++ b/Source/cmExportBuildAndroidMKGenerator.cxx
@@ -57,8 +57,8 @@ void cmExportBuildAndroidMKGenerator::GenerateImportTargetCode(
}
void cmExportBuildAndroidMKGenerator::GenerateImportPropertyCode(
- std::ostream&, const std::string&, cmGeneratorTarget const*,
- ImportPropertyMap const&)
+ std::ostream&, const std::string&, const std::string&,
+ cmGeneratorTarget const*, ImportPropertyMap const&, const std::string&)
{
}
diff --git a/Source/cmExportBuildAndroidMKGenerator.h b/Source/cmExportBuildAndroidMKGenerator.h
index 7067488..9562cee 100644
--- a/Source/cmExportBuildAndroidMKGenerator.h
+++ b/Source/cmExportBuildAndroidMKGenerator.h
@@ -51,10 +51,11 @@ protected:
void GenerateExpectedTargetsCode(
std::ostream& os, const std::string& expectedTargets) override;
void GenerateImportPropertyCode(
- std::ostream& os, const std::string& config,
- cmGeneratorTarget const* target,
- ImportPropertyMap const& properties) override;
+ std::ostream& os, const std::string& config, const std::string& suffix,
+ cmGeneratorTarget const* target, ImportPropertyMap const& properties,
+ const std::string& importedXcFrameworkLocation) override;
void GenerateMissingTargetsCheckCode(std::ostream& os) override;
+ void GenerateFindDependencyCalls(std::ostream&) override {}
void GenerateInterfaceProperties(
cmGeneratorTarget const* target, std::ostream& os,
const ImportPropertyMap& properties) override;
diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx
index bf3bb8b..8ba8d97 100644
--- a/Source/cmExportBuildFileGenerator.cxx
+++ b/Source/cmExportBuildFileGenerator.cxx
@@ -10,7 +10,6 @@
#include <utility>
#include <cm/string_view>
-#include <cmext/algorithm>
#include <cmext/string_view>
#include "cmExportSet.h"
@@ -54,15 +53,15 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
{
std::string expectedTargets;
std::string sep;
- std::vector<std::string> targets;
+ std::vector<TargetExport> targets;
bool generatedInterfaceRequired = false;
this->GetTargets(targets);
- for (std::string const& tei : targets) {
- cmGeneratorTarget* te = this->LG->FindGeneratorTargetToUse(tei);
+ for (auto const& tei : targets) {
+ cmGeneratorTarget* te = this->LG->FindGeneratorTargetToUse(tei.Name);
expectedTargets += sep + this->Namespace + te->GetExportName();
sep = " ";
if (this->ExportedTargets.insert(te).second) {
- this->Exports.push_back(te);
+ this->Exports.emplace_back(te, tei.XcFrameworkLocation);
} else {
std::ostringstream e;
e << "given target \"" << te->GetName() << "\" more than once.";
@@ -76,13 +75,14 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
}
if (generatedInterfaceRequired) {
- this->GenerateRequiredCMakeVersion(os, "3.0.0");
+ this->SetRequiredCMakeVersion(3, 0, 0);
}
this->GenerateExpectedTargetsCode(os, expectedTargets);
}
// Create all the imported targets.
- for (cmGeneratorTarget* gte : this->Exports) {
+ for (auto const& exp : this->Exports) {
+ cmGeneratorTarget* gte = exp.Target;
this->GenerateImportTargetCode(os, gte, this->GetExportTargetType(gte));
gte->Target->AppendBuildInterfaceIncludes();
@@ -176,7 +176,9 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
void cmExportBuildFileGenerator::GenerateImportTargetsConfig(
std::ostream& os, const std::string& config, std::string const& suffix)
{
- for (cmGeneratorTarget* target : this->Exports) {
+ for (auto const& exp : this->Exports) {
+ cmGeneratorTarget* target = exp.Target;
+
// Collect import properties for this target.
ImportPropertyMap properties;
@@ -200,7 +202,23 @@ void cmExportBuildFileGenerator::GenerateImportTargetsConfig(
// properties);
// Generate code in the export file.
- this->GenerateImportPropertyCode(os, config, target, properties);
+ std::string importedXcFrameworkLocation = exp.XcFrameworkLocation;
+ if (!importedXcFrameworkLocation.empty()) {
+ importedXcFrameworkLocation = cmGeneratorExpression::Preprocess(
+ importedXcFrameworkLocation,
+ cmGeneratorExpression::PreprocessContext::BuildInterface);
+ importedXcFrameworkLocation = cmGeneratorExpression::Evaluate(
+ importedXcFrameworkLocation, exp.Target->GetLocalGenerator(), config,
+ exp.Target, nullptr, exp.Target);
+ if (!importedXcFrameworkLocation.empty() &&
+ !cmSystemTools::FileIsFullPath(importedXcFrameworkLocation)) {
+ importedXcFrameworkLocation =
+ cmStrCat(this->LG->GetCurrentBinaryDirectory(), '/',
+ importedXcFrameworkLocation);
+ }
+ }
+ this->GenerateImportPropertyCode(os, config, suffix, target, properties,
+ importedXcFrameworkLocation);
}
}
}
@@ -308,7 +326,7 @@ void cmExportBuildFileGenerator::HandleMissingTarget(
}
void cmExportBuildFileGenerator::GetTargets(
- std::vector<std::string>& targets) const
+ std::vector<TargetExport>& targets) const
{
if (this->ExportSet) {
for (std::unique_ptr<cmTargetExport> const& te :
@@ -316,7 +334,7 @@ void cmExportBuildFileGenerator::GetTargets(
if (te->NamelinkOnly) {
continue;
}
- targets.push_back(te->TargetName);
+ targets.emplace_back(te->TargetName, te->XcFrameworkLocation);
}
return;
}
@@ -334,9 +352,11 @@ cmExportBuildFileGenerator::FindBuildExportInfo(cmGlobalGenerator* gg,
for (auto const& exp : exportSets) {
const auto& exportSet = exp.second;
- std::vector<std::string> targets;
+ std::vector<TargetExport> targets;
exportSet->GetTargets(targets);
- if (cm::contains(targets, name)) {
+ if (std::any_of(
+ targets.begin(), targets.end(),
+ [&name](const TargetExport& te) { return te.Name == name; })) {
exportFiles.push_back(exp.first);
ns = exportSet->GetNamespace();
}
diff --git a/Source/cmExportBuildFileGenerator.h b/Source/cmExportBuildFileGenerator.h
index 4636196..9f11d13 100644
--- a/Source/cmExportBuildFileGenerator.h
+++ b/Source/cmExportBuildFileGenerator.h
@@ -33,15 +33,27 @@ class cmTargetExport;
class cmExportBuildFileGenerator : public cmExportFileGenerator
{
public:
+ struct TargetExport
+ {
+ TargetExport(std::string name, std::string xcFrameworkLocation)
+ : Name(std::move(name))
+ , XcFrameworkLocation(std::move(xcFrameworkLocation))
+ {
+ }
+
+ std::string Name;
+ std::string XcFrameworkLocation;
+ };
+
cmExportBuildFileGenerator();
/** Set the list of targets to export. */
- void SetTargets(std::vector<std::string> const& targets)
+ void SetTargets(std::vector<TargetExport> const& targets)
{
this->Targets = targets;
}
- void GetTargets(std::vector<std::string>& targets) const;
- void AppendTargets(std::vector<std::string> const& targets)
+ void GetTargets(std::vector<TargetExport>& targets) const;
+ void AppendTargets(std::vector<TargetExport> const& targets)
{
cm::append(this->Targets, targets);
}
@@ -90,6 +102,7 @@ protected:
cmTargetExport* te) override;
std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet,
cmTargetExport* te) override;
+ cmExportSet* GetExportSet() const override { return this->ExportSet; }
std::string GetCxxModulesDirectory() const override;
void GenerateCxxModuleConfigInformation(std::ostream&) const override;
@@ -98,9 +111,22 @@ protected:
std::pair<std::vector<std::string>, std::string> FindBuildExportInfo(
cmGlobalGenerator* gg, const std::string& name);
- std::vector<std::string> Targets;
+ struct TargetExportPrivate
+ {
+ TargetExportPrivate(cmGeneratorTarget* target,
+ std::string xcFrameworkLocation)
+ : Target(target)
+ , XcFrameworkLocation(std::move(xcFrameworkLocation))
+ {
+ }
+
+ cmGeneratorTarget* Target;
+ std::string XcFrameworkLocation;
+ };
+
+ std::vector<TargetExport> Targets;
cmExportSet* ExportSet;
- std::vector<cmGeneratorTarget*> Exports;
+ std::vector<TargetExportPrivate> Exports;
cmLocalGenerator* LG;
// The directory for C++ module information.
std::string CxxModulesDirectory;
diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx
index 7e44210..7d23c91 100644
--- a/Source/cmExportCommand.cxx
+++ b/Source/cmExportCommand.cxx
@@ -8,6 +8,7 @@
#include <cm/memory>
#include <cm/optional>
+#include <cmext/algorithm>
#include <cmext/string_view>
#include "cmsys/RegularExpression.hxx"
@@ -24,10 +25,12 @@
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmPolicies.h"
+#include "cmRange.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
+#include "cmValue.h"
#if defined(__HAIKU__)
# include <FindDirectory.h>
@@ -66,6 +69,11 @@ bool cmExportCommand(std::vector<std::string> const& args,
std::string CxxModulesDirectory;
bool Append = false;
bool ExportOld = false;
+
+ std::vector<std::vector<std::string>> PackageDependencyArgs;
+ bool ExportPackageDependencies = false;
+
+ std::vector<std::vector<std::string>> TargetArgs;
};
auto parser =
@@ -75,7 +83,13 @@ bool cmExportCommand(std::vector<std::string> const& args,
.Bind("CXX_MODULES_DIRECTORY"_s, &Arguments::CxxModulesDirectory);
if (args[0] == "EXPORT") {
- parser.Bind("EXPORT"_s, &Arguments::ExportSetName);
+ parser.Bind("EXPORT"_s, &Arguments::ExportSetName)
+ .Bind("EXPORT_PACKAGE_DEPENDENCIES"_s,
+ &Arguments::ExportPackageDependencies);
+ } else if (args[0] == "SETUP") {
+ parser.Bind("SETUP"_s, &Arguments::ExportSetName);
+ parser.Bind("PACKAGE_DEPENDENCY"_s, &Arguments::PackageDependencyArgs);
+ parser.Bind("TARGET"_s, &Arguments::TargetArgs);
} else {
parser.Bind("TARGETS"_s, &Arguments::Targets);
parser.Bind("ANDROID_MK"_s, &Arguments::AndroidMKFile);
@@ -91,6 +105,91 @@ bool cmExportCommand(std::vector<std::string> const& args,
return false;
}
+ if (args[0] == "SETUP") {
+ cmMakefile& mf = status.GetMakefile();
+ cmGlobalGenerator* gg = mf.GetGlobalGenerator();
+
+ cmExportSetMap& setMap = gg->GetExportSets();
+ auto& exportSet = setMap[arguments.ExportSetName];
+
+ struct PackageDependencyArguments
+ {
+ std::string Enabled;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> ExtraArgs;
+ };
+
+ auto packageDependencyParser =
+ cmArgumentParser<PackageDependencyArguments>{}
+ .Bind("ENABLED"_s, &PackageDependencyArguments::Enabled)
+ .Bind("EXTRA_ARGS"_s, &PackageDependencyArguments::ExtraArgs);
+
+ for (auto const& packageDependencyArgs : arguments.PackageDependencyArgs) {
+ if (packageDependencyArgs.empty()) {
+ continue;
+ }
+
+ PackageDependencyArguments const packageDependencyArguments =
+ packageDependencyParser.Parse(
+ cmMakeRange(packageDependencyArgs).advance(1), &unknownArgs);
+
+ if (!unknownArgs.empty()) {
+ status.SetError("Unknown argument: \"" + unknownArgs.front() + "\".");
+ return false;
+ }
+
+ auto& packageDependency =
+ exportSet.GetPackageDependencyForSetup(packageDependencyArgs.front());
+
+ if (!packageDependencyArguments.Enabled.empty()) {
+ if (packageDependencyArguments.Enabled == "AUTO") {
+ packageDependency.Enabled =
+ cmExportSet::PackageDependencyExportEnabled::Auto;
+ } else if (cmIsOff(packageDependencyArguments.Enabled)) {
+ packageDependency.Enabled =
+ cmExportSet::PackageDependencyExportEnabled::Off;
+ } else if (cmIsOn(packageDependencyArguments.Enabled)) {
+ packageDependency.Enabled =
+ cmExportSet::PackageDependencyExportEnabled::On;
+ } else {
+ status.SetError(
+ cmStrCat("Invalid enable setting for package dependency: \"",
+ packageDependencyArguments.Enabled, "\""));
+ return false;
+ }
+ }
+
+ cm::append(packageDependency.ExtraArguments,
+ packageDependencyArguments.ExtraArgs);
+ }
+
+ struct TargetArguments
+ {
+ std::string XcFrameworkLocation;
+ };
+
+ auto targetParser = cmArgumentParser<TargetArguments>{}.Bind(
+ "XCFRAMEWORK_LOCATION"_s, &TargetArguments::XcFrameworkLocation);
+
+ for (auto const& targetArgs : arguments.TargetArgs) {
+ if (targetArgs.empty()) {
+ continue;
+ }
+
+ TargetArguments const targetArguments =
+ targetParser.Parse(cmMakeRange(targetArgs).advance(1), &unknownArgs);
+
+ if (!unknownArgs.empty()) {
+ status.SetError("Unknown argument: \"" + unknownArgs.front() + "\".");
+ return false;
+ }
+
+ exportSet.SetXcFrameworkLocation(targetArgs.front(),
+ targetArguments.XcFrameworkLocation);
+ }
+
+ return true;
+ }
+
std::string fname;
bool android = false;
if (!arguments.AndroidMKFile.empty()) {
@@ -133,7 +232,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
fname = dir + "/" + fname;
}
- std::vector<std::string> targets;
+ std::vector<cmExportBuildFileGenerator::TargetExport> targets;
cmGlobalGenerator* gg = mf.GetGlobalGenerator();
@@ -171,7 +270,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
status.SetError(e.str());
return false;
}
- targets.push_back(currentTarget);
+ targets.emplace_back(currentTarget, std::string{});
}
if (arguments.Append) {
if (cmExportBuildFileGenerator* ebfg =
@@ -224,6 +323,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
ebfg->SetTargets(targets);
}
ebfg->SetExportOld(arguments.ExportOld);
+ ebfg->SetExportPackageDependencies(arguments.ExportPackageDependencies);
// Compute the set of configurations exported.
std::vector<std::string> configurationTypes =
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx
index d0e69fb..e2c3edd 100644
--- a/Source/cmExportFileGenerator.cxx
+++ b/Source/cmExportFileGenerator.cxx
@@ -2,6 +2,7 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmExportFileGenerator.h"
+#include <algorithm>
#include <array>
#include <cassert>
#include <cstring>
@@ -9,12 +10,15 @@
#include <utility>
#include <cm/memory>
+#include <cm/optional>
#include <cmext/string_view>
#include "cmsys/FStream.hxx"
#include "cmComputeLinkInformation.h"
+#include "cmExportSet.h"
#include "cmFileSet.h"
+#include "cmFindPackageStack.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorTarget.h"
#include "cmLinkItem.h"
@@ -30,6 +34,7 @@
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmValue.h"
+#include "cmVersion.h"
static std::string cmExportFileGeneratorEscape(std::string const& str)
{
@@ -93,17 +98,35 @@ bool cmExportFileGenerator::GenerateImportFile()
return false;
}
std::ostream& os = *foutPtr;
+ std::stringstream mainFileWithHeadersAndFootersBuffer;
// Start with the import file header.
- this->GeneratePolicyHeaderCode(os);
- this->GenerateImportHeaderCode(os);
+ this->GenerateImportHeaderCode(mainFileWithHeadersAndFootersBuffer);
// Create all the imported targets.
- bool result = this->GenerateMainFile(os);
+ std::stringstream mainFileBuffer;
+ bool result = this->GenerateMainFile(mainFileBuffer);
+
+ // Export find_dependency() calls. Must be done after GenerateMainFile(),
+ // because that's when target dependencies are gathered, which we need for
+ // the find_dependency() calls.
+ if (!this->AppendMode && this->GetExportSet() &&
+ this->ExportPackageDependencies) {
+ this->SetRequiredCMakeVersion(3, 9, 0);
+ this->GenerateFindDependencyCalls(mainFileWithHeadersAndFootersBuffer);
+ }
+
+ // Write cached import code.
+ mainFileWithHeadersAndFootersBuffer << mainFileBuffer.rdbuf();
// End with the import file footer.
- this->GenerateImportFooterCode(os);
- this->GeneratePolicyFooterCode(os);
+ this->GenerateImportFooterCode(mainFileWithHeadersAndFootersBuffer);
+ this->GeneratePolicyFooterCode(mainFileWithHeadersAndFootersBuffer);
+
+ // This has to be done last, after the minimum CMake version has been
+ // determined.
+ this->GeneratePolicyHeaderCode(os);
+ os << mainFileWithHeadersAndFootersBuffer.rdbuf();
return result;
}
@@ -156,17 +179,6 @@ void cmExportFileGenerator::PopulateInterfaceProperty(
}
}
-void cmExportFileGenerator::GenerateRequiredCMakeVersion(
- std::ostream& os, const char* versionString)
-{
- /* clang-format off */
- os << "if(CMAKE_VERSION VERSION_LESS " << versionString << ")\n"
- " message(FATAL_ERROR \"This file relies on consumers using "
- "CMake " << versionString << " or greater.\")\n"
- "endif()\n\n";
- /* clang-format on */
-}
-
bool cmExportFileGenerator::PopulateInterfaceLinkLibrariesProperty(
cmGeneratorTarget const* target,
cmGeneratorExpression::PreprocessContext preprocessRule,
@@ -620,6 +632,12 @@ bool cmExportFileGenerator::AddTargetNamespace(std::string& input,
return false;
}
+ cmFindPackageStack const& pkgStack = tgt->Target->GetFindPackageStack();
+ if (!pkgStack.Empty() ||
+ tgt->Target->GetProperty("EXPORT_FIND_PACKAGE_NAME")) {
+ this->ExternalTargets.emplace(tgt);
+ }
+
if (tgt->IsImported()) {
input = tgt->GetName();
return true;
@@ -867,12 +885,14 @@ void cmExportFileGenerator::SetImportDetailProperties(
// Export IMPORTED_LINK_DEPENDENT_LIBRARIES to help consuming linkers
// find private dependencies of shared libraries.
std::size_t oldMissingTargetsSize = this->MissingTargets.size();
+ auto oldExternalTargets = this->ExternalTargets;
this->SetImportLinkProperty(
suffix, target, "IMPORTED_LINK_DEPENDENT_LIBRARIES", iface->SharedDeps,
properties, ImportLinkPropertyTargetNames::Yes);
// Avoid enforcing shared library private dependencies as public package
// dependencies by ignoring missing targets added for them.
this->MissingTargets.resize(oldMissingTargetsSize);
+ this->ExternalTargets = std::move(oldExternalTargets);
if (iface->Multiplicity > 0) {
std::string prop =
@@ -953,20 +973,29 @@ void cmExportFileGenerator::GeneratePolicyHeaderCode(std::ostream& os)
os << "if(\"${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}\" LESS 2.8)\n"
<< " message(FATAL_ERROR \"CMake >= 2.8.0 required\")\n"
<< "endif()\n"
- << "if(CMAKE_VERSION VERSION_LESS \"2.8.3\")\n"
- << " message(FATAL_ERROR \"CMake >= 2.8.3 required\")\n"
+ << "if(CMAKE_VERSION VERSION_LESS \""
+ << this->RequiredCMakeVersionMajor << '.'
+ << this->RequiredCMakeVersionMinor << '.'
+ << this->RequiredCMakeVersionPatch << "\")\n"
+ << " message(FATAL_ERROR \"CMake >= "
+ << this->RequiredCMakeVersionMajor << '.'
+ << this->RequiredCMakeVersionMinor << '.'
+ << this->RequiredCMakeVersionPatch << " required\")\n"
<< "endif()\n";
/* clang-format on */
// Isolate the file policy level.
// Support CMake versions as far back as 2.6 but also support using NEW
- // policy settings for up to CMake 3.26 (this upper limit may be reviewed
+ // policy settings for up to CMake 3.27 (this upper limit may be reviewed
// and increased from time to time). This reduces the opportunity for CMake
// warnings when an older export file is later used with newer CMake
// versions.
/* clang-format off */
os << "cmake_policy(PUSH)\n"
- << "cmake_policy(VERSION 2.8.3...3.26)\n";
+ << "cmake_policy(VERSION "
+ << this->RequiredCMakeVersionMajor << '.'
+ << this->RequiredCMakeVersionMinor << '.'
+ << this->RequiredCMakeVersionPatch << "...3.27)\n";
/* clang-format on */
}
@@ -1125,8 +1154,9 @@ void cmExportFileGenerator::GenerateImportTargetCode(
}
void cmExportFileGenerator::GenerateImportPropertyCode(
- std::ostream& os, const std::string& config, cmGeneratorTarget const* target,
- ImportPropertyMap const& properties)
+ std::ostream& os, const std::string& config, const std::string& suffix,
+ cmGeneratorTarget const* target, ImportPropertyMap const& properties,
+ const std::string& importedXcFrameworkLocation)
{
// Construct the imported target name.
std::string targetName = this->Namespace;
@@ -1145,12 +1175,98 @@ void cmExportFileGenerator::GenerateImportPropertyCode(
}
os << ")\n";
os << "set_target_properties(" << targetName << " PROPERTIES\n";
+ std::string importedLocationProp = cmStrCat("IMPORTED_LOCATION", suffix);
for (auto const& property : properties) {
- os << " " << property.first << " "
- << cmExportFileGeneratorEscape(property.second) << "\n";
+ if (importedXcFrameworkLocation.empty() ||
+ property.first != importedLocationProp) {
+ os << " " << property.first << " "
+ << cmExportFileGeneratorEscape(property.second) << "\n";
+ }
}
- os << " )\n"
- << "\n";
+ os << " )\n";
+ if (!importedXcFrameworkLocation.empty()) {
+ auto importedLocationIt = properties.find(importedLocationProp);
+ if (importedLocationIt != properties.end()) {
+ os << "if(NOT CMAKE_VERSION VERSION_LESS \"3.28\" AND IS_DIRECTORY "
+ << cmExportFileGeneratorEscape(importedXcFrameworkLocation)
+ << ")\n"
+ " set_property(TARGET "
+ << targetName << " PROPERTY " << importedLocationProp << " "
+ << cmExportFileGeneratorEscape(importedXcFrameworkLocation)
+ << ")\nelse()\n set_property(TARGET " << targetName << " PROPERTY "
+ << importedLocationProp << " "
+ << cmExportFileGeneratorEscape(importedLocationIt->second)
+ << ")\nendif()\n";
+ }
+ }
+ os << "\n";
+}
+
+void cmExportFileGenerator::GenerateFindDependencyCalls(std::ostream& os)
+{
+ os << "include(CMakeFindDependencyMacro)\n";
+ std::map<std::string, cmExportSet::PackageDependency> packageDependencies;
+ auto* exportSet = this->GetExportSet();
+ if (exportSet) {
+ packageDependencies = exportSet->GetPackageDependencies();
+ }
+
+ for (cmGeneratorTarget const* gt : this->ExternalTargets) {
+ std::string findPackageName;
+ auto exportFindPackageName = gt->GetProperty("EXPORT_FIND_PACKAGE_NAME");
+ cmFindPackageStack pkgStack = gt->Target->GetFindPackageStack();
+ if (!exportFindPackageName.IsEmpty()) {
+ findPackageName = *exportFindPackageName;
+ } else {
+ if (!pkgStack.Empty()) {
+ cmFindPackageCall const& fpc = pkgStack.Top();
+ findPackageName = fpc.Name;
+ }
+ }
+ if (!findPackageName.empty()) {
+ auto& dep = packageDependencies[findPackageName];
+ if (!pkgStack.Empty()) {
+ dep.FindPackageIndex = pkgStack.Top().Index;
+ }
+ if (dep.Enabled == cmExportSet::PackageDependencyExportEnabled::Auto) {
+ dep.Enabled = cmExportSet::PackageDependencyExportEnabled::On;
+ }
+ }
+ }
+
+ std::vector<std::pair<std::string, cmExportSet::PackageDependency>>
+ packageDependenciesSorted(packageDependencies.begin(),
+ packageDependencies.end());
+ std::sort(
+ packageDependenciesSorted.begin(), packageDependenciesSorted.end(),
+ [](const std::pair<std::string, cmExportSet::PackageDependency>& lhs,
+ const std::pair<std::string, cmExportSet::PackageDependency>& rhs)
+ -> bool {
+ if (lhs.second.SpecifiedIndex) {
+ if (rhs.second.SpecifiedIndex) {
+ return lhs.second.SpecifiedIndex < rhs.second.SpecifiedIndex;
+ }
+ assert(rhs.second.FindPackageIndex);
+ return true;
+ }
+ assert(lhs.second.FindPackageIndex);
+ if (rhs.second.SpecifiedIndex) {
+ return false;
+ }
+ assert(rhs.second.FindPackageIndex);
+ return lhs.second.FindPackageIndex < rhs.second.FindPackageIndex;
+ });
+
+ for (auto const& it : packageDependenciesSorted) {
+ if (it.second.Enabled == cmExportSet::PackageDependencyExportEnabled::On) {
+ os << "find_dependency(" << it.first << " REQUIRED";
+ for (auto const& arg : it.second.ExtraArguments) {
+ os << " " << cmOutputConverter::EscapeForCMake(arg);
+ }
+ os << ")\n";
+ }
+ }
+ os << "\n\n";
}
void cmExportFileGenerator::GenerateMissingTargetsCheckCode(std::ostream& os)
@@ -1214,10 +1330,16 @@ void cmExportFileGenerator::GenerateImportedFileCheckLoop(std::ostream& os)
/* clang-format off */
os << "# Loop over all imported files and verify that they actually exist\n"
"foreach(_cmake_target IN LISTS _cmake_import_check_targets)\n"
- " foreach(_cmake_file IN LISTS \"_cmake_import_check_files_for_${_cmake_target}\")\n"
- " if(NOT EXISTS \"${_cmake_file}\")\n"
- " message(FATAL_ERROR \"The imported target \\\"${_cmake_target}\\\""
- " references the file\n"
+ " if(CMAKE_VERSION VERSION_LESS \"3.28\"\n"
+ " OR NOT DEFINED "
+ "_cmake_import_check_xcframework_for_${_cmake_target}\n"
+ " OR NOT IS_DIRECTORY "
+ "\"${_cmake_import_check_xcframework_for_${_cmake_target}}\")\n"
+ " foreach(_cmake_file IN LISTS "
+ "\"_cmake_import_check_files_for_${_cmake_target}\")\n"
+ " if(NOT EXISTS \"${_cmake_file}\")\n"
+ " message(FATAL_ERROR \"The imported target "
+ "\\\"${_cmake_target}\\\" references the file\n"
" \\\"${_cmake_file}\\\"\n"
"but this file does not exist. Possible reasons include:\n"
"* The file was deleted, renamed, or moved to another location.\n"
@@ -1226,8 +1348,9 @@ void cmExportFileGenerator::GenerateImportedFileCheckLoop(std::ostream& os)
" \\\"${CMAKE_CURRENT_LIST_FILE}\\\"\n"
"but not all the files it references.\n"
"\")\n"
- " endif()\n"
- " endforeach()\n"
+ " endif()\n"
+ " endforeach()\n"
+ " endif()\n"
" unset(_cmake_file)\n"
" unset(\"_cmake_import_check_files_for_${_cmake_target}\")\n"
"endforeach()\n"
@@ -1240,15 +1363,18 @@ void cmExportFileGenerator::GenerateImportedFileCheckLoop(std::ostream& os)
void cmExportFileGenerator::GenerateImportedFileChecksCode(
std::ostream& os, cmGeneratorTarget* target,
ImportPropertyMap const& properties,
- const std::set<std::string>& importedLocations)
+ const std::set<std::string>& importedLocations,
+ const std::string& importedXcFrameworkLocation)
{
// Construct the imported target name.
std::string targetName = cmStrCat(this->Namespace, target->GetExportName());
- os << "list(APPEND _cmake_import_check_targets " << targetName
- << " )\n"
- "list(APPEND _cmake_import_check_files_for_"
- << targetName << " ";
+ os << "list(APPEND _cmake_import_check_targets " << targetName << " )\n";
+ if (!importedXcFrameworkLocation.empty()) {
+ os << "set(_cmake_import_check_xcframework_for_" << targetName << ' '
+ << cmExportFileGeneratorEscape(importedXcFrameworkLocation) << ")\n";
+ }
+ os << "list(APPEND _cmake_import_check_files_for_" << targetName << " ";
for (std::string const& li : importedLocations) {
auto pi = properties.find(li);
@@ -1485,3 +1611,17 @@ void cmExportFileGenerator::GenerateCxxModuleInformation(std::ostream& os)
this->GenerateCxxModuleConfigInformation(ap);
}
+
+void cmExportFileGenerator::SetRequiredCMakeVersion(unsigned int major,
+ unsigned int minor,
+ unsigned int patch)
+{
+ if (CMake_VERSION_ENCODE(major, minor, patch) >
+ CMake_VERSION_ENCODE(this->RequiredCMakeVersionMajor,
+ this->RequiredCMakeVersionMinor,
+ this->RequiredCMakeVersionPatch)) {
+ this->RequiredCMakeVersionMajor = major;
+ this->RequiredCMakeVersionMinor = minor;
+ this->RequiredCMakeVersionPatch = patch;
+ }
+}
diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h
index f396e0e..554dd4a 100644
--- a/Source/cmExportFileGenerator.h
+++ b/Source/cmExportFileGenerator.h
@@ -15,6 +15,7 @@
#include "cmVersion.h"
#include "cmVersionConfig.h"
+class cmExportSet;
class cmFileSet;
class cmGeneratorTarget;
class cmLocalGenerator;
@@ -61,6 +62,11 @@ public:
error. */
bool GenerateImportFile();
+ void SetExportPackageDependencies(bool exportPackageDependencies)
+ {
+ this->ExportPackageDependencies = exportPackageDependencies;
+ }
+
protected:
using ImportPropertyMap = std::map<std::string, std::string>;
@@ -78,16 +84,18 @@ protected:
virtual void GenerateImportTargetCode(std::ostream& os,
cmGeneratorTarget const* target,
cmStateEnums::TargetType targetType);
- virtual void GenerateImportPropertyCode(std::ostream& os,
- const std::string& config,
- cmGeneratorTarget const* target,
- ImportPropertyMap const& properties);
+ virtual void GenerateImportPropertyCode(
+ std::ostream& os, const std::string& config, const std::string& suffix,
+ cmGeneratorTarget const* target, ImportPropertyMap const& properties,
+ const std::string& importedXcFrameworkLocation);
virtual void GenerateImportedFileChecksCode(
std::ostream& os, cmGeneratorTarget* target,
ImportPropertyMap const& properties,
- const std::set<std::string>& importedLocations);
+ const std::set<std::string>& importedLocations,
+ const std::string& importedXcFrameworkLocation);
virtual void GenerateImportedFileCheckLoop(std::ostream& os);
virtual void GenerateMissingTargetsCheckCode(std::ostream& os);
+ virtual void GenerateFindDependencyCalls(std::ostream& os);
virtual void GenerateExpectedTargetsCode(std::ostream& os,
const std::string& expectedTargets);
@@ -173,9 +181,6 @@ protected:
std::string& input, cmGeneratorTarget const* target,
FreeTargetsReplace replace = NoReplaceFreeTargets);
- virtual void GenerateRequiredCMakeVersion(std::ostream& os,
- const char* versionString);
-
bool PopulateCxxModuleExportProperties(
cmGeneratorTarget const* gte, ImportPropertyMap& properties,
cmGeneratorExpression::PreprocessContext ctx,
@@ -196,6 +201,11 @@ protected:
cmFileSet* fileSet,
cmTargetExport* te) = 0;
+ virtual cmExportSet* GetExportSet() const { return nullptr; }
+
+ void SetRequiredCMakeVersion(unsigned int major, unsigned int minor,
+ unsigned int patch);
+
// The namespace in which the exports are placed in the generated file.
std::string Namespace;
@@ -216,6 +226,14 @@ protected:
std::vector<std::string> MissingTargets;
+ std::set<cmGeneratorTarget const*> ExternalTargets;
+
+ unsigned int RequiredCMakeVersionMajor = 2;
+ unsigned int RequiredCMakeVersionMinor = 8;
+ unsigned int RequiredCMakeVersionPatch = 3;
+
+ bool ExportPackageDependencies = false;
+
private:
void PopulateInterfaceProperty(const std::string&, const std::string&,
cmGeneratorTarget const* target,
diff --git a/Source/cmExportInstallAndroidMKGenerator.cxx b/Source/cmExportInstallAndroidMKGenerator.cxx
index d53254d..eaa85f3 100644
--- a/Source/cmExportInstallAndroidMKGenerator.cxx
+++ b/Source/cmExportInstallAndroidMKGenerator.cxx
@@ -80,8 +80,8 @@ void cmExportInstallAndroidMKGenerator::GenerateExpectedTargetsCode(
}
void cmExportInstallAndroidMKGenerator::GenerateImportPropertyCode(
- std::ostream&, const std::string&, cmGeneratorTarget const*,
- ImportPropertyMap const&)
+ std::ostream&, const std::string&, const std::string&,
+ cmGeneratorTarget const*, ImportPropertyMap const&, const std::string&)
{
}
@@ -110,11 +110,6 @@ void cmExportInstallAndroidMKGenerator::GenerateImportPrefix(std::ostream&)
{
}
-void cmExportInstallAndroidMKGenerator::GenerateRequiredCMakeVersion(
- std::ostream&, const char*)
-{
-}
-
void cmExportInstallAndroidMKGenerator::CleanupTemporaryVariables(
std::ostream&)
{
@@ -127,7 +122,7 @@ void cmExportInstallAndroidMKGenerator::GenerateImportedFileCheckLoop(
void cmExportInstallAndroidMKGenerator::GenerateImportedFileChecksCode(
std::ostream&, cmGeneratorTarget*, ImportPropertyMap const&,
- const std::set<std::string>&)
+ const std::set<std::string>&, const std::string&)
{
}
diff --git a/Source/cmExportInstallAndroidMKGenerator.h b/Source/cmExportInstallAndroidMKGenerator.h
index 061358d..b1778ef 100644
--- a/Source/cmExportInstallAndroidMKGenerator.h
+++ b/Source/cmExportInstallAndroidMKGenerator.h
@@ -45,22 +45,22 @@ protected:
void GenerateExpectedTargetsCode(
std::ostream& os, const std::string& expectedTargets) override;
void GenerateImportPropertyCode(
- std::ostream& os, const std::string& config,
- cmGeneratorTarget const* target,
- ImportPropertyMap const& properties) override;
+ std::ostream& os, const std::string& config, const std::string& suffix,
+ cmGeneratorTarget const* target, ImportPropertyMap const& properties,
+ const std::string& importedXcFrameworkLocation) override;
void GenerateMissingTargetsCheckCode(std::ostream& os) override;
+ void GenerateFindDependencyCalls(std::ostream&) override {}
void GenerateInterfaceProperties(
cmGeneratorTarget const* target, std::ostream& os,
const ImportPropertyMap& properties) override;
void GenerateImportPrefix(std::ostream& os) override;
void LoadConfigFiles(std::ostream&) override;
- void GenerateRequiredCMakeVersion(std::ostream& os,
- const char* versionString) override;
void CleanupTemporaryVariables(std::ostream&) override;
void GenerateImportedFileCheckLoop(std::ostream& os) override;
void GenerateImportedFileChecksCode(
std::ostream& os, cmGeneratorTarget* target,
ImportPropertyMap const& properties,
- const std::set<std::string>& importedLocations) override;
+ const std::set<std::string>& importedLocations,
+ const std::string& importedXcFrameworkLocation) override;
bool GenerateImportFileConfig(const std::string& config) override;
};
diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx
index a264f5e..64e694c 100644
--- a/Source/cmExportInstallFileGenerator.cxx
+++ b/Source/cmExportInstallFileGenerator.cxx
@@ -76,9 +76,6 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
// Compute the relative import prefix for the file
this->GenerateImportPrefix(os);
- bool require2_8_12 = false;
- bool require3_0_0 = false;
- bool require3_1_0 = false;
bool requiresConfigFiles = false;
// Create all the imported targets.
for (cmTargetExport* te : allTargets) {
@@ -147,16 +144,16 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
if (this->PopulateInterfaceLinkLibrariesProperty(
gt, cmGeneratorExpression::InstallInterface, properties) &&
!this->ExportOld) {
- require2_8_12 = true;
+ this->SetRequiredCMakeVersion(2, 8, 12);
}
}
if (targetType == cmStateEnums::INTERFACE_LIBRARY) {
- require3_0_0 = true;
+ this->SetRequiredCMakeVersion(3, 0, 0);
}
if (gt->GetProperty("INTERFACE_SOURCES")) {
// We can only generate INTERFACE_SOURCES in CMake 3.3, but CMake 3.1
// can consume them.
- require3_1_0 = true;
+ this->SetRequiredCMakeVersion(3, 1, 0);
}
this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gt,
@@ -169,14 +166,6 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
this->GenerateTargetFileSets(gt, os, te);
}
- if (require3_1_0) {
- this->GenerateRequiredCMakeVersion(os, "3.1.0");
- } else if (require3_0_0) {
- this->GenerateRequiredCMakeVersion(os, "3.0.0");
- } else if (require2_8_12) {
- this->GenerateRequiredCMakeVersion(os, "2.8.12");
- }
-
this->LoadConfigFiles(os);
bool result = true;
@@ -386,9 +375,26 @@ void cmExportInstallFileGenerator::GenerateImportTargetsConfig(
// properties);
// Generate code in the export file.
- this->GenerateImportPropertyCode(os, config, gtgt, properties);
- this->GenerateImportedFileChecksCode(os, gtgt, properties,
- importedLocations);
+ std::string importedXcFrameworkLocation = te->XcFrameworkLocation;
+ if (!importedXcFrameworkLocation.empty()) {
+ importedXcFrameworkLocation = cmGeneratorExpression::Preprocess(
+ importedXcFrameworkLocation,
+ cmGeneratorExpression::PreprocessContext::InstallInterface, true);
+ importedXcFrameworkLocation = cmGeneratorExpression::Evaluate(
+ importedXcFrameworkLocation, te->Target->GetLocalGenerator(), config,
+ te->Target, nullptr, te->Target);
+ if (!importedXcFrameworkLocation.empty() &&
+ !cmSystemTools::FileIsFullPath(importedXcFrameworkLocation) &&
+ !cmHasLiteralPrefix(importedXcFrameworkLocation,
+ "${_IMPORT_PREFIX}/")) {
+ importedXcFrameworkLocation =
+ cmStrCat("${_IMPORT_PREFIX}/", importedXcFrameworkLocation);
+ }
+ }
+ this->GenerateImportPropertyCode(os, config, suffix, gtgt, properties,
+ importedXcFrameworkLocation);
+ this->GenerateImportedFileChecksCode(
+ os, gtgt, properties, importedLocations, importedXcFrameworkLocation);
}
}
}
diff --git a/Source/cmExportInstallFileGenerator.h b/Source/cmExportInstallFileGenerator.h
index e073a31..9de0c8e 100644
--- a/Source/cmExportInstallFileGenerator.h
+++ b/Source/cmExportInstallFileGenerator.h
@@ -12,12 +12,13 @@
#include <vector>
#include "cmExportFileGenerator.h"
+#include "cmInstallExportGenerator.h"
#include "cmStateTypes.h"
+class cmExportSet;
class cmFileSet;
class cmGeneratorTarget;
class cmGlobalGenerator;
-class cmInstallExportGenerator;
class cmInstallTargetGenerator;
class cmTargetExport;
@@ -121,6 +122,11 @@ protected:
void GenerateCxxModuleConfigInformation(std::ostream&) const override;
bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&);
+ cmExportSet* GetExportSet() const override
+ {
+ return this->IEGen->GetExportSet();
+ }
+
cmInstallExportGenerator* IEGen;
// The import file generated for each configuration.
diff --git a/Source/cmExportSet.cxx b/Source/cmExportSet.cxx
index 3d4ef0a..b32bb8d 100644
--- a/Source/cmExportSet.cxx
+++ b/Source/cmExportSet.cxx
@@ -1,6 +1,6 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
-#include "cmExportSet.h"
+#include "cmExportSet.h" // IWYU pragma: associated
#include <algorithm>
#include <tuple>
@@ -11,7 +11,7 @@
#include "cmMessageType.h"
#include "cmStringAlgorithms.h"
#include "cmTarget.h"
-#include "cmTargetExport.h"
+#include "cmTargetExport.h" // IWYU pragma: associated
cmExportSet::cmExportSet(std::string name)
: Name(std::move(name))
@@ -20,6 +20,17 @@ cmExportSet::cmExportSet(std::string name)
cmExportSet::~cmExportSet() = default;
+cmExportSet::PackageDependency& cmExportSet::GetPackageDependencyForSetup(
+ const std::string& name)
+{
+ auto& dep = this->PackageDependencies[name];
+ if (!dep.SpecifiedIndex) {
+ dep.SpecifiedIndex = this->NextPackageDependencyIndex;
+ this->NextPackageDependencyIndex++;
+ }
+ return dep;
+}
+
bool cmExportSet::Compute(cmLocalGenerator* lg)
{
for (std::unique_ptr<cmTargetExport>& tgtExport : this->TargetExports) {
@@ -61,6 +72,16 @@ void cmExportSet::AddInstallation(cmInstallExportGenerator const* installation)
this->Installations.push_back(installation);
}
+void cmExportSet::SetXcFrameworkLocation(const std::string& name,
+ const std::string& location)
+{
+ for (auto& te : this->TargetExports) {
+ if (name == te->TargetName) {
+ te->XcFrameworkLocation = location;
+ }
+ }
+}
+
cmExportSet& cmExportSetMap::operator[](const std::string& name)
{
auto it = this->find(name);
diff --git a/Source/cmExportSet.h b/Source/cmExportSet.h
index b75a26d..f2fc4a7 100644
--- a/Source/cmExportSet.h
+++ b/Source/cmExportSet.h
@@ -9,6 +9,8 @@
#include <string>
#include <vector>
+#include <cm/optional>
+
class cmInstallExportGenerator;
class cmLocalGenerator;
class cmTargetExport;
@@ -31,6 +33,9 @@ public:
void AddInstallation(cmInstallExportGenerator const* installation);
+ void SetXcFrameworkLocation(const std::string& name,
+ const std::string& location);
+
std::string const& GetName() const { return this->Name; }
std::vector<std::unique_ptr<cmTargetExport>> const& GetTargetExports() const
@@ -43,10 +48,36 @@ public:
return &this->Installations;
}
+ enum class PackageDependencyExportEnabled
+ {
+ Auto,
+ Off,
+ On,
+ };
+
+ struct PackageDependency
+ {
+ PackageDependencyExportEnabled Enabled =
+ PackageDependencyExportEnabled::Auto;
+ std::vector<std::string> ExtraArguments;
+ cm::optional<unsigned int> SpecifiedIndex;
+ cm::optional<unsigned int> FindPackageIndex;
+ };
+
+ PackageDependency& GetPackageDependencyForSetup(const std::string& name);
+
+ const std::map<std::string, PackageDependency>& GetPackageDependencies()
+ const
+ {
+ return this->PackageDependencies;
+ }
+
private:
std::vector<std::unique_ptr<cmTargetExport>> TargetExports;
std::string Name;
std::vector<cmInstallExportGenerator const*> Installations;
+ std::map<std::string, PackageDependency> PackageDependencies;
+ unsigned int NextPackageDependencyIndex = 0;
};
/// A name -> cmExportSet map with overloaded operator[].
diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx
index 8b0f309..4524ba6 100644
--- a/Source/cmFileAPI.cxx
+++ b/Source/cmFileAPI.cxx
@@ -727,7 +727,7 @@ std::string cmFileAPI::NoSupportedVersion(
// The "codemodel" object kind.
// Update Help/manual/cmake-file-api.7.rst when updating this constant.
-static unsigned int const CodeModelV2Minor = 6;
+static unsigned int const CodeModelV2Minor = 7;
void cmFileAPI::BuildClientRequestCodeModel(
ClientRequest& r, std::vector<RequestVersion> const& versions)
diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx
index d069186..30a7e67 100644
--- a/Source/cmFileAPICodemodel.cxx
+++ b/Source/cmFileAPICodemodel.cxx
@@ -41,10 +41,12 @@
#include "cmInstallSubdirectoryGenerator.h"
#include "cmInstallTargetGenerator.h"
#include "cmLinkLineComputer.h" // IWYU pragma: keep
+#include "cmList.h"
#include "cmListFileCache.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
+#include "cmRange.h"
#include "cmSourceFile.h"
#include "cmSourceGroup.h"
#include "cmState.h"
@@ -503,6 +505,8 @@ class Target
Json::Value DumpDependencies();
Json::Value DumpDependency(cmTargetDepend const& td);
Json::Value DumpFolder();
+ Json::Value DumpLauncher(const char* name, const char* type);
+ Json::Value DumpLaunchers();
public:
Target(cmGeneratorTarget* gt, std::string const& config);
@@ -1223,6 +1227,13 @@ Json::Value Target::Dump()
target["archive"] = this->DumpArchive();
}
+ if (type == cmStateEnums::EXECUTABLE) {
+ Json::Value launchers = this->DumpLaunchers();
+ if (!launchers.empty()) {
+ target["launchers"] = std::move(launchers);
+ }
+ }
+
Json::Value dependencies = this->DumpDependencies();
if (!dependencies.empty()) {
target["dependencies"] = dependencies;
@@ -2075,6 +2086,46 @@ Json::Value Target::DumpFolder()
}
return folder;
}
+
+Json::Value Target::DumpLauncher(const char* name, const char* type)
+{
+ cmValue property = this->GT->GetProperty(name);
+ Json::Value launcher;
+ if (property) {
+ cmList commandWithArgs{ *property };
+ std::string command(commandWithArgs[0]);
+ cmSystemTools::ConvertToUnixSlashes(command);
+ launcher = Json::objectValue;
+ launcher["command"] = RelativeIfUnder(this->TopSource, command);
+ launcher["type"] = type;
+ Json::Value args;
+ for (std::string const& arg : cmMakeRange(commandWithArgs).advance(1)) {
+ args.append(arg);
+ }
+ if (!args.empty()) {
+ launcher["arguments"] = std::move(args);
+ }
+ }
+ return launcher;
+}
+
+Json::Value Target::DumpLaunchers()
+{
+ Json::Value launchers;
+ {
+ Json::Value launcher = DumpLauncher("TEST_LAUNCHER", "test");
+ if (!launcher.empty()) {
+ launchers.append(std::move(launcher));
+ }
+ }
+ if (this->GT->Makefile->IsOn("CMAKE_CROSSCOMPILING")) {
+ Json::Value emulator = DumpLauncher("CROSSCOMPILING_EMULATOR", "emulator");
+ if (!emulator.empty()) {
+ launchers.append(std::move(emulator));
+ }
+ }
+ return launchers;
+}
}
Json::Value cmFileAPICodemodelDump(cmFileAPI& fileAPI, unsigned long version)
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index c65136c..c1b7b48 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -1322,13 +1322,15 @@ bool HandleRealPathCommand(std::vector<std::string> const& args,
if (oldPolicyPath != realPath) {
status.GetMakefile().IssueMessage(
MessageType::AUTHOR_WARNING,
- cmStrCat(
- cmPolicies::GetPolicyWarning(cmPolicies::CMP0152), '\n',
- "From input path:\n ", input,
- "\nthe policy OLD behavior produces path:\n ", oldPolicyPath,
- "\nbut the policy NEW behavior produces path:\n ", realPath,
- "\nSince the policy is not set, CMake is using the OLD "
- "behavior for compatibility."));
+ cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0152),
+ "\n"
+ "From input path:\n ",
+ input, "\nthe policy OLD behavior produces path:\n ",
+ oldPolicyPath,
+ "\nbut the policy NEW behavior produces path:\n ",
+ realPath,
+ "\nSince the policy is not set, CMake is using the OLD "
+ "behavior for compatibility."));
}
}
realPath = oldPolicyPath;
diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx
index 30458cd..9b51b1a 100644
--- a/Source/cmFindPackageCommand.cxx
+++ b/Source/cmFindPackageCommand.cxx
@@ -1044,6 +1044,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
PushPopRootPathStack pushPopRootPathStack(*this);
SetRestoreFindDefinitions setRestoreFindDefinitions(*this, components,
componentVarDefs);
+ cmMakefile::FindPackageStackRAII findPackageStackRAII(this->Makefile,
+ this->Name);
// See if we have been told to delegate to FetchContent or some other
// redirected config package first. We have to check all names that
diff --git a/Source/cmFindPackageStack.cxx b/Source/cmFindPackageStack.cxx
new file mode 100644
index 0000000..1aeb2a7
--- /dev/null
+++ b/Source/cmFindPackageStack.cxx
@@ -0,0 +1,7 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#define cmFindPackageStack_cxx
+#include "cmFindPackageStack.h"
+
+#include "cmConstStack.tcc" // IWYU pragma: keep
+template class cmConstStack<cmFindPackageCall, cmFindPackageStack>;
diff --git a/Source/cmFindPackageStack.h b/Source/cmFindPackageStack.h
new file mode 100644
index 0000000..2062fbc
--- /dev/null
+++ b/Source/cmFindPackageStack.h
@@ -0,0 +1,33 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <memory>
+#include <string>
+
+#include "cmConstStack.h"
+
+/**
+ * Represents one call to find_package.
+ */
+class cmFindPackageCall
+{
+public:
+ std::string Name;
+ unsigned int Index;
+};
+
+/**
+ * Represents a stack of find_package calls with efficient value semantics.
+ */
+class cmFindPackageStack
+ : public cmConstStack<cmFindPackageCall, cmFindPackageStack>
+{
+ using cmConstStack::cmConstStack;
+ friend class cmConstStack<cmFindPackageCall, cmFindPackageStack>;
+};
+#ifndef cmFindPackageStack_cxx
+extern template class cmConstStack<cmFindPackageCall, cmFindPackageStack>;
+#endif
diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx
index d51dbd0..4e46df7 100644
--- a/Source/cmGeneratorExpressionDAGChecker.cxx
+++ b/Source/cmGeneratorExpressionDAGChecker.cxx
@@ -175,14 +175,15 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingLinkExpression() const
cm::string_view property(this->Top()->Property);
return property == "LINK_DIRECTORIES"_s || property == "LINK_OPTIONS"_s ||
- property == "LINK_DEPENDS"_s || property == "LINK_LIBRARY_OVERRIDE"_s;
+ property == "LINK_DEPENDS"_s || property == "LINK_LIBRARY_OVERRIDE"_s ||
+ property == "LINKER_TYPE"_s;
}
bool cmGeneratorExpressionDAGChecker::EvaluatingLinkOptionsExpression() const
{
cm::string_view property(this->Top()->Property);
- return property == "LINK_OPTIONS"_s;
+ return property == "LINK_OPTIONS"_s || property == "LINKER_TYPE"_s;
}
bool cmGeneratorExpressionDAGChecker::EvaluatingLinkerLauncher() const
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 9bd9fef..289bb24 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -868,6 +868,31 @@ cmValue cmGeneratorTarget::GetFeature(const std::string& feature,
return this->LocalGenerator->GetFeature(feature, config);
}
+std::string cmGeneratorTarget::GetLinkerTypeProperty(
+ std::string const& lang, std::string const& config) const
+{
+ std::string propName{ "LINKER_TYPE" };
+ auto linkerType = this->GetProperty(propName);
+ if (!linkerType.IsEmpty()) {
+ cmGeneratorExpressionDAGChecker dagChecker(this, propName, nullptr,
+ nullptr);
+ auto ltype =
+ cmGeneratorExpression::Evaluate(*linkerType, this->GetLocalGenerator(),
+ config, this, &dagChecker, this, lang);
+ if (this->IsDeviceLink()) {
+ cmList list{ ltype };
+ const auto DL_BEGIN = "<DEVICE_LINK>"_s;
+ const auto DL_END = "</DEVICE_LINK>"_s;
+ cm::erase_if(list, [&](const std::string& item) {
+ return item == DL_BEGIN || item == DL_END;
+ });
+ return list.to_string();
+ }
+ return ltype;
+ }
+ return std::string{};
+}
+
const char* cmGeneratorTarget::GetLinkPIEProperty(
const std::string& config) const
{
@@ -1821,32 +1846,31 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetSourceFilePaths(
AddInterfaceEntries(this, config, "INTERFACE_SOURCES", std::string(),
&dagChecker, linkInterfaceSourcesEntries,
IncludeRuntimeInterface::No, LinkInterfaceFor::Usage);
- std::vector<std::string>::size_type numFilesBefore = files.size();
bool contextDependentInterfaceSources = processSources(
this, linkInterfaceSourcesEntries, files, uniqueSrcs, debugSources);
// Collect TARGET_OBJECTS of direct object link-dependencies.
bool contextDependentObjects = false;
- std::vector<std::string>::size_type numFilesBefore2 = files.size();
if (this->GetType() != cmStateEnums::OBJECT_LIBRARY) {
EvaluatedTargetPropertyEntries linkObjectsEntries;
AddObjectEntries(this, config, &dagChecker, linkObjectsEntries);
contextDependentObjects = processSources(this, linkObjectsEntries, files,
uniqueSrcs, debugSources);
+ // Note that for imported targets or multi-config generators supporting
+ // cross-config builds the paths to the object files must be per-config,
+ // so contextDependentObjects will be true here even if object libraries
+ // are specified without per-config generator expressions.
}
// Collect this target's file sets.
- std::vector<std::string>::size_type numFilesBefore3 = files.size();
EvaluatedTargetPropertyEntries fileSetEntries;
AddFileSetEntries(this, config, &dagChecker, fileSetEntries);
bool contextDependentFileSets =
processSources(this, fileSetEntries, files, uniqueSrcs, debugSources);
// Determine if sources are context-dependent or not.
- if (!contextDependentDirectSources &&
- !(contextDependentInterfaceSources && numFilesBefore < files.size()) &&
- !(contextDependentObjects && numFilesBefore2 < files.size()) &&
- !(contextDependentFileSets && numFilesBefore3 < files.size())) {
+ if (!contextDependentDirectSources && !contextDependentInterfaceSources &&
+ !contextDependentObjects && !contextDependentFileSets) {
this->SourcesAreContextDependent = Tribool::False;
} else {
this->SourcesAreContextDependent = Tribool::True;
@@ -5528,6 +5552,50 @@ std::string cmGeneratorTarget::GetLinkerLanguage(
return this->GetLinkClosure(config)->LinkerLanguage;
}
+std::string cmGeneratorTarget::GetLinkerTool(const std::string& config) const
+{
+ return this->GetLinkerTool(this->GetLinkerLanguage(config), config);
+}
+
+std::string cmGeneratorTarget::GetLinkerTool(const std::string& lang,
+ const std::string& config) const
+{
+ auto usingLinker =
+ cmStrCat("CMAKE_", lang, "_USING_", this->IsDeviceLink() ? "DEVICE_" : "",
+ "LINKER_");
+ auto format = this->Makefile->GetDefinition(cmStrCat(usingLinker, "MODE"));
+ if (!format || format != "TOOL"_s) {
+ return this->Makefile->GetDefinition("CMAKE_LINKER");
+ }
+
+ auto linkerType = this->GetLinkerTypeProperty(lang, config);
+ if (linkerType.empty()) {
+ linkerType = "DEFAULT";
+ }
+ usingLinker = cmStrCat(usingLinker, linkerType);
+ auto linkerTool = this->Makefile->GetDefinition(usingLinker);
+
+ if (!linkerTool) {
+ if (this->GetGlobalGenerator()->IsVisualStudio() &&
+ linkerType == "DEFAULT"_s) {
+ return std::string{};
+ }
+
+ // fall-back to generic definition
+ linkerTool = this->Makefile->GetDefinition("CMAKE_LINKER");
+
+ if (linkerType != "DEFAULT"_s) {
+ this->LocalGenerator->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("LINKER_TYPE '", linkerType,
+ "' is unknown. Did you forgot to define '", usingLinker,
+ "' variable?"));
+ }
+ }
+
+ return linkerTool;
+}
+
std::string cmGeneratorTarget::GetPDBOutputName(
const std::string& config) const
{
@@ -6846,7 +6914,8 @@ bool cmGeneratorTarget::IsLinkLookupScope(std::string const& n,
cm::optional<cmLinkItem> cmGeneratorTarget::LookupLinkItem(
std::string const& n, cmListFileBacktrace const& bt,
- LookupLinkItemScope* scope, LookupSelf lookupSelf) const
+ std::string const& linkFeature, LookupLinkItemScope* scope,
+ LookupSelf lookupSelf) const
{
cm::optional<cmLinkItem> maybeItem;
if (this->IsLinkLookupScope(n, scope->LG)) {
@@ -6858,7 +6927,8 @@ cm::optional<cmLinkItem> cmGeneratorTarget::LookupLinkItem(
(lookupSelf == LookupSelf::No && name == this->GetName())) {
return maybeItem;
}
- maybeItem = this->ResolveLinkItem(BT<std::string>(name, bt), scope->LG);
+ maybeItem =
+ this->ResolveLinkItem(BT<std::string>(name, bt), scope->LG, linkFeature);
return maybeItem;
}
@@ -6889,9 +6959,16 @@ void cmGeneratorTarget::ExpandLinkItems(
cmList libs{ cge->Evaluate(this->LocalGenerator, config, headTarget,
&dagChecker, this,
headTarget->LinkerLanguage) };
+
+ auto linkFeature = cmLinkItem::DEFAULT;
for (auto const& lib : libs) {
+ if (auto maybeLinkFeature = ParseLinkFeature(lib)) {
+ linkFeature = std::move(*maybeLinkFeature);
+ continue;
+ }
+
if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem(
- lib, cge->GetBacktrace(), &scope,
+ lib, cge->GetBacktrace(), linkFeature, &scope,
field == LinkInterfaceField::Libraries ? LookupSelf::No
: LookupSelf::Yes)) {
cmLinkItem item = std::move(*maybeItem);
@@ -7637,9 +7714,16 @@ const cmLinkInterface* cmGeneratorTarget::GetImportLinkInterface(
LinkInterfaceField::Libraries, iface);
cmList deps{ info->SharedDeps };
LookupLinkItemScope scope{ this->LocalGenerator };
+
+ auto linkFeature = cmLinkItem::DEFAULT;
for (auto const& dep : deps) {
+ if (auto maybeLinkFeature = ParseLinkFeature(dep)) {
+ linkFeature = std::move(*maybeLinkFeature);
+ continue;
+ }
+
if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem(
- dep, cmListFileBacktrace(), &scope, LookupSelf::No)) {
+ dep, cmListFileBacktrace(), linkFeature, &scope, LookupSelf::No)) {
iface.SharedDeps.emplace_back(std::move(*maybeItem));
}
}
@@ -8439,7 +8523,13 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
impl.HadLinkLanguageSensitiveCondition = true;
}
+ auto linkFeature = cmLinkItem::DEFAULT;
for (auto const& lib : llibs) {
+ if (auto maybeLinkFeature = ParseLinkFeature(lib)) {
+ linkFeature = std::move(*maybeLinkFeature);
+ continue;
+ }
+
if (this->IsLinkLookupScope(lib, lg)) {
continue;
}
@@ -8486,8 +8576,8 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
}
// The entry is meant for this configuration.
- cmLinkItem item =
- this->ResolveLinkItem(BT<std::string>(name, entry.Backtrace), lg);
+ cmLinkItem item = this->ResolveLinkItem(
+ BT<std::string>(name, entry.Backtrace), lg, linkFeature);
if (item.Target) {
auto depsForTarget = synthTargetsForConfig.find(item.Target);
if (depsForTarget != synthTargetsForConfig.end()) {
@@ -8535,7 +8625,14 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
CMP0003_ComputeLinkType(config, debugConfigs);
cmTarget::LinkLibraryVectorType const& oldllibs =
this->Target->GetOriginalLinkLibraries();
+
+ auto linkFeature = cmLinkItem::DEFAULT;
for (cmTarget::LibraryID const& oldllib : oldllibs) {
+ if (auto maybeLinkFeature = ParseLinkFeature(oldllib.first)) {
+ linkFeature = std::move(*maybeLinkFeature);
+ continue;
+ }
+
if (oldllib.second != GENERAL_LibraryType && oldllib.second != linkType) {
std::string name = this->CheckCMP0004(oldllib.first);
if (name == this->GetName() || name.empty()) {
@@ -8543,7 +8640,7 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
}
// Support OLD behavior for CMP0003.
impl.WrongConfigLibraries.push_back(
- this->ResolveLinkItem(BT<std::string>(name)));
+ this->ResolveLinkItem(BT<std::string>(name), linkFeature));
}
}
}
@@ -8569,19 +8666,20 @@ cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference(
}
cmLinkItem cmGeneratorTarget::ResolveLinkItem(
- BT<std::string> const& name) const
+ BT<std::string> const& name, std::string const& linkFeature) const
{
- return this->ResolveLinkItem(name, this->LocalGenerator);
+ return this->ResolveLinkItem(name, this->LocalGenerator, linkFeature);
}
-cmLinkItem cmGeneratorTarget::ResolveLinkItem(BT<std::string> const& name,
- cmLocalGenerator const* lg) const
+cmLinkItem cmGeneratorTarget::ResolveLinkItem(
+ BT<std::string> const& name, cmLocalGenerator const* lg,
+ std::string const& linkFeature) const
{
auto bt = name.Backtrace;
TargetOrString resolved = this->ResolveTargetReference(name.Value, lg);
if (!resolved.Target) {
- return cmLinkItem(resolved.String, false, bt);
+ return cmLinkItem(resolved.String, false, bt, linkFeature);
}
// Check deprecation, issue message with `bt` backtrace.
@@ -8602,10 +8700,10 @@ cmLinkItem cmGeneratorTarget::ResolveLinkItem(BT<std::string> const& name,
// within the project.
if (resolved.Target->GetType() == cmStateEnums::EXECUTABLE &&
!resolved.Target->IsExecutableWithExports()) {
- return cmLinkItem(resolved.Target->GetName(), false, bt);
+ return cmLinkItem(resolved.Target->GetName(), false, bt, linkFeature);
}
- return cmLinkItem(resolved.Target, false, bt);
+ return cmLinkItem(resolved.Target, false, bt, linkFeature);
}
bool cmGeneratorTarget::HasPackageReferences() const
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index c13b2f6..cfb08fa 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -206,6 +206,9 @@ public:
cmValue GetFeature(const std::string& feature,
const std::string& config) const;
+ std::string GetLinkerTypeProperty(std::string const& lang,
+ std::string const& config) const;
+
const char* GetLinkPIEProperty(const std::string& config) const;
bool IsIPOEnabled(std::string const& lang, std::string const& config) const;
@@ -447,9 +450,12 @@ public:
TargetOrString ResolveTargetReference(std::string const& name,
cmLocalGenerator const* lg) const;
- cmLinkItem ResolveLinkItem(BT<std::string> const& name) const;
- cmLinkItem ResolveLinkItem(BT<std::string> const& name,
- cmLocalGenerator const* lg) const;
+ cmLinkItem ResolveLinkItem(
+ BT<std::string> const& name,
+ std::string const& linkFeature = cmLinkItem::DEFAULT) const;
+ cmLinkItem ResolveLinkItem(
+ BT<std::string> const& name, cmLocalGenerator const* lg,
+ std::string const& linkFeature = cmLinkItem::DEFAULT) const;
bool HasPackageReferences() const;
std::vector<std::string> GetPackageReferences() const;
@@ -794,6 +800,10 @@ public:
//! Return the preferred linker language for this target
std::string GetLinkerLanguage(const std::string& config) const;
+ //! Return the preferred linker tool for this target
+ std::string GetLinkerTool(const std::string& config) const;
+ std::string GetLinkerTool(const std::string& lang,
+ const std::string& config) const;
/** Does this target have a GNU implib to convert to MS format? */
bool HasImplibGNUtoMS(std::string const& config) const;
@@ -1175,6 +1185,7 @@ private:
};
cm::optional<cmLinkItem> LookupLinkItem(std::string const& n,
cmListFileBacktrace const& bt,
+ std::string const& linkFeature,
LookupLinkItemScope* scope,
LookupSelf lookupSelf) const;
diff --git a/Source/cmGhsMultiTargetGenerator.cxx b/Source/cmGhsMultiTargetGenerator.cxx
index 95e2187..940f49d 100644
--- a/Source/cmGhsMultiTargetGenerator.cxx
+++ b/Source/cmGhsMultiTargetGenerator.cxx
@@ -116,6 +116,19 @@ void cmGhsMultiTargetGenerator::Generate()
void cmGhsMultiTargetGenerator::GenerateTarget()
{
+ if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE &&
+ !this->GeneratorTarget
+ ->GetLinkerTypeProperty(
+ this->GeneratorTarget->GetLinkerLanguage(this->ConfigName),
+ this->ConfigName)
+ .empty()) {
+ // Green Hill MULTI does not support this feature.
+ cmSystemTools::Message(
+ cmStrCat("'LINKER_TYPE' property, specified on target '",
+ this->GeneratorTarget->GetName(),
+ "', is not supported by this generator."));
+ }
+
// Open the target file in copy-if-different mode.
std::string fproj =
cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index c2b972d..e74a8b0 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -694,7 +694,22 @@ void cmGlobalGenerator::EnableLanguage(
std::string includes =
mf->GetSafeDefinition("CMAKE_PROJECT_TOP_LEVEL_INCLUDES");
cmList includesList{ includes };
- for (std::string const& setupFile : includesList) {
+ for (std::string setupFile : includesList) {
+ // Any relative path without a .cmake extension is checked for valid
+ // cmake modules. This logic should be consistent with CMake's include()
+ // command. Otherwise default to checking relative path w.r.t. source
+ // directory
+ if (!cmSystemTools::FileIsFullPath(setupFile) &&
+ !cmHasLiteralSuffix(setupFile, ".cmake")) {
+ std::string mfile = mf->GetModulesFile(cmStrCat(setupFile, ".cmake"));
+ if (mfile.empty()) {
+ cmSystemTools::Error(cmStrCat(
+ "CMAKE_PROJECT_TOP_LEVEL_INCLUDES module:\n ", setupFile));
+ mf->GetState()->SetInTopLevelIncludes(false);
+ return;
+ }
+ setupFile = mfile;
+ }
std::string absSetupFile = cmSystemTools::CollapseFullPath(
setupFile, mf->GetCurrentSourceDirectory());
if (!cmSystemTools::FileExists(absSetupFile)) {
@@ -859,7 +874,11 @@ void cmGlobalGenerator::EnableLanguage(
noCompiler <<
"The " << compilerName << ":\n"
" " << *compilerFile << "\n"
- "is not a full path and was not found in the PATH.\n"
+ "is not a full path and was not found in the PATH."
+#ifdef _WIN32
+ " Perhaps the extension is missing?"
+#endif
+ "\n"
;
/* clang-format on */
} else if (!cmSystemTools::FileExists(*compilerFile)) {
@@ -1139,24 +1158,30 @@ std::string cmGlobalGenerator::GetLanguageOutputExtension(
{
const std::string& lang = source.GetLanguage();
if (!lang.empty()) {
- auto const it = this->LanguageToOutputExtension.find(lang);
- if (it != this->LanguageToOutputExtension.end()) {
- return it->second;
- }
- } else {
- // if no language is found then check to see if it is already an
- // output extension for some language. In that case it should be ignored
- // and in this map, so it will not be compiled but will just be used.
- std::string const& ext = source.GetExtension();
- if (!ext.empty()) {
- if (this->OutputExtensions.count(ext)) {
- return ext;
- }
+ return this->GetLanguageOutputExtension(lang);
+ }
+ // if no language is found then check to see if it is already an
+ // output extension for some language. In that case it should be ignored
+ // and in this map, so it will not be compiled but will just be used.
+ std::string const& ext = source.GetExtension();
+ if (!ext.empty()) {
+ if (this->OutputExtensions.count(ext)) {
+ return ext;
}
}
return "";
}
+std::string cmGlobalGenerator::GetLanguageOutputExtension(
+ std::string const& lang) const
+{
+ auto const it = this->LanguageToOutputExtension.find(lang);
+ if (it != this->LanguageToOutputExtension.end()) {
+ return it->second;
+ }
+ return "";
+}
+
std::string cmGlobalGenerator::GetLanguageFromExtension(const char* ext) const
{
// if there is an extension and it starts with . then move past the
@@ -2848,6 +2873,14 @@ void cmGlobalGenerator::AddGlobalTarget_Test(
gti.Name = this->GetTestTargetName();
gti.Message = "Running tests...";
gti.UsesTerminal = true;
+ // Unlike the 'install' target, the 'test' target does not depend on 'all'
+ // by default. Enable it only if CMAKE_SKIP_TEST_ALL_DEPENDENCY is
+ // explicitly set to OFF.
+ if (cmValue noall = mf->GetDefinition("CMAKE_SKIP_TEST_ALL_DEPENDENCY")) {
+ if (cmIsOff(noall)) {
+ gti.Depends.emplace_back(this->GetAllTargetName());
+ }
+ }
cmCustomCommandLine singleLine;
singleLine.push_back(cmSystemTools::GetCTestCommand());
singleLine.push_back("--force-new-ctest-process");
diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h
index aa54f69..d83b669 100644
--- a/Source/cmGlobalGenerator.h
+++ b/Source/cmGlobalGenerator.h
@@ -349,6 +349,8 @@ public:
int GetLinkerPreference(const std::string& lang) const;
//! What is the object file extension for a given source file?
std::string GetLanguageOutputExtension(cmSourceFile const&) const;
+ //! What is the object file extension for a given language?
+ std::string GetLanguageOutputExtension(std::string const& lang) const;
//! What is the configurations directory variable called?
virtual const char* GetCMakeCFGIntDir() const { return "."; }
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index 22fd90d..55eb9b5 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -378,6 +378,15 @@ void cmGlobalNinjaGenerator::WriteCustomCommandBuild(
}
{
+ std::string ninjaDepfilePath;
+ bool depfileIsOutput = false;
+ if (!depfile.empty()) {
+ ninjaDepfilePath = this->ConvertToNinjaPath(depfile);
+ depfileIsOutput =
+ std::find(outputs.ExplicitOuts.begin(), outputs.ExplicitOuts.end(),
+ ninjaDepfilePath) != outputs.ExplicitOuts.end();
+ }
+
cmNinjaBuild build("CUSTOM_COMMAND");
build.Comment = comment;
build.Outputs = std::move(outputs.ExplicitOuts);
@@ -405,7 +414,13 @@ void cmGlobalNinjaGenerator::WriteCustomCommandBuild(
vars["pool"] = job_pool;
}
if (!depfile.empty()) {
- vars["depfile"] = depfile;
+ vars["depfile"] = ninjaDepfilePath;
+ // Add the depfile to the `.ninja_deps` database. Since this (generally)
+ // removes the file, it cannot be declared as an output or byproduct of
+ // the command.
+ if (!depfileIsOutput) {
+ vars["deps"] = "gcc";
+ }
}
if (config.empty()) {
this->WriteBuild(*this->GetCommonFileStream(), build);
@@ -1359,10 +1374,10 @@ void cmGlobalNinjaGenerator::AppendTargetDepends(
} else {
cmNinjaDeps outs;
- auto computeISPCOuputs = [](cmGlobalNinjaGenerator* gg,
- cmGeneratorTarget const* depTarget,
- cmNinjaDeps& outputDeps,
- const std::string& targetConfig) {
+ auto computeISPCOutputs = [](cmGlobalNinjaGenerator* gg,
+ cmGeneratorTarget const* depTarget,
+ cmNinjaDeps& outputDeps,
+ const std::string& targetConfig) {
if (depTarget->CanCompileSources()) {
auto headers = depTarget->GetGeneratedISPCHeaders(targetConfig);
if (!headers.empty()) {
@@ -1386,10 +1401,10 @@ void cmGlobalNinjaGenerator::AppendTargetDepends(
}
if (targetDep.IsCross()) {
this->AppendTargetOutputs(targetDep, outs, fileConfig, depends);
- computeISPCOuputs(this, targetDep, outs, fileConfig);
+ computeISPCOutputs(this, targetDep, outs, fileConfig);
} else {
this->AppendTargetOutputs(targetDep, outs, config, depends);
- computeISPCOuputs(this, targetDep, outs, config);
+ computeISPCOutputs(this, targetDep, outs, config);
}
}
std::sort(outs.begin(), outs.end());
@@ -3242,10 +3257,3 @@ std::string cmGlobalNinjaMultiGenerator::OrderDependsTargetForTarget(
return cmStrCat("cmake_object_order_depends_target_", target->GetName(), '_',
cmSystemTools::UpperCase(config));
}
-
-std::string cmGlobalNinjaMultiGenerator::OrderDependsTargetForTargetPrivate(
- cmGeneratorTarget const* target, const std::string& config) const
-{
- return cmStrCat(this->OrderDependsTargetForTarget(target, config),
- "_private");
-}
diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h
index 220d393..3443643 100644
--- a/Source/cmGlobalNinjaGenerator.h
+++ b/Source/cmGlobalNinjaGenerator.h
@@ -349,7 +349,7 @@ public:
virtual std::string OrderDependsTargetForTarget(
cmGeneratorTarget const* target, const std::string& config) const;
- virtual std::string OrderDependsTargetForTargetPrivate(
+ std::string OrderDependsTargetForTargetPrivate(
cmGeneratorTarget const* target, const std::string& config) const;
void AppendTargetOutputs(cmGeneratorTarget const* target,
@@ -742,9 +742,6 @@ public:
std::string OrderDependsTargetForTarget(
cmGeneratorTarget const* target, const std::string& config) const override;
- std::string OrderDependsTargetForTargetPrivate(
- cmGeneratorTarget const* target, const std::string& config) const override;
-
protected:
bool OpenBuildFileStreams() override;
void CloseBuildFileStreams() override;
diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx
index 541db63..c93b140 100644
--- a/Source/cmGlobalVisualStudio10Generator.cxx
+++ b/Source/cmGlobalVisualStudio10Generator.cxx
@@ -191,6 +191,23 @@ bool cmGlobalVisualStudio10Generator::SetGeneratorToolset(
}
}
+ if (this->GeneratorToolsetFortran) {
+ if (*this->GeneratorToolsetFortran != "ifx" &&
+ *this->GeneratorToolsetFortran != "ifort") {
+ mf->IssueMessage(MessageType::FATAL_ERROR,
+ cmStrCat("Generator\n"
+ " ",
+ this->GetName(),
+ "\n"
+ "given toolset\n"
+ " fortran=",
+ *this->GeneratorToolsetFortran,
+ "\n"
+ "but the value is not \"ifx\" or \"ifort\"."));
+ this->GeneratorToolsetFortran = cm::nullopt;
+ }
+ }
+
if (!this->GeneratorToolsetVersion.empty() &&
this->GeneratorToolsetVersion != "Test Toolset Version"_s) {
// If a specific minor version of the MSVC toolset is requested, verify
@@ -300,6 +317,9 @@ bool cmGlobalVisualStudio10Generator::SetGeneratorToolset(
if (const char* cudaDir = this->GetPlatformToolsetCudaCustomDir()) {
mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR", cudaDir);
}
+ if (cm::optional<std::string> fortran = this->GetPlatformToolsetFortran()) {
+ mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET_FORTRAN", *fortran);
+ }
if (const char* vcTargetsDir = this->GetCustomVCTargetsPath()) {
mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET_VCTARGETS_CUSTOM_DIR",
vcTargetsDir);
@@ -410,6 +430,10 @@ bool cmGlobalVisualStudio10Generator::ProcessGeneratorToolsetField(
cmSystemTools::ConvertToUnixSlashes(this->CustomFlagTableDir);
return true;
}
+ if (key == "fortran"_s) {
+ this->GeneratorToolsetFortran = value;
+ return true;
+ }
if (key == "version"_s) {
this->GeneratorToolsetVersion = value;
return true;
diff --git a/Source/cmGlobalVisualStudio10Generator.h b/Source/cmGlobalVisualStudio10Generator.h
index 40bdd71..a2b351c 100644
--- a/Source/cmGlobalVisualStudio10Generator.h
+++ b/Source/cmGlobalVisualStudio10Generator.h
@@ -93,6 +93,12 @@ public:
* directory */
std::string const& GetPlatformToolsetCudaVSIntegrationSubdirString() const;
+ /** The fortran toolset name. */
+ cm::optional<std::string> GetPlatformToolsetFortran() const override
+ {
+ return this->GeneratorToolsetFortran;
+ }
+
/** Return whether we need to use No/Debug instead of false/true
for GenerateDebugInformation. */
bool GetPlatformToolsetNeedsDebugEnum() const
@@ -221,6 +227,7 @@ protected:
std::string GeneratorToolsetCudaCustomDir;
std::string GeneratorToolsetCudaNvccSubdir;
std::string GeneratorToolsetCudaVSIntegrationSubdir;
+ cm::optional<std::string> GeneratorToolsetFortran;
std::string DefaultPlatformToolset;
std::string DefaultPlatformToolsetHostArchitecture;
std::string DefaultAndroidToolset;
diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx
index 1abdd0b..9739a09 100644
--- a/Source/cmGlobalVisualStudio7Generator.cxx
+++ b/Source/cmGlobalVisualStudio7Generator.cxx
@@ -94,8 +94,8 @@ const std::string& cmGlobalVisualStudio7Generator::GetIntelProjectVersion()
cmSystemTools::ReadRegistryValue(vskey, intelVersion,
cmSystemTools::KeyWOW64_32);
unsigned int intelVersionNumber = ~0u;
- sscanf(intelVersion.c_str(), "%u", &intelVersionNumber);
- if (intelVersionNumber >= 11) {
+ if (sscanf(intelVersion.c_str(), "%u", &intelVersionNumber) != 1 ||
+ intelVersionNumber >= 11) {
// Default to latest known project file version.
intelVersion = "11.0";
} else if (intelVersionNumber == 10) {
diff --git a/Source/cmGlobalVisualStudio7Generator.h b/Source/cmGlobalVisualStudio7Generator.h
index 6f6109e..2056b2f 100644
--- a/Source/cmGlobalVisualStudio7Generator.h
+++ b/Source/cmGlobalVisualStudio7Generator.h
@@ -10,6 +10,8 @@
#include <utility>
#include <vector>
+#include <cm/optional>
+
#include <cm3p/json/value.h>
#include "cmGlobalVisualStudioGenerator.h"
@@ -102,6 +104,10 @@ public:
}
const std::string& GetIntelProjectVersion();
+ virtual cm::optional<std::string> GetPlatformToolsetFortran() const
+ {
+ return cm::nullopt;
+ }
bool FindMakeProgram(cmMakefile* mf) override;
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index 72dba42..c6bb3df 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -2480,6 +2480,25 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
buildSettings->AddAttribute("SWIFT_ACTIVE_COMPILATION_CONDITIONS",
swiftDefs.CreateList());
}
+
+ if (cm::optional<cmSwiftCompileMode> swiftCompileMode =
+ this->CurrentLocalGenerator->GetSwiftCompileMode(gtgt, configName)) {
+ switch (*swiftCompileMode) {
+ case cmSwiftCompileMode::Wholemodule:
+ buildSettings->AddAttribute("SWIFT_COMPILATION_MODE",
+ this->CreateString("wholemodule"));
+ break;
+ case cmSwiftCompileMode::Incremental:
+ case cmSwiftCompileMode::Singlefile:
+ break;
+ case cmSwiftCompileMode::Unknown:
+ this->CurrentLocalGenerator->IssueMessage(
+ MessageType::AUTHOR_WARNING,
+ cmStrCat("Unknown Swift_COMPILATION_MODE on target '",
+ gtgt->GetName(), "'"));
+ break;
+ }
+ }
}
std::string extraLinkOptionsVar;
@@ -2501,6 +2520,9 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
this->CurrentLocalGenerator->GetStaticLibraryFlags(
extraLinkOptions, configName, llang, gtgt);
} else {
+ this->CurrentLocalGenerator->AppendLinkerTypeFlags(extraLinkOptions, gtgt,
+ configName, llang);
+
cmValue targetLinkFlags = gtgt->GetProperty("LINK_FLAGS");
if (targetLinkFlags) {
this->CurrentLocalGenerator->AppendFlags(extraLinkOptions,
@@ -4278,6 +4300,15 @@ void cmGlobalXCodeGenerator::AddEmbeddedResources(cmXCodeObject* target)
dstSubfolderSpec, NoActionOnCopyByDefault);
}
+void cmGlobalXCodeGenerator::AddEmbeddedXPCServices(cmXCodeObject* target)
+{
+ static const auto dstSubfolderSpec = "16";
+
+ this->AddEmbeddedObjects(
+ target, "Embed XPC Services", "XCODE_EMBED_XPC_SERVICES", dstSubfolderSpec,
+ NoActionOnCopyByDefault, "$(CONTENTS_FOLDER_PATH)/XPCServices");
+}
+
bool cmGlobalXCodeGenerator::CreateGroups(
std::vector<cmLocalGenerator*>& generators)
{
@@ -4607,6 +4638,10 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects(
buildSettings->AddAttribute("CODE_SIGNING_ALLOWED",
this->CreateString("NO"));
}
+
+ // This code supports the OLD behavior of CMP0157. We should be able to
+ // remove computing the debug configuration set once the old behavior is
+ // removed.
auto debugConfigs = this->GetCMakeInstance()->GetDebugConfigs();
std::set<std::string> debugConfigSet(debugConfigs.begin(),
debugConfigs.end());
@@ -4616,9 +4651,16 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects(
cmXCodeObject* buildSettingsForCfg = this->CreateFlatClone(buildSettings);
- if (debugConfigSet.count(cmSystemTools::UpperCase(config.first)) == 0) {
- buildSettingsForCfg->AddAttribute("SWIFT_COMPILATION_MODE",
- this->CreateString("wholemodule"));
+ // Supports the OLD behavior of CMP0157. CMP0157 OLD behavior globally set
+ // wholemodule compilation for all non-debug configurations, for all
+ // targets.
+ if (this->CurrentMakefile
+ ->GetDefinition("CMAKE_Swift_COMPILATION_MODE_DEFAULT")
+ .IsEmpty()) {
+ if (debugConfigSet.count(cmSystemTools::UpperCase(config.first)) == 0) {
+ buildSettingsForCfg->AddAttribute("SWIFT_COMPILATION_MODE",
+ this->CreateString("wholemodule"));
+ }
}
// Put this last so it can override existing settings
@@ -4680,6 +4722,7 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects(
this->AddEmbeddedAppExtensions(t);
this->AddEmbeddedExtensionKitExtensions(t);
this->AddEmbeddedResources(t);
+ this->AddEmbeddedXPCServices(t);
// Inherit project-wide values for any target-specific search paths.
this->InheritBuildSettingAttribute(t, "HEADER_SEARCH_PATHS");
this->InheritBuildSettingAttribute(t, "SYSTEM_HEADER_SEARCH_PATHS");
diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h
index da0a4ea..12a5cad 100644
--- a/Source/cmGlobalXCodeGenerator.h
+++ b/Source/cmGlobalXCodeGenerator.h
@@ -225,6 +225,7 @@ private:
void AddEmbeddedAppExtensions(cmXCodeObject* target);
void AddEmbeddedExtensionKitExtensions(cmXCodeObject* target);
void AddEmbeddedResources(cmXCodeObject* target);
+ void AddEmbeddedXPCServices(cmXCodeObject* target);
void AddPositionIndependentLinkAttribute(cmGeneratorTarget* target,
cmXCodeObject* buildSettings,
const std::string& configName);
diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx
index 0fc4011..e2755da 100644
--- a/Source/cmInstallCommand.cxx
+++ b/Source/cmInstallCommand.cxx
@@ -2030,7 +2030,7 @@ bool HandleExportAndroidMKMode(std::vector<std::string> const& args,
cm::make_unique<cmInstallExportGenerator>(
&exportSet, ica.GetDestination(), ica.GetPermissions(),
ica.GetConfigurations(), ica.GetComponent(), message,
- ica.GetExcludeFromAll(), fname, name_space, "", exportOld, true,
+ ica.GetExcludeFromAll(), fname, name_space, "", exportOld, true, false,
helper.Makefile->GetBacktrace()));
return true;
@@ -2054,12 +2054,14 @@ bool HandleExportMode(std::vector<std::string> const& args,
bool exportOld = false;
std::string filename;
std::string cxx_modules_directory;
+ bool exportPackageDependencies = false;
ica.Bind("EXPORT"_s, exp);
ica.Bind("NAMESPACE"_s, name_space);
ica.Bind("EXPORT_LINK_INTERFACE_LIBRARIES"_s, exportOld);
ica.Bind("FILE"_s, filename);
ica.Bind("CXX_MODULES_DIRECTORY"_s, cxx_modules_directory);
+ ica.Bind("EXPORT_PACKAGE_DEPENDENCIES"_s, exportPackageDependencies);
std::vector<std::string> unknownArgs;
ica.Parse(args, &unknownArgs);
@@ -2147,7 +2149,8 @@ bool HandleExportMode(std::vector<std::string> const& args,
&exportSet, ica.GetDestination(), ica.GetPermissions(),
ica.GetConfigurations(), ica.GetComponent(), message,
ica.GetExcludeFromAll(), fname, name_space, cxx_modules_directory,
- exportOld, false, helper.Makefile->GetBacktrace()));
+ exportOld, false, exportPackageDependencies,
+ helper.Makefile->GetBacktrace()));
return true;
}
diff --git a/Source/cmInstallExportGenerator.cxx b/Source/cmInstallExportGenerator.cxx
index 71d5471..72f0ac7 100644
--- a/Source/cmInstallExportGenerator.cxx
+++ b/Source/cmInstallExportGenerator.cxx
@@ -27,7 +27,7 @@ cmInstallExportGenerator::cmInstallExportGenerator(
std::string const& component, MessageLevel message, bool exclude_from_all,
std::string filename, std::string name_space,
std::string cxx_modules_directory, bool exportOld, bool android,
- cmListFileBacktrace backtrace)
+ bool exportPackageDependencies, cmListFileBacktrace backtrace)
: cmInstallGenerator(destination, configurations, component, message,
exclude_from_all, false, std::move(backtrace))
, ExportSet(exportSet)
@@ -36,6 +36,7 @@ cmInstallExportGenerator::cmInstallExportGenerator(
, Namespace(std::move(name_space))
, CxxModulesDirectory(std::move(cxx_modules_directory))
, ExportOld(exportOld)
+ , ExportPackageDependencies(exportPackageDependencies)
{
if (android) {
#ifndef CMAKE_BOOTSTRAP
@@ -119,6 +120,7 @@ void cmInstallExportGenerator::GenerateScript(std::ostream& os)
this->EFGen->AddConfiguration(c);
}
}
+ this->EFGen->SetExportPackageDependencies(this->ExportPackageDependencies);
this->EFGen->GenerateImportFile();
// Perform the main install script generation.
diff --git a/Source/cmInstallExportGenerator.h b/Source/cmInstallExportGenerator.h
index f2d4a05..5f92851 100644
--- a/Source/cmInstallExportGenerator.h
+++ b/Source/cmInstallExportGenerator.h
@@ -29,7 +29,8 @@ public:
bool exclude_from_all, std::string filename,
std::string name_space,
std::string cxx_modules_directory, bool exportOld,
- bool android, cmListFileBacktrace backtrace);
+ bool android, bool exportPackageDependencies,
+ cmListFileBacktrace backtrace);
cmInstallExportGenerator(const cmInstallExportGenerator&) = delete;
~cmInstallExportGenerator() override;
@@ -70,6 +71,7 @@ protected:
std::string const Namespace;
std::string const CxxModulesDirectory;
bool const ExportOld;
+ bool const ExportPackageDependencies;
cmLocalGenerator* LocalGenerator = nullptr;
std::string TempDir;
diff --git a/Source/cmJSONState.cxx b/Source/cmJSONState.cxx
index 1abdaa6..5c44fba 100644
--- a/Source/cmJSONState.cxx
+++ b/Source/cmJSONState.cxx
@@ -45,7 +45,7 @@ cmJSONState::cmJSONState(const std::string& filename, Json::Value* root)
void cmJSONState::AddError(std::string const& errMsg)
{
- this->errors.push_back(Error(errMsg));
+ this->errors.emplace_back(errMsg);
}
void cmJSONState::AddErrorAtValue(std::string const& errMsg,
@@ -65,7 +65,7 @@ void cmJSONState::AddErrorAtOffset(std::string const& errMsg,
this->AddError(errMsg);
} else {
Location loc = LocateInDocument(offset);
- this->errors.push_back(Error(loc, errMsg));
+ this->errors.emplace_back(loc, errMsg);
}
}
@@ -118,7 +118,7 @@ const Json::Value* cmJSONState::value_after(std::string const& k)
void cmJSONState::push_stack(std::string const& k, const Json::Value* value)
{
- this->parseStack.push_back(JsonPair(k, value));
+ this->parseStack.emplace_back(k, value);
}
void cmJSONState::pop_stack()
diff --git a/Source/cmLinkItem.cxx b/Source/cmLinkItem.cxx
index 2dc40ff..3654176 100644
--- a/Source/cmLinkItem.cxx
+++ b/Source/cmLinkItem.cxx
@@ -4,20 +4,30 @@
#include <utility> // IWYU pragma: keep
+#include <cm/optional>
+#include <cm/string_view>
+#include <cmext/string_view>
+
#include "cmGeneratorTarget.h"
+#include "cmStringAlgorithms.h"
+
+const std::string cmLinkItem::DEFAULT = "DEFAULT";
cmLinkItem::cmLinkItem() = default;
-cmLinkItem::cmLinkItem(std::string n, bool c, cmListFileBacktrace bt)
+cmLinkItem::cmLinkItem(std::string n, bool c, cmListFileBacktrace bt,
+ std::string feature)
: String(std::move(n))
+ , Feature(std::move(feature))
, Cross(c)
, Backtrace(std::move(bt))
{
}
cmLinkItem::cmLinkItem(cmGeneratorTarget const* t, bool c,
- cmListFileBacktrace bt)
+ cmListFileBacktrace bt, std::string feature)
: Target(t)
+ , Feature(std::move(feature))
, Cross(c)
, Backtrace(std::move(bt))
{
@@ -73,3 +83,19 @@ cmLinkImplItem::cmLinkImplItem(cmLinkItem item, bool checkCMP0027)
, CheckCMP0027(checkCMP0027)
{
}
+
+namespace {
+const cm::string_view LL_BEGIN = "<LINK_LIBRARY:"_s;
+const cm::string_view LL_END = "</LINK_LIBRARY:"_s;
+}
+cm::optional<std::string> ParseLinkFeature(std::string const& item)
+{
+ if (cmHasPrefix(item, LL_BEGIN) && cmHasSuffix(item, '>')) {
+ return item.substr(LL_BEGIN.length(),
+ item.find('>', LL_BEGIN.length()) - LL_BEGIN.length());
+ }
+ if (cmHasPrefix(item, LL_END) && cmHasSuffix(item, '>')) {
+ return cmLinkItem::DEFAULT;
+ }
+ return cm::nullopt;
+}
diff --git a/Source/cmLinkItem.h b/Source/cmLinkItem.h
index e4444d3..1946c9b 100644
--- a/Source/cmLinkItem.h
+++ b/Source/cmLinkItem.h
@@ -10,6 +10,7 @@
#include <unordered_map>
#include <vector>
+#include <cm/optional>
#include <cmext/algorithm>
#include "cmListFileCache.h"
@@ -25,14 +26,20 @@ class cmLinkItem
std::string String;
public:
+ // default feature: link library without decoration
+ static const std::string DEFAULT;
+
cmLinkItem();
- cmLinkItem(std::string s, bool c, cmListFileBacktrace bt);
- cmLinkItem(cmGeneratorTarget const* t, bool c, cmListFileBacktrace bt);
+ cmLinkItem(std::string s, bool c, cmListFileBacktrace bt,
+ std::string feature = DEFAULT);
+ cmLinkItem(cmGeneratorTarget const* t, bool c, cmListFileBacktrace bt,
+ std::string feature = DEFAULT);
std::string const& AsStr() const;
cmGeneratorTarget const* Target = nullptr;
// The source file representing the external object (used when linking
// `$<TARGET_OBJECTS>`)
cmSourceFile const* ObjectSource = nullptr;
+ std::string Feature;
bool Cross = false;
cmListFileBacktrace Backtrace;
friend bool operator<(cmLinkItem const& l, cmLinkItem const& r);
@@ -160,3 +167,6 @@ inline cmTargetLinkLibraryType CMP0003_ComputeLinkType(
// The current configuration is not a debug configuration.
return OPTIMIZED_LibraryType;
}
+
+// Parse LINK_LIBRARY genex markers.
+cm::optional<std::string> ParseLinkFeature(std::string const& item);
diff --git a/Source/cmLinkItemGraphVisitor.cxx b/Source/cmLinkItemGraphVisitor.cxx
index 0ad846b..a63b794 100644
--- a/Source/cmLinkItemGraphVisitor.cxx
+++ b/Source/cmLinkItemGraphVisitor.cxx
@@ -82,7 +82,7 @@ bool cmLinkItemGraphVisitor::ItemVisited(cmLinkItem const& item)
bool cmLinkItemGraphVisitor::LinkVisited(cmLinkItem const& depender,
cmLinkItem const& dependee)
{
- auto const link = std::make_pair<>(depender.AsStr(), dependee.AsStr());
+ auto const link = std::make_pair(depender.AsStr(), dependee.AsStr());
bool const linkVisited =
this->VisitedLinks.find(link) != this->VisitedLinks.cend();
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index 168cd41..38c49ed 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -82,7 +82,6 @@ static auto ruleReplaceVars = { "CMAKE_${LANG}_COMPILER",
"CMAKE_CURRENT_SOURCE_DIR",
"CMAKE_CURRENT_BINARY_DIR",
"CMAKE_RANLIB",
- "CMAKE_LINKER",
"CMAKE_MT",
"CMAKE_TAPI",
"CMAKE_CUDA_HOST_COMPILER",
@@ -1367,7 +1366,7 @@ std::vector<BT<std::string>> cmLocalGenerator::GetStaticLibraryFlags(
{
const std::string configUpper = cmSystemTools::UpperCase(config);
std::vector<BT<std::string>> flags;
- if (linkLanguage != "Swift") {
+ if (linkLanguage != "Swift" && !this->IsSplitSwiftBuild()) {
std::string staticLibFlags;
this->AppendFlags(
staticLibFlags,
@@ -1604,6 +1603,7 @@ void cmLocalGenerator::GetTargetFlags(
}
std::string extraLinkFlags;
+ this->AppendLinkerTypeFlags(extraLinkFlags, target, config, linkLanguage);
this->AppendPositionIndependentLinkerFlags(extraLinkFlags, target, config,
linkLanguage);
this->AppendIPOLinkerFlags(extraLinkFlags, target, config, linkLanguage);
@@ -1651,6 +1651,39 @@ std::vector<BT<std::string>> cmLocalGenerator::GetTargetCompileFlags(
if (lang == "Fortran") {
this->AppendFlags(compileFlags,
this->GetTargetFortranFlags(target, config));
+ } else if (lang == "Swift") {
+ // Only set the compile mode if CMP0157 is set
+ if (cm::optional<cmSwiftCompileMode> swiftCompileMode =
+ this->GetSwiftCompileMode(target, config)) {
+ std::string swiftCompileModeFlag;
+ switch (*swiftCompileMode) {
+ case cmSwiftCompileMode::Incremental: {
+ swiftCompileModeFlag = "-incremental";
+ if (cmValue flag =
+ mf->GetDefinition("CMAKE_Swift_COMPILE_OPTIONS_INCREMENTAL")) {
+ swiftCompileModeFlag = *flag;
+ }
+ break;
+ }
+ case cmSwiftCompileMode::Wholemodule: {
+ swiftCompileModeFlag = "-wmo";
+ if (cmValue flag =
+ mf->GetDefinition("CMAKE_Swift_COMPILE_OPTIONS_WMO")) {
+ swiftCompileModeFlag = *flag;
+ }
+ break;
+ }
+ case cmSwiftCompileMode::Singlefile:
+ break;
+ case cmSwiftCompileMode::Unknown: {
+ this->IssueMessage(
+ MessageType::AUTHOR_WARNING,
+ cmStrCat("Unknown Swift_COMPILATION_MODE on target '",
+ target->GetName(), "'"));
+ }
+ }
+ this->AppendFlags(compileFlags, swiftCompileModeFlag);
+ }
}
this->AddCMP0018Flags(compileFlags, target, lang, config);
@@ -2959,6 +2992,40 @@ cm::optional<std::string> cmLocalGenerator::GetMSVCDebugFormatName(
return msvcDebugInformationFormat;
}
+cm::optional<cmSwiftCompileMode> cmLocalGenerator::GetSwiftCompileMode(
+ cmGeneratorTarget const* target, std::string const& config)
+{
+ cmMakefile const* mf = this->GetMakefile();
+ cmValue const swiftCompileModeDefault =
+ mf->GetDefinition("CMAKE_Swift_COMPILATION_MODE_DEFAULT");
+ if (!cmNonempty(swiftCompileModeDefault)) {
+ return {};
+ }
+ cmValue swiftCompileMode = target->GetProperty("Swift_COMPILATION_MODE");
+ if (!swiftCompileMode) {
+ swiftCompileMode = swiftCompileModeDefault;
+ }
+
+ std::string const expandedCompileMode =
+ cmGeneratorExpression::Evaluate(*swiftCompileMode, this, config, target);
+ if (expandedCompileMode == "wholemodule") {
+ return cmSwiftCompileMode::Wholemodule;
+ }
+ if (expandedCompileMode == "singlefile") {
+ return cmSwiftCompileMode::Singlefile;
+ }
+ if (expandedCompileMode == "incremental") {
+ return cmSwiftCompileMode::Incremental;
+ }
+ return cmSwiftCompileMode::Unknown;
+}
+
+bool cmLocalGenerator::IsSplitSwiftBuild() const
+{
+ return cmNonempty(this->GetMakefile()->GetDefinition(
+ "CMAKE_Swift_COMPILATION_MODE_DEFAULT"));
+}
+
namespace {
inline void RegisterUnitySources(cmGeneratorTarget* target, cmSourceFile* sf,
@@ -3067,8 +3134,17 @@ cmLocalGenerator::AddUnityFilesModeAuto(
chunk = std::min(itemsLeft, batchSize);
- std::string filename = cmStrCat(filename_base, "unity_", batch,
- (lang == "C") ? "_c.c" : "_cxx.cxx");
+ std::string extension;
+ if (lang == "C") {
+ extension = "_c.c";
+ } else if (lang == "CXX") {
+ extension = "_cxx.cxx";
+ } else if (lang == "OBJC") {
+ extension = "_m.m";
+ } else if (lang == "OBJCXX") {
+ extension = "_mm.mm";
+ }
+ std::string filename = cmStrCat(filename_base, "unity_", batch, extension);
auto const begin = filtered_sources.begin() + batch * batchSize;
auto const end = begin + chunk;
unity_files.emplace_back(this->WriteUnitySource(
@@ -3159,7 +3235,7 @@ void cmLocalGenerator::AddUnityBuild(cmGeneratorTarget* target)
cmValue afterInclude = target->GetProperty("UNITY_BUILD_CODE_AFTER_INCLUDE");
cmValue unityMode = target->GetProperty("UNITY_BUILD_MODE");
- for (std::string lang : { "C", "CXX" }) {
+ for (std::string lang : { "C", "CXX", "OBJC", "OBJCXX" }) {
std::vector<UnityBatchedSource> filtered_sources;
std::copy_if(unitySources.begin(), unitySources.end(),
std::back_inserter(filtered_sources),
@@ -3205,6 +3281,49 @@ void cmLocalGenerator::AddUnityBuild(cmGeneratorTarget* target)
}
}
+void cmLocalGenerator::AppendLinkerTypeFlags(std::string& flags,
+ cmGeneratorTarget* target,
+ const std::string& config,
+ const std::string& linkLanguage)
+{
+ switch (target->GetType()) {
+ case cmStateEnums::EXECUTABLE:
+ case cmStateEnums::SHARED_LIBRARY:
+ case cmStateEnums::MODULE_LIBRARY:
+ break;
+ default:
+ return;
+ }
+
+ auto usingLinker =
+ cmStrCat("CMAKE_", linkLanguage, "_USING_",
+ target->IsDeviceLink() ? "DEVICE_" : "", "LINKER_");
+
+ auto format = this->Makefile->GetDefinition(cmStrCat(usingLinker, "MODE"));
+ if (format && format != "FLAG"_s) {
+ return;
+ }
+
+ auto linkerType = target->GetLinkerTypeProperty(linkLanguage, config);
+ if (linkerType.empty()) {
+ linkerType = "DEFAULT";
+ }
+ usingLinker = cmStrCat(usingLinker, linkerType);
+ auto linkerTypeFlags = this->Makefile->GetDefinition(usingLinker);
+ if (linkerTypeFlags) {
+ if (!linkerTypeFlags.IsEmpty()) {
+ auto linkerFlags = cmExpandListWithBacktrace(linkerTypeFlags);
+ target->ResolveLinkerWrapper(linkerFlags, linkLanguage);
+ this->AppendFlags(flags, linkerFlags);
+ }
+ } else if (linkerType != "DEFAULT"_s) {
+ this->IssueMessage(MessageType::FATAL_ERROR,
+ cmStrCat("LINKER_TYPE '", linkerType,
+ "' is unknown. Did you forgot to define '",
+ usingLinker, "' variable?"));
+ }
+}
+
void cmLocalGenerator::AppendIPOLinkerFlags(std::string& flags,
cmGeneratorTarget* target,
const std::string& config,
diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h
index a920cfe..3ec349b 100644
--- a/Source/cmLocalGenerator.h
+++ b/Source/cmLocalGenerator.h
@@ -67,6 +67,15 @@ enum class cmBuildStep
Link
};
+/** What compilation mode the swift files are in */
+enum class cmSwiftCompileMode
+{
+ Wholemodule,
+ Incremental,
+ Singlefile,
+ Unknown,
+};
+
/** Target and source file which have a specific output. */
struct cmSourcesWithOutput
{
@@ -177,6 +186,9 @@ public:
void AddPchDependencies(cmGeneratorTarget* target);
void AddUnityBuild(cmGeneratorTarget* target);
virtual void AddXCConfigSources(cmGeneratorTarget* /* target */) {}
+ void AppendLinkerTypeFlags(std::string& flags, cmGeneratorTarget* target,
+ const std::string& config,
+ const std::string& linkLanguage);
void AppendIPOLinkerFlags(std::string& flags, cmGeneratorTarget* target,
const std::string& config,
const std::string& lang);
@@ -546,6 +558,14 @@ public:
const std::string& prop,
const std::string& config);
+ // Return Swift_COMPILATION_MODE value if CMP0157 is NEW.
+ cm::optional<cmSwiftCompileMode> GetSwiftCompileMode(
+ cmGeneratorTarget const* target, std::string const& config);
+
+ // Can we build Swift with a separate object build and link step
+ // (If CMP0157 is NEW, we can do a split build)
+ bool IsSplitSwiftBuild() const;
+
protected:
// The default implementation converts to a Windows shortpath to
// help older toolchains handle spaces and such. A generator may
diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx
index 7b02c56..d315f0f 100644
--- a/Source/cmLocalVisualStudio7Generator.cxx
+++ b/Source/cmLocalVisualStudio7Generator.cxx
@@ -32,6 +32,7 @@
#include "cmList.h"
#include "cmListFileCache.h"
#include "cmMakefile.h"
+#include "cmMessageType.h"
#include "cmOutputConverter.h"
#include "cmPolicies.h"
#include "cmSourceFile.h"
@@ -794,6 +795,9 @@ void cmLocalVisualStudio7Generator::WriteConfiguration(
target->GetType() == cmStateEnums::OBJECT_LIBRARY
? ".lib"
: cmSystemTools::GetFilenameLastExtension(targetNameFull);
+ if (cm::optional<std::string> fortran = gg->GetPlatformToolsetFortran()) {
+ fout << "\t\t\tUseCompiler=\"" << *fortran << "Compiler\"\n";
+ }
/* clang-format off */
fout <<
"\t\t\tTargetName=\"" << this->EscapeForXML(targetName) << "\"\n"
@@ -1085,6 +1089,16 @@ void cmLocalVisualStudio7Generator::OutputBuildTool(
cmComputeLinkInformation& cli = *pcli;
std::string linkLanguage = cli.GetLinkLanguage();
+ if (!target->GetLinkerTypeProperty(linkLanguage, configName).empty()) {
+ // Visual Studio 10 or upper is required for this feature
+ this->GetCMakeInstance()->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("'LINKER_TYPE' property, specified on target '",
+ target->GetName(),
+ "', is not supported by this generator."),
+ target->GetBacktrace());
+ }
+
// Compute the variable name to lookup standard libraries for this
// language.
std::string standardLibsVar =
@@ -1161,6 +1175,16 @@ void cmLocalVisualStudio7Generator::OutputBuildTool(
cmComputeLinkInformation& cli = *pcli;
std::string linkLanguage = cli.GetLinkLanguage();
+ if (!target->GetLinkerTypeProperty(linkLanguage, configName).empty()) {
+ // Visual Studio 10 or upper is required for this feature
+ this->GetCMakeInstance()->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("'LINKER_TYPE' property, specified on target '",
+ target->GetName(),
+ "', is not supported by this generator."),
+ target->GetBacktrace());
+ }
+
bool isWin32Executable = target->IsWin32Executable(configName);
// Compute the variable name to lookup standard libraries for this
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 93fb8b4..936b282 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -302,6 +302,11 @@ cmListFileBacktrace cmMakefile::GetBacktrace() const
return this->Backtrace;
}
+cmFindPackageStack cmMakefile::GetFindPackageStack() const
+{
+ return this->FindPackageStack;
+}
+
void cmMakefile::PrintCommandTrace(cmListFileFunction const& lff,
cmListFileBacktrace const& bt,
CommandMissingFromStack missing) const
@@ -1215,8 +1220,8 @@ cmTarget* cmMakefile::AddCustomCommandToTarget(
// Dispatch command creation to allow generator expressions in outputs.
this->AddGeneratorAction(
std::move(cc),
- [=](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt,
- std::unique_ptr<cmCustomCommand> tcc) {
+ [this, t, type](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt,
+ std::unique_ptr<cmCustomCommand> tcc) {
BacktraceGuard guard(this->Backtrace, lfbt);
tcc->SetBacktrace(lfbt);
detail::AddCustomCommandToTarget(lg, cmCommandOrigin::Project, t, type,
@@ -1254,8 +1259,9 @@ void cmMakefile::AddCustomCommandToOutput(
// Dispatch command creation to allow generator expressions in outputs.
this->AddGeneratorAction(
std::move(cc),
- [=](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt,
- std::unique_ptr<cmCustomCommand> tcc) {
+ [this, replace, callback](cmLocalGenerator& lg,
+ const cmListFileBacktrace& lfbt,
+ std::unique_ptr<cmCustomCommand> tcc) {
BacktraceGuard guard(this->Backtrace, lfbt);
tcc->SetBacktrace(lfbt);
cmSourceFile* sf = detail::AddCustomCommandToOutput(
@@ -1341,7 +1347,8 @@ void cmMakefile::AppendCustomCommandToOutput(
if (this->ValidateCustomCommand(commandLines)) {
// Dispatch command creation to allow generator expressions in outputs.
this->AddGeneratorAction(
- [=](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt) {
+ [this, output, depends, implicit_depends,
+ commandLines](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt) {
BacktraceGuard guard(this->Backtrace, lfbt);
detail::AppendCustomCommandToOutput(lg, lfbt, output, depends,
implicit_depends, commandLines);
@@ -1372,8 +1379,8 @@ cmTarget* cmMakefile::AddUtilityCommand(const std::string& utilityName,
// Dispatch command creation to allow generator expressions in outputs.
this->AddGeneratorAction(
std::move(cc),
- [=](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt,
- std::unique_ptr<cmCustomCommand> tcc) {
+ [this, target](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt,
+ std::unique_ptr<cmCustomCommand> tcc) {
BacktraceGuard guard(this->Backtrace, lfbt);
tcc->SetBacktrace(lfbt);
detail::AddUtilityCommand(lg, cmCommandOrigin::Project, target,
@@ -4633,12 +4640,13 @@ bool cmMakefile::SetPolicy(cmPolicies::PolicyID id,
}
// Deprecate old policies.
- if (status == cmPolicies::OLD && id <= cmPolicies::CMP0120 &&
+ if (status == cmPolicies::OLD && id <= cmPolicies::CMP0126 &&
!(this->GetCMakeInstance()->GetIsInTryCompile() &&
(
// Policies set by cmCoreTryCompile::TryCompileCode.
id == cmPolicies::CMP0065 || id == cmPolicies::CMP0083 ||
- id == cmPolicies::CMP0091 || id == cmPolicies::CMP0104)) &&
+ id == cmPolicies::CMP0091 || id == cmPolicies::CMP0104 ||
+ id == cmPolicies::CMP0123 || id == cmPolicies::CMP0126)) &&
(!this->IsSet("CMAKE_WARN_DEPRECATED") ||
this->IsOn("CMAKE_WARN_DEPRECATED"))) {
this->IssueMessage(MessageType::DEPRECATION_WARNING,
@@ -4771,6 +4779,36 @@ cmMakefile::MacroPushPop::~MacroPushPop()
this->Makefile->PopMacroScope(this->ReportError);
}
+cmMakefile::FindPackageStackRAII::FindPackageStackRAII(cmMakefile* mf,
+ std::string const& name)
+ : Makefile(mf)
+{
+ this->Makefile->FindPackageStack =
+ this->Makefile->FindPackageStack.Push(cmFindPackageCall{
+ name,
+ this->Makefile->FindPackageStackNextIndex,
+ });
+ this->Makefile->FindPackageStackNextIndex++;
+}
+
+cmMakefile::FindPackageStackRAII::~FindPackageStackRAII()
+{
+ this->Makefile->FindPackageStackNextIndex =
+ this->Makefile->FindPackageStack.Top().Index + 1;
+ this->Makefile->FindPackageStack = this->Makefile->FindPackageStack.Pop();
+
+ if (!this->Makefile->FindPackageStack.Empty()) {
+ auto top = this->Makefile->FindPackageStack.Top();
+ this->Makefile->FindPackageStack = this->Makefile->FindPackageStack.Pop();
+
+ top.Index = this->Makefile->FindPackageStackNextIndex;
+ this->Makefile->FindPackageStackNextIndex++;
+
+ this->Makefile->FindPackageStack =
+ this->Makefile->FindPackageStack.Push(top);
+ }
+}
+
cmMakefile::DebugFindPkgRAII::DebugFindPkgRAII(cmMakefile* mf,
std::string const& pkg)
: Makefile(mf)
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h
index 24daa72..e5edbae 100644
--- a/Source/cmMakefile.h
+++ b/Source/cmMakefile.h
@@ -25,6 +25,7 @@
#include "cmAlgorithms.h"
#include "cmCustomCommand.h"
+#include "cmFindPackageStack.h"
#include "cmFunctionBlocker.h"
#include "cmListFileCache.h"
#include "cmMessageType.h" // IWYU pragma: keep
@@ -660,6 +661,11 @@ public:
cmListFileBacktrace GetBacktrace() const;
/**
+ * Get the current stack of find_package calls.
+ */
+ cmFindPackageStack GetFindPackageStack() const;
+
+ /**
* Get the vector of files created by this makefile
*/
const std::vector<std::string>& GetOutputFiles() const
@@ -1020,6 +1026,15 @@ public:
// searches
std::deque<std::vector<std::string>> FindPackageRootPathStack;
+ class FindPackageStackRAII
+ {
+ cmMakefile* Makefile;
+
+ public:
+ FindPackageStackRAII(cmMakefile* mf, std::string const& pkg);
+ ~FindPackageStackRAII();
+ };
+
class DebugFindPkgRAII
{
cmMakefile* Makefile;
@@ -1210,6 +1225,9 @@ private:
std::vector<BT<GeneratorAction>> GeneratorActions;
bool GeneratorActionsInvoked = false;
+ cmFindPackageStack FindPackageStack;
+ unsigned int FindPackageStackNextIndex = 0;
+
bool DebugFindPkg = false;
bool CheckSystemVars;
diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx
index 4a2b9e8..96a0d5c 100644
--- a/Source/cmMakefileExecutableTargetGenerator.cxx
+++ b/Source/cmMakefileExecutableTargetGenerator.cxx
@@ -344,6 +344,8 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
return;
}
+ auto linker = this->GeneratorTarget->GetLinkerTool(this->GetConfigName());
+
// Build list of dependencies.
std::vector<std::string> depends;
this->AppendLinkDepends(depends, linkLanguage);
@@ -533,6 +535,7 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
vars.CMTargetType =
cmState::GetTargetTypeName(this->GeneratorTarget->GetType()).c_str();
vars.Language = linkLanguage.c_str();
+ vars.Linker = linker.c_str();
vars.AIXExports = aixExports.c_str();
vars.Objects = buildObjs.c_str();
std::string objectDir = this->GeneratorTarget->GetSupportDirectory();
diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx
index fc3caa1..bc48a3b 100644
--- a/Source/cmMakefileLibraryTargetGenerator.cxx
+++ b/Source/cmMakefileLibraryTargetGenerator.cxx
@@ -218,6 +218,9 @@ void cmMakefileLibraryTargetGenerator::WriteModuleLibraryRules(bool relink)
extraFlags, this->GeneratorTarget, linkLineComputer.get(),
this->GetConfigName());
+ this->UseLWYU = this->LocalGenerator->AppendLWYUFlags(
+ extraFlags, this->GeneratorTarget, linkLanguage);
+
this->WriteLibraryRules(linkRuleVar, extraFlags, relink);
}
@@ -441,6 +444,8 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
return;
}
+ auto linker = this->GeneratorTarget->GetLinkerTool(this->GetConfigName());
+
// Build list of dependencies.
std::vector<std::string> depends;
this->AppendLinkDepends(depends, linkLanguage);
@@ -766,6 +771,7 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
vars.CMTargetType =
cmState::GetTargetTypeName(this->GeneratorTarget->GetType()).c_str();
vars.Language = linkLanguage.c_str();
+ vars.Linker = linker.c_str();
vars.AIXExports = aixExports.c_str();
vars.Objects = buildObjs.c_str();
std::string objectDir = this->GeneratorTarget->GetSupportDirectory();
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx
index 74b4b75..8fda774 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/Source/cmMakefileTargetGenerator.cxx
@@ -153,6 +153,8 @@ void cmMakefileTargetGenerator::GetTargetLinkFlags(
this->LocalGenerator->AppendCompileOptions(flags, opts);
this->LocalGenerator->SetLinkScriptShell(false);
+ this->LocalGenerator->AppendLinkerTypeFlags(
+ flags, this->GeneratorTarget, this->GetConfigName(), linkLanguage);
this->LocalGenerator->AppendPositionIndependentLinkerFlags(
flags, this->GeneratorTarget, this->GetConfigName(), linkLanguage);
this->LocalGenerator->AppendDependencyInfoLinkerFlags(
diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx
index 48c30b6..99ea009 100644
--- a/Source/cmNinjaNormalTargetGenerator.cxx
+++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -294,6 +294,9 @@ void cmNinjaNormalTargetGenerator::WriteNvidiaDeviceLinkRule(
.c_str();
vars.Language = "CUDA";
+ std::string linker =
+ this->GetGeneratorTarget()->GetLinkerTool("CUDA", config);
+ vars.Linker = linker.c_str();
// build response file name
std::string responseFlag = this->GetMakefile()->GetSafeDefinition(
@@ -400,6 +403,9 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkRules(
vars.Fatbinary = "$FATBIN";
vars.RegisterFile = "$REGISTER";
vars.LinkFlags = "$LINK_FLAGS";
+ std::string linker =
+ this->GetGeneratorTarget()->GetLinkerTool("CUDA", config);
+ vars.Linker = linker.c_str();
std::string flags = this->GetFlags("CUDA", config);
vars.Flags = flags.c_str();
@@ -441,6 +447,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile,
vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str();
vars.CMTargetType = cmState::GetTargetTypeName(targetType).c_str();
+ std::string linker = this->GetGeneratorTarget()->GetLinkerTool(config);
+ vars.Linker = linker.c_str();
std::string lang = this->TargetLinkLanguage(config);
vars.Language = lang.c_str();
vars.AIXExports = "$AIX_EXPORTS";
@@ -1201,7 +1209,10 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
globalGen->GetByproductsForCleanTarget(config).push_back(targetOutputReal);
}
- if (this->TargetLinkLanguage(config) == "Swift") {
+ // If we can't split the Swift build model (CMP0157 is OLD or unset), fall
+ // back on the old one-step "build/link" logic.
+ if (!this->GetLocalGenerator()->IsSplitSwiftBuild() &&
+ this->TargetLinkLanguage(config) == "Swift") {
vars["SWIFT_LIBRARY_NAME"] = [this, config]() -> std::string {
cmGeneratorTarget::Names targetNames =
this->GetGeneratorTarget()->GetLibraryNames(config);
@@ -1214,12 +1225,12 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
cmOutputConverter::SHELL);
vars["SWIFT_SOURCES"] = [this, config]() -> std::string {
- std::vector<cmSourceFile const*> sources;
+ std::vector<cmSourceFile const*> sourceFiles;
std::stringstream oss;
- this->GetGeneratorTarget()->GetObjectSources(sources, config);
+ this->GetGeneratorTarget()->GetObjectSources(sourceFiles, config);
cmLocalGenerator const* LocalGen = this->GetLocalGenerator();
- for (const auto& source : sources) {
+ for (const auto& source : sourceFiles) {
const std::string sourcePath = source->GetLanguage() == "Swift"
? this->GetCompiledSourceNinjaPath(source)
: this->GetObjectFilePath(source, config);
@@ -1237,10 +1248,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
vars["FLAGS"] = this->GetFlags("Swift", config);
vars["INCLUDES"] = this->GetIncludes("Swift", config);
this->GenerateSwiftOutputFileMap(config, vars["FLAGS"]);
- }
- // Compute specific libraries to link with.
- if (this->TargetLinkLanguage(config) == "Swift") {
+ // Compute specific libraries to link with.
std::vector<cmSourceFile const*> sources;
gt->GetObjectSources(sources, config);
for (const auto& source : sources) {
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index 1d90194..7ea479e 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -14,6 +14,7 @@
#include <utility>
#include <cm/memory>
+#include <cm/optional>
#include <cm/string_view>
#include <cmext/algorithm>
#include <cmext/string_view>
@@ -31,6 +32,7 @@
#include "cmGlobalCommonGenerator.h"
#include "cmGlobalNinjaGenerator.h"
#include "cmList.h"
+#include "cmListFileCache.h"
#include "cmLocalGenerator.h"
#include "cmLocalNinjaGenerator.h"
#include "cmMakefile.h"
@@ -47,6 +49,7 @@
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
+#include "cmTargetDepend.h"
#include "cmValue.h"
#include "cmake.h"
@@ -145,7 +148,7 @@ std::string cmNinjaTargetGenerator::LanguageScanRule(
bool cmNinjaTargetGenerator::NeedExplicitPreprocessing(
std::string const& lang) const
{
- return lang == "Fortran";
+ return lang == "Fortran" || lang == "Swift";
}
bool cmNinjaTargetGenerator::CompileWithDefines(std::string const& lang) const
@@ -1136,9 +1139,19 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements(
std::vector<cmSourceFile const*> objectSources;
this->GeneratorTarget->GetObjectSources(objectSources, config);
+ std::vector<cmSourceFile const*> swiftSources;
+
for (cmSourceFile const* sf : objectSources) {
- this->WriteObjectBuildStatement(sf, config, fileConfig, firstForConfig);
+ if (this->GetLocalGenerator()->IsSplitSwiftBuild() &&
+ sf->GetLanguage() == "Swift") {
+ swiftSources.push_back(sf);
+ } else {
+ this->WriteObjectBuildStatement(sf, config, fileConfig,
+ firstForConfig);
+ }
}
+ WriteSwiftObjectBuildStatement(swiftSources, config, fileConfig,
+ firstForConfig);
}
{
@@ -1234,9 +1247,11 @@ void cmNinjaTargetGenerator::GenerateSwiftOutputFileMap(
if (cmValue name = target->GetProperty("Swift_DEPENDENCIES_FILE")) {
return *name;
}
- return this->ConvertToNinjaPath(cmStrCat(target->GetSupportDirectory(),
- '/', config, '/',
- target->GetName(), ".swiftdeps"));
+ return this->GetLocalGenerator()->ConvertToOutputFormat(
+ this->ConvertToNinjaPath(cmStrCat(target->GetSupportDirectory(), '/',
+ config, '/', target->GetName(),
+ ".swiftdeps")),
+ cmOutputConverter::SHELL);
}();
std::string mapFilePath =
@@ -1254,8 +1269,10 @@ void cmNinjaTargetGenerator::GenerateSwiftOutputFileMap(
// Add flag
this->LocalGenerator->AppendFlags(flags, "-output-file-map");
- this->LocalGenerator->AppendFlagEscape(flags,
- ConvertToNinjaPath(mapFilePath));
+ this->LocalGenerator->AppendFlagEscape(
+ flags,
+ this->GetLocalGenerator()->ConvertToOutputFormat(
+ ConvertToNinjaPath(mapFilePath), cmOutputConverter::SHELL));
}
namespace {
@@ -1433,10 +1450,13 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
}
}
+ this->SetMsvcTargetPdbVariable(vars, config);
+
if (firstForConfig) {
this->ExportObjectCompileCommand(
language, sourceFilePath, objectDir, objectFileName, objectFileDir,
- vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], config, withScanning);
+ vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"],
+ vars["TARGET_COMPILE_PDB"], vars["TARGET_PDB"], config, withScanning);
}
objBuild.Outputs.push_back(objectFileName);
@@ -1627,8 +1647,6 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
}
}
- this->SetMsvcTargetPdbVariable(vars, config);
-
objBuild.RspFile = cmStrCat(objectFileName, ".rsp");
if (language == "ISPC") {
@@ -1779,10 +1797,13 @@ void cmNinjaTargetGenerator::WriteCxxModuleBmiBuildStatement(
vars["CLANG_TIDY_EXPORT_FIXES"] = fixesFile;
}
+ this->SetMsvcTargetPdbVariable(vars, config);
+
if (firstForConfig) {
this->ExportObjectCompileCommand(
language, sourceFilePath, bmiDir, bmiFileName, bmiFileDir, vars["FLAGS"],
- vars["DEFINES"], vars["INCLUDES"], config, WithScanning::Yes);
+ vars["DEFINES"], vars["INCLUDES"], vars["TARGET_COMPILE_PDB"],
+ vars["TARGET_PDB"], config, WithScanning::Yes);
}
bmiBuild.Outputs.push_back(bmiFileName);
@@ -1853,14 +1874,220 @@ void cmNinjaTargetGenerator::WriteCxxModuleBmiBuildStatement(
this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(),
vars);
- this->SetMsvcTargetPdbVariable(vars, config);
-
bmiBuild.RspFile = cmStrCat(bmiFileName, ".rsp");
this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
bmiBuild, commandLineLengthLimit);
}
+void cmNinjaTargetGenerator::WriteSwiftObjectBuildStatement(
+ std::vector<cmSourceFile const*> const& sources, std::string const& config,
+ std::string const& fileConfig, bool firstForConfig)
+{
+ // Swift sources are compiled as a module, not individually like with C/C++.
+ // Flags, header search paths, and definitions are passed to the entire
+ // module build, but we still need to emit compile-commands for each source
+ // file in order to support CMAKE_EXPORT_COMPILE_COMMANDS.
+ // In whole-module mode, with a single thread, the Swift compiler will
+ // only emit a single object file, but if more than one thread is specified,
+ // or building in other modes, the compiler will emit multiple object files.
+ // When building a single-output, we do not provide an output-file-map (OFM),
+ // and instead pass `-o` to tell the compiler where to write the object.
+ // When building multiple outputs, we provide an OFM to tell the compiler
+ // where to put each object.
+ //
+ //
+ // Per-Target (module):
+ // - Flags
+ // - Definitions
+ // - Include paths
+ // - (single-output) output object filename
+ // - Swiftmodule
+ //
+ // Per-File:
+ // - compile-command
+ // - (multi-output) OFM data
+ // - (multi-output) output object filename
+ //
+ // Note: Due to the differences in the build models, we are only able to
+ // build the object build-graph if we know what mode the target is built in.
+ // For that, we need the "NEW" behavior for CMP0157. Otherwise, we have to
+ // fall back on the old "linker" build. Otherwise, this should be
+ // indistinguishable from the old behavior.
+ //
+ // FIXME(#25490): Add response file support to Swift object build step
+ // FIXME(#25491): Include all files in module in compile_commands.json
+
+ if (sources.empty()) {
+ return;
+ }
+
+ cmSwiftCompileMode compileMode;
+ if (cm::optional<cmSwiftCompileMode> optionalCompileMode =
+ this->LocalGenerator->GetSwiftCompileMode(this->GeneratorTarget,
+ config)) {
+ compileMode = *optionalCompileMode;
+ } else {
+ // CMP0157 is not NEW, bailing early!
+ return;
+ }
+
+ auto getTargetPropertyOrDefault =
+ [](cmGeneratorTarget const& target, std::string const& property,
+ std::string defaultValue) -> std::string {
+ if (cmValue value = target.GetProperty(property)) {
+ return *value;
+ }
+ return defaultValue;
+ };
+
+ std::string const language = "Swift";
+ std::string const objectDir = this->ConvertToNinjaPath(
+ cmStrCat(this->GeneratorTarget->GetSupportDirectory(),
+ this->GetGlobalGenerator()->ConfigDirectory(config)));
+
+ cmGeneratorTarget const& target = *this->GeneratorTarget;
+ cmNinjaBuild objBuild(
+ this->LanguageCompilerRule(language, config, WithScanning::No));
+ cmNinjaVars& vars = objBuild.Variables;
+
+ std::string const moduleName =
+ getTargetPropertyOrDefault(target, "Swift_MODULE_NAME", target.GetName());
+ std::string const moduleDirectory = getTargetPropertyOrDefault(
+ target, "Swift_MODULE_DIRECTORY",
+ target.LocalGenerator->GetCurrentBinaryDirectory());
+ std::string const moduleFilename = getTargetPropertyOrDefault(
+ target, "Swift_MODULE", cmStrCat(moduleName, ".swiftmodule"));
+ std::string const moduleFilepath =
+ this->ConvertToNinjaPath(cmStrCat(moduleDirectory, '/', moduleFilename));
+
+ bool const isSingleOutput = [this, compileMode]() -> bool {
+ bool isMultiThread = false;
+ if (cmValue numThreadStr =
+ this->GetMakefile()->GetDefinition("CMAKE_Swift_NUM_THREADS")) {
+ unsigned long numThreads;
+ cmStrToULong(*numThreadStr, &numThreads);
+ // numThreads == 1 is multi-threaded according to swiftc
+ isMultiThread = numThreads > 0;
+ }
+ return !isMultiThread && compileMode == cmSwiftCompileMode::Wholemodule;
+ }();
+
+ // Swift modules only make sense to emit from things that can be imported.
+ // Executables that don't export symbols can't be imported, so don't try to
+ // emit a swiftmodule for them. It will break.
+ if (target.GetType() != cmStateEnums::EXECUTABLE ||
+ target.IsExecutableWithExports()) {
+ std::string const emitModuleFlag = "-emit-module";
+ std::string const modulePathFlag = "-emit-module-path";
+ this->LocalGenerator->AppendFlags(
+ vars["FLAGS"], { emitModuleFlag, modulePathFlag, moduleFilepath });
+ objBuild.Outputs.push_back(moduleFilepath);
+
+ std::string const moduleNameFlag = "-module-name";
+ this->LocalGenerator->AppendFlags(
+ vars["FLAGS"], cmStrCat(moduleNameFlag, ' ', moduleName));
+ }
+
+ if (target.GetType() != cmStateEnums::EXECUTABLE) {
+ std::string const libraryLinkNameFlag = "-module-link-name";
+ std::string const libraryLinkName =
+ this->GetGeneratorTarget()->GetLibraryNames(config).Base;
+ this->LocalGenerator->AppendFlags(
+ vars["FLAGS"], cmStrCat(libraryLinkNameFlag, ' ', libraryLinkName));
+ }
+
+ // Without `-emit-library` or `-emit-executable`, targets with a single
+ // source file parse as a Swift script instead of like normal source. For
+ // non-executable targets, append this to ensure that they are parsed like a
+ // normal source.
+ if (target.GetType() != cmStateEnums::EXECUTABLE) {
+ this->LocalGenerator->AppendFlags(vars["FLAGS"], "-parse-as-library");
+ }
+
+ this->LocalGenerator->AppendFlags(vars["FLAGS"],
+ this->GetFlags(language, config));
+ vars["DEFINES"] = this->GetDefines(language, config);
+ vars["INCLUDES"] = this->GetIncludes(language, config);
+
+ // target-level object filename
+ std::string const targetObjectFilename = this->ConvertToNinjaPath(cmStrCat(
+ objectDir, '/', moduleName,
+ this->GetGlobalGenerator()->GetLanguageOutputExtension(language)));
+
+ if (isSingleOutput) {
+ this->LocalGenerator->AppendFlags(vars["FLAGS"],
+ cmStrCat("-o ", targetObjectFilename));
+ objBuild.Outputs.push_back(targetObjectFilename);
+ this->Configs[config].Objects.push_back(targetObjectFilename);
+ }
+
+ for (cmSourceFile const* sf : sources) {
+ // Add dependency to object build on each source file
+ std::string const sourceFilePath = this->GetCompiledSourceNinjaPath(sf);
+ objBuild.ExplicitDeps.push_back(sourceFilePath);
+
+ if (isSingleOutput) {
+ if (firstForConfig) {
+ this->ExportObjectCompileCommand(
+ language, sourceFilePath, objectDir, targetObjectFilename,
+ cmSystemTools::GetFilenamePath(targetObjectFilename), vars["FLAGS"],
+ vars["DEFINES"], vars["INCLUDES"],
+ /*compile pdb*/ "", /*target pdb*/ "", config, WithScanning::No);
+ }
+ } else {
+ // Object outputs
+ std::string const objectFilepath =
+ this->ConvertToNinjaPath(this->GetObjectFilePath(sf, config));
+ this->EnsureParentDirectoryExists(objectFilepath);
+ objBuild.Outputs.push_back(objectFilepath);
+ this->Configs[config].Objects.push_back(objectFilepath);
+
+ // Add OFM data
+ this->EmitSwiftDependencyInfo(sf, config);
+
+ // Emit compile commands
+ if (firstForConfig) {
+ this->ExportObjectCompileCommand(
+ language, sourceFilePath, objectDir, objectFilepath,
+ cmSystemTools::GetFilenamePath(objectFilepath), vars["FLAGS"],
+ vars["DEFINES"], vars["INCLUDES"],
+ /*compile pdb*/ "",
+ /*target pdb*/ "", config, WithScanning::No);
+ }
+ }
+ }
+
+ if (!isSingleOutput) {
+ this->GenerateSwiftOutputFileMap(config, vars["FLAGS"]);
+ }
+
+ for (cmTargetDepend const& dep :
+ this->GetGlobalGenerator()->GetTargetDirectDepends(&target)) {
+ if (!dep->IsLanguageUsed("Swift", config)) {
+ continue;
+ }
+ // Add dependencies on the emitted swiftmodule file from swift targets we
+ // depend on
+ std::string const depModuleName =
+ getTargetPropertyOrDefault(*dep, "Swift_MODULE_NAME", dep->GetName());
+ std::string const depModuleDir = getTargetPropertyOrDefault(
+ *dep, "Swift_MODULE_DIRECTORY",
+ dep->LocalGenerator->GetCurrentBinaryDirectory());
+ std::string const depModuleFilename = getTargetPropertyOrDefault(
+ *dep, "Swift_MODULE", cmStrCat(depModuleName, ".swiftmodule"));
+ std::string const depModuleFilepath =
+ this->ConvertToNinjaPath(cmStrCat(depModuleDir, '/', depModuleFilename));
+ objBuild.ImplicitDeps.push_back(depModuleFilepath);
+ }
+
+ objBuild.OrderOnlyDeps.push_back(this->OrderDependsTargetForTarget(config));
+
+ // Write object build
+ this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
+ objBuild);
+}
+
void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang,
const std::string& config)
{
@@ -1994,6 +2221,7 @@ void cmNinjaTargetGenerator::ExportObjectCompileCommand(
std::string const& objectDir, std::string const& objectFileName,
std::string const& objectFileDir, std::string const& flags,
std::string const& defines, std::string const& includes,
+ std::string const& targetCompilePdb, std::string const& targetPdb,
std::string const& outputConfig, WithScanning withScanning)
{
if (!this->GeneratorTarget->GetPropertyAsBool("EXPORT_COMPILE_COMMANDS")) {
@@ -2042,6 +2270,8 @@ void cmNinjaTargetGenerator::ExportObjectCompileCommand(
compileObjectVars.Flags = fullFlags.c_str();
compileObjectVars.Defines = defines.c_str();
compileObjectVars.Includes = includes.c_str();
+ compileObjectVars.TargetCompilePDB = targetCompilePdb.c_str();
+ compileObjectVars.TargetPDB = targetPdb.c_str();
// Rule for compiling object file.
std::string cudaCompileMode;
diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h
index a9bff1d..b55c460 100644
--- a/Source/cmNinjaTargetGenerator.h
+++ b/Source/cmNinjaTargetGenerator.h
@@ -171,6 +171,9 @@ protected:
const std::string& config,
const std::string& fileConfig,
bool firstForConfig);
+ void WriteSwiftObjectBuildStatement(
+ std::vector<cmSourceFile const*> const& sources, const std::string& config,
+ const std::string& fileConfig, bool firstForConfig);
void WriteObjectBuildStatement(cmSourceFile const* source,
const std::string& config,
const std::string& fileConfig,
@@ -189,6 +192,7 @@ protected:
std::string const& objectDir, std::string const& objectFileName,
std::string const& objectFileDir, std::string const& flags,
std::string const& defines, std::string const& includes,
+ std::string const& targetCompilePdb, std::string const& targetPdb,
std::string const& outputConfig, WithScanning withScanning);
void AdditionalCleanFiles(const std::string& config);
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index 8838de4..f038c6b 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -473,6 +473,17 @@ class cmMakefile;
SELECT(POLICY, CMP0155, \
"C++ sources in targets with at least C++20 are scanned for " \
"imports when supported.", \
+ 3, 28, 0, cmPolicies::WARN) \
+ SELECT( \
+ POLICY, CMP0156, \
+ "De-duplicate libraries on link lines based on linker capabilities.", 3, \
+ 29, 0, cmPolicies::WARN) \
+ SELECT(POLICY, CMP0157, \
+ "Swift compilation mode selected by an abstraction.", 3, 29, 0, \
+ cmPolicies::WARN) \
+ SELECT(POLICY, CMP0158, \
+ "add_test() honors CMAKE_CROSSCOMPILING_EMULATOR only when " \
+ "cross-compiling.", \
3, 28, 0, cmPolicies::WARN)
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
@@ -513,7 +524,9 @@ class cmMakefile;
F(CMP0131) \
F(CMP0142) \
F(CMP0154) \
- F(CMP0155)
+ F(CMP0155) \
+ F(CMP0156) \
+ F(CMP0157)
#define CM_FOR_EACH_CUSTOM_COMMAND_POLICY(F) \
F(CMP0116) \
diff --git a/Source/cmProjectCommand.cxx b/Source/cmProjectCommand.cxx
index 3aef299..53166c1 100644
--- a/Source/cmProjectCommand.cxx
+++ b/Source/cmProjectCommand.cxx
@@ -11,6 +11,7 @@
#include "cmsys/RegularExpression.hxx"
#include "cmExecutionStatus.h"
+#include "cmList.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmPolicies.h"
@@ -371,29 +372,55 @@ static bool IncludeByVariable(cmExecutionStatus& status,
if (!include) {
return true;
}
+ cmList includeFiles{ *include };
+
+ bool failed = false;
+ for (auto filePath : includeFiles) {
+ // Any relative path without a .cmake extension is checked for valid cmake
+ // modules. This logic should be consistent with CMake's include() command.
+ // Otherwise default to checking relative path w.r.t. source directory
+ if (!cmSystemTools::FileIsFullPath(filePath) &&
+ !cmHasLiteralSuffix(filePath, ".cmake")) {
+ std::string mfile = mf.GetModulesFile(cmStrCat(filePath, ".cmake"));
+ if (mfile.empty()) {
+ status.SetError(
+ cmStrCat("could not find requested module:\n ", filePath));
+ failed = true;
+ continue;
+ }
+ filePath = mfile;
+ }
+ std::string includeFile = cmSystemTools::CollapseFullPath(
+ filePath, mf.GetCurrentSourceDirectory());
+ if (!cmSystemTools::FileExists(includeFile)) {
+ status.SetError(
+ cmStrCat("could not find requested file:\n ", filePath));
+ failed = true;
+ continue;
+ }
+ if (cmSystemTools::FileIsDirectory(includeFile)) {
+ status.SetError(
+ cmStrCat("requested file is a directory:\n ", filePath));
+ failed = true;
+ continue;
+ }
- std::string includeFile =
- cmSystemTools::CollapseFullPath(*include, mf.GetCurrentSourceDirectory());
- if (!cmSystemTools::FileExists(includeFile)) {
- status.SetError(cmStrCat("could not find requested file:\n ", *include));
- return false;
- }
- if (cmSystemTools::FileIsDirectory(includeFile)) {
- status.SetError(cmStrCat("requested file is a directory:\n ", *include));
- return false;
- }
+ const bool readit = mf.ReadDependentFile(filePath);
+ if (readit) {
+ // If the included file ran successfully, continue to the next file
+ continue;
+ }
- const bool readit = mf.ReadDependentFile(*include);
- if (readit) {
- return true;
- }
+ if (cmSystemTools::GetFatalErrorOccurred()) {
+ failed = true;
+ continue;
+ }
- if (cmSystemTools::GetFatalErrorOccurred()) {
- return true;
+ status.SetError(cmStrCat("could not load requested file:\n ", filePath));
+ failed = true;
}
-
- status.SetError(cmStrCat("could not load requested file:\n ", *include));
- return false;
+ // At this point all files were processed
+ return !failed;
}
static void TopLevelCMakeVarCondSet(cmMakefile& mf, std::string const& name,
diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx
index adbdba8..0a394b5 100644
--- a/Source/cmQtAutoGen.cxx
+++ b/Source/cmQtAutoGen.cxx
@@ -76,13 +76,6 @@ static void MergeOptions(std::vector<std::string>& baseOpts,
unsigned int const cmQtAutoGen::ParallelMax = 64;
-#ifdef _WIN32
-// Actually 32767 (see
-// https://devblogs.microsoft.com/oldnewthing/20031210-00/?p=41553) but we
-// allow for a small margin
-size_t const cmQtAutoGen::CommandLineLengthMax = 32000;
-#endif
-
cm::string_view cmQtAutoGen::GeneratorName(GenT genType)
{
switch (genType) {
diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h
index d111422..5a23ae9 100644
--- a/Source/cmQtAutoGen.h
+++ b/Source/cmQtAutoGen.h
@@ -64,11 +64,6 @@ public:
/// @brief Maximum number of parallel threads/processes in a generator
static unsigned int const ParallelMax;
-#ifdef _WIN32
- /// @brief Maximum number of characters on command line
- static size_t const CommandLineLengthMax;
-#endif
-
/// @brief Returns the generator name
static cm::string_view GeneratorName(GenT genType);
/// @brief Returns the generator name in upper case
diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx
index 50b0ebe..8213274 100644
--- a/Source/cmQtAutoGenInitializer.cxx
+++ b/Source/cmQtAutoGenInitializer.cxx
@@ -485,6 +485,38 @@ bool cmQtAutoGenInitializer::InitCustomTargets()
}
}
+#ifdef _WIN32
+ {
+ const auto& value =
+ this->GenTarget->GetProperty("AUTOGEN_COMMAND_LINE_LENGTH_MAX");
+ if (value.IsSet()) {
+ using maxCommandLineLengthType =
+ decltype(this->AutogenTarget.MaxCommandLineLength);
+ unsigned long propInt = 0;
+ if (cmStrToULong(value, &propInt) && propInt > 0 &&
+ propInt <= std::numeric_limits<maxCommandLineLengthType>::max()) {
+ this->AutogenTarget.MaxCommandLineLength =
+ static_cast<maxCommandLineLengthType>(propInt);
+ } else {
+ // Warn the project author that AUTOGEN_PARALLEL is not valid.
+ this->Makefile->IssueMessage(
+ MessageType::AUTHOR_WARNING,
+ cmStrCat("AUTOGEN_COMMAND_LINE_LENGTH_MAX=\"", *value,
+ "\" for target \"", this->GenTarget->GetName(),
+ "\" is not valid. Using no limit for "
+ "AUTOGEN_COMMAND_LINE_LENGTH_MAX"));
+ this->AutogenTarget.MaxCommandLineLength =
+ std::numeric_limits<maxCommandLineLengthType>::max();
+ }
+ } else {
+ // Actually 32767 (see
+ // https://devblogs.microsoft.com/oldnewthing/20031210-00/?p=41553) but
+ // we allow for a small margin
+ this->AutogenTarget.MaxCommandLineLength = 32000;
+ }
+ }
+#endif
+
// Autogen target info and settings files
{
// Info file
@@ -1692,6 +1724,10 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
// General
info.SetBool("MULTI_CONFIG", this->MultiConfig);
info.SetUInt("PARALLEL", this->AutogenTarget.Parallel);
+#ifdef _WIN32
+ info.SetUInt("AUTOGEN_COMMAND_LINE_LENGTH_MAX",
+ this->AutogenTarget.MaxCommandLineLength);
+#endif
info.SetUInt("VERBOSITY", this->Verbosity);
// Directories
diff --git a/Source/cmQtAutoGenInitializer.h b/Source/cmQtAutoGenInitializer.h
index a44d33f..3f7ab9f 100644
--- a/Source/cmQtAutoGenInitializer.h
+++ b/Source/cmQtAutoGenInitializer.h
@@ -5,6 +5,7 @@
#include "cmConfigure.h" // IWYU pragma: keep
#include <cstddef>
+#include <limits>
#include <memory>
#include <set>
#include <string>
@@ -194,6 +195,8 @@ private:
bool GlobalTarget = false;
// Settings
unsigned int Parallel = 1;
+ unsigned int MaxCommandLineLength =
+ std::numeric_limits<unsigned int>::max();
// Configuration files
std::string InfoFile;
ConfigString SettingsFile;
diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx
index ece657d..a49125e 100644
--- a/Source/cmQtAutoMocUic.cxx
+++ b/Source/cmQtAutoMocUic.cxx
@@ -5,6 +5,7 @@
#include <algorithm>
#include <atomic>
#include <cstddef>
+#include <limits>
#include <map>
#include <mutex>
#include <set>
@@ -172,6 +173,8 @@ public:
bool MultiConfig = false;
IntegerVersion QtVersion = { 4, 0 };
unsigned int ThreadCount = 0;
+ unsigned int MaxCommandLineLength =
+ std::numeric_limits<unsigned int>::max();
// - Directories
std::string AutogenBuildDir;
std::string AutogenIncludeDir;
@@ -190,7 +193,7 @@ public:
{
public:
// -- Parse Cache
- std::atomic<bool> ParseCacheChanged = ATOMIC_VAR_INIT(false);
+ std::atomic<bool> ParseCacheChanged{ false };
cmFileTime ParseCacheTime;
ParseCacheT ParseCache;
@@ -333,6 +336,13 @@ public:
std::vector<std::string> const& command,
std::string const& output) const;
+ /*
+ * Check if command line exceeds maximum length supported by OS
+ * (if on Windows) and switch to using a response file instead.
+ */
+ void MaybeWriteResponseFile(std::string const& outputFile,
+ std::vector<std::string>& cmd) const;
+
/** @brief Run an external process. Use only during Process() call! */
bool RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result,
std::vector<std::string> const& command,
@@ -498,10 +508,6 @@ public:
protected:
ParseCacheT::FileHandleT CacheEntry;
-
- private:
- void MaybeWriteMocResponseFile(std::string const& outputFile,
- std::vector<std::string>& cmd) const;
};
/** uic compiles a file. */
@@ -583,7 +589,7 @@ private:
std::string SettingsStringMoc_;
std::string SettingsStringUic_;
// -- Worker thread pool
- std::atomic<bool> JobError_ = ATOMIC_VAR_INIT(false);
+ std::atomic<bool> JobError_{ false };
cmWorkerPool WorkerPool_;
// -- Concurrent processing
mutable std::mutex CMakeLibMutex_;
@@ -795,6 +801,51 @@ void cmQtAutoMocUicT::JobT::LogCommandError(
this->Gen()->Log().ErrorCommand(genType, message, command, output);
}
+/*
+ * Check if command line exceeds maximum length supported by OS
+ * (if on Windows) and switch to using a response file instead.
+ */
+void cmQtAutoMocUicT::JobT::MaybeWriteResponseFile(
+ std::string const& outputFile, std::vector<std::string>& cmd) const
+{
+#ifdef _WIN32
+ // Ensure cmd is less than CommandLineLengthMax characters
+ size_t commandLineLength = cmd.size(); // account for separating spaces
+ for (std::string const& str : cmd) {
+ commandLineLength += str.length();
+ }
+ if (commandLineLength >= this->BaseConst().MaxCommandLineLength) {
+ // Command line exceeds maximum size allowed by OS
+ // => create response file
+ std::string const responseFile = cmStrCat(outputFile, ".rsp");
+
+ cmsys::ofstream fout(responseFile.c_str());
+ if (!fout) {
+ this->LogError(
+ GenT::MOC,
+ cmStrCat("AUTOMOC was unable to create a response file at\n ",
+ this->MessagePath(responseFile)));
+ return;
+ }
+
+ auto it = cmd.begin();
+ while (++it != cmd.end()) {
+ fout << *it << "\n";
+ }
+ fout.close();
+
+ // Keep all but executable
+ cmd.resize(1);
+
+ // Specify response file
+ cmd.emplace_back(cmStrCat('@', responseFile));
+ }
+#else
+ static_cast<void>(outputFile);
+ static_cast<void>(cmd);
+#endif
+}
+
bool cmQtAutoMocUicT::JobT::RunProcess(GenT genType,
cmWorkerPool::ProcessResultT& result,
std::vector<std::string> const& command,
@@ -836,6 +887,8 @@ void cmQtAutoMocUicT::JobMocPredefsT::Process()
cm::append(cmd, this->MocConst().OptionsDefinitions);
// Add includes
cm::append(cmd, this->MocConst().OptionsIncludes);
+ // Check if response file is necessary
+ MaybeWriteResponseFile(this->MocConst().PredefsFileAbs, cmd);
// Execute command
if (!this->RunProcess(GenT::MOC, result, cmd, reason.get())) {
this->LogCommandError(GenT::MOC,
@@ -2034,7 +2087,7 @@ void cmQtAutoMocUicT::JobCompileMocT::Process()
// Add source file
cmd.push_back(sourceFile);
- MaybeWriteMocResponseFile(outputFile, cmd);
+ MaybeWriteResponseFile(outputFile, cmd);
}
// Execute moc command
@@ -2080,51 +2133,6 @@ void cmQtAutoMocUicT::JobCompileMocT::Process()
}
}
-/*
- * Check if command line exceeds maximum length supported by OS
- * (if on Windows) and switch to using a response file instead.
- */
-void cmQtAutoMocUicT::JobCompileMocT::MaybeWriteMocResponseFile(
- std::string const& outputFile, std::vector<std::string>& cmd) const
-{
-#ifdef _WIN32
- // Ensure cmd is less than CommandLineLengthMax characters
- size_t commandLineLength = cmd.size(); // account for separating spaces
- for (std::string const& str : cmd) {
- commandLineLength += str.length();
- }
- if (commandLineLength >= CommandLineLengthMax) {
- // Command line exceeds maximum size allowed by OS
- // => create response file
- std::string const responseFile = cmStrCat(outputFile, ".rsp");
-
- cmsys::ofstream fout(responseFile.c_str());
- if (!fout) {
- this->LogError(
- GenT::MOC,
- cmStrCat("AUTOMOC was unable to create a response file at\n ",
- this->MessagePath(responseFile)));
- return;
- }
-
- auto it = cmd.begin();
- while (++it != cmd.end()) {
- fout << *it << "\n";
- }
- fout.close();
-
- // Keep all but executable
- cmd.resize(1);
-
- // Specify response file
- cmd.emplace_back(cmStrCat('@', responseFile));
- }
-#else
- static_cast<void>(outputFile);
- static_cast<void>(cmd);
-#endif
-}
-
void cmQtAutoMocUicT::JobCompileUicT::Process()
{
std::string const& sourceFile = this->Mapping->SourceFile->FileName;
@@ -2377,6 +2385,10 @@ bool cmQtAutoMocUicT::InitFromInfo(InfoT const& info)
!info.GetUInt("QT_VERSION_MINOR", this->BaseConst_.QtVersion.Minor,
true) ||
!info.GetUInt("PARALLEL", this->BaseConst_.ThreadCount, false) ||
+#ifdef _WIN32
+ !info.GetUInt("AUTOGEN_COMMAND_LINE_LENGTH_MAX",
+ this->BaseConst_.MaxCommandLineLength, false) ||
+#endif
!info.GetString("BUILD_DIR", this->BaseConst_.AutogenBuildDir, true) ||
!info.GetStringConfig("INCLUDE_DIR", this->BaseConst_.AutogenIncludeDir,
true) ||
diff --git a/Source/cmRulePlaceholderExpander.cxx b/Source/cmRulePlaceholderExpander.cxx
index 638bb42..a8c81d0 100644
--- a/Source/cmRulePlaceholderExpander.cxx
+++ b/Source/cmRulePlaceholderExpander.cxx
@@ -27,6 +27,19 @@ std::string cmRulePlaceholderExpander::ExpandVariable(
return this->ReplaceValues->LinkFlags;
}
}
+ if (this->ReplaceValues->Linker) {
+ if (variable == "CMAKE_LINKER") {
+ auto result = this->OutputConverter->ConvertToOutputForExisting(
+ this->ReplaceValues->Linker);
+ if (this->ReplaceValues->Launcher) {
+ // Add launcher as part of expansion so that it always appears
+ // immediately before the command itself, regardless of whether the
+ // overall rule template contains other content at the front.
+ result = cmStrCat(this->ReplaceValues->Launcher, " ", result);
+ }
+ return result;
+ }
+ }
if (this->ReplaceValues->Manifests) {
if (variable == "MANIFESTS") {
return this->ReplaceValues->Manifests;
@@ -325,17 +338,7 @@ std::string cmRulePlaceholderExpander::ExpandVariable(
auto mapIt = this->VariableMappings.find(variable);
if (mapIt != this->VariableMappings.end()) {
if (variable.find("_FLAG") == std::string::npos) {
- std::string ret =
- this->OutputConverter->ConvertToOutputForExisting(mapIt->second);
-
- if (this->ReplaceValues->Launcher && variable == "CMAKE_LINKER") {
- // Add launcher as part of expansion so that it always appears
- // immediately before the command itself, regardless of whether the
- // overall rule template contains other content at the front.
- ret = cmStrCat(this->ReplaceValues->Launcher, " ", ret);
- }
-
- return ret;
+ return this->OutputConverter->ConvertToOutputForExisting(mapIt->second);
}
return mapIt->second;
}
diff --git a/Source/cmRulePlaceholderExpander.h b/Source/cmRulePlaceholderExpander.h
index 5d1f199..225abd4 100644
--- a/Source/cmRulePlaceholderExpander.h
+++ b/Source/cmRulePlaceholderExpander.h
@@ -53,6 +53,7 @@ public:
const char* SONameFlag = nullptr;
const char* TargetSOName = nullptr;
const char* TargetInstallNameDir = nullptr;
+ const char* Linker = nullptr;
const char* LinkFlags = nullptr;
const char* Manifests = nullptr;
const char* LanguageCompileFlags = nullptr;
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index abbf29e..832bf57 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -20,6 +20,7 @@
#include "cmAlgorithms.h"
#include "cmCustomCommand.h"
#include "cmFileSet.h"
+#include "cmFindPackageStack.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
@@ -438,6 +439,7 @@ TargetProperty const StaticTargetProperties[] = {
// ---- Swift
{ "Swift_LANGUAGE_VERSION"_s, IC::CanCompileSources },
{ "Swift_MODULE_DIRECTORY"_s, IC::CanCompileSources },
+ { "Swift_COMPILATION_MODE"_s, IC::CanCompileSources },
// ---- moc
{ "AUTOMOC"_s, IC::CanCompileSources },
{ "AUTOMOC_COMPILER_PREDEFINES"_s, IC::CanCompileSources },
@@ -456,6 +458,7 @@ TargetProperty const StaticTargetProperties[] = {
{ "AUTORCC_EXECUTABLE"_s, IC::CanCompileSources },
// Linking properties
+ { "LINKER_TYPE"_s, IC::CanCompileSources },
{ "ENABLE_EXPORTS"_s, IC::TargetWithSymbolExports },
{ "LINK_LIBRARIES_ONLY_TARGETS"_s, IC::NormalNonImportedTarget },
{ "LINK_SEARCH_START_STATIC"_s, IC::CanCompileSources },
@@ -549,6 +552,7 @@ TargetProperty const StaticTargetProperties[] = {
{ "ANDROID_PROCESS_MAX"_s, IC::CanCompileSources },
{ "ANDROID_SKIP_ANT_STEP"_s, IC::CanCompileSources },
// -- Autogen
+ { "AUTOGEN_COMMAND_LINE_LENGTH_MAX"_s, IC::CanCompileSources },
{ "AUTOGEN_ORIGIN_DEPENDS"_s, IC::CanCompileSources },
{ "AUTOGEN_PARALLEL"_s, IC::CanCompileSources },
{ "AUTOGEN_USE_SYSTEM_INCLUDE"_s, IC::CanCompileSources },
@@ -584,11 +588,13 @@ TargetProperty const StaticTargetProperties[] = {
// Usage requirement properties
{ "LINK_INTERFACE_LIBRARIES"_s, IC::CanCompileSources },
{ "MAP_IMPORTED_CONFIG_"_s, IC::NormalTarget, R::PerConfig },
+ { "EXPORT_FIND_PACKAGE_NAME"_s, IC::NormalTarget },
// Metadata
{ "CROSSCOMPILING_EMULATOR"_s, IC::ExecutableTarget },
{ "EXPORT_COMPILE_COMMANDS"_s, IC::CanCompileSources },
{ "FOLDER"_s },
+ { "TEST_LAUNCHER"_s, IC::ExecutableTarget },
// Xcode properties
{ "XCODE_GENERATE_SCHEME"_s, IC::NeedsXcode },
@@ -660,6 +666,7 @@ public:
TLLCommands;
std::map<std::string, cmFileSet> FileSets;
cmListFileBacktrace Backtrace;
+ cmFindPackageStack FindPackageStack;
UsageRequirementProperty IncludeDirectories;
UsageRequirementProperty CompileOptions;
@@ -960,6 +967,9 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
// Save the backtrace of target construction.
this->impl->Backtrace = this->impl->Makefile->GetBacktrace();
+ if (this->impl->IsImported()) {
+ this->impl->FindPackageStack = this->impl->Makefile->GetFindPackageStack();
+ }
if (this->IsNormal()) {
// Initialize the INCLUDE_DIRECTORIES property based on the current value
@@ -1247,6 +1257,11 @@ cmListFileBacktrace const& cmTarget::GetBacktrace() const
return this->impl->Backtrace;
}
+cmFindPackageStack const& cmTarget::GetFindPackageStack() const
+{
+ return this->impl->FindPackageStack;
+}
+
bool cmTarget::IsExecutableWithExports() const
{
return (this->GetType() == cmStateEnums::EXECUTABLE &&
diff --git a/Source/cmTarget.h b/Source/cmTarget.h
index 584856a..385dfe7 100644
--- a/Source/cmTarget.h
+++ b/Source/cmTarget.h
@@ -24,6 +24,7 @@
class cmCustomCommand;
class cmFileSet;
+class cmFindPackageStack;
class cmGlobalGenerator;
class cmInstallTargetGenerator;
class cmMakefile;
@@ -239,6 +240,9 @@ public:
//! Get a backtrace from the creation of the target.
cmListFileBacktrace const& GetBacktrace() const;
+ //! Get a find_package call stack from the creation of the target.
+ cmFindPackageStack const& GetFindPackageStack() const;
+
void InsertInclude(BT<std::string> const& entry, bool before = false);
void InsertCompileOption(BT<std::string> const& entry, bool before = false);
void InsertCompileDefinition(BT<std::string> const& entry);
diff --git a/Source/cmTargetExport.h b/Source/cmTargetExport.h
index 1cef888..caeb54d 100644
--- a/Source/cmTargetExport.h
+++ b/Source/cmTargetExport.h
@@ -4,6 +4,7 @@
#include "cmConfigure.h" // IWYU pragma: keep
+#include <map>
#include <string>
class cmFileSet;
@@ -37,4 +38,5 @@ public:
///@}
bool NamelinkOnly = false;
+ std::string XcFrameworkLocation;
};
diff --git a/Source/cmTest.cxx b/Source/cmTest.cxx
index b0d9c2d..7c9969c 100644
--- a/Source/cmTest.cxx
+++ b/Source/cmTest.cxx
@@ -9,6 +9,7 @@
cmTest::cmTest(cmMakefile* mf)
: Backtrace(mf->GetBacktrace())
+ , PolicyStatusCMP0158(mf->GetPolicyStatus(cmPolicies::CMP0158))
{
this->Makefile = mf;
this->OldStyle = true;
diff --git a/Source/cmTest.h b/Source/cmTest.h
index 8b50b87..480966a 100644
--- a/Source/cmTest.h
+++ b/Source/cmTest.h
@@ -9,6 +9,7 @@
#include <vector>
#include "cmListFileCache.h"
+#include "cmPolicies.h"
#include "cmPropertyMap.h"
#include "cmValue.h"
@@ -60,6 +61,12 @@ public:
bool GetOldStyle() const { return this->OldStyle; }
void SetOldStyle(bool b) { this->OldStyle = b; }
+ /** Get/Set if CMP0158 policy is NEW */
+ bool GetCMP0158IsNew() const
+ {
+ return this->PolicyStatusCMP0158 == cmPolicies::NEW;
+ }
+
/** Set/Get whether lists in command lines should be expanded. */
bool GetCommandExpandLists() const;
void SetCommandExpandLists(bool b);
@@ -74,4 +81,5 @@ private:
cmMakefile* Makefile;
cmListFileBacktrace Backtrace;
+ cmPolicies::PolicyStatus PolicyStatusCMP0158;
};
diff --git a/Source/cmTestGenerator.cxx b/Source/cmTestGenerator.cxx
index ca1226a..3194d8f 100644
--- a/Source/cmTestGenerator.cxx
+++ b/Source/cmTestGenerator.cxx
@@ -167,10 +167,24 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os,
if (target && target->GetType() == cmStateEnums::EXECUTABLE) {
// Use the target file on disk.
exe = target->GetFullPath(config);
+ auto useEmulator = !this->GetTest()->GetCMP0158IsNew() ||
+ this->LG->GetMakefile()->IsOn("CMAKE_CROSSCOMPILING");
+
+ // Prepend with the test launcher if specified.
+ cmValue launcher = target->GetProperty("TEST_LAUNCHER");
+ if (cmNonempty(launcher)) {
+ cmList launcherWithArgs{ *launcher };
+ std::string launcherExe(launcherWithArgs[0]);
+ cmSystemTools::ConvertToUnixSlashes(launcherExe);
+ os << cmOutputConverter::EscapeForCMake(launcherExe) << " ";
+ for (std::string const& arg : cmMakeRange(launcherWithArgs).advance(1)) {
+ os << cmOutputConverter::EscapeForCMake(arg) << " ";
+ }
+ }
// Prepend with the emulator when cross compiling if required.
cmValue emulator = target->GetProperty("CROSSCOMPILING_EMULATOR");
- if (cmNonempty(emulator)) {
+ if (cmNonempty(emulator) && useEmulator) {
cmList emulatorWithArgs{ *emulator };
std::string emulatorExe(emulatorWithArgs[0]);
cmSystemTools::ConvertToUnixSlashes(emulatorExe);
diff --git a/Source/cmTransformDepfile.cxx b/Source/cmTransformDepfile.cxx
index 914172b..ffc4de9 100644
--- a/Source/cmTransformDepfile.cxx
+++ b/Source/cmTransformDepfile.cxx
@@ -16,6 +16,9 @@
#include "cmGccDepfileReaderTypes.h"
#include "cmGlobalGenerator.h"
#include "cmLocalGenerator.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
namespace {
@@ -121,6 +124,10 @@ bool cmTransformDepfile(cmDepfileFormat format, const cmLocalGenerator& lg,
return false;
}
content = *std::move(result);
+ } else {
+ lg.GetMakefile()->IssueMessage(
+ MessageType::WARNING,
+ cmStrCat("Expected depfile does not exist.\n ", infile));
}
cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(outfile));
diff --git a/Source/cmUVHandlePtr.cxx b/Source/cmUVHandlePtr.cxx
index 34e6a70..168f1a6 100644
--- a/Source/cmUVHandlePtr.cxx
+++ b/Source/cmUVHandlePtr.cxx
@@ -6,6 +6,9 @@
#include <cassert>
#include <cstdlib>
#include <mutex>
+#include <utility>
+
+#include <cm/memory>
#include <cm3p/uv.h>
@@ -44,7 +47,7 @@ void uv_loop_ptr::reset()
this->loop.reset();
}
-uv_loop_ptr::operator uv_loop_t*()
+uv_loop_ptr::operator uv_loop_t*() const
{
return this->loop.get();
}
@@ -54,6 +57,11 @@ uv_loop_t* uv_loop_ptr::operator->() const noexcept
return this->loop.get();
}
+uv_loop_t& uv_loop_ptr::operator*() const
+{
+ return *this->loop;
+}
+
uv_loop_t* uv_loop_ptr::get() const
{
return this->loop.get();
@@ -97,13 +105,19 @@ void uv_handle_ptr_base_<T>::allocate(void* data)
}
template <typename T>
+uv_handle_ptr_base_<T>::operator bool() const
+{
+ return this->handle.get();
+}
+
+template <typename T>
void uv_handle_ptr_base_<T>::reset()
{
this->handle.reset();
}
template <typename T>
-uv_handle_ptr_base_<T>::operator uv_handle_t*()
+uv_handle_ptr_base_<T>::operator uv_handle_t*() const
{
return reinterpret_cast<uv_handle_t*>(this->handle.get());
}
@@ -235,6 +249,12 @@ int uv_timer_ptr::start(uv_timer_cb cb, uint64_t timeout, uint64_t repeat)
return uv_timer_start(*this, cb, timeout, repeat);
}
+void uv_timer_ptr::stop()
+{
+ assert(this->handle);
+ uv_timer_stop(*this);
+}
+
#ifndef CMAKE_BOOTSTRAP
uv_tty_ptr::operator uv_stream_t*() const
{
@@ -248,12 +268,32 @@ int uv_tty_ptr::init(uv_loop_t& loop, int fd, int readable, void* data)
}
#endif
+int uv_idle_ptr::init(uv_loop_t& loop, void* data)
+{
+ this->allocate(data);
+ return uv_idle_init(&loop, *this);
+}
+
+int uv_idle_ptr::start(uv_idle_cb cb)
+{
+ assert(this->handle);
+ return uv_idle_start(*this, cb);
+}
+
+void uv_idle_ptr::stop()
+{
+ assert(this->handle);
+ uv_idle_stop(*this);
+}
+
template class uv_handle_ptr_base_<uv_handle_t>;
#define UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(NAME) \
template class uv_handle_ptr_base_<uv_##NAME##_t>; \
template class uv_handle_ptr_<uv_##NAME##_t>;
+UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(idle)
+
UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(signal)
UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(pipe)
@@ -269,4 +309,38 @@ UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(async)
UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(tty)
#endif
+
+namespace {
+struct write_req : public uv_write_t
+{
+ std::weak_ptr<std::function<void(int)>> cb_;
+ write_req(std::weak_ptr<std::function<void(int)>> wcb)
+ : cb_(std::move(wcb))
+ {
+ }
+};
+
+void write_req_cb(uv_write_t* req, int status)
+{
+ // Ownership has been transferred from the event loop.
+ std::unique_ptr<write_req> self(static_cast<write_req*>(req));
+
+ // Notify the original uv_write caller if it is still interested.
+ if (auto cb = self->cb_.lock()) {
+ (*cb)(status);
+ }
+}
+}
+
+int uv_write(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs,
+ std::weak_ptr<std::function<void(int)>> cb)
+{
+ auto req = cm::make_unique<write_req>(std::move(cb));
+ int status = uv_write(req.get(), handle, bufs, nbufs, write_req_cb);
+ if (status == 0) {
+ // Ownership has been transferred to the event loop.
+ static_cast<void>(req.release());
+ }
+ return status;
+}
}
diff --git a/Source/cmUVHandlePtr.h b/Source/cmUVHandlePtr.h
index 027d690..b8b3491 100644
--- a/Source/cmUVHandlePtr.h
+++ b/Source/cmUVHandlePtr.h
@@ -5,6 +5,7 @@
#include <cstddef>
#include <cstdint>
+#include <functional>
#include <memory>
#include <type_traits>
@@ -61,10 +62,11 @@ public:
* Allow less verbose calling of uv_loop_* functions
* @return reinterpreted handle
*/
- operator uv_loop_t*();
+ operator uv_loop_t*() const;
uv_loop_t* get() const;
uv_loop_t* operator->() const noexcept;
+ uv_loop_t& operator*() const;
};
/***
@@ -130,6 +132,16 @@ public:
uv_handle_ptr_base_(std::nullptr_t) {}
~uv_handle_ptr_base_() { this->reset(); }
+#if defined(__SUNPRO_CC)
+ // The Oracle Studio compiler recognizes 'explicit operator bool()' in
+ // 'if(foo)' but not 'if(foo && ...)'. The purpose of 'explicit' here
+ // is to avoid accidental conversion in non-boolean contexts. Just
+ // leave it out on this compiler so we can compile valid code.
+ operator bool() const;
+#else
+ explicit operator bool() const;
+#endif
+
/**
* Properly close the handle if needed and sets the inner handle to nullptr
*/
@@ -139,7 +151,7 @@ public:
* Allow less verbose calling of uv_handle_* functions
* @return reinterpreted handle
*/
- operator uv_handle_t*();
+ operator uv_handle_t*() const;
T* get() const;
T* operator->() const noexcept;
@@ -194,6 +206,17 @@ public:
void send();
};
+struct uv_idle_ptr : public uv_handle_ptr_<uv_idle_t>
+{
+ CM_INHERIT_CTOR(uv_idle_ptr, uv_handle_ptr_, <uv_idle_t>);
+
+ int init(uv_loop_t& loop, void* data = nullptr);
+
+ int start(uv_idle_cb cb);
+
+ void stop();
+};
+
struct uv_signal_ptr : public uv_handle_ptr_<uv_signal_t>
{
CM_INHERIT_CTOR(uv_signal_ptr, uv_handle_ptr_, <uv_signal_t>);
@@ -229,6 +252,8 @@ struct uv_timer_ptr : public uv_handle_ptr_<uv_timer_t>
int init(uv_loop_t& loop, void* data = nullptr);
int start(uv_timer_cb cb, uint64_t timeout, uint64_t repeat);
+
+ void stop();
};
struct uv_tty_ptr : public uv_handle_ptr_<uv_tty_t>
@@ -253,6 +278,8 @@ extern template class uv_handle_ptr_base_<uv_handle_t>;
UV_HANDLE_PTR_INSTANTIATE_EXTERN(async)
+UV_HANDLE_PTR_INSTANTIATE_EXTERN(idle)
+
UV_HANDLE_PTR_INSTANTIATE_EXTERN(signal)
UV_HANDLE_PTR_INSTANTIATE_EXTERN(pipe)
@@ -268,4 +295,27 @@ UV_HANDLE_PTR_INSTANTIATE_EXTERN(tty)
# undef UV_HANDLE_PTR_INSTANTIATE_EXTERN
#endif
+
+/**
+ * Wraps uv_write to add synchronous cancellation.
+ *
+ * libuv provides no way to synchronously cancel a write request.
+ * Closing a write handle will cancel its pending write request, but its
+ * callback will still be called asynchronously later with UV_ECANCELED.
+ *
+ * This wrapper provides a solution by handing ownership of the uv_write_t
+ * request object to the event loop and taking it back in the callback.
+ * Use this in combination with uv_loop_ptr to ensure the event loop
+ * runs to completion and cleans up all resources.
+ *
+ * The caller may optionally provide a callback it owns with std::shared_ptr.
+ * If the caller's lifetime ends before the write request completes, the
+ * callback can be safely deleted and will not be called.
+ *
+ * The bufs array does not need to live beyond this call, but the memory
+ * referenced by the uv_buf_t values must remain alive until the callback
+ * is made or the stream is closed.
+ */
+int uv_write(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs,
+ std::weak_ptr<std::function<void(int)>> cb);
}
diff --git a/Source/cmUVProcessChain.cxx b/Source/cmUVProcessChain.cxx
index 655e52a..4c72261 100644
--- a/Source/cmUVProcessChain.cxx
+++ b/Source/cmUVProcessChain.cxx
@@ -38,7 +38,8 @@ struct cmUVProcessChain::InternalData
bool Valid = false;
- cm::uv_loop_ptr Loop;
+ cm::uv_loop_ptr BuiltinLoop;
+ uv_loop_t* Loop;
StreamData InputStreamData;
StreamData OutputStreamData;
@@ -74,6 +75,19 @@ cmUVProcessChainBuilder& cmUVProcessChainBuilder::AddCommand(
return *this;
}
+cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetBuiltinLoop()
+{
+ this->Loop = nullptr;
+ return *this;
+}
+
+cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetExternalLoop(
+ uv_loop_t& loop)
+{
+ this->Loop = &loop;
+ return *this;
+}
+
cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetNoStream(Stream stdio)
{
switch (stdio) {
@@ -135,6 +149,11 @@ cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetWorkingDirectory(
return *this;
}
+uv_loop_t* cmUVProcessChainBuilder::GetLoop() const
+{
+ return this->Loop;
+}
+
cmUVProcessChain cmUVProcessChainBuilder::Start() const
{
cmUVProcessChain chain;
@@ -158,6 +177,13 @@ bool cmUVProcessChain::InternalData::Prepare(
{
this->Builder = builder;
+ if (this->Builder->Loop) {
+ this->Loop = this->Builder->Loop;
+ } else {
+ this->BuiltinLoop.init();
+ this->Loop = this->BuiltinLoop;
+ }
+
auto const& input =
this->Builder->Stdio[cmUVProcessChainBuilder::Stream_INPUT];
auto& inputData = this->InputStreamData;
@@ -353,7 +379,6 @@ void cmUVProcessChain::InternalData::Finish()
cmUVProcessChain::cmUVProcessChain()
: Data(cm::make_unique<InternalData>())
{
- this->Data->Loop.init();
}
cmUVProcessChain::cmUVProcessChain(cmUVProcessChain&& other) noexcept
diff --git a/Source/cmUVProcessChain.h b/Source/cmUVProcessChain.h
index 0f37e7d..139588b 100644
--- a/Source/cmUVProcessChain.h
+++ b/Source/cmUVProcessChain.h
@@ -30,12 +30,16 @@ public:
cmUVProcessChainBuilder& AddCommand(
const std::vector<std::string>& arguments);
+ cmUVProcessChainBuilder& SetBuiltinLoop();
+ cmUVProcessChainBuilder& SetExternalLoop(uv_loop_t& loop);
cmUVProcessChainBuilder& SetNoStream(Stream stdio);
cmUVProcessChainBuilder& SetBuiltinStream(Stream stdio);
cmUVProcessChainBuilder& SetMergedBuiltinStreams();
cmUVProcessChainBuilder& SetExternalStream(Stream stdio, int fd);
cmUVProcessChainBuilder& SetWorkingDirectory(std::string dir);
+ uv_loop_t* GetLoop() const;
+
cmUVProcessChain Start() const;
private:
@@ -63,6 +67,7 @@ private:
std::vector<ProcessConfiguration> Processes;
std::string WorkingDirectory;
bool MergedBuiltinStreams = false;
+ uv_loop_t* Loop = nullptr;
};
class cmUVProcessChain
diff --git a/Source/cmUVSignalHackRAII.h b/Source/cmUVSignalHackRAII.h
deleted file mode 100644
index 60e4ca8..0000000
--- a/Source/cmUVSignalHackRAII.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file Copyright.txt or https://cmake.org/licensing for details. */
-#pragma once
-#include "cmConfigure.h" // IWYU pragma: keep
-
-#include <cm3p/uv.h>
-
-#if defined(CMAKE_USE_SYSTEM_LIBUV) && !defined(_WIN32) && \
- UV_VERSION_MAJOR == 1 && UV_VERSION_MINOR < 19
-# define CMAKE_UV_SIGNAL_HACK
-# include "cmUVHandlePtr.h"
-/*
- libuv does not use SA_RESTART on its signal handler, but C++ streams
- depend on it for reliable i/o operations. This RAII helper convinces
- libuv to install its handler, and then revises the handler to add the
- SA_RESTART flag. We use a distinct uv loop that never runs to avoid
- ever really getting a callback. libuv may fill the hack loop's signal
- pipe and then stop writing, but that won't break any real loops.
- */
-class cmUVSignalHackRAII
-{
- uv_loop_t HackLoop;
- cm::uv_signal_ptr HackSignal;
- static void HackCB(uv_signal_t*, int) {}
-
-public:
- cmUVSignalHackRAII()
- {
- uv_loop_init(&this->HackLoop);
- this->HackSignal.init(this->HackLoop);
- this->HackSignal.start(HackCB, SIGCHLD);
- struct sigaction hack_sa;
- sigaction(SIGCHLD, nullptr, &hack_sa);
- if (!(hack_sa.sa_flags & SA_RESTART)) {
- hack_sa.sa_flags |= SA_RESTART;
- sigaction(SIGCHLD, &hack_sa, nullptr);
- }
- }
- ~cmUVSignalHackRAII()
- {
- this->HackSignal.stop();
- uv_loop_close(&this->HackLoop);
- }
-};
-#endif
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 4860d9a..1bbd934 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -2996,6 +2996,16 @@ void cmVisualStudio10TargetGenerator::WritePathAndIncrementalLinkOptions(
e1.WritePlatformConfigTag(
"IntDir", cond, R"($(Platform)\$(Configuration)\$(ProjectName)\)");
} else {
+ if (ttype == cmStateEnums::SHARED_LIBRARY ||
+ ttype == cmStateEnums::MODULE_LIBRARY ||
+ ttype == cmStateEnums::EXECUTABLE) {
+ auto linker = this->GeneratorTarget->GetLinkerTool(config);
+ if (!linker.empty()) {
+ ConvertToWindowsSlash(linker);
+ e1.WritePlatformConfigTag("LinkToolExe", cond, linker);
+ }
+ }
+
std::string intermediateDir = cmStrCat(
this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget), '/',
config, '/');
diff --git a/Source/cmVisualStudioGeneratorOptions.cxx b/Source/cmVisualStudioGeneratorOptions.cxx
index 6188134..9dd2e6c 100644
--- a/Source/cmVisualStudioGeneratorOptions.cxx
+++ b/Source/cmVisualStudioGeneratorOptions.cxx
@@ -137,14 +137,18 @@ bool cmVisualStudioGeneratorOptions::IsManaged() const
bool cmVisualStudioGeneratorOptions::UsingUnicode() const
{
// Look for a _UNICODE definition.
- return std::any_of(this->Defines.begin(), this->Defines.end(),
- [](std::string const& di) { return di == "_UNICODE"_s; });
+ return std::any_of(
+ this->Defines.begin(), this->Defines.end(), [](std::string const& di) {
+ return di == "_UNICODE"_s || cmHasLiteralPrefix(di, "_UNICODE=");
+ });
}
bool cmVisualStudioGeneratorOptions::UsingSBCS() const
{
// Look for a _SBCS definition.
- return std::any_of(this->Defines.begin(), this->Defines.end(),
- [](std::string const& di) { return di == "_SBCS"_s; });
+ return std::any_of(
+ this->Defines.begin(), this->Defines.end(), [](std::string const& di) {
+ return di == "_SBCS"_s || cmHasLiteralPrefix(di, "_SBCS=");
+ });
}
void cmVisualStudioGeneratorOptions::FixCudaCodeGeneration()
diff --git a/Source/cmWorkerPool.cxx b/Source/cmWorkerPool.cxx
index 27cdbba..dd8f459 100644
--- a/Source/cmWorkerPool.cxx
+++ b/Source/cmWorkerPool.cxx
@@ -18,7 +18,6 @@
#include "cmRange.h"
#include "cmStringAlgorithms.h"
#include "cmUVHandlePtr.h"
-#include "cmUVSignalHackRAII.h" // IWYU pragma: keep
/**
* @brief libuv pipe buffer class
@@ -516,9 +515,6 @@ public:
static void UVSlotEnd(uv_async_t* handle);
// -- UV loop
-#ifdef CMAKE_UV_SIGNAL_HACK
- std::unique_ptr<cmUVSignalHackRAII> UVHackRAII;
-#endif
std::unique_ptr<uv_loop_t> UVLoop;
cm::uv_async_ptr UVRequestBegin;
cm::uv_async_ptr UVRequestEnd;
@@ -563,9 +559,6 @@ cmWorkerPoolInternal::cmWorkerPoolInternal(cmWorkerPool* pool)
{
// Initialize libuv loop
uv_disable_stdio_inheritance();
-#ifdef CMAKE_UV_SIGNAL_HACK
- UVHackRAII = cm::make_unique<cmUVSignalHackRAII>();
-#endif
this->UVLoop = cm::make_unique<uv_loop_t>();
uv_loop_init(this->UVLoop.get());
}
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index f54196b..32064b4 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -1576,14 +1576,15 @@ void cmake::SetArgs(const std::vector<std::string>& args)
if (!expandedPreset->ArchitectureStrategy ||
expandedPreset->ArchitectureStrategy ==
cmCMakePresetsGraph::ArchToolsetStrategy::Set) {
- if (!this->GeneratorPlatformSet) {
+ if (!this->GeneratorPlatformSet &&
+ !expandedPreset->Architecture.empty()) {
this->SetGeneratorPlatform(expandedPreset->Architecture);
}
}
if (!expandedPreset->ToolsetStrategy ||
expandedPreset->ToolsetStrategy ==
cmCMakePresetsGraph::ArchToolsetStrategy::Set) {
- if (!this->GeneratorToolsetSet) {
+ if (!this->GeneratorToolsetSet && !expandedPreset->Toolset.empty()) {
this->SetGeneratorToolset(expandedPreset->Toolset);
}
}
@@ -2395,8 +2396,15 @@ int cmake::ActualConfigure()
cmSystemTools::RemoveADirectory(redirectsDir);
if (!cmSystemTools::MakeDirectory(redirectsDir)) {
cmSystemTools::Error(
- "Unable to (re)create the private pkgRedirects directory:\n" +
- redirectsDir);
+ cmStrCat("Unable to (re)create the private pkgRedirects directory:\n ",
+ redirectsDir,
+ "\n"
+ "This may be caused by not having read/write access to "
+ "the build directory.\n"
+ "Try specifying a location with read/write access like:\n"
+ " cmake -B build\n"
+ "If using a CMake presets file, ensure that preset parameter\n"
+ "'binaryDir' expands to a writable directory.\n"));
return -1;
}
this->AddCacheEntry("CMAKE_FIND_PACKAGE_REDIRECTS_DIR", redirectsDir,
@@ -2508,6 +2516,16 @@ int cmake::ActualConfigure()
"Name of generator toolset.", cmStateEnums::INTERNAL);
}
+ if (!this->State->GetInitializedCacheValue("CMAKE_TEST_LAUNCHER")) {
+ cm::optional<std::string> testLauncher =
+ cmSystemTools::GetEnvVar("CMAKE_TEST_LAUNCHER");
+ if (testLauncher && !testLauncher->empty()) {
+ std::string message = "Test launcher to run tests executable.";
+ this->AddCacheEntry("CMAKE_TEST_LAUNCHER", *testLauncher, message,
+ cmStateEnums::STRING);
+ }
+ }
+
if (!this->State->GetInitializedCacheValue(
"CMAKE_CROSSCOMPILING_EMULATOR")) {
cm::optional<std::string> emulator =
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx
index 43a945f..93b0086 100644
--- a/Source/cmcmd.cxx
+++ b/Source/cmcmd.cxx
@@ -203,11 +203,16 @@ bool cmTarFilesFrom(std::string const& file, std::vector<std::string>& files)
void cmCatFile(const std::string& fileToAppend)
{
#ifdef _WIN32
+ _setmode(fileno(stdin), _O_BINARY);
_setmode(fileno(stdout), _O_BINARY);
#endif
- cmsys::ifstream source(fileToAppend.c_str(),
- (std::ios::binary | std::ios::in));
- std::cout << source.rdbuf();
+ std::streambuf* buf = std::cin.rdbuf();
+ cmsys::ifstream source;
+ if (fileToAppend != "-") {
+ source.open(fileToAppend.c_str(), (std::ios::binary | std::ios::in));
+ buf = source.rdbuf();
+ }
+ std::cout << buf;
}
bool cmRemoveDirectory(const std::string& dir, bool recursive = true)
@@ -1147,7 +1152,12 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
int return_value = 0;
bool doing_options = true;
for (auto const& arg : cmMakeRange(args).advance(2)) {
- if (doing_options && cmHasLiteralPrefix(arg, "-")) {
+ if (arg == "-") {
+ doing_options = false;
+ // Destroy console buffers to drop cout/cerr encoding transform.
+ consoleBuf.reset();
+ cmCatFile(arg);
+ } else if (doing_options && cmHasLiteralPrefix(arg, "-")) {
if (arg == "--") {
doing_options = false;
} else {
diff --git a/Source/kwsys/CMakeLists.txt b/Source/kwsys/CMakeLists.txt
index 2b7f2cc..562d5e6 100644
--- a/Source/kwsys/CMakeLists.txt
+++ b/Source/kwsys/CMakeLists.txt
@@ -298,14 +298,6 @@ endif()
set(KWSYS_HEADER_INSTALL_DIR)
set(KWSYS_LIBRARY_INSTALL_DIR)
-# Generated source files will need this header.
-string(COMPARE EQUAL "${PROJECT_SOURCE_DIR}" "${PROJECT_BINARY_DIR}"
- KWSYS_IN_SOURCE_BUILD)
-if(NOT KWSYS_IN_SOURCE_BUILD)
- configure_file(${PROJECT_SOURCE_DIR}/kwsysPrivate.h
- ${PROJECT_BINARY_DIR}/kwsysPrivate.h COPYONLY IMMEDIATE)
-endif()
-
# Select plugin module file name convention.
if(NOT KWSYS_DynamicLoader_PREFIX)
set(KWSYS_DynamicLoader_PREFIX ${CMAKE_SHARED_MODULE_PREFIX})
diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx
index 3bb7869..53b55f6 100644
--- a/Source/kwsys/SystemTools.cxx
+++ b/Source/kwsys/SystemTools.cxx
@@ -412,18 +412,6 @@ inline void Realpath(const std::string& path, std::string& resolved_path,
}
#endif
-#if !defined(_WIN32) && defined(__COMO__)
-// Hack for como strict mode to avoid defining _SVID_SOURCE or _BSD_SOURCE.
-extern "C" {
-extern FILE* popen(__const char* __command, __const char* __modes) __THROW;
-extern int pclose(FILE* __stream) __THROW;
-extern char* realpath(__const char* __restrict __name,
- char* __restrict __resolved) __THROW;
-extern char* strdup(__const char* __s) __THROW;
-extern int putenv(char* __string) __THROW;
-}
-#endif
-
namespace KWSYS_NAMESPACE {
double SystemTools::GetTime()
@@ -777,12 +765,16 @@ const char* SystemTools::GetEnv(const std::string& key)
bool SystemTools::GetEnv(const char* key, std::string& result)
{
#if defined(_WIN32)
- const std::wstring wkey = Encoding::ToWide(key);
- const wchar_t* wv = _wgetenv(wkey.c_str());
- if (wv) {
- result = Encoding::ToNarrow(wv);
- return true;
+ auto wide_key = Encoding::ToWide(key);
+ auto result_size = GetEnvironmentVariableW(wide_key.data(), nullptr, 0);
+ if (result_size <= 0) {
+ return false;
}
+ std::wstring wide_result;
+ wide_result.resize(result_size - 1);
+ GetEnvironmentVariableW(wide_key.data(), &wide_result[0], result_size);
+ result = Encoding::ToNarrow(wide_result);
+ return true;
#else
const char* v = getenv(key);
if (v) {
@@ -2802,14 +2794,14 @@ Status SystemTools::RemoveFile(std::string const& source)
Status SystemTools::RemoveADirectory(std::string const& source)
{
- // Add write permission to the directory so we can modify its
- // content to remove files and directories from it.
+ // Add read and write permission to the directory so we can read
+ // and modify its content to remove files and directories from it.
mode_t mode = 0;
if (SystemTools::GetPermissions(source, mode)) {
#if defined(_WIN32) && !defined(__CYGWIN__)
- mode |= S_IWRITE;
+ mode |= S_IREAD | S_IWRITE;
#else
- mode |= S_IWUSR;
+ mode |= S_IRUSR | S_IWUSR;
#endif
SystemTools::SetPermissions(source, mode);
}