summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
Diffstat (limited to 'Source')
-rw-r--r--Source/CMakeLists.txt2
-rw-r--r--Source/CMakeVersion.cmake4
-rw-r--r--Source/CTest/cmCTestMemCheckHandler.cxx3
-rw-r--r--Source/CTest/cmCTestRunTest.cxx11
-rw-r--r--Source/CTest/cmCTestTestHandler.cxx92
-rw-r--r--Source/CTest/cmCTestTestHandler.h11
-rw-r--r--Source/CTest/cmCTestTypes.h16
-rw-r--r--Source/CursesDialog/form/frm_driver.c2
-rw-r--r--Source/cmAddExecutableCommand.cxx4
-rw-r--r--Source/cmAddLibraryCommand.cxx4
-rw-r--r--Source/cmArchiveWrite.cxx59
-rw-r--r--Source/cmCMakeLanguageCommand.cxx3
-rw-r--r--Source/cmCMakePresetsGraph.cxx5
-rw-r--r--Source/cmCMakePresetsGraph.h4
-rw-r--r--Source/cmCMakePresetsGraphReadJSON.cxx7
-rw-r--r--Source/cmCMakePresetsGraphReadJSONTestPresets.cxx39
-rw-r--r--Source/cmCPluginAPI.cxx2
-rw-r--r--Source/cmCTest.cxx12
-rw-r--r--Source/cmComputeLinkDepends.cxx639
-rw-r--r--Source/cmComputeLinkDepends.h51
-rw-r--r--Source/cmComputeLinkInformation.cxx701
-rw-r--r--Source/cmComputeLinkInformation.h122
-rw-r--r--Source/cmExportFileGenerator.cxx31
-rw-r--r--Source/cmExportTryCompileFileGenerator.cxx2
-rw-r--r--Source/cmFileCommand.cxx31
-rw-r--r--Source/cmFindPackageCommand.cxx14
-rw-r--r--Source/cmFindPackageCommand.h1
-rw-r--r--Source/cmGeneratorExpressionDAGChecker.cxx4
-rw-r--r--Source/cmGeneratorExpressionNode.cxx148
-rw-r--r--Source/cmGeneratorTarget.cxx222
-rw-r--r--Source/cmGeneratorTarget.h13
-rw-r--r--Source/cmGhsMultiTargetGenerator.cxx114
-rw-r--r--Source/cmGhsMultiTargetGenerator.h3
-rw-r--r--Source/cmGlobalGenerator.cxx42
-rw-r--r--Source/cmGlobalGenerator.h7
-rw-r--r--Source/cmGlobalGhsMultiGenerator.cxx352
-rw-r--r--Source/cmGlobalGhsMultiGenerator.h14
-rw-r--r--Source/cmGlobalNinjaGenerator.cxx3
-rw-r--r--Source/cmGlobalVisualStudio10Generator.cxx8
-rw-r--r--Source/cmGlobalVisualStudio10Generator.h2
-rw-r--r--Source/cmGlobalVisualStudio7Generator.cxx20
-rw-r--r--Source/cmGlobalVisualStudio7Generator.h2
-rw-r--r--Source/cmGlobalVisualStudio8Generator.cxx2
-rw-r--r--Source/cmGlobalVisualStudioGenerator.cxx6
-rw-r--r--Source/cmGlobalVisualStudioGenerator.h3
-rw-r--r--Source/cmGlobalXCodeGenerator.cxx110
-rw-r--r--Source/cmGlobalXCodeGenerator.h2
-rw-r--r--Source/cmLinkItem.h6
-rw-r--r--Source/cmLinkLibrariesCommand.cxx2
-rw-r--r--Source/cmLinkLineComputer.cxx10
-rw-r--r--Source/cmLinkLineDeviceComputer.cxx8
-rw-r--r--Source/cmListFileCache.cxx10
-rw-r--r--Source/cmListFileCache.h14
-rw-r--r--Source/cmLocalGenerator.cxx28
-rw-r--r--Source/cmLocalGenerator.h1
-rw-r--r--Source/cmLocalUnixMakefileGenerator3.cxx21
-rw-r--r--Source/cmLocalVisualStudio7Generator.cxx4
-rw-r--r--Source/cmLocalVisualStudioGenerator.cxx2
-rw-r--r--Source/cmMacroCommand.cxx2
-rw-r--r--Source/cmMakefile.cxx42
-rw-r--r--Source/cmMakefile.h40
-rw-r--r--Source/cmOutputConverter.cxx57
-rw-r--r--Source/cmOutputConverter.h13
-rw-r--r--Source/cmPlaceholderExpander.cxx54
-rw-r--r--Source/cmPlaceholderExpander.h19
-rw-r--r--Source/cmPolicies.h4
-rw-r--r--Source/cmRulePlaceholderExpander.cxx245
-rw-r--r--Source/cmRulePlaceholderExpander.h14
-rw-r--r--Source/cmSystemTools.cxx14
-rw-r--r--Source/cmTarget.cxx200
-rw-r--r--Source/cmTarget.h2
-rw-r--r--Source/cmTargetLinkLibrariesCommand.cxx3
-rw-r--r--Source/cmVariableWatchCommand.cxx2
-rw-r--r--Source/cmVisualStudio10TargetGenerator.cxx276
-rw-r--r--Source/cmVisualStudio10TargetGenerator.h10
-rw-r--r--Source/cmVisualStudioGeneratorOptions.cxx3
-rw-r--r--Source/cmVsProjectType.h3
-rw-r--r--Source/cmWhileCommand.cxx33
-rw-r--r--Source/cmake.cxx8
-rw-r--r--Source/cmcmd.cxx29
-rw-r--r--Source/ctest.cxx3
-rw-r--r--Source/kwsys/ProcessUNIX.c2
-rw-r--r--Source/kwsys/ProcessWin32.c2
83 files changed, 3265 insertions, 870 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index ddcdd7e..d6e0096 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -358,6 +358,8 @@ set(SRCS
cmLocalCommonGenerator.h
cmLocalGenerator.cxx
cmLocalGenerator.h
+ cmPlaceholderExpander.cxx
+ cmPlaceholderExpander.h
cmRulePlaceholderExpander.cxx
cmRulePlaceholderExpander.h
cmLocalUnixMakefileGenerator3.cxx
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index 6603a56..8487a85 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,8 +1,8 @@
# CMake version number components.
set(CMake_VERSION_MAJOR 3)
set(CMake_VERSION_MINOR 23)
-set(CMake_VERSION_PATCH 0)
-set(CMake_VERSION_RC 5)
+set(CMake_VERSION_PATCH 20220323)
+#set(CMake_VERSION_RC 0)
set(CMake_VERSION_IS_DIRTY 0)
# Start with the full version number used in tags. It has no dev info.
diff --git a/Source/CTest/cmCTestMemCheckHandler.cxx b/Source/CTest/cmCTestMemCheckHandler.cxx
index 6bb8e79..2d8276a 100644
--- a/Source/CTest/cmCTestMemCheckHandler.cxx
+++ b/Source/CTest/cmCTestMemCheckHandler.cxx
@@ -371,7 +371,8 @@ void cmCTestMemCheckHandler::GenerateCTestXML(cmXMLWriter& xml)
}
this->CleanTestOutput(
memcheckstr,
- static_cast<size_t>(this->CustomMaximumFailedTestOutputSize));
+ static_cast<size_t>(this->CustomMaximumFailedTestOutputSize),
+ this->TestOutputTruncation);
this->WriteTestResultHeader(xml, result);
xml.StartElement("Results");
int memoryErrors = 0;
diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx
index 6cd3b09..2a2cb1c 100644
--- a/Source/CTest/cmCTestRunTest.cxx
+++ b/Source/CTest/cmCTestRunTest.cxx
@@ -277,7 +277,8 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started)
static_cast<size_t>(
this->TestResult.Status == cmCTestTestHandler::COMPLETED
? this->TestHandler->CustomMaximumPassedTestOutputSize
- : this->TestHandler->CustomMaximumFailedTestOutputSize));
+ : this->TestHandler->CustomMaximumFailedTestOutputSize),
+ this->TestHandler->TestOutputTruncation);
}
this->TestResult.Reason = reason;
if (this->TestHandler->LogFile) {
@@ -694,6 +695,14 @@ void cmCTestRunTest::ComputeArguments()
<< " command: " << testCommand << std::endl);
// Print any test-specific env vars in verbose mode
+ if (!this->TestProperties->Directory.empty()) {
+ cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+ this->Index << ": "
+ << "Working Directory: "
+ << this->TestProperties->Directory << std::endl);
+ }
+
+ // Print any test-specific env vars in verbose mode
if (!this->TestProperties->Environment.empty()) {
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
this->Index << ": "
diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx
index 5a3a8d0..a794e3d 100644
--- a/Source/CTest/cmCTestTestHandler.cxx
+++ b/Source/CTest/cmCTestTestHandler.cxx
@@ -281,6 +281,7 @@ cmCTestTestHandler::cmCTestTestHandler()
this->CustomMaximumPassedTestOutputSize = 1 * 1024;
this->CustomMaximumFailedTestOutputSize = 300 * 1024;
+ this->TestOutputTruncation = cmCTestTypes::TruncationMode::Tail;
this->MemCheck = false;
@@ -325,6 +326,7 @@ void cmCTestTestHandler::Initialize()
this->CustomPostTest.clear();
this->CustomMaximumPassedTestOutputSize = 1 * 1024;
this->CustomMaximumFailedTestOutputSize = 300 * 1024;
+ this->TestOutputTruncation = cmCTestTypes::TruncationMode::Tail;
this->TestsToRun.clear();
@@ -358,6 +360,11 @@ void cmCTestTestHandler::PopulateCustomVectors(cmMakefile* mf)
this->CTest->PopulateCustomInteger(
mf, "CTEST_CUSTOM_MAXIMUM_FAILED_TEST_OUTPUT_SIZE",
this->CustomMaximumFailedTestOutputSize);
+
+ cmValue dval = mf->GetDefinition("CTEST_CUSTOM_TEST_OUTPUT_TRUNCATION");
+ if (dval) {
+ this->SetTestOutputTruncation(dval);
+ }
}
int cmCTestTestHandler::PreProcessHandler()
@@ -2076,6 +2083,20 @@ void cmCTestTestHandler::SetExcludeRegExp(const std::string& arg)
this->ExcludeRegExp = arg;
}
+bool cmCTestTestHandler::SetTestOutputTruncation(const std::string& mode)
+{
+ if (mode == "tail") {
+ this->TestOutputTruncation = cmCTestTypes::TruncationMode::Tail;
+ } else if (mode == "middle") {
+ this->TestOutputTruncation = cmCTestTypes::TruncationMode::Middle;
+ } else if (mode == "head") {
+ this->TestOutputTruncation = cmCTestTypes::TruncationMode::Head;
+ } else {
+ return false;
+ }
+ return true;
+}
+
void cmCTestTestHandler::SetTestsToRunInformation(cmValue in)
{
if (!in) {
@@ -2094,41 +2115,58 @@ void cmCTestTestHandler::SetTestsToRunInformation(cmValue in)
}
}
-void cmCTestTestHandler::CleanTestOutput(std::string& output, size_t length)
+void cmCTestTestHandler::CleanTestOutput(std::string& output, size_t length,
+ cmCTestTypes::TruncationMode truncate)
{
if (!length || length >= output.size() ||
output.find("CTEST_FULL_OUTPUT") != std::string::npos) {
return;
}
- // Truncate at given length but do not break in the middle of a multi-byte
- // UTF-8 encoding.
+ // Advance n bytes in string delimited by begin/end but do not break in the
+ // middle of a multi-byte UTF-8 encoding.
+ auto utf8_advance = [](char const* const begin, char const* const end,
+ size_t n) -> const char* {
+ char const* const stop = begin + n;
+ char const* current = begin;
+ while (current < stop) {
+ unsigned int ch;
+ if (const char* next = cm_utf8_decode_character(current, end, &ch)) {
+ if (next > stop) {
+ break;
+ }
+ current = next;
+ } else // Bad byte will be handled by cmXMLWriter.
+ {
+ ++current;
+ }
+ }
+ return current;
+ };
+
+ // Truncation message.
+ const std::string msg =
+ "\n[This part of the test output was removed since it "
+ "exceeds the threshold of " +
+ std::to_string(length) + " bytes.]\n";
+
char const* const begin = output.c_str();
char const* const end = begin + output.size();
- char const* const truncate = begin + length;
- char const* current = begin;
- while (current < truncate) {
- unsigned int ch;
- if (const char* next = cm_utf8_decode_character(current, end, &ch)) {
- if (next > truncate) {
- break;
- }
- current = next;
- } else // Bad byte will be handled by cmXMLWriter.
- {
- ++current;
- }
- }
- output.erase(current - begin);
-
- // Append truncation message.
- std::ostringstream msg;
- msg << "...\n"
- "The rest of the test output was removed since it exceeds the "
- "threshold "
- "of "
- << length << " bytes.\n";
- output += msg.str();
+
+ // Erase head, middle or tail of output.
+ if (truncate == cmCTestTypes::TruncationMode::Head) {
+ char const* current = utf8_advance(begin, end, output.size() - length);
+ output.erase(0, current - begin);
+ output.insert(0, msg + "...");
+ } else if (truncate == cmCTestTypes::TruncationMode::Middle) {
+ char const* current = utf8_advance(begin, end, length / 2);
+ output.erase(current - begin, output.size() - length);
+ output.insert(current - begin, "..." + msg + "...");
+ } else { // default or "tail"
+ char const* current = utf8_advance(begin, end, length);
+ output.erase(current - begin);
+ output += ("..." + msg);
+ }
}
bool cmCTestTestHandler::SetTestsProperties(
diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h
index 135e764..d0049da 100644
--- a/Source/CTest/cmCTestTestHandler.h
+++ b/Source/CTest/cmCTestTestHandler.h
@@ -19,6 +19,7 @@
#include "cmCTest.h"
#include "cmCTestGenericHandler.h"
#include "cmCTestResourceSpec.h"
+#include "cmCTestTypes.h"
#include "cmDuration.h"
#include "cmListFileCache.h"
#include "cmValue.h"
@@ -32,6 +33,7 @@ class cmXMLWriter;
*/
class cmCTestTestHandler : public cmCTestGenericHandler
{
+ friend class cmCTest;
friend class cmCTestRunTest;
friend class cmCTestMultiProcessHandler;
@@ -80,6 +82,9 @@ public:
this->CustomMaximumFailedTestOutputSize = n;
}
+ //! Set test output truncation mode. Return false if unknown mode.
+ bool SetTestOutputTruncation(const std::string& mode);
+
//! pass the -I argument down
void SetTestsToRunInformation(cmValue);
@@ -242,8 +247,9 @@ protected:
void AttachFile(cmXMLWriter& xml, std::string const& file,
std::string const& name);
- //! Clean test output to specified length
- void CleanTestOutput(std::string& output, size_t length);
+ //! Clean test output to specified length and truncation mode
+ void CleanTestOutput(std::string& output, size_t length,
+ cmCTestTypes::TruncationMode truncate);
cmDuration ElapsedTestingTime;
@@ -258,6 +264,7 @@ protected:
bool MemCheck;
int CustomMaximumPassedTestOutputSize;
int CustomMaximumFailedTestOutputSize;
+ cmCTestTypes::TruncationMode TestOutputTruncation;
int MaxIndex;
public:
diff --git a/Source/CTest/cmCTestTypes.h b/Source/CTest/cmCTestTypes.h
new file mode 100644
index 0000000..843d27a
--- /dev/null
+++ b/Source/CTest/cmCTestTypes.h
@@ -0,0 +1,16 @@
+/* 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
+
+namespace cmCTestTypes {
+
+enum class TruncationMode
+{ // Test output truncation mode
+ Tail,
+ Middle,
+ Head
+};
+}
diff --git a/Source/CursesDialog/form/frm_driver.c b/Source/CursesDialog/form/frm_driver.c
index 112ab08..9cbb12f 100644
--- a/Source/CursesDialog/form/frm_driver.c
+++ b/Source/CursesDialog/form/frm_driver.c
@@ -1456,7 +1456,7 @@ static int IFN_Beginning_Of_Field(FORM * form)
|
| Description : Place the cursor after the last non-pad character in
| the field. If the field occupies the last position in
-| the buffer, the cursos is positioned on the last
+| the buffer, the cursor is positioned on the last
| character.
|
| Return Values : E_OK - success
diff --git a/Source/cmAddExecutableCommand.cxx b/Source/cmAddExecutableCommand.cxx
index 9dd8a19..16a8965 100644
--- a/Source/cmAddExecutableCommand.cxx
+++ b/Source/cmAddExecutableCommand.cxx
@@ -54,6 +54,10 @@ bool cmAddExecutableCommand(std::vector<std::string> const& args,
}
}
+ if (importTarget && !importGlobal) {
+ importGlobal = mf.IsImportedTargetGlobalScope();
+ }
+
bool nameOk = cmGeneratorExpression::IsValidTargetName(exename) &&
!cmGlobalGenerator::IsReservedTarget(exename);
diff --git a/Source/cmAddLibraryCommand.cxx b/Source/cmAddLibraryCommand.cxx
index a5d1f6a..29fc09b 100644
--- a/Source/cmAddLibraryCommand.cxx
+++ b/Source/cmAddLibraryCommand.cxx
@@ -131,6 +131,10 @@ bool cmAddLibraryCommand(std::vector<std::string> const& args,
}
}
+ if (importTarget && !importGlobal) {
+ importGlobal = mf.IsImportedTargetGlobalScope();
+ }
+
if (type == cmStateEnums::INTERFACE_LIBRARY) {
if (importGlobal && !importTarget) {
status.SetError(
diff --git a/Source/cmArchiveWrite.cxx b/Source/cmArchiveWrite.cxx
index 9e0d80c..cfde37c 100644
--- a/Source/cmArchiveWrite.cxx
+++ b/Source/cmArchiveWrite.cxx
@@ -95,6 +95,19 @@ cmArchiveWrite::cmArchiveWrite(std::ostream& os, Compress c,
, Verbose(false)
, Format(format)
{
+ // Upstream fixed an issue with their integer parsing in 3.4.0
+ // which would cause spurious errors to be raised from `strtoull`.
+
+ if (numThreads < 1) {
+ int upperLimit = (numThreads == 0) ? std::numeric_limits<int>::max()
+ : std::abs(numThreads);
+
+ numThreads =
+ cm::clamp<int>(std::thread::hardware_concurrency(), 1, upperLimit);
+ }
+
+ std::string sNumThreads = std::to_string(numThreads);
+
switch (c) {
case CompressNone:
if (archive_write_add_filter_none(this->Archive) != ARCHIVE_OK) {
@@ -150,36 +163,23 @@ cmArchiveWrite::cmArchiveWrite(std::ostream& os, Compress c,
return;
}
- {
#if ARCHIVE_VERSION_NUMBER >= 3004000
- // Upstream fixed an issue with their integer parsing in 3.4.0
- // which would cause spurious errors to be raised from `strtoull`.
-
- if (numThreads < 1) {
- int upperLimit = (numThreads == 0) ? std::numeric_limits<int>::max()
- : std::abs(numThreads);
-
- numThreads =
- cm::clamp<int>(std::thread::hardware_concurrency(), 1, upperLimit);
- }
# ifdef _AIX
- // FIXME: Using more than 2 threads creates an empty archive.
- // Enforce this limit pending further investigation.
- numThreads = std::min(numThreads, 2);
+ // FIXME: Using more than 2 threads creates an empty archive.
+ // Enforce this limit pending further investigation.
+ if (numThreads > 2) {
+ numThreads = 2;
+ sNumThreads = std::to_string(numThreads);
+ }
# endif
-
- std::string sNumThreads = std::to_string(numThreads);
-
- if (archive_write_set_filter_option(this->Archive, "xz", "threads",
- sNumThreads.c_str()) !=
- ARCHIVE_OK) {
- this->Error = cmStrCat("archive_compressor_xz_options: ",
- cm_archive_error_string(this->Archive));
- return;
- }
-#endif
+ if (archive_write_set_filter_option(this->Archive, "xz", "threads",
+ sNumThreads.c_str()) != ARCHIVE_OK) {
+ this->Error = cmStrCat("archive_compressor_xz_options: ",
+ cm_archive_error_string(this->Archive));
+ return;
}
+#endif
break;
case CompressZstd:
@@ -188,6 +188,15 @@ cmArchiveWrite::cmArchiveWrite(std::ostream& os, Compress c,
cm_archive_error_string(this->Archive));
return;
}
+
+#if ARCHIVE_VERSION_NUMBER >= 3006000
+ if (archive_write_set_filter_option(this->Archive, "zstd", "threads",
+ sNumThreads.c_str()) != ARCHIVE_OK) {
+ this->Error = cmStrCat("archive_compressor_zstd_options: ",
+ cm_archive_error_string(this->Archive));
+ return;
+ }
+#endif
break;
}
diff --git a/Source/cmCMakeLanguageCommand.cxx b/Source/cmCMakeLanguageCommand.cxx
index 789c78d..27d8cb8 100644
--- a/Source/cmCMakeLanguageCommand.cxx
+++ b/Source/cmCMakeLanguageCommand.cxx
@@ -84,7 +84,8 @@ bool cmCMakeLanguageCommandCALL(std::vector<cmListFileArgument> const& args,
for (size_t i = startArg; i < args.size(); ++i) {
funcArgs.emplace_back(args[i].Value, args[i].Delim, context.Line);
}
- cmListFileFunction func{ callCommand, context.Line, std::move(funcArgs) };
+ cmListFileFunction func{ callCommand, context.Line, context.Line,
+ std::move(funcArgs) };
if (defer) {
if (defer->Id.empty()) {
diff --git a/Source/cmCMakePresetsGraph.cxx b/Source/cmCMakePresetsGraph.cxx
index dc14831..478c175 100644
--- a/Source/cmCMakePresetsGraph.cxx
+++ b/Source/cmCMakePresetsGraph.cxx
@@ -781,6 +781,8 @@ cmCMakePresetsGraph::TestPreset::VisitPresetInherit(
parentOutput.MaxPassedTestOutputSize);
InheritOptionalValue(output.MaxFailedTestOutputSize,
parentOutput.MaxFailedTestOutputSize);
+ InheritOptionalValue(output.TestOutputTruncation,
+ parentOutput.TestOutputTruncation);
InheritOptionalValue(output.MaxTestNameWidth,
parentOutput.MaxTestNameWidth);
} else {
@@ -1035,6 +1037,9 @@ const char* cmCMakePresetsGraph::ResultToString(ReadFileResult result)
"support.";
case ReadFileResult::CYCLIC_INCLUDE:
return "Cyclic include among preset files";
+ case ReadFileResult::TEST_OUTPUT_TRUNCATION_UNSUPPORTED:
+ return "File version must be 5 or higher for testOutputTruncation "
+ "preset support.";
}
return "Unknown error";
diff --git a/Source/cmCMakePresetsGraph.h b/Source/cmCMakePresetsGraph.h
index 9d6c61a..f1f8662 100644
--- a/Source/cmCMakePresetsGraph.h
+++ b/Source/cmCMakePresetsGraph.h
@@ -14,6 +14,8 @@
#include <cm/optional>
+#include "CTest/cmCTestTypes.h"
+
enum class PackageResolveMode;
class cmCMakePresetsGraph
@@ -47,6 +49,7 @@ public:
CONDITION_UNSUPPORTED,
TOOLCHAIN_FILE_UNSUPPORTED,
CYCLIC_INCLUDE,
+ TEST_OUTPUT_TRUNCATION_UNSUPPORTED,
};
enum class ArchToolsetStrategy
@@ -226,6 +229,7 @@ public:
cm::optional<bool> SubprojectSummary;
cm::optional<int> MaxPassedTestOutputSize;
cm::optional<int> MaxFailedTestOutputSize;
+ cm::optional<cmCTestTypes::TruncationMode> TestOutputTruncation;
cm::optional<int> MaxTestNameWidth;
};
diff --git a/Source/cmCMakePresetsGraphReadJSON.cxx b/Source/cmCMakePresetsGraphReadJSON.cxx
index 85cf5be..e68f0fa 100644
--- a/Source/cmCMakePresetsGraphReadJSON.cxx
+++ b/Source/cmCMakePresetsGraphReadJSON.cxx
@@ -33,7 +33,7 @@ using TestPreset = cmCMakePresetsGraph::TestPreset;
using ArchToolsetStrategy = cmCMakePresetsGraph::ArchToolsetStrategy;
constexpr int MIN_VERSION = 1;
-constexpr int MAX_VERSION = 4;
+constexpr int MAX_VERSION = 5;
struct CMakeVersion
{
@@ -568,6 +568,11 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile(
return ReadFileResult::CONDITION_UNSUPPORTED;
}
+ // Support for TestOutputTruncation added in version 5.
+ if (v < 5 && preset.Output) {
+ return ReadFileResult::TEST_OUTPUT_TRUNCATION_UNSUPPORTED;
+ }
+
this->TestPresetOrder.push_back(preset.Name);
}
diff --git a/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx b/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx
index 4d6474a..43eccfe 100644
--- a/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx
+++ b/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx
@@ -16,6 +16,8 @@
#include "cmCMakePresetsGraphInternal.h"
#include "cmJSONHelpers.h"
+#include "CTest/cmCTestTypes.h"
+
namespace {
using ReadFileResult = cmCMakePresetsGraph::ReadFileResult;
using TestPreset = cmCMakePresetsGraph::TestPreset;
@@ -55,6 +57,40 @@ auto const TestPresetOptionalOutputVerbosityHelper =
ReadFileResult>(ReadFileResult::READ_OK,
TestPresetOutputVerbosityHelper);
+ReadFileResult TestPresetOutputTruncationHelper(
+ cmCTestTypes::TruncationMode& out, const Json::Value* value)
+{
+ if (!value) {
+ out = cmCTestTypes::TruncationMode::Tail;
+ return ReadFileResult::READ_OK;
+ }
+
+ if (!value->isString()) {
+ return ReadFileResult::INVALID_PRESET;
+ }
+
+ if (value->asString() == "tail") {
+ out = cmCTestTypes::TruncationMode::Tail;
+ return ReadFileResult::READ_OK;
+ }
+
+ if (value->asString() == "middle") {
+ out = cmCTestTypes::TruncationMode::Middle;
+ return ReadFileResult::READ_OK;
+ }
+
+ if (value->asString() == "head") {
+ out = cmCTestTypes::TruncationMode::Head;
+ return ReadFileResult::READ_OK;
+ }
+
+ return ReadFileResult::INVALID_PRESET;
+}
+
+auto const TestPresetOptionalTruncationHelper =
+ cmJSONOptionalHelper<cmCTestTypes::TruncationMode, ReadFileResult>(
+ ReadFileResult::READ_OK, TestPresetOutputTruncationHelper);
+
auto const TestPresetOptionalOutputHelper =
cmJSONOptionalHelper<TestPreset::OutputOptions, ReadFileResult>(
ReadFileResult::READ_OK,
@@ -83,6 +119,9 @@ auto const TestPresetOptionalOutputHelper =
.Bind("maxFailedTestOutputSize"_s,
&TestPreset::OutputOptions::MaxFailedTestOutputSize,
cmCMakePresetsGraphInternal::PresetOptionalIntHelper, false)
+ .Bind("testOutputTruncation"_s,
+ &TestPreset::OutputOptions::TestOutputTruncation,
+ TestPresetOptionalTruncationHelper, false)
.Bind("maxTestNameWidth"_s, &TestPreset::OutputOptions::MaxTestNameWidth,
cmCMakePresetsGraphInternal::PresetOptionalIntHelper, false));
diff --git a/Source/cmCPluginAPI.cxx b/Source/cmCPluginAPI.cxx
index 1b11f20..abec968 100644
--- a/Source/cmCPluginAPI.cxx
+++ b/Source/cmCPluginAPI.cxx
@@ -432,7 +432,7 @@ static int CCONV cmExecuteCommand(void* arg, const char* name, int numArgs,
lffArgs.emplace_back(args[i], cmListFileArgument::Quoted, 0);
}
- cmListFileFunction lff{ name, 0, std::move(lffArgs) };
+ cmListFileFunction lff{ name, 0, 0, std::move(lffArgs) };
cmExecutionStatus status(*mf);
return mf->ExecuteCommand(lff, status);
}
diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx
index a1e920e..710b4d7 100644
--- a/Source/cmCTest.cxx
+++ b/Source/cmCTest.cxx
@@ -2036,6 +2036,13 @@ bool cmCTest::HandleCommandLineArguments(size_t& i,
"Invalid value for '--test-output-size-failed': " << args[i]
<< "\n");
}
+ } else if (this->CheckArgument(arg, "--test-output-truncation"_s) &&
+ i < args.size() - 1) {
+ i++;
+ if (!this->Impl->TestHandler.SetTestOutputTruncation(args[i])) {
+ errormsg = "Invalid value for '--test-output-truncation': " + args[i];
+ return false;
+ }
} else if (this->CheckArgument(arg, "-N"_s, "--show-only")) {
this->Impl->ShowOnly = true;
} else if (cmHasLiteralPrefix(arg, "--show-only=")) {
@@ -2464,6 +2471,11 @@ bool cmCTest::SetArgsFromPreset(const std::string& presetName,
*expandedPreset->Output->MaxFailedTestOutputSize);
}
+ if (expandedPreset->Output->TestOutputTruncation) {
+ this->Impl->TestHandler.TestOutputTruncation =
+ *expandedPreset->Output->TestOutputTruncation;
+ }
+
if (expandedPreset->Output->MaxTestNameWidth) {
this->Impl->MaxTestNameWidth = *expandedPreset->Output->MaxTestNameWidth;
}
diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx
index 370ddff..a4dc01b 100644
--- a/Source/cmComputeLinkDepends.cxx
+++ b/Source/cmComputeLinkDepends.cxx
@@ -11,13 +11,18 @@
#include <utility>
#include <cm/memory>
+#include <cm/string_view>
+#include <cmext/string_view>
#include "cmComputeComponentGraph.h"
+#include "cmGeneratorExpression.h"
+#include "cmGeneratorExpressionDAGChecker.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
#include "cmListFileCache.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
+#include "cmMessageType.h"
#include "cmRange.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
@@ -174,8 +179,62 @@ items that we know the linker will re-use 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)
+{
+ auto featureSupported = cmStrCat(
+ "CMAKE_", linkLanguage, "_LINK_LIBRARY_USING_", feature, "_SUPPORTED");
+ if (makefile->GetDefinition(featureSupported).IsOn()) {
+ return true;
+ }
+
+ featureSupported =
+ cmStrCat("CMAKE_LINK_LIBRARY_USING_", feature, "_SUPPORTED");
+ return makefile->GetDefinition(featureSupported).IsOn();
+}
+
+// LINK_GROUP helpers
+const auto LG_BEGIN = "<LINK_GROUP:"_s;
+const auto LG_END = "</LINK_GROUP:"_s;
+
+inline std::string ExtractGroupFeature(std::string const& item)
+{
+ return item.substr(LG_BEGIN.length(),
+ item.find(':', LG_BEGIN.length()) - LG_BEGIN.length());
+}
+
+bool IsGroupFeatureSupported(cmMakefile* makefile,
+ std::string const& linkLanguage,
+ std::string const& feature)
+{
+ auto featureSupported = cmStrCat(
+ "CMAKE_", linkLanguage, "_LINK_GROUP_USING_", feature, "_SUPPORTED");
+ if (makefile->GetDefinition(featureSupported).IsOn()) {
+ return true;
+ }
+
+ featureSupported =
+ cmStrCat("CMAKE_LINK_GROUP_USING_", feature, "_SUPPORTED");
+ return makefile->GetDefinition(featureSupported).IsOn();
+}
+}
+
+const std::string cmComputeLinkDepends::LinkEntry::DEFAULT = "DEFAULT";
+
cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target,
- const std::string& config)
+ const std::string& config,
+ const std::string& linkLanguage)
{
// Store context information.
this->Target = target;
@@ -183,6 +242,50 @@ cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target,
this->GlobalGenerator =
this->Target->GetLocalGenerator()->GetGlobalGenerator();
this->CMakeInstance = this->GlobalGenerator->GetCMakeInstance();
+ this->LinkLanguage = linkLanguage;
+
+ // target oriented feature override property takes precedence over
+ // global override property
+ cm::string_view lloPrefix = "LINK_LIBRARY_OVERRIDE_"_s;
+ auto const& keys = this->Target->GetPropertyKeys();
+ std::for_each(
+ keys.cbegin(), keys.cend(),
+ [this, &lloPrefix, &config, &linkLanguage](std::string const& key) {
+ if (cmHasPrefix(key, lloPrefix)) {
+ if (cmValue feature = this->Target->GetProperty(key)) {
+ if (!feature->empty() && key.length() > lloPrefix.length()) {
+ auto item = key.substr(lloPrefix.length());
+ cmGeneratorExpressionDAGChecker dag{ this->Target->GetBacktrace(),
+ this->Target,
+ "LINK_LIBRARY_OVERRIDE",
+ nullptr, nullptr };
+ auto overrideFeature = cmGeneratorExpression::Evaluate(
+ feature, this->Target->GetLocalGenerator(), config, this->Target,
+ &dag, this->Target, linkLanguage);
+ this->LinkLibraryOverride.emplace(item, overrideFeature);
+ }
+ }
+ }
+ });
+ // global override property
+ if (cmValue linkLibraryOverride =
+ this->Target->GetProperty("LINK_LIBRARY_OVERRIDE")) {
+ cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(), target,
+ "LINK_LIBRARY_OVERRIDE", nullptr,
+ nullptr };
+ auto overrideValue = cmGeneratorExpression::Evaluate(
+ linkLibraryOverride, target->GetLocalGenerator(), config, target, &dag,
+ target, linkLanguage);
+
+ auto overrideList = cmTokenize(overrideValue, ","_s);
+ if (overrideList.size() >= 2) {
+ auto const& feature = overrideList.front();
+ for_each(overrideList.cbegin() + 1, overrideList.cend(),
+ [this, &feature](std::string const& item) {
+ this->LinkLibraryOverride.emplace(item, feature);
+ });
+ }
+ }
// The configuration being linked.
this->HasConfig = !config.empty();
@@ -234,6 +337,11 @@ cmComputeLinkDepends::Compute()
// Infer dependencies of targets for which they were not known.
this->InferDependencies();
+ // finalize groups dependencies
+ // All dependencies which are raw items must be replaced by the group
+ // it belongs to, if any.
+ this->UpdateGroupDependencies();
+
// Cleanup the constraint graph.
this->CleanConstraintGraph();
@@ -248,8 +356,21 @@ cmComputeLinkDepends::Compute()
this->DisplayConstraintGraph();
}
+ // Compute the DAG of strongly connected components. The algorithm
+ // used by cmComputeComponentGraph should identify the components in
+ // the same order in which the items were originally discovered in
+ // the BFS. This should preserve the original order when no
+ // constraints disallow it.
+ this->CCG =
+ cm::make_unique<cmComputeComponentGraph>(this->EntryConstraintGraph);
+ this->CCG->Compute();
+
+ if (!this->CheckCircularDependencies()) {
+ return this->FinalLinkEntries;
+ }
+
// Compute the final ordering.
- this->OrderLinkEntires();
+ this->OrderLinkEntries();
// Compute the final set of link entries.
// Iterate in reverse order so we can keep only the last occurrence
@@ -273,6 +394,29 @@ cmComputeLinkDepends::Compute()
// 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>";
+ }
+ }
+ }
+
// Display the final set.
if (this->DebugMode) {
this->DisplayFinalEntries();
@@ -281,76 +425,91 @@ cmComputeLinkDepends::Compute()
return this->FinalLinkEntries;
}
-std::map<cmLinkItem, int>::iterator cmComputeLinkDepends::AllocateLinkEntry(
- cmLinkItem const& item)
+std::string const& cmComputeLinkDepends::GetCurrentFeature(
+ std::string const& item, std::string const& defaultFeature) const
+{
+ auto it = this->LinkLibraryOverride.find(item);
+ return it == this->LinkLibraryOverride.end() ? defaultFeature : it->second;
+}
+
+std::pair<std::map<cmLinkItem, int>::iterator, bool>
+cmComputeLinkDepends::AllocateLinkEntry(cmLinkItem const& item)
{
std::map<cmLinkItem, int>::value_type index_entry(
item, static_cast<int>(this->EntryList.size()));
- auto lei = this->LinkEntryIndex.insert(index_entry).first;
- this->EntryList.emplace_back();
- this->InferredDependSets.emplace_back();
- this->EntryConstraintGraph.emplace_back();
+ auto lei = this->LinkEntryIndex.insert(index_entry);
+ if (lei.second) {
+ this->EntryList.emplace_back();
+ this->InferredDependSets.emplace_back();
+ this->EntryConstraintGraph.emplace_back();
+ }
return lei;
}
-int cmComputeLinkDepends::AddLinkEntry(cmLinkItem const& item)
+std::pair<int, bool> cmComputeLinkDepends::AddLinkEntry(cmLinkItem const& item,
+ int groupIndex)
{
+ // Allocate a spot for the item entry.
+ auto lei = this->AllocateLinkEntry(item);
+
// Check if the item entry has already been added.
- auto lei = this->LinkEntryIndex.find(item);
- if (lei != this->LinkEntryIndex.end()) {
+ if (!lei.second) {
// Yes. We do not need to follow the item's dependencies again.
- return lei->second;
+ return { lei.first->second, false };
}
- // Allocate a spot for the item entry.
- lei = this->AllocateLinkEntry(item);
-
// Initialize the item entry.
- int index = lei->second;
+ int index = lei.first->second;
LinkEntry& entry = this->EntryList[index];
entry.Item = BT<std::string>(item.AsStr(), item.Backtrace);
entry.Target = item.Target;
- entry.IsFlag = (!entry.Target && entry.Item.Value[0] == '-' &&
- entry.Item.Value[1] != 'l' &&
- entry.Item.Value.substr(0, 10) != "-framework");
+ if (!entry.Target && entry.Item.Value[0] == '-' &&
+ entry.Item.Value[1] != 'l' &&
+ entry.Item.Value.substr(0, 10) != "-framework") {
+ entry.Kind = LinkEntry::Flag;
+ } else if (cmHasPrefix(entry.Item.Value, LG_BEGIN) &&
+ cmHasSuffix(entry.Item.Value, '>')) {
+ entry.Kind = LinkEntry::Group;
+ }
- // If the item has dependencies queue it to follow them.
- if (entry.Target) {
- // Target dependencies are always known. Follow them.
- BFSEntry qe = { index, nullptr };
- this->BFSQueue.push(qe);
- } else {
- // Look for an old-style <item>_LIB_DEPENDS variable.
- std::string var = cmStrCat(entry.Item.Value, "_LIB_DEPENDS");
- if (cmValue val = this->Makefile->GetDefinition(var)) {
- // The item dependencies are known. Follow them.
- BFSEntry qe = { index, val->c_str() };
+ if (entry.Kind != LinkEntry::Group) {
+ // If the item has dependencies queue it to follow them.
+ if (entry.Target) {
+ // Target dependencies are always known. Follow them.
+ BFSEntry qe = { index, groupIndex, nullptr };
this->BFSQueue.push(qe);
- } else if (!entry.IsFlag) {
- // The item dependencies are not known. We need to infer them.
- this->InferredDependSets[index].Initialized = true;
+ } else {
+ // Look for an old-style <item>_LIB_DEPENDS variable.
+ std::string var = cmStrCat(entry.Item.Value, "_LIB_DEPENDS");
+ if (cmValue val = this->Makefile->GetDefinition(var)) {
+ // The item dependencies are known. Follow them.
+ BFSEntry qe = { index, groupIndex, val->c_str() };
+ this->BFSQueue.push(qe);
+ } else if (entry.Kind != LinkEntry::Flag) {
+ // The item dependencies are not known. We need to infer them.
+ this->InferredDependSets[index].Initialized = true;
+ }
}
}
- return index;
+ return { index, true };
}
void cmComputeLinkDepends::AddLinkObject(cmLinkItem const& item)
{
+ // Allocate a spot for the item entry.
+ auto lei = this->AllocateLinkEntry(item);
+
// Check if the item entry has already been added.
- auto lei = this->LinkEntryIndex.find(item);
- if (lei != this->LinkEntryIndex.end()) {
+ if (!lei.second) {
return;
}
- // Allocate a spot for the item entry.
- lei = this->AllocateLinkEntry(item);
-
// Initialize the item entry.
- int index = lei->second;
+ int index = lei.first->second;
LinkEntry& entry = this->EntryList[index];
entry.Item = BT<std::string>(item.AsStr(), item.Backtrace);
- entry.IsObject = true;
+ entry.Kind = LinkEntry::Object;
// Record explicitly linked object files separately.
this->ObjectEntries.emplace_back(index);
@@ -359,8 +518,8 @@ void cmComputeLinkDepends::AddLinkObject(cmLinkItem const& item)
void cmComputeLinkDepends::FollowLinkEntry(BFSEntry qe)
{
// Get this entry representation.
- int depender_index = qe.Index;
- LinkEntry const& entry = this->EntryList[depender_index];
+ int depender_index = qe.GroupIndex == -1 ? qe.Index : qe.GroupIndex;
+ LinkEntry const& entry = this->EntryList[qe.Index];
// Follow the item's dependencies.
if (entry.Target) {
@@ -423,25 +582,24 @@ void cmComputeLinkDepends::QueueSharedDependencies(
void cmComputeLinkDepends::HandleSharedDependency(SharedDepEntry const& dep)
{
- // Check if the target already has an entry.
- auto lei = this->LinkEntryIndex.find(dep.Item);
- if (lei == this->LinkEntryIndex.end()) {
- // Allocate a spot for the item entry.
- lei = this->AllocateLinkEntry(dep.Item);
+ // Allocate a spot for the item entry.
+ auto lei = this->AllocateLinkEntry(dep.Item);
+ int index = lei.first->second;
+ // Check if the target does not already has an entry.
+ if (lei.second) {
// Initialize the item entry.
- LinkEntry& entry = this->EntryList[lei->second];
+ LinkEntry& entry = this->EntryList[index];
entry.Item = BT<std::string>(dep.Item.AsStr(), dep.Item.Backtrace);
entry.Target = dep.Item.Target;
// This item was added specifically because it is a dependent
// shared library. It may get special treatment
// in cmComputeLinkInformation.
- entry.IsSharedDep = true;
+ entry.Kind = LinkEntry::SharedDep;
}
// Get the link entry for this target.
- int index = lei->second;
LinkEntry& entry = this->EntryList[index];
// This shared library dependency must follow the item that listed
@@ -541,6 +699,11 @@ void cmComputeLinkDepends::AddLinkEntries(int depender_index,
{
// Track inferred dependency sets implied by this list.
std::map<int, DependSet> dependSets;
+ std::string feature = LinkEntry::DEFAULT;
+
+ bool inGroup = false;
+ std::pair<int, bool> groupIndex{ -1, false };
+ std::vector<int> groupItems;
// Loop over the libraries linked directly by the depender.
for (T const& l : libs) {
@@ -551,35 +714,233 @@ void cmComputeLinkDepends::AddLinkEntries(int 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 >= 0) {
+ 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());
+ }
+ }
+ continue;
+ }
+ if (cmHasPrefix(item.AsStr(), LL_END) && cmHasSuffix(item.AsStr(), '>')) {
+ feature = LinkEntry::DEFAULT;
+ continue;
+ }
+
+ if (cmHasPrefix(item.AsStr(), LG_BEGIN) &&
+ cmHasSuffix(item.AsStr(), '>')) {
+ groupIndex = this->AddLinkEntry(item);
+ if (groupIndex.second) {
+ LinkEntry& entry = this->EntryList[groupIndex.first];
+ entry.Feature = ExtractGroupFeature(item.AsStr());
+ }
+ inGroup = true;
+ if (depender_index >= 0) {
+ this->EntryConstraintGraph[depender_index].emplace_back(
+ groupIndex.first, false, false, cmListFileBacktrace());
+ } else {
+ // This is a direct dependency of the target being linked.
+ this->OriginalEntries.push_back(groupIndex.first);
+ }
+ continue;
+ }
+
+ int dependee_index;
+
+ if (cmHasPrefix(item.AsStr(), LG_END) && cmHasSuffix(item.AsStr(), '>')) {
+ dependee_index = groupIndex.first;
+ if (groupIndex.second) {
+ this->GroupItems.emplace(groupIndex.first, groupItems);
+ }
+ inGroup = false;
+ groupIndex = std::make_pair(-1, false);
+ groupItems.clear();
+ continue;
+ }
+
+ if (depender_index >= 0 && inGroup) {
+ const auto& depender = this->EntryList[depender_index];
+ const auto& groupFeature = this->EntryList[groupIndex.first].Feature;
+ if (depender.Target != nullptr && depender.Target->IsImported() &&
+ !IsGroupFeatureSupported(this->Makefile, this->LinkLanguage,
+ groupFeature)) {
+ this->CMakeInstance->IssueMessage(
+ MessageType::AUTHOR_ERROR,
+ cmStrCat("The 'IMPORTED' target '", depender.Target->GetName(),
+ "' uses the generator-expression '$<LINK_GROUP>' with "
+ "the feature '",
+ groupFeature,
+ "', which is undefined or unsupported.\nDid you miss to "
+ "define it by setting variables \"CMAKE_",
+ this->LinkLanguage, "_LINK_GROUP_USING_", groupFeature,
+ "\" and \"CMAKE_", this->LinkLanguage, "_LINK_GROUP_USING_",
+ groupFeature, "_SUPPORTED\"?"),
+ this->Target->GetBacktrace());
+ }
+ }
+
// Add a link entry for this item.
- int dependee_index = this->AddLinkEntry(l);
+ auto ale = this->AddLinkEntry(item, groupIndex.first);
+ dependee_index = ale.first;
+ LinkEntry& entry = this->EntryList[dependee_index];
+ auto const& itemFeature =
+ this->GetCurrentFeature(entry.Item.Value, feature);
+ if (inGroup && ale.second && entry.Target != nullptr &&
+ (entry.Target->GetType() == cmStateEnums::TargetType::OBJECT_LIBRARY ||
+ entry.Target->GetType() ==
+ cmStateEnums::TargetType::INTERFACE_LIBRARY)) {
+ const auto& groupFeature = this->EntryList[groupIndex.first].Feature;
+ this->CMakeInstance->IssueMessage(
+ MessageType::AUTHOR_WARNING,
+ cmStrCat(
+ "The feature '", groupFeature,
+ "', specified as part of a generator-expression "
+ "'$",
+ LG_BEGIN, groupFeature, ">', will not be applied to the ",
+ (entry.Target->GetType() == cmStateEnums::TargetType::OBJECT_LIBRARY
+ ? "OBJECT"
+ : "INTERFACE"),
+ " 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;
+ }
+ }
+ }
- // The dependee must come after the depender.
- if (depender_index >= 0) {
- this->EntryConstraintGraph[depender_index].emplace_back(
- dependee_index, false, false, cmListFileBacktrace());
- } else {
- // This is a direct dependency of the target being linked.
- this->OriginalEntries.push_back(dependee_index);
- }
-
- // Update the inferred dependencies for earlier items.
- for (auto& dependSet : dependSets) {
- // Add this item to the inferred dependencies of other items.
- // Target items are never inferred dependees because unknown
- // items are outside libraries that should not be depending on
- // targets.
- if (!this->EntryList[dependee_index].Target &&
- !this->EntryList[dependee_index].IsFlag &&
- dependee_index != dependSet.first) {
- dependSet.second.insert(dependee_index);
+ bool supportedItem = entry.Target == nullptr ||
+ (entry.Target->GetType() != cmStateEnums::TargetType::OBJECT_LIBRARY &&
+ entry.Target->GetType() != cmStateEnums::TargetType::INTERFACE_LIBRARY);
+
+ if (supportedItem) {
+ if (inGroup) {
+ const auto& currentFeature = this->EntryList[groupIndex.first].Feature;
+ for (const auto& g : this->GroupItems) {
+ const auto& groupFeature = this->EntryList[g.first].Feature;
+ if (groupFeature == currentFeature) {
+ continue;
+ }
+ if (std::find(g.second.cbegin(), g.second.cend(), dependee_index) !=
+ g.second.cend()) {
+ this->CMakeInstance->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("Impossible to link target '", this->Target->GetName(),
+ "' because the link item '", entry.Item.Value,
+ "', specified with the group feature '", currentFeature,
+ '\'', ", has already occurred with the feature '",
+ groupFeature, '\'', ", which is not allowed."),
+ this->Target->GetBacktrace());
+ continue;
+ }
+ }
+ }
+ if (entry.Feature != itemFeature) {
+ // incompatibles features occurred
+ this->CMakeInstance->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("Impossible to link target '", this->Target->GetName(),
+ "' because the link item '", entry.Item.Value,
+ "', specified ",
+ (itemFeature == LinkEntry::DEFAULT
+ ? "without any feature or 'DEFAULT' feature"
+ : cmStrCat("with the feature '", itemFeature, '\'')),
+ ", has already occurred ",
+ (entry.Feature == LinkEntry::DEFAULT
+ ? "without any feature or 'DEFAULT' feature"
+ : cmStrCat("with the feature '", entry.Feature, '\'')),
+ ", which is not allowed."),
+ this->Target->GetBacktrace());
}
}
- // If this item needs to have dependencies inferred, do so.
- if (this->InferredDependSets[dependee_index].Initialized) {
- // Make sure an entry exists to hold the set for the item.
- dependSets[dependee_index];
+ if (inGroup) {
+ // store item index for dependencies handling
+ groupItems.push_back(dependee_index);
+ } else {
+ std::vector<int> indexes;
+ bool entryHandled = false;
+ // search any occurrence of the library in already defined groups
+ for (const auto& group : this->GroupItems) {
+ for (auto index : group.second) {
+ if (entry.Item.Value == this->EntryList[index].Item.Value) {
+ indexes.push_back(group.first);
+ entryHandled = true;
+ break;
+ }
+ }
+ }
+ if (!entryHandled) {
+ indexes.push_back(dependee_index);
+ }
+
+ for (auto index : indexes) {
+ // The dependee must come after the depender.
+ if (depender_index >= 0) {
+ this->EntryConstraintGraph[depender_index].emplace_back(
+ index, false, false, cmListFileBacktrace());
+ } else {
+ // This is a direct dependency of the target being linked.
+ this->OriginalEntries.push_back(index);
+ }
+
+ // Update the inferred dependencies for earlier items.
+ for (auto& dependSet : dependSets) {
+ // Add this item to the inferred dependencies of other items.
+ // Target items are never inferred dependees because unknown
+ // items are outside libraries that should not be depending on
+ // targets.
+ if (!this->EntryList[index].Target &&
+ this->EntryList[index].Kind != LinkEntry::Flag &&
+ this->EntryList[index].Kind != LinkEntry::Group &&
+ dependee_index != dependSet.first) {
+ dependSet.second.insert(index);
+ }
+ }
+
+ // If this item needs to have dependencies inferred, do so.
+ if (this->InferredDependSets[index].Initialized) {
+ // Make sure an entry exists to hold the set for the item.
+ dependSets[index];
+ }
+ }
}
}
@@ -642,6 +1003,36 @@ void cmComputeLinkDepends::InferDependencies()
}
}
+void cmComputeLinkDepends::UpdateGroupDependencies()
+{
+ if (this->GroupItems.empty()) {
+ return;
+ }
+
+ // Walks through all entries of the constraint graph to replace dependencies
+ // over raw items by the group it belongs to, if any.
+ for (auto& edgeList : this->EntryConstraintGraph) {
+ for (auto& edge : edgeList) {
+ int index = edge;
+ if (this->EntryList[index].Kind == LinkEntry::Group ||
+ this->EntryList[index].Kind == LinkEntry::Flag ||
+ this->EntryList[index].Kind == LinkEntry::Object) {
+ continue;
+ }
+ // search the item in the defined groups
+ for (const auto& groupItems : this->GroupItems) {
+ auto pos = std::find(groupItems.second.cbegin(),
+ groupItems.second.cend(), index);
+ if (pos != groupItems.second.cend()) {
+ // replace lib dependency by the group it belongs to
+ edge = cmGraphEdge{ groupItems.first, false, false,
+ cmListFileBacktrace() };
+ }
+ }
+ }
+ }
+}
+
void cmComputeLinkDepends::CleanConstraintGraph()
{
for (cmGraphEdgeList& edgeList : this->EntryConstraintGraph) {
@@ -655,6 +1046,76 @@ void cmComputeLinkDepends::CleanConstraintGraph()
}
}
+bool cmComputeLinkDepends::CheckCircularDependencies() const
+{
+ std::vector<NodeList> const& components = this->CCG->GetComponents();
+ int nc = static_cast<int>(components.size());
+ for (int c = 0; c < nc; ++c) {
+ // Get the current component.
+ NodeList const& nl = components[c];
+
+ // Skip trivial components.
+ if (nl.size() < 2) {
+ continue;
+ }
+
+ // no group must be evolved
+ bool cycleDetected = false;
+ for (int ni : nl) {
+ if (this->EntryList[ni].Kind == LinkEntry::Group) {
+ cycleDetected = true;
+ break;
+ }
+ }
+ if (!cycleDetected) {
+ continue;
+ }
+
+ // Construct the error message.
+ auto formatItem = [](LinkEntry const& entry) -> std::string {
+ if (entry.Kind == LinkEntry::Group) {
+ auto items =
+ entry.Item.Value.substr(entry.Item.Value.find(':', 12) + 1);
+ items.pop_back();
+ std::replace(items.begin(), items.end(), '|', ',');
+ return cmStrCat("group \"", ExtractGroupFeature(entry.Item.Value),
+ ":{", items, "}\"");
+ }
+ return cmStrCat('"', entry.Item.Value, '"');
+ };
+
+ std::ostringstream e;
+ e << "The inter-target dependency graph, for the target \""
+ << this->Target->GetName()
+ << "\", contains the following strongly connected component "
+ "(cycle):\n";
+ std::vector<int> const& cmap = this->CCG->GetComponentMap();
+ for (int i : nl) {
+ // Get the depender.
+ LinkEntry const& depender = this->EntryList[i];
+
+ // Describe the depender.
+ e << " " << formatItem(depender) << "\n";
+
+ // List its dependencies that are inside the component.
+ EdgeList const& el = this->EntryConstraintGraph[i];
+ for (cmGraphEdge const& ni : el) {
+ int j = ni;
+ if (cmap[j] == c) {
+ LinkEntry const& dependee = this->EntryList[j];
+ e << " depends on " << formatItem(dependee) << "\n";
+ }
+ }
+ }
+ this->CMakeInstance->IssueMessage(MessageType::FATAL_ERROR, e.str(),
+ this->Target->GetBacktrace());
+
+ return false;
+ }
+
+ return true;
+}
+
void cmComputeLinkDepends::DisplayConstraintGraph()
{
// Display the graph nodes and their edges.
@@ -667,17 +1128,8 @@ void cmComputeLinkDepends::DisplayConstraintGraph()
fprintf(stderr, "%s\n", e.str().c_str());
}
-void cmComputeLinkDepends::OrderLinkEntires()
+void cmComputeLinkDepends::OrderLinkEntries()
{
- // Compute the DAG of strongly connected components. The algorithm
- // used by cmComputeComponentGraph should identify the components in
- // the same order in which the items were originally discovered in
- // the BFS. This should preserve the original order when no
- // constraints disallow it.
- this->CCG =
- cm::make_unique<cmComputeComponentGraph>(this->EntryConstraintGraph);
- this->CCG->Compute();
-
// The component graph is guaranteed to be acyclic. Start a DFS
// from every entry to compute a topological order for the
// components.
@@ -867,12 +1319,23 @@ int cmComputeLinkDepends::ComputeComponentCount(NodeList const& nl)
void cmComputeLinkDepends::DisplayFinalEntries()
{
fprintf(stderr, "target [%s] links to:\n", this->Target->GetName().c_str());
+ char space[] = " ";
+ int count = 2;
for (LinkEntry const& lei : this->FinalLinkEntries) {
- if (lei.Target) {
- fprintf(stderr, " target [%s]\n", lei.Target->GetName().c_str());
+ if (lei.Kind == LinkEntry::Group) {
+ fprintf(stderr, " %s group",
+ lei.Item.Value == "<LINK_GROUP>" ? "start" : "end");
+ count = lei.Item.Value == "<LINK_GROUP>" ? 4 : 2;
+ } else if (lei.Target) {
+ fprintf(stderr, "%*starget [%s]", count, space,
+ lei.Target->GetName().c_str());
} else {
- fprintf(stderr, " item [%s]\n", lei.Item.Value.c_str());
+ fprintf(stderr, "%*sitem [%s]", count, space, lei.Item.Value.c_str());
+ }
+ if (lei.Feature != LinkEntry::DEFAULT) {
+ fprintf(stderr, ", feature [%s]", lei.Feature.c_str());
}
+ fprintf(stderr, "\n");
}
fprintf(stderr, "\n");
}
diff --git a/Source/cmComputeLinkDepends.h b/Source/cmComputeLinkDepends.h
index 72316f1..8cc916a 100644
--- a/Source/cmComputeLinkDepends.h
+++ b/Source/cmComputeLinkDepends.h
@@ -9,6 +9,7 @@
#include <queue>
#include <set>
#include <string>
+#include <utility>
#include <vector>
#include "cmGraphAdjacencyList.h"
@@ -29,7 +30,8 @@ class cmComputeLinkDepends
{
public:
cmComputeLinkDepends(cmGeneratorTarget const* target,
- const std::string& config);
+ const std::string& config,
+ const std::string& linkLanguage);
~cmComputeLinkDepends();
cmComputeLinkDepends(const cmComputeLinkDepends&) = delete;
@@ -38,11 +40,32 @@ public:
// Basic information about each link item.
struct LinkEntry
{
+ LinkEntry() = default;
+ LinkEntry(BT<std::string> item, cmGeneratorTarget const* target = nullptr)
+ : Item(std::move(item))
+ , Target(target)
+ {
+ }
+
+ static const std::string DEFAULT;
+
+ enum EntryKind
+ {
+ Library,
+ Object,
+ SharedDep,
+ Flag,
+ // The following member is for the management of items specified
+ // through genex $<LINK_GROUP:...>
+ Group
+ };
+
BT<std::string> Item;
cmGeneratorTarget const* Target = nullptr;
- bool IsSharedDep = false;
- bool IsFlag = false;
- bool IsObject = false;
+ EntryKind Kind = Library;
+ // The following member is for the management of items specified
+ // through genex $<LINK_LIBRARY:...>
+ std::string Feature = std::string(DEFAULT);
};
using EntryVector = std::vector<LinkEntry>;
@@ -60,12 +83,18 @@ private:
cmMakefile* Makefile;
cmGlobalGenerator const* GlobalGenerator;
cmake* CMakeInstance;
+ std::string LinkLanguage;
std::string Config;
EntryVector FinalLinkEntries;
+ std::map<std::string, std::string> LinkLibraryOverride;
+
+ std::string const& GetCurrentFeature(
+ std::string const& item, std::string const& defaultFeature) const;
- std::map<cmLinkItem, int>::iterator AllocateLinkEntry(
+ std::pair<std::map<cmLinkItem, int>::iterator, bool> AllocateLinkEntry(
cmLinkItem const& item);
- int AddLinkEntry(cmLinkItem const& item);
+ std::pair<int, bool> AddLinkEntry(cmLinkItem const& item,
+ int groupIndex = -1);
void AddLinkObject(cmLinkItem const& item);
void AddVarLinkEntries(int depender_index, const char* value);
void AddDirectLinkEntries();
@@ -78,10 +107,14 @@ private:
std::vector<LinkEntry> EntryList;
std::map<cmLinkItem, int> LinkEntryIndex;
+ // map storing, for each group, the list of items
+ std::map<int, std::vector<int>> GroupItems;
+
// BFS of initial dependencies.
struct BFSEntry
{
int Index;
+ int GroupIndex;
const char* LibDepends;
};
std::queue<BFSEntry> BFSQueue;
@@ -114,16 +147,20 @@ private:
std::vector<DependSetList> InferredDependSets;
void InferDependencies();
+ // To finalize dependencies over groups in place of raw items
+ void UpdateGroupDependencies();
+
// Ordering constraint graph adjacency list.
using NodeList = cmGraphNodeList;
using EdgeList = cmGraphEdgeList;
using Graph = cmGraphAdjacencyList;
Graph EntryConstraintGraph;
void CleanConstraintGraph();
+ bool CheckCircularDependencies() const;
void DisplayConstraintGraph();
// Ordering algorithm.
- void OrderLinkEntires();
+ void OrderLinkEntries();
std::vector<char> ComponentVisited;
std::vector<int> ComponentOrder;
diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx
index 2ff91fe..67214f1 100644
--- a/Source/cmComputeLinkInformation.cxx
+++ b/Source/cmComputeLinkInformation.cxx
@@ -8,7 +8,9 @@
#include <utility>
#include <cm/memory>
+#include <cm/optional>
#include <cmext/algorithm>
+#include <cmext/string_view>
#include "cmComputeLinkDepends.h"
#include "cmGeneratorTarget.h"
@@ -18,7 +20,7 @@
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmOrderDirectories.h"
-#include "cmOutputConverter.h"
+#include "cmPlaceholderExpander.h"
#include "cmPolicies.h"
#include "cmState.h"
#include "cmStateTypes.h"
@@ -344,6 +346,29 @@ cmComputeLinkInformation::cmComputeLinkInformation(
this->LinkWithRuntimePath = this->Makefile->IsOn(var);
}
+ // Define some Feature descriptors to handle standard library and object link
+ if (!this->GetLibLinkFileFlag().empty()) {
+ this->LibraryFeatureDescriptors.emplace(
+ "__CMAKE_LINK_LIBRARY",
+ LibraryFeatureDescriptor{
+ "__CMAKE_LINK_LIBRARY",
+ cmStrCat(this->GetLibLinkFileFlag(), "<LIBRARY>") });
+ }
+ if (!this->GetObjLinkFileFlag().empty()) {
+ this->LibraryFeatureDescriptors.emplace(
+ "__CMAKE_LINK_OBJECT",
+ LibraryFeatureDescriptor{
+ "__CMAKE_LINK_OBJECT",
+ cmStrCat(this->GetObjLinkFileFlag(), "<LIBRARY>") });
+ }
+ if (!this->LoaderFlag->empty()) {
+ // Define a Feature descriptor for the link of an executable with exports
+ this->LibraryFeatureDescriptors.emplace(
+ "__CMAKE_LINK_EXECUTABLE",
+ LibraryFeatureDescriptor{ "__CMAKE_LINK_EXECUTABLE",
+ cmStrCat(this->LoaderFlag, "<LIBRARY>") });
+ }
+
// Check the platform policy for missing soname case.
this->NoSONameUsesPath =
this->Makefile->IsOn("CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME");
@@ -409,6 +434,10 @@ cmComputeLinkInformation::cmComputeLinkInformation(
cmComputeLinkInformation::~cmComputeLinkInformation() = default;
+namespace {
+const std::string& DEFAULT = cmComputeLinkDepends::LinkEntry::DEFAULT;
+}
+
void cmComputeLinkInformation::AppendValues(
std::string& result, std::vector<BT<std::string>>& values)
{
@@ -510,17 +539,67 @@ bool cmComputeLinkInformation::Compute()
}
// Compute the ordered link line items.
- cmComputeLinkDepends cld(this->Target, this->Config);
+ cmComputeLinkDepends cld(this->Target, this->Config, this->LinkLanguage);
cld.SetOldLinkDirMode(this->OldLinkDirMode);
cmComputeLinkDepends::EntryVector const& linkEntries = cld.Compute();
+ FeatureDescriptor const* currentFeature = nullptr;
// Add the link line items.
for (cmComputeLinkDepends::LinkEntry const& linkEntry : linkEntries) {
- if (linkEntry.IsSharedDep) {
- this->AddSharedDepItem(linkEntry.Item, linkEntry.Target);
+ if (linkEntry.Kind == cmComputeLinkDepends::LinkEntry::Group) {
+ const auto& groupFeature = this->GetGroupFeature(linkEntry.Feature);
+ if (groupFeature.Supported) {
+ this->Items.emplace_back(
+ BT<std::string>{ linkEntry.Item.Value == "<LINK_GROUP>"
+ ? groupFeature.Prefix
+ : groupFeature.Suffix,
+ linkEntry.Item.Backtrace },
+ ItemIsPath::No);
+ }
+ continue;
+ }
+
+ if (currentFeature != nullptr &&
+ linkEntry.Feature != currentFeature->Name) {
+ // emit feature suffix, if any
+ if (!currentFeature->Suffix.empty()) {
+ this->Items.emplace_back(
+ BT<std::string>{ currentFeature->Suffix,
+ this->Items.back().Value.Backtrace },
+ ItemIsPath::No);
+ }
+ currentFeature = nullptr;
+ }
+
+ if (linkEntry.Feature != DEFAULT &&
+ (currentFeature == nullptr ||
+ linkEntry.Feature != currentFeature->Name)) {
+ if (!this->AddLibraryFeature(linkEntry.Feature)) {
+ continue;
+ }
+ currentFeature = this->FindLibraryFeature(linkEntry.Feature);
+ // emit feature prefix, if any
+ if (!currentFeature->Prefix.empty()) {
+ this->Items.emplace_back(
+ BT<std::string>{ currentFeature->Prefix, linkEntry.Item.Backtrace },
+ ItemIsPath::No);
+ }
+ }
+
+ if (linkEntry.Kind == cmComputeLinkDepends::LinkEntry::SharedDep) {
+ this->AddSharedDepItem(linkEntry);
} else {
- this->AddItem(linkEntry.Item, linkEntry.Target,
- linkEntry.IsObject ? ItemIsObject::Yes : ItemIsObject::No);
+ this->AddItem(linkEntry);
+ }
+ }
+
+ if (currentFeature != nullptr) {
+ // emit feature suffix, if any
+ if (!currentFeature->Suffix.empty()) {
+ this->Items.emplace_back(
+ BT<std::string>{ currentFeature->Suffix,
+ this->Items.back().Value.Backtrace },
+ ItemIsPath::No);
}
}
@@ -576,6 +655,373 @@ bool cmComputeLinkInformation::Compute()
return true;
}
+namespace {
+void FinalizeFeatureFormat(std::string& format, const std::string& activeTag,
+ const std::string& otherTag)
+{
+ auto pos = format.find(otherTag);
+ if (pos != std::string::npos) {
+ format.erase(pos, format.find('}', pos) - pos + 1);
+ }
+ pos = format.find(activeTag);
+ if (pos != std::string::npos) {
+ format.erase(pos, activeTag.length());
+ pos = format.find('}', pos);
+ if (pos != std::string::npos) {
+ format.erase(pos, 1);
+ }
+ }
+}
+
+bool IsValidFeatureFormat(const std::string& format)
+{
+ return format.find("<LIBRARY>") != std::string::npos ||
+ format.find("<LIB_ITEM>") != std::string::npos ||
+ format.find("<LINK_ITEM>") != std::string::npos;
+}
+
+class FeaturePlaceHolderExpander : public cmPlaceholderExpander
+{
+public:
+ FeaturePlaceHolderExpander(const std::string* library,
+ const std::string* libItem = nullptr,
+ const std::string* linkItem = nullptr)
+ : Library(library)
+ , LibItem(libItem)
+ , LinkItem(linkItem)
+ {
+ }
+
+private:
+ std::string ExpandVariable(std::string const& variable) override
+ {
+ if (this->Library != nullptr && variable == "LIBRARY") {
+ return *this->Library;
+ }
+ if (this->LibItem != nullptr && variable == "LIB_ITEM") {
+ return *this->LibItem;
+ }
+ if (this->LinkItem != nullptr && variable == "LINK_ITEM") {
+ return *this->LinkItem;
+ }
+
+ return variable;
+ }
+
+ const std::string* Library = nullptr;
+ const std::string* LibItem = nullptr;
+ const std::string* LinkItem = nullptr;
+};
+}
+
+cmComputeLinkInformation::FeatureDescriptor::FeatureDescriptor(
+ std::string name, std::string itemFormat)
+ : Name(std::move(name))
+ , Supported(true)
+ , ItemPathFormat(std::move(itemFormat))
+ , ItemNameFormat(this->ItemPathFormat)
+{
+}
+cmComputeLinkInformation::FeatureDescriptor::FeatureDescriptor(
+ std::string name, std::string itemPathFormat, std::string itemNameFormat)
+ : Name(std::move(name))
+ , Supported(true)
+ , ItemPathFormat(std::move(itemPathFormat))
+ , ItemNameFormat(std::move(itemNameFormat))
+{
+}
+cmComputeLinkInformation::FeatureDescriptor::FeatureDescriptor(
+ std::string name, std::string prefix, std::string itemPathFormat,
+ std::string itemNameFormat, std::string suffix)
+ : Name(std::move(name))
+ , Supported(true)
+ , Prefix(std::move(prefix))
+ , Suffix(std::move(suffix))
+ , ItemPathFormat(std::move(itemPathFormat))
+ , ItemNameFormat(std::move(itemNameFormat))
+{
+}
+cmComputeLinkInformation::FeatureDescriptor::FeatureDescriptor(
+ std::string name, std::string prefix, std::string suffix, bool)
+ : Name(std::move(name))
+ , Supported(true)
+ , Prefix(std::move(prefix))
+ , Suffix(std::move(suffix))
+{
+}
+
+std::string cmComputeLinkInformation::FeatureDescriptor::GetDecoratedItem(
+ std::string const& library, ItemIsPath isPath) const
+{
+ auto format =
+ isPath == ItemIsPath::Yes ? this->ItemPathFormat : this->ItemNameFormat;
+
+ // replace <LIBRARY>, <LIB_ITEM> and <LINK_ITEM> patterns with library path
+ FeaturePlaceHolderExpander expander(&library, &library, &library);
+ return expander.ExpandVariables(format);
+}
+std::string cmComputeLinkInformation::FeatureDescriptor::GetDecoratedItem(
+ std::string const& library, std::string const& libItem,
+ std::string const& linkItem, ItemIsPath isPath) const
+{
+ auto format =
+ isPath == ItemIsPath::Yes ? this->ItemPathFormat : this->ItemNameFormat;
+
+ // replace <LIBRARY>, <LIB_ITEM> and <LINK_ITEM> patterns
+ FeaturePlaceHolderExpander expander(&library, &libItem, &linkItem);
+ return expander.ExpandVariables(format);
+}
+
+cmComputeLinkInformation::LibraryFeatureDescriptor::LibraryFeatureDescriptor(
+ std::string name, std::string itemFormat)
+ : FeatureDescriptor(std::move(name), std::move(itemFormat))
+{
+}
+cmComputeLinkInformation::LibraryFeatureDescriptor::LibraryFeatureDescriptor(
+ std::string name, std::string itemPathFormat, std::string itemNameFormat)
+ : FeatureDescriptor(std::move(name), std::move(itemPathFormat),
+ std::move(itemNameFormat))
+{
+}
+cmComputeLinkInformation::LibraryFeatureDescriptor::LibraryFeatureDescriptor(
+ std::string name, std::string prefix, std::string itemPathFormat,
+ std::string itemNameFormat, std::string suffix)
+ : FeatureDescriptor(std::move(name), std::move(prefix),
+ std::move(itemPathFormat), std::move(itemNameFormat),
+ std::move(suffix))
+{
+}
+
+bool cmComputeLinkInformation::AddLibraryFeature(std::string const& feature)
+{
+ auto it = this->LibraryFeatureDescriptors.find(feature);
+ if (it != this->LibraryFeatureDescriptors.end()) {
+ return it->second.Supported;
+ }
+
+ auto featureName =
+ cmStrCat("CMAKE_", this->LinkLanguage, "_LINK_LIBRARY_USING_", feature);
+ cmValue featureSupported =
+ this->Makefile->GetDefinition(cmStrCat(featureName, "_SUPPORTED"));
+ if (!featureSupported.IsOn()) {
+ featureName = cmStrCat("CMAKE_LINK_LIBRARY_USING_", feature);
+ featureSupported =
+ this->Makefile->GetDefinition(cmStrCat(featureName, "_SUPPORTED"));
+ }
+ if (!featureSupported.IsOn()) {
+ this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{});
+
+ this->CMakeInstance->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat(
+ "Feature '", feature,
+ "', specified through generator-expression '$<LINK_LIBRARY>' to "
+ "link target '",
+ this->Target->GetName(), "', is not supported for the '",
+ this->LinkLanguage, "' link language."),
+ this->Target->GetBacktrace());
+
+ return false;
+ }
+
+ cmValue langFeature = this->Makefile->GetDefinition(featureName);
+ if (!langFeature) {
+ this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{});
+
+ this->CMakeInstance->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat(
+ "Feature '", feature,
+ "', specified through generator-expression '$<LINK_LIBRARY>' to "
+ "link target '",
+ this->Target->GetName(), "', is not defined for the '",
+ this->LinkLanguage, "' link language."),
+ this->Target->GetBacktrace());
+
+ return false;
+ }
+
+ auto items =
+ cmExpandListWithBacktrace(langFeature, this->Target->GetBacktrace(), true);
+
+ if ((items.size() == 1 && !IsValidFeatureFormat(items.front().Value)) ||
+ (items.size() == 3 && !IsValidFeatureFormat(items[1].Value))) {
+ this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{});
+ this->CMakeInstance->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("Feature '", feature, "', specified by variable '", featureName,
+ "', is malformed (\"<LIBRARY>\", \"<LIB_ITEM>\", or "
+ "\"<LINK_ITEM>\" patterns "
+ "are missing) and cannot be used to link target '",
+ this->Target->GetName(), "'."),
+ this->Target->GetBacktrace());
+
+ return false;
+ }
+
+ // now, handle possible "PATH{}" and "NAME{}" patterns
+ if (items.size() == 1) {
+ items.push_back(items.front());
+ FinalizeFeatureFormat(items[0].Value, "PATH{", "NAME{");
+ FinalizeFeatureFormat(items[1].Value, "NAME{", "PATH{");
+ } else if (items.size() == 3) {
+ items.insert(items.begin() + 1, items[1]);
+ FinalizeFeatureFormat(items[1].Value, "PATH{", "NAME{");
+ FinalizeFeatureFormat(items[2].Value, "NAME{", "PATH{");
+ } else {
+ this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{});
+ this->CMakeInstance->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("Feature '", feature, "', specified by variable '", featureName,
+ "', is malformed (wrong number of elements) and cannot be used "
+ "to link target '",
+ this->Target->GetName(), "'."),
+ this->Target->GetBacktrace());
+
+ return false;
+ }
+ if ((items.size() == 2 && !IsValidFeatureFormat(items[0].Value)) ||
+ (items.size() == 4 && !IsValidFeatureFormat(items[1].Value))) {
+ // PATH{} has wrong format
+ this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{});
+ this->CMakeInstance->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("Feature '", feature, "', specified by variable '", featureName,
+ "', is malformed (\"<LIBRARY>\", \"<LIB_ITEM>\", or "
+ "\"<LINK_ITEM>\" patterns "
+ "are missing for \"PATH{}\" alternative) and cannot be used to "
+ "link target '",
+ this->Target->GetName(), "'."),
+ this->Target->GetBacktrace());
+
+ return false;
+ }
+ if ((items.size() == 2 && !IsValidFeatureFormat(items[1].Value)) ||
+ (items.size() == 4 && !IsValidFeatureFormat(items[2].Value))) {
+ // NAME{} has wrong format
+ this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{});
+ this->CMakeInstance->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("Feature '", feature, "', specified by variable '", featureName,
+ "', is malformed (\"<LIBRARY>\", \"<LIB_ITEM>\", or "
+ "\"<LINK_ITEM>\" patterns "
+ "are missing for \"NAME{}\" alternative) and cannot be used to "
+ "link target '",
+ this->Target->GetName(), "'."),
+ this->Target->GetBacktrace());
+
+ return false;
+ }
+
+ // replace LINKER: pattern
+ this->Target->ResolveLinkerWrapper(items, this->LinkLanguage, true);
+
+ if (items.size() == 2) {
+ this->LibraryFeatureDescriptors.emplace(
+ feature,
+ LibraryFeatureDescriptor{ feature, items[0].Value, items[1].Value });
+ } else {
+ this->LibraryFeatureDescriptors.emplace(
+ feature,
+ LibraryFeatureDescriptor{ feature, items[0].Value, items[1].Value,
+ items[2].Value, items[3].Value });
+ }
+
+ return true;
+}
+
+cmComputeLinkInformation::FeatureDescriptor const&
+cmComputeLinkInformation::GetLibraryFeature(std::string const& feature) const
+{
+ return this->LibraryFeatureDescriptors.find(feature)->second;
+}
+cmComputeLinkInformation::FeatureDescriptor const*
+cmComputeLinkInformation::FindLibraryFeature(std::string const& feature) const
+{
+ auto it = this->LibraryFeatureDescriptors.find(feature);
+ if (it == this->LibraryFeatureDescriptors.end()) {
+ return nullptr;
+ }
+
+ return &it->second;
+}
+
+cmComputeLinkInformation::GroupFeatureDescriptor::GroupFeatureDescriptor(
+ std::string name, std::string prefix, std::string suffix)
+ : FeatureDescriptor(std::move(name), std::move(prefix), std::move(suffix),
+ true)
+{
+}
+
+cmComputeLinkInformation::FeatureDescriptor const&
+cmComputeLinkInformation::GetGroupFeature(std::string const& feature)
+{
+ auto it = this->GroupFeatureDescriptors.find(feature);
+ if (it != this->GroupFeatureDescriptors.end()) {
+ return it->second;
+ }
+
+ auto featureName =
+ cmStrCat("CMAKE_", this->LinkLanguage, "_LINK_GROUP_USING_", feature);
+ cmValue featureSupported =
+ this->Makefile->GetDefinition(cmStrCat(featureName, "_SUPPORTED"));
+ if (!featureSupported.IsOn()) {
+ featureName = cmStrCat("CMAKE_LINK_GROUP_USING_", feature);
+ featureSupported =
+ this->Makefile->GetDefinition(cmStrCat(featureName, "_SUPPORTED"));
+ }
+ if (!featureSupported.IsOn()) {
+ this->CMakeInstance->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("Feature '", feature,
+ "', specified through generator-expression '$<LINK_GROUP>' to "
+ "link target '",
+ this->Target->GetName(), "', is not supported for the '",
+ this->LinkLanguage, "' link language."),
+ this->Target->GetBacktrace());
+ return this->GroupFeatureDescriptors.emplace(feature, FeatureDescriptor{})
+ .first->second;
+ }
+
+ cmValue langFeature = this->Makefile->GetDefinition(featureName);
+ if (!langFeature) {
+ this->CMakeInstance->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("Feature '", feature,
+ "', specified through generator-expression '$<LINK_GROUP>' to "
+ "link target '",
+ this->Target->GetName(), "', is not defined for the '",
+ this->LinkLanguage, "' link language."),
+ this->Target->GetBacktrace());
+ return this->GroupFeatureDescriptors.emplace(feature, FeatureDescriptor{})
+ .first->second;
+ }
+
+ auto items =
+ cmExpandListWithBacktrace(langFeature, this->Target->GetBacktrace(), true);
+
+ // replace LINKER: pattern
+ this->Target->ResolveLinkerWrapper(items, this->LinkLanguage, true);
+
+ if (items.size() == 2) {
+ return this->GroupFeatureDescriptors
+ .emplace(
+ feature,
+ GroupFeatureDescriptor{ feature, items[0].Value, items[1].Value })
+ .first->second;
+ }
+
+ this->CMakeInstance->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("Feature '", feature, "', specified by variable '", featureName,
+ "', is malformed (wrong number of elements) and cannot be used "
+ "to link target '",
+ this->Target->GetName(), "'."),
+ this->Target->GetBacktrace());
+ return this->GroupFeatureDescriptors.emplace(feature, FeatureDescriptor{})
+ .first->second;
+}
+
void cmComputeLinkInformation::AddImplicitLinkInfo()
{
// The link closure lists all languages whose implicit info is needed.
@@ -610,7 +1056,7 @@ void cmComputeLinkInformation::AddRuntimeLinkLibrary(std::string const& lang)
std::vector<std::string> libsVec = cmExpandedList(*runtimeLinkOptions);
for (std::string const& i : libsVec) {
if (!cm::contains(this->ImplicitLinkLibs, i)) {
- this->AddItem(i, nullptr);
+ this->AddItem({ i });
}
}
}
@@ -625,7 +1071,7 @@ void cmComputeLinkInformation::AddImplicitLinkInfo(std::string const& lang)
std::vector<std::string> libsVec = cmExpandedList(*libs);
for (std::string const& i : libsVec) {
if (!cm::contains(this->ImplicitLinkLibs, i)) {
- this->AddItem(i, nullptr);
+ this->AddItem({ i });
}
}
}
@@ -639,10 +1085,11 @@ void cmComputeLinkInformation::AddImplicitLinkInfo(std::string const& lang)
}
}
-void cmComputeLinkInformation::AddItem(BT<std::string> const& item,
- cmGeneratorTarget const* tgt,
- ItemIsObject isObject)
+void cmComputeLinkInformation::AddItem(LinkEntry const& entry)
{
+ cmGeneratorTarget const* tgt = entry.Target;
+ BT<std::string> const& item = entry.Item;
+
// Compute the proper name to use to link this library.
const std::string& config = this->Config;
bool impexe = (tgt && tgt->IsExecutableWithExports());
@@ -657,28 +1104,27 @@ void cmComputeLinkInformation::AddItem(BT<std::string> const& item,
if (impexe && this->LoaderFlag) {
// This link item is an executable that may provide symbols
// used by this target. A special flag is needed on this
- // platform. Add it now.
- std::string linkItem = this->LoaderFlag;
+ // platform. Add it now using a special feature.
cmStateEnums::ArtifactType artifact = tgt->HasImportLibrary(config)
? cmStateEnums::ImportLibraryArtifact
: cmStateEnums::RuntimeBinaryArtifact;
-
std::string exe = tgt->GetFullPath(config, artifact, true);
- linkItem += exe;
- this->Items.emplace_back(BT<std::string>(linkItem, item.Backtrace),
- ItemIsPath::Yes, ItemIsObject::No, tgt);
+ this->Items.emplace_back(
+ BT<std::string>(exe, item.Backtrace), ItemIsPath::Yes, tgt,
+ this->FindLibraryFeature(entry.Feature == DEFAULT
+ ? "__CMAKE_LINK_EXECUTABLE"
+ : entry.Feature));
this->Depends.push_back(std::move(exe));
} else if (tgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
// Add the interface library as an item so it can be considered as part
// of COMPATIBLE_INTERFACE_ enforcement. The generators will ignore
// this for the actual link line.
- this->Items.emplace_back(std::string(), ItemIsPath::No, ItemIsObject::No,
- tgt);
+ this->Items.emplace_back(std::string(), ItemIsPath::No, tgt);
// Also add the item the interface specifies to be used in its place.
std::string const& libName = tgt->GetImportedLibName(config);
if (!libName.empty()) {
- this->AddItem(BT<std::string>(libName, item.Backtrace), nullptr);
+ this->AddItem(BT<std::string>(libName, item.Backtrace));
}
} else if (tgt->GetType() == cmStateEnums::OBJECT_LIBRARY) {
// Ignore object library!
@@ -706,7 +1152,9 @@ void cmComputeLinkInformation::AddItem(BT<std::string> const& item,
this->Depends.push_back(lib.Value);
}
- this->AddTargetItem(lib, tgt);
+ LinkEntry libEntry{ entry };
+ libEntry.Item = lib;
+ this->AddTargetItem(libEntry);
this->AddLibraryRuntimeInfo(lib.Value, tgt);
if (tgt && tgt->GetType() == cmStateEnums::SHARED_LIBRARY &&
this->Target->IsDLLPlatform()) {
@@ -715,30 +1163,34 @@ void cmComputeLinkInformation::AddItem(BT<std::string> const& item,
}
} else {
// This is not a CMake target. Use the name given.
- if (cmSystemTools::FileIsFullPath(item.Value)) {
- if (cmSystemTools::IsPathToFramework(item.Value) &&
- this->Makefile->IsOn("APPLE")) {
- // This is a framework.
- this->AddFrameworkItem(item.Value);
- } else if (cmSystemTools::FileIsDirectory(item.Value)) {
+ if (cmHasSuffix(entry.Feature, "FRAMEWORK"_s) ||
+ (entry.Feature == DEFAULT &&
+ cmSystemTools::IsPathToFramework(item.Value) &&
+ this->Makefile->IsOn("APPLE"))) {
+ // This is a framework.
+ this->AddFrameworkItem(entry);
+ } else if (cmSystemTools::FileIsFullPath(item.Value)) {
+ if (cmSystemTools::FileIsDirectory(item.Value)) {
// This is a directory.
this->DropDirectoryItem(item);
} else {
// Use the full path given to the library file.
this->Depends.push_back(item.Value);
- this->AddFullItem(item, isObject);
+ this->AddFullItem(entry);
this->AddLibraryRuntimeInfo(item.Value);
}
} else {
// This is a library or option specified by the user.
- this->AddUserItem(item, true);
+ this->AddUserItem(entry, true);
}
}
}
-void cmComputeLinkInformation::AddSharedDepItem(BT<std::string> const& item,
- const cmGeneratorTarget* tgt)
+void cmComputeLinkInformation::AddSharedDepItem(LinkEntry const& entry)
{
+ BT<std::string> const& item = entry.Item;
+ const cmGeneratorTarget* tgt = entry.Target;
+
// Record dependencies on DLLs.
if (tgt && tgt->GetType() == cmStateEnums::SHARED_LIBRARY &&
this->Target->IsDLLPlatform() &&
@@ -776,7 +1228,7 @@ void cmComputeLinkInformation::AddSharedDepItem(BT<std::string> const& item,
// If in linking mode, just link to the shared library.
if (this->SharedDependencyMode == SharedDepModeLink) {
- this->AddItem(item, tgt);
+ this->AddItem(entry);
return;
}
@@ -1058,8 +1510,7 @@ void cmComputeLinkInformation::SetCurrentLinkType(LinkType lt)
}
}
-void cmComputeLinkInformation::AddTargetItem(BT<std::string> const& item,
- cmGeneratorTarget const* target)
+void cmComputeLinkInformation::AddTargetItem(LinkEntry const& entry)
{
// This is called to handle a link item that is a full path to a target.
// If the target is not a static library make sure the link type is
@@ -1067,6 +1518,9 @@ void cmComputeLinkInformation::AddTargetItem(BT<std::string> const& item,
// shared and static libraries but static-mode can handle only
// static libraries. If a previous user item changed the link type
// to static we need to make sure it is back to shared.
+ BT<std::string> const& item = entry.Item;
+ cmGeneratorTarget const* target = entry.Target;
+
if (target->GetType() != cmStateEnums::STATIC_LIBRARY) {
this->SetCurrentLinkType(LinkShared);
}
@@ -1079,7 +1533,7 @@ void cmComputeLinkInformation::AddTargetItem(BT<std::string> const& item,
// Handle case of an imported shared library with no soname.
if (this->NoSONameUsesPath &&
target->IsImportedSharedLibWithoutSOName(this->Config)) {
- this->AddSharedLibNoSOName(item.Value);
+ this->AddSharedLibNoSOName(entry);
return;
}
@@ -1091,20 +1545,54 @@ void cmComputeLinkInformation::AddTargetItem(BT<std::string> const& item,
this->OldLinkDirItems.push_back(item.Value);
}
- // Now add the full path to the library.
- this->Items.emplace_back(item, ItemIsPath::Yes, ItemIsObject::No, target);
+ if (target->IsFrameworkOnApple() && this->GlobalGenerator->IsXcode() &&
+ entry.Feature == DEFAULT) {
+ // ensure FRAMEWORK feature is loaded
+ this->AddLibraryFeature("FRAMEWORK");
+ }
+
+ if (cmHasSuffix(entry.Feature, "FRAMEWORK"_s) &&
+ target->IsFrameworkOnApple() && !this->GlobalGenerator->IsXcode()) {
+ // Add the framework directory and the framework item itself
+ auto fwItems = this->GlobalGenerator->SplitFrameworkPath(item.Value, true);
+ if (!fwItems) {
+ this->CMakeInstance->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("Could not parse framework path \"", item.Value,
+ "\" linked by target ", this->Target->GetName(), '.'),
+ item.Backtrace);
+ return;
+ }
+ if (!fwItems->first.empty()) {
+ // Add the directory portion to the framework search path.
+ this->AddFrameworkPath(fwItems->first);
+ }
+ this->Items.emplace_back(fwItems->second, ItemIsPath::Yes, target,
+ this->FindLibraryFeature(entry.Feature));
+ } else {
+ // Now add the full path to the library.
+ this->Items.emplace_back(
+ item, ItemIsPath::Yes, target,
+ this->FindLibraryFeature(
+ entry.Feature == DEFAULT
+ ? (target->IsFrameworkOnApple() && this->GlobalGenerator->IsXcode()
+ ? "FRAMEWORK"
+ : "__CMAKE_LINK_LIBRARY")
+ : entry.Feature));
+ }
}
-void cmComputeLinkInformation::AddFullItem(BT<std::string> const& item,
- ItemIsObject isObject)
+void cmComputeLinkInformation::AddFullItem(LinkEntry const& entry)
{
+ BT<std::string> const& item = entry.Item;
+
// Check for the implicit link directory special case.
- if (this->CheckImplicitDirItem(item.Value)) {
+ if (this->CheckImplicitDirItem(entry)) {
return;
}
// Check for case of shared library with no builtin soname.
- if (this->NoSONameUsesPath && this->CheckSharedLibNoSOName(item.Value)) {
+ if (this->NoSONameUsesPath && this->CheckSharedLibNoSOName(entry)) {
return;
}
@@ -1116,7 +1604,7 @@ void cmComputeLinkInformation::AddFullItem(BT<std::string> const& item,
generator.find("Xcode") != std::string::npos)) {
std::string file = cmSystemTools::GetFilenameName(item.Value);
if (!this->ExtractAnyLibraryName.find(file)) {
- this->HandleBadFullItem(item.Value, file);
+ this->HandleBadFullItem(entry, file);
return;
}
}
@@ -1147,11 +1635,20 @@ void cmComputeLinkInformation::AddFullItem(BT<std::string> const& item,
}
// Now add the full path to the library.
- this->Items.emplace_back(item, ItemIsPath::Yes, isObject);
+ this->Items.emplace_back(
+ item, ItemIsPath::Yes, nullptr,
+ this->FindLibraryFeature(
+ entry.Feature == DEFAULT
+ ? (entry.Kind == cmComputeLinkDepends::LinkEntry::Object
+ ? "__CMAKE_LINK_OBJECT"
+ : "__CMAKE_LINK_LIBRARY")
+ : entry.Feature));
}
-bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item)
+bool cmComputeLinkInformation::CheckImplicitDirItem(LinkEntry const& entry)
{
+ BT<std::string> const& item = entry.Item;
+
// We only switch to a pathless item if the link type may be
// enforced. Fortunately only platforms that support link types
// seem to have magic per-architecture implicit link directories.
@@ -1160,7 +1657,7 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item)
}
// Check if this item is in an implicit link directory.
- std::string dir = cmSystemTools::GetFilenamePath(item);
+ std::string dir = cmSystemTools::GetFilenamePath(item.Value);
if (!cm::contains(this->ImplicitLinkDirs, dir)) {
// Only libraries in implicit link directories are converted to
// pathless items.
@@ -1169,7 +1666,7 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item)
// Only apply the policy below if the library file is one that can
// be found by the linker.
- std::string file = cmSystemTools::GetFilenameName(item);
+ std::string file = cmSystemTools::GetFilenameName(item.Value);
if (!this->ExtractAnyLibraryName.find(file)) {
return false;
}
@@ -1179,10 +1676,10 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item)
case cmPolicies::WARN:
if (this->CMP0060Warn) {
// Print the warning at most once for this item.
- std::string const& wid = "CMP0060-WARNING-GIVEN-" + item;
+ std::string const& wid = "CMP0060-WARNING-GIVEN-" + item.Value;
if (!this->CMakeInstance->GetPropertyAsBool(wid)) {
this->CMakeInstance->SetProperty(wid, "1");
- this->CMP0060WarnItems.insert(item);
+ this->CMP0060WarnItems.insert(item.Value);
}
}
CM_FALLTHROUGH;
@@ -1200,15 +1697,17 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item)
// directory then just report the file name without the directory
// portion. This will allow the system linker to locate the proper
// library for the architecture at link time.
- this->AddUserItem(file, false);
+ LinkEntry fileEntry{ entry };
+ fileEntry.Item = file;
+ this->AddUserItem(fileEntry, false);
// Make sure the link directory ordering will find the library.
- this->OrderLinkerSearchPath->AddLinkLibrary(item);
+ this->OrderLinkerSearchPath->AddLinkLibrary(item.Value);
return true;
}
-void cmComputeLinkInformation::AddUserItem(BT<std::string> const& item,
+void cmComputeLinkInformation::AddUserItem(LinkEntry const& entry,
bool pathNotKnown)
{
// This is called to handle a link item that does not match a CMake
@@ -1219,8 +1718,10 @@ void cmComputeLinkInformation::AddUserItem(BT<std::string> const& item,
// foo ==> -lfoo
// libfoo.a ==> -Wl,-Bstatic -lfoo
- // Pass flags through untouched.
+ BT<std::string> const& item = entry.Item;
+
if (item.Value[0] == '-' || item.Value[0] == '$' || item.Value[0] == '`') {
+ // Pass flags through untouched.
// if this is a -l option then we might need to warn about
// CMP0003 so put it in OldUserFlagItems, if it is not a -l
// or -Wl,-l (-framework -pthread), then allow it without a
@@ -1305,9 +1806,20 @@ void cmComputeLinkInformation::AddUserItem(BT<std::string> const& item,
}
// Create an option to ask the linker to search for the library.
- std::string out = cmStrCat(this->LibLinkFlag, lib, this->LibLinkSuffix);
- this->Items.emplace_back(BT<std::string>(out, item.Backtrace),
- ItemIsPath::No);
+ auto out = cmStrCat(this->LibLinkFlag, lib, this->LibLinkSuffix);
+
+ if (entry.Feature != DEFAULT) {
+ auto const& feature = this->GetLibraryFeature(entry.Feature);
+ this->Items.emplace_back(
+ BT<std::string>(
+ feature.GetDecoratedItem(cmStrCat(lib, this->LibLinkSuffix),
+ item.Value, out, ItemIsPath::No),
+ item.Backtrace),
+ ItemIsPath::No);
+ } else {
+ this->Items.emplace_back(BT<std::string>(out, item.Backtrace),
+ ItemIsPath::No);
+ }
// Here we could try to find the library the linker will find and
// add a runtime information entry for it. It would probably not be
@@ -1315,10 +1827,14 @@ void cmComputeLinkInformation::AddUserItem(BT<std::string> const& item,
// specification.
}
-void cmComputeLinkInformation::AddFrameworkItem(std::string const& item)
+void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry)
{
+ std::string const& item = entry.Item.Value;
+
// Try to separate the framework name and path.
- if (!this->SplitFramework.find(item)) {
+ auto fwItems =
+ this->GlobalGenerator->SplitFrameworkPath(item, entry.Feature != DEFAULT);
+ if (!fwItems) {
std::ostringstream e;
e << "Could not parse framework path \"" << item << "\" "
<< "linked by target " << this->Target->GetName() << ".";
@@ -1326,26 +1842,36 @@ void cmComputeLinkInformation::AddFrameworkItem(std::string const& item)
return;
}
- std::string fw_path = this->SplitFramework.match(1);
- std::string fw = this->SplitFramework.match(2);
- std::string full_fw = cmStrCat(fw_path, '/', fw, ".framework/", fw);
+ std::string fw_path = std::move(fwItems->first);
+ std::string fw = std::move(fwItems->second);
+ std::string full_fw = cmStrCat(fw, ".framework/", fw);
- // Add the directory portion to the framework search path.
- this->AddFrameworkPath(fw_path);
+ if (!fw_path.empty()) {
+ full_fw = cmStrCat(fw_path, '/', full_fw);
+ // Add the directory portion to the framework search path.
+ this->AddFrameworkPath(fw_path);
+ }
// add runtime information
this->AddLibraryRuntimeInfo(full_fw);
+ if (entry.Feature == DEFAULT) {
+ // ensure FRAMEWORK feature is loaded
+ this->AddLibraryFeature("FRAMEWORK");
+ }
+
if (this->GlobalGenerator->IsXcode()) {
// Add framework path - it will be handled by Xcode after it's added to
// "Link Binary With Libraries" build phase
- this->Items.emplace_back(item, ItemIsPath::Yes);
+ this->Items.emplace_back(item, ItemIsPath::Yes, nullptr,
+ this->FindLibraryFeature(entry.Feature == DEFAULT
+ ? "FRAMEWORK"
+ : entry.Feature));
} else {
- // Add the item using the -framework option.
- this->Items.emplace_back(std::string("-framework"), ItemIsPath::No);
- cmOutputConverter converter(this->Makefile->GetStateSnapshot());
- fw = converter.EscapeForShell(fw);
- this->Items.emplace_back(fw, ItemIsPath::No);
+ this->Items.emplace_back(fw, ItemIsPath::Yes, nullptr,
+ this->FindLibraryFeature(entry.Feature == DEFAULT
+ ? "FRAMEWORK"
+ : entry.Feature));
}
}
@@ -1355,10 +1881,10 @@ void cmComputeLinkInformation::DropDirectoryItem(BT<std::string> const& item)
// user.
this->CMakeInstance->IssueMessage(
MessageType::WARNING,
- cmStrCat(
- "Target \"", this->Target->GetName(),
- "\" requests linking to directory \"", item.Value,
- "\". Targets may link only to libraries. CMake is dropping the item."),
+ cmStrCat("Target \"", this->Target->GetName(),
+ "\" requests linking to directory \"", item.Value,
+ "\". Targets may link only to libraries. CMake is dropping "
+ "the item."),
item.Backtrace);
}
@@ -1378,9 +1904,6 @@ void cmComputeLinkInformation::ComputeFrameworkInfo()
this->FrameworkPathsEmitted.insert(implicitDirVec.begin(),
implicitDirVec.end());
-
- // Regular expression to extract a framework path and name.
- this->SplitFramework.compile("(.*)/(.*)\\.framework$");
}
void cmComputeLinkInformation::AddFrameworkPath(std::string const& p)
@@ -1390,42 +1913,44 @@ void cmComputeLinkInformation::AddFrameworkPath(std::string const& p)
}
}
-bool cmComputeLinkInformation::CheckSharedLibNoSOName(std::string const& item)
+bool cmComputeLinkInformation::CheckSharedLibNoSOName(LinkEntry const& entry)
{
// This platform will use the path to a library as its soname if the
// library is given via path and was not built with an soname. If
// this is a shared library that might be the case.
- std::string file = cmSystemTools::GetFilenameName(item);
+ std::string file = cmSystemTools::GetFilenameName(entry.Item.Value);
if (this->ExtractSharedLibraryName.find(file)) {
// If we can guess the soname fairly reliably then assume the
// library has one. Otherwise assume the library has no builtin
// soname.
std::string soname;
- if (!cmSystemTools::GuessLibrarySOName(item, soname)) {
- this->AddSharedLibNoSOName(item);
+ if (!cmSystemTools::GuessLibrarySOName(entry.Item.Value, soname)) {
+ this->AddSharedLibNoSOName(entry);
return true;
}
}
return false;
}
-void cmComputeLinkInformation::AddSharedLibNoSOName(std::string const& item)
+void cmComputeLinkInformation::AddSharedLibNoSOName(LinkEntry const& entry)
{
// We have a full path to a shared library with no soname. We need
// to ask the linker to locate the item because otherwise the path
// we give to it will be embedded in the target linked. Then at
// runtime the dynamic linker will search for the library using the
// path instead of just the name.
- std::string file = cmSystemTools::GetFilenameName(item);
- this->AddUserItem(file, false);
+ LinkEntry fileEntry{ entry };
+ fileEntry.Item = cmSystemTools::GetFilenameName(entry.Item.Value);
+ this->AddUserItem(fileEntry, false);
// Make sure the link directory ordering will find the library.
- this->OrderLinkerSearchPath->AddLinkLibrary(item);
+ this->OrderLinkerSearchPath->AddLinkLibrary(entry.Item.Value);
}
-void cmComputeLinkInformation::HandleBadFullItem(std::string const& item,
+void cmComputeLinkInformation::HandleBadFullItem(LinkEntry const& entry,
std::string const& file)
{
+ std::string const& item = entry.Item.Value;
// Do not depend on things that do not exist.
auto i = std::find(this->Depends.begin(), this->Depends.end(), item);
if (i != this->Depends.end()) {
@@ -1435,7 +1960,9 @@ void cmComputeLinkInformation::HandleBadFullItem(std::string const& item,
// Tell the linker to search for the item and provide the proper
// path for it. Do not contribute to any CMP0003 warning (do not
// put in OldLinkDirItems or OldUserFlagItems).
- this->AddUserItem(file, false);
+ LinkEntry fileEntry{ entry };
+ fileEntry.Item = file;
+ this->AddUserItem(fileEntry, false);
this->OrderLinkerSearchPath->AddLinkLibrary(item);
// Produce any needed message.
@@ -1781,8 +2308,8 @@ void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs,
// Add directories explicitly specified by user
std::string build_rpath;
if (this->Target->GetBuildRPATH(this->Config, build_rpath)) {
- // This will not resolve entries to use $ORIGIN, the user is expected to
- // do that if necessary.
+ // This will not resolve entries to use $ORIGIN, the user is expected
+ // to do that if necessary.
cmCLI_ExpandListUnique(build_rpath, runtimeDirs, emitted);
}
}
diff --git a/Source/cmComputeLinkInformation.h b/Source/cmComputeLinkInformation.h
index 0315540..a4ada1f 100644
--- a/Source/cmComputeLinkInformation.h
+++ b/Source/cmComputeLinkInformation.h
@@ -5,6 +5,7 @@
#include "cmConfigure.h" // IWYU pragma: keep
#include <iosfwd>
+#include <map>
#include <memory>
#include <set>
#include <string>
@@ -13,6 +14,7 @@
#include "cmsys/RegularExpression.hxx"
+#include "cmComputeLinkDepends.h"
#include "cmListFileCache.h"
#include "cmValue.h"
@@ -27,6 +29,9 @@ class cmake;
*/
class cmComputeLinkInformation
{
+private:
+ class FeatureDescriptor;
+
public:
cmComputeLinkInformation(cmGeneratorTarget const* target,
const std::string& config);
@@ -42,28 +47,38 @@ public:
Yes,
};
- enum class ItemIsObject
- {
- No,
- Yes,
- };
-
struct Item
{
- Item() = default;
Item(BT<std::string> v, ItemIsPath isPath,
- ItemIsObject isObject = ItemIsObject::No,
- cmGeneratorTarget const* target = nullptr)
+ cmGeneratorTarget const* target = nullptr,
+ FeatureDescriptor const* feature = nullptr)
: Value(std::move(v))
, IsPath(isPath)
- , IsObject(isObject)
, Target(target)
+ , Feature(feature)
{
}
BT<std::string> Value;
- ItemIsPath IsPath = ItemIsPath::Yes;
- ItemIsObject IsObject = ItemIsObject::No;
+ ItemIsPath IsPath = ItemIsPath::No;
cmGeneratorTarget const* Target = nullptr;
+
+ bool HasFeature() const { return this->Feature != nullptr; }
+ const std::string& GetFeatureName() const
+ {
+ return HasFeature() ? this->Feature->Name
+ : cmComputeLinkDepends::LinkEntry::DEFAULT;
+ }
+
+ BT<std::string> GetFormattedItem(std::string const& path) const
+ {
+ return { (this->Feature != nullptr)
+ ? this->Feature->GetDecoratedItem(path, this->IsPath)
+ : path,
+ Value.Backtrace };
+ }
+
+ private:
+ FeatureDescriptor const* Feature = nullptr;
};
using ItemVector = std::vector<Item>;
void AppendValues(std::string& result, std::vector<BT<std::string>>& values);
@@ -104,10 +119,10 @@ public:
const cmGeneratorTarget* GetTarget() { return this->Target; }
private:
- void AddItem(BT<std::string> const& item, const cmGeneratorTarget* tgt,
- ItemIsObject isObject = ItemIsObject::No);
- void AddSharedDepItem(BT<std::string> const& item,
- cmGeneratorTarget const* tgt);
+ using LinkEntry = cmComputeLinkDepends::LinkEntry;
+
+ void AddItem(LinkEntry const& entry);
+ void AddSharedDepItem(LinkEntry const& entry);
void AddRuntimeDLL(cmGeneratorTarget const* tgt);
// Output information.
@@ -181,22 +196,20 @@ private:
std::string NoCaseExpression(std::string const& str);
// Handling of link items.
- void AddTargetItem(BT<std::string> const& item,
- const cmGeneratorTarget* target);
- void AddFullItem(BT<std::string> const& item, ItemIsObject isObject);
- bool CheckImplicitDirItem(std::string const& item);
- void AddUserItem(BT<std::string> const& item, bool pathNotKnown);
- void AddFrameworkItem(std::string const& item);
+ void AddTargetItem(LinkEntry const& entry);
+ void AddFullItem(LinkEntry const& entry);
+ bool CheckImplicitDirItem(LinkEntry const& entry);
+ void AddUserItem(LinkEntry const& entry, bool pathNotKnown);
+ void AddFrameworkItem(LinkEntry const& entry);
void DropDirectoryItem(BT<std::string> const& item);
- bool CheckSharedLibNoSOName(std::string const& item);
- void AddSharedLibNoSOName(std::string const& item);
- void HandleBadFullItem(std::string const& item, std::string const& file);
+ bool CheckSharedLibNoSOName(LinkEntry const& entry);
+ void AddSharedLibNoSOName(LinkEntry const& entry);
+ void HandleBadFullItem(LinkEntry const& entry, std::string const& file);
// Framework info.
void ComputeFrameworkInfo();
void AddFrameworkPath(std::string const& p);
std::set<std::string> FrameworkPathsEmitted;
- cmsys::RegularExpression SplitFramework;
// Linker search path computation.
std::unique_ptr<cmOrderDirectories> OrderLinkerSearchPath;
@@ -237,4 +250,61 @@ private:
void AddLibraryRuntimeInfo(std::string const& fullPath,
const cmGeneratorTarget* target);
void AddLibraryRuntimeInfo(std::string const& fullPath);
+
+ class FeatureDescriptor
+ {
+ public:
+ FeatureDescriptor() = default;
+
+ const std::string Name;
+ const bool Supported = false;
+ const std::string Prefix;
+ const std::string Suffix;
+ std::string GetDecoratedItem(std::string const& library,
+ ItemIsPath isPath) const;
+ std::string GetDecoratedItem(std::string const& library,
+ std::string const& linkItem,
+ std::string const& defaultValue,
+ ItemIsPath isPath) const;
+
+ protected:
+ FeatureDescriptor(std::string name, std::string itemFormat);
+ FeatureDescriptor(std::string name, std::string itemPathFormat,
+ std::string itemNameFormat);
+ FeatureDescriptor(std::string name, std::string prefix,
+ std::string itemPathFormat, std::string itemNameFormat,
+ std::string suffix);
+
+ FeatureDescriptor(std::string name, std::string prefix, std::string suffix,
+ bool isGroup);
+
+ private:
+ std::string ItemPathFormat;
+ std::string ItemNameFormat;
+ };
+
+ class LibraryFeatureDescriptor : public FeatureDescriptor
+ {
+ public:
+ LibraryFeatureDescriptor(std::string name, std::string itemFormat);
+ LibraryFeatureDescriptor(std::string name, std::string itemPathFormat,
+ std::string itemNameFormat);
+ LibraryFeatureDescriptor(std::string name, std::string prefix,
+ std::string itemPathFormat,
+ std::string itemNameFormat, std::string suffix);
+ };
+ std::map<std::string, FeatureDescriptor> LibraryFeatureDescriptors;
+ bool AddLibraryFeature(std::string const& feature);
+ FeatureDescriptor const& GetLibraryFeature(std::string const& feature) const;
+ FeatureDescriptor const* FindLibraryFeature(
+ std::string const& feature) const;
+
+ class GroupFeatureDescriptor : public FeatureDescriptor
+ {
+ public:
+ GroupFeatureDescriptor(std::string name, std::string prefix,
+ std::string suffix);
+ };
+ std::map<std::string, FeatureDescriptor> GroupFeatureDescriptors;
+ FeatureDescriptor const& GetGroupFeature(std::string const& feature);
};
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx
index ed1e4cb..a21e614 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 <array>
#include <cassert>
#include <cstring>
#include <sstream>
@@ -175,18 +176,24 @@ bool cmExportFileGenerator::PopulateInterfaceLinkLibrariesProperty(
if (!target->IsLinkable()) {
return false;
}
- cmValue input = target->GetProperty("INTERFACE_LINK_LIBRARIES");
- if (input) {
- std::string prepro =
- cmGeneratorExpression::Preprocess(*input, preprocessRule);
- if (!prepro.empty()) {
- this->ResolveTargetsInGeneratorExpressions(
- prepro, target, missingTargets, ReplaceFreeTargets);
- properties["INTERFACE_LINK_LIBRARIES"] = prepro;
- return true;
+ static const std::array<std::string, 3> linkIfaceProps = {
+ { "INTERFACE_LINK_LIBRARIES", "INTERFACE_LINK_LIBRARIES_DIRECT",
+ "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE" }
+ };
+ bool hadINTERFACE_LINK_LIBRARIES = false;
+ for (std::string const& linkIfaceProp : linkIfaceProps) {
+ if (cmValue input = target->GetProperty(linkIfaceProp)) {
+ std::string prepro =
+ cmGeneratorExpression::Preprocess(*input, preprocessRule);
+ if (!prepro.empty()) {
+ this->ResolveTargetsInGeneratorExpressions(
+ prepro, target, missingTargets, ReplaceFreeTargets);
+ properties[linkIfaceProp] = prepro;
+ hadINTERFACE_LINK_LIBRARIES = true;
+ }
}
}
- return false;
+ return hadINTERFACE_LINK_LIBRARIES;
}
static bool isSubDirectory(std::string const& a, std::string const& b)
@@ -927,13 +934,13 @@ void cmExportFileGenerator::GeneratePolicyHeaderCode(std::ostream& os)
// Isolate the file policy level.
// Support CMake versions as far back as 2.6 but also support using NEW
- // policy settings for up to CMake 3.21 (this upper limit may be reviewed
+ // policy settings for up to CMake 3.22 (this upper limit may be reviewed
// and increased from time to time). This reduces the opportunity for CMake
// warnings when an older export file is later used with newer CMake
// versions.
/* clang-format off */
os << "cmake_policy(PUSH)\n"
- << "cmake_policy(VERSION 2.6...3.21)\n";
+ << "cmake_policy(VERSION 2.6...3.22)\n";
/* clang-format on */
}
diff --git a/Source/cmExportTryCompileFileGenerator.cxx b/Source/cmExportTryCompileFileGenerator.cxx
index db9b05b..e98aa05 100644
--- a/Source/cmExportTryCompileFileGenerator.cxx
+++ b/Source/cmExportTryCompileFileGenerator.cxx
@@ -111,6 +111,8 @@ void cmExportTryCompileFileGenerator::PopulateProperties(
std::vector<std::string> props = target->GetPropertyKeys();
// Include special properties that might be relevant here.
props.emplace_back("INTERFACE_LINK_LIBRARIES");
+ props.emplace_back("INTERFACE_LINK_LIBRARIES_DIRECT");
+ props.emplace_back("INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE");
for (std::string const& p : props) {
cmValue v = target->GetProperty(p);
if (!v) {
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index da2f15f..09e5015 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -15,6 +15,7 @@
#include <vector>
#include <cm/memory>
+#include <cm/optional>
#include <cm/string_view>
#include <cmext/algorithm>
#include <cmext/string_view>
@@ -1778,6 +1779,7 @@ bool HandleDownloadCommand(std::vector<std::string> const& args,
std::string userpwd;
std::vector<std::string> curl_headers;
+ std::vector<std::pair<std::string, cm::optional<std::string>>> curl_ranges;
while (i != args.end()) {
if (*i == "TIMEOUT") {
@@ -1890,6 +1892,27 @@ bool HandleDownloadCommand(std::vector<std::string> const& args,
return false;
}
curl_headers.push_back(*i);
+ } else if (*i == "RANGE_START") {
+ ++i;
+ if (i == args.end()) {
+ status.SetError("DOWNLOAD missing value for RANGE_START.");
+ return false;
+ }
+ curl_ranges.emplace_back(*i, cm::nullopt);
+ } else if (*i == "RANGE_END") {
+ ++i;
+ if (curl_ranges.empty()) {
+ curl_ranges.emplace_back("0", *i);
+ } else {
+ auto& last_range = curl_ranges.back();
+ if (!last_range.second.has_value()) {
+ last_range.second = *i;
+ } else {
+ status.SetError("Multiple RANGE_END values is provided without "
+ "the corresponding RANGE_START.");
+ return false;
+ }
+ }
} else if (file.empty()) {
file = *i;
} else {
@@ -1899,6 +1922,7 @@ bool HandleDownloadCommand(std::vector<std::string> const& args,
}
++i;
}
+
// Can't calculate hash if we don't save the file.
// TODO Incrementally calculate hash in the write callback as the file is
// being downloaded so this check can be relaxed.
@@ -1984,6 +2008,13 @@ bool HandleDownloadCommand(std::vector<std::string> const& args,
check_curl_result(res, "DOWNLOAD cannot set TLS/SSL Verify off: ");
}
+ for (const auto& range : curl_ranges) {
+ std::string curl_range = range.first + '-' +
+ (range.second.has_value() ? range.second.value() : "");
+ res = ::curl_easy_setopt(curl, CURLOPT_RANGE, curl_range.c_str());
+ check_curl_result(res, "DOWNLOAD cannot set range: ");
+ }
+
// check to see if a CAINFO file has been specified
// command arg comes first
std::string const& cainfo_err = cmCurlSetCAInfo(curl, cainfo);
diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx
index f55d838..18457a7 100644
--- a/Source/cmFindPackageCommand.cxx
+++ b/Source/cmFindPackageCommand.cxx
@@ -262,6 +262,9 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
} else if (args[i] == "EXACT") {
this->VersionExact = true;
doing = DoingNone;
+ } else if (args[i] == "GLOBAL") {
+ this->GlobalScope = true;
+ doing = DoingNone;
} else if (args[i] == "MODULE") {
moduleArgs.insert(i);
doing = DoingNone;
@@ -364,6 +367,12 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
}
}
+ if (!this->GlobalScope) {
+ cmValue value(
+ this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_TARGETS_GLOBAL"));
+ this->GlobalScope = value.IsOn();
+ }
+
std::vector<std::string> doubledComponents;
std::set_intersection(requiredComponents.begin(), requiredComponents.end(),
optionalComponents.begin(), optionalComponents.end(),
@@ -1200,6 +1209,11 @@ bool cmFindPackageCommand::ReadListFile(const std::string& f,
PolicyScopeRule psr)
{
const bool noPolicyScope = !this->PolicyScope || psr == NoPolicyScope;
+
+ using ITScope = cmMakefile::ImportedTargetScope;
+ ITScope scope = this->GlobalScope ? ITScope::Global : ITScope::Local;
+ cmMakefile::SetGlobalTargetImportScope globScope(this->Makefile, scope);
+
if (this->Makefile->ReadDependentFile(f, noPolicyScope)) {
return true;
}
diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h
index f921bb0..b9f19e4 100644
--- a/Source/cmFindPackageCommand.h
+++ b/Source/cmFindPackageCommand.h
@@ -199,6 +199,7 @@ private:
bool UseLibx32Paths = false;
bool UseRealPath = false;
bool PolicyScope = true;
+ bool GlobalScope = false;
std::string LibraryArchitecture;
std::vector<std::string> Names;
std::vector<std::string> Configs;
diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx
index 187db73..d35d428 100644
--- a/Source/cmGeneratorExpressionDAGChecker.cxx
+++ b/Source/cmGeneratorExpressionDAGChecker.cxx
@@ -167,7 +167,7 @@ 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_DEPENDS"_s || property == "LINK_LIBRARY_OVERRIDE"_s;
}
bool cmGeneratorExpressionDAGChecker::EvaluatingLinkOptionsExpression() const
@@ -189,6 +189,8 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingLinkLibraries(
}
return prop == "LINK_LIBRARIES"_s || prop == "INTERFACE_LINK_LIBRARIES"_s ||
+ prop == "INTERFACE_LINK_LIBRARIES_DIRECT"_s ||
+ prop == "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE"_s ||
prop == "LINK_INTERFACE_LIBRARIES"_s ||
prop == "IMPORTED_LINK_INTERFACE_LIBRARIES"_s ||
cmHasLiteralPrefix(prop, "LINK_INTERFACE_LIBRARIES_") ||
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index 396e9c9..db043ec 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -1198,6 +1198,152 @@ static const struct LinkLanguageAndIdNode : public cmGeneratorExpressionNode
}
} linkLanguageAndIdNode;
+static const struct LinkLibraryNode : public cmGeneratorExpressionNode
+{
+ LinkLibraryNode() {} // NOLINT(modernize-use-equals-default)
+
+ int NumExpectedParameters() const override { return OneOrMoreParameters; }
+
+ std::string Evaluate(
+ const std::vector<std::string>& parameters,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content,
+ cmGeneratorExpressionDAGChecker* dagChecker) const override
+ {
+ if (!context->HeadTarget || !dagChecker ||
+ !dagChecker->EvaluatingLinkLibraries()) {
+ reportError(context, content->GetOriginalExpression(),
+ "$<LINK_LIBRARY:...> may only be used with binary targets "
+ "to specify link libraries.");
+ return std::string();
+ }
+
+ std::vector<std::string> list;
+ cmExpandLists(parameters.begin(), parameters.end(), list);
+ if (list.empty()) {
+ reportError(
+ context, content->GetOriginalExpression(),
+ "$<LINK_LIBRARY:...> expects a feature name as first argument.");
+ return std::string();
+ }
+ if (list.size() == 1) {
+ // no libraries specified, ignore this genex
+ return std::string();
+ }
+
+ static cmsys::RegularExpression featureNameValidator("^[A-Za-z0-9_]+$");
+ auto const& feature = list.front();
+ if (!featureNameValidator.find(feature)) {
+ reportError(context, content->GetOriginalExpression(),
+ cmStrCat("The feature name '", feature,
+ "' contains invalid characters."));
+ return std::string();
+ }
+
+ const auto LL_BEGIN = cmStrCat("<LINK_LIBRARY:", feature, '>');
+ const auto LL_END = cmStrCat("</LINK_LIBRARY:", feature, '>');
+
+ // filter out $<LINK_LIBRARY:..> tags with same feature
+ // and raise an error for any different feature
+ cm::erase_if(list, [&](const std::string& item) -> bool {
+ return item == LL_BEGIN || item == LL_END;
+ });
+ auto it =
+ std::find_if(list.cbegin() + 1, list.cend(),
+ [&feature](const std::string& item) -> bool {
+ return cmHasPrefix(item, "<LINK_LIBRARY:"_s) &&
+ item.substr(14, item.find('>', 14) - 14) != feature;
+ });
+ if (it != list.cend()) {
+ reportError(
+ context, content->GetOriginalExpression(),
+ "$<LINK_LIBRARY:...> with different features cannot be nested.");
+ return std::string();
+ }
+ // $<LINK_GROUP:...> must not appear as part of $<LINK_LIBRARY:...>
+ it = std::find_if(list.cbegin() + 1, list.cend(),
+ [](const std::string& item) -> bool {
+ return cmHasPrefix(item, "<LINK_GROUP:"_s);
+ });
+ if (it != list.cend()) {
+ reportError(context, content->GetOriginalExpression(),
+ "$<LINK_GROUP:...> cannot be nested inside a "
+ "$<LINK_LIBRARY:...> expression.");
+ return std::string();
+ }
+
+ list.front() = LL_BEGIN;
+ list.push_back(LL_END);
+
+ return cmJoin(list, ";"_s);
+ }
+} linkLibraryNode;
+
+static const struct LinkGroupNode : public cmGeneratorExpressionNode
+{
+ LinkGroupNode() {} // NOLINT(modernize-use-equals-default)
+
+ int NumExpectedParameters() const override { return OneOrMoreParameters; }
+
+ std::string Evaluate(
+ const std::vector<std::string>& parameters,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content,
+ cmGeneratorExpressionDAGChecker* dagChecker) const override
+ {
+ if (!context->HeadTarget || !dagChecker ||
+ !dagChecker->EvaluatingLinkLibraries()) {
+ reportError(context, content->GetOriginalExpression(),
+ "$<LINK_GROUP:...> may only be used with binary targets "
+ "to specify group of link libraries.");
+ return std::string();
+ }
+
+ std::vector<std::string> list;
+ cmExpandLists(parameters.begin(), parameters.end(), list);
+ if (list.empty()) {
+ reportError(
+ context, content->GetOriginalExpression(),
+ "$<LINK_GROUP:...> expects a feature name as first argument.");
+ return std::string();
+ }
+ // $<LINK_GROUP:..> cannot be nested
+ if (std::find_if(list.cbegin(), list.cend(),
+ [](const std::string& item) -> bool {
+ return cmHasPrefix(item, "<LINK_GROUP"_s);
+ }) != list.cend()) {
+ reportError(context, content->GetOriginalExpression(),
+ "$<LINK_GROUP:...> cannot be nested.");
+ return std::string();
+ }
+ if (list.size() == 1) {
+ // no libraries specified, ignore this genex
+ return std::string();
+ }
+
+ static cmsys::RegularExpression featureNameValidator("^[A-Za-z0-9_]+$");
+ auto const& feature = list.front();
+ if (!featureNameValidator.find(feature)) {
+ reportError(context, content->GetOriginalExpression(),
+ cmStrCat("The feature name '", feature,
+ "' contains invalid characters."));
+ return std::string();
+ }
+
+ const auto LG_BEGIN = cmStrCat(
+ "<LINK_GROUP:", feature, ':',
+ cmJoin(cmRange<decltype(list.cbegin())>(list.cbegin() + 1, list.cend()),
+ "|"_s),
+ '>');
+ const auto LG_END = cmStrCat("</LINK_GROUP:", feature, '>');
+
+ list.front() = LG_BEGIN;
+ list.push_back(LG_END);
+
+ return cmJoin(list, ";"_s);
+ }
+} linkGroupNode;
+
static const struct HostLinkNode : public cmGeneratorExpressionNode
{
HostLinkNode() {} // NOLINT(modernize-use-equals-default)
@@ -2668,6 +2814,8 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
{ "COMPILE_LANGUAGE", &languageNode },
{ "LINK_LANG_AND_ID", &linkLanguageAndIdNode },
{ "LINK_LANGUAGE", &linkLanguageNode },
+ { "LINK_LIBRARY", &linkLibraryNode },
+ { "LINK_GROUP", &linkGroupNode },
{ "HOST_LINK", &hostLinkNode },
{ "DEVICE_LINK", &deviceLinkNode },
{ "SHELL_PATH", &shellPathNode }
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index a8bc91c..ab3ed12 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -58,6 +58,10 @@ using LinkInterfaceFor = cmGeneratorTarget::LinkInterfaceFor;
const cmsys::RegularExpression FrameworkRegularExpression(
"^(.*/)?([^/]*)\\.framework/(.*)$");
const std::string kINTERFACE_LINK_LIBRARIES = "INTERFACE_LINK_LIBRARIES";
+const std::string kINTERFACE_LINK_LIBRARIES_DIRECT =
+ "INTERFACE_LINK_LIBRARIES_DIRECT";
+const std::string kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE =
+ "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE";
}
template <>
@@ -2043,7 +2047,11 @@ void cmGeneratorTarget::ComputeKindedSources(KindedSources& files,
} else if (ext == "appxmanifest") {
kind = SourceKindAppManifest;
} else if (ext == "manifest") {
- kind = SourceKindManifest;
+ if (sf->GetPropertyAsBool("VS_DEPLOYMENT_CONTENT")) {
+ kind = SourceKindExtra;
+ } else {
+ kind = SourceKindManifest;
+ }
} else if (ext == "pfx") {
kind = SourceKindCertificate;
} else if (ext == "xaml") {
@@ -3459,6 +3467,23 @@ void cmGeneratorTarget::AddCUDAArchitectureFlags(std::string& flags) const
property =
*this->Makefile->GetDefinition("CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR");
}
+ } else if (property == "native") {
+ cmValue native =
+ this->Makefile->GetDefinition("CMAKE_CUDA_ARCHITECTURES_NATIVE");
+ if (native.IsEmpty()) {
+ this->Makefile->IssueMessage(
+ MessageType::FATAL_ERROR,
+ "CUDA_ARCHITECTURES is set to \"native\", but no GPU was detected.");
+ }
+ if (compiler == "NVIDIA" &&
+ cmSystemTools::VersionCompare(
+ cmSystemTools::OP_GREATER_EQUAL,
+ this->Makefile->GetDefinition("CMAKE_CUDA_COMPILER_VERSION"),
+ "11.6")) {
+ flags = cmStrCat(flags, " -arch=", property);
+ return;
+ }
+ property = *native;
}
struct CudaArchitecture
@@ -4619,7 +4644,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetLinkOptions(
}
std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper(
- std::vector<BT<std::string>>& result, const std::string& language) const
+ std::vector<BT<std::string>>& result, const std::string& language,
+ bool joinItems) const
{
// replace "LINKER:" prefixed elements by actual linker wrapper
const std::string wrapper(this->Makefile->GetSafeDefinition(
@@ -4678,7 +4704,14 @@ std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper(
std::vector<BT<std::string>> options = wrapOptions(
linkerOptions, bt, wrapperFlag, wrapperSep, concatFlagAndArgs);
- result.insert(entry, options.begin(), options.end());
+ if (joinItems) {
+ result.insert(entry,
+ cmJoin(cmRange<decltype(options.cbegin())>(
+ options.cbegin(), options.cend()),
+ " "_s));
+ } else {
+ result.insert(entry, options.begin(), options.end());
+ }
}
return result;
}
@@ -6325,7 +6358,8 @@ cm::string_view missingTargetPossibleReasons =
bool cmGeneratorTarget::VerifyLinkItemColons(LinkItemRole role,
cmLinkItem const& item) const
{
- if (item.Target || item.AsStr().find("::") == std::string::npos) {
+ if (item.Target || cmHasPrefix(item.AsStr(), "<LINK_GROUP:"_s) ||
+ item.AsStr().find("::") == std::string::npos) {
return true;
}
MessageType messageType = MessageType::FATAL_ERROR;
@@ -6371,7 +6405,9 @@ bool cmGeneratorTarget::VerifyLinkItemIsTarget(LinkItemRole role,
std::string const& str = item.AsStr();
if (!str.empty() &&
(str[0] == '-' || str[0] == '$' || str[0] == '`' ||
- str.find_first_of("/\\") != std::string::npos)) {
+ str.find_first_of("/\\") != std::string::npos ||
+ cmHasPrefix(str, "<LINK_LIBRARY:"_s) ||
+ cmHasPrefix(str, "<LINK_GROUP:"_s))) {
return true;
}
@@ -6667,12 +6703,10 @@ cm::optional<cmLinkItem> cmGeneratorTarget::LookupLinkItem(
return maybeItem;
}
-void cmGeneratorTarget::ExpandLinkItems(std::string const& prop,
- cmBTStringRange entries,
- std::string const& config,
- cmGeneratorTarget const* headTarget,
- LinkInterfaceFor interfaceFor,
- cmLinkInterface& iface) const
+void cmGeneratorTarget::ExpandLinkItems(
+ std::string const& prop, cmBTStringRange entries, std::string const& config,
+ cmGeneratorTarget const* headTarget, LinkInterfaceFor interfaceFor,
+ LinkInterfaceField field, cmLinkInterface& iface) const
{
if (entries.empty()) {
return;
@@ -6696,9 +6730,19 @@ void cmGeneratorTarget::ExpandLinkItems(std::string const& prop,
this, headTarget->LinkerLanguage));
for (std::string const& lib : libs) {
if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem(
- lib, cge->GetBacktrace(), &scope, LookupSelf::No)) {
+ lib, cge->GetBacktrace(), &scope,
+ field == LinkInterfaceField::Libraries ? LookupSelf::No
+ : LookupSelf::Yes)) {
cmLinkItem item = std::move(*maybeItem);
+ if (field == LinkInterfaceField::HeadInclude) {
+ iface.HeadInclude.emplace_back(std::move(item));
+ continue;
+ }
+ if (field == LinkInterfaceField::HeadExclude) {
+ iface.HeadExclude.emplace_back(std::move(item));
+ continue;
+ }
if (!item.Target) {
// Report explicitly linked object files separately.
std::string const& maybeObj = item.AsStr();
@@ -7169,7 +7213,9 @@ void cmGeneratorTarget::ComputeLinkInterfaceLibraries(
this->GetPolicyStatusCMP0022() != cmPolicies::WARN);
if (cmp0022NEW) {
// CMP0022 NEW behavior is to use INTERFACE_LINK_LIBRARIES.
- haveExplicitLibraries = !this->Target->GetLinkInterfaceEntries().empty();
+ haveExplicitLibraries = !this->Target->GetLinkInterfaceEntries().empty() ||
+ !this->Target->GetLinkInterfaceDirectEntries().empty() ||
+ !this->Target->GetLinkInterfaceDirectExcludeEntries().empty();
} else {
// CMP0022 OLD behavior is to use LINK_INTERFACE_LIBRARIES if set on a
// shared lib or executable.
@@ -7234,15 +7280,24 @@ void cmGeneratorTarget::ComputeLinkInterfaceLibraries(
if (cmp0022NEW) {
// The interface libraries are specified by INTERFACE_LINK_LIBRARIES.
// Use its special representation directly to get backtraces.
- this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES,
- this->Target->GetLinkInterfaceEntries(), config,
- headTarget, interfaceFor, iface);
+ this->ExpandLinkItems(
+ kINTERFACE_LINK_LIBRARIES, this->Target->GetLinkInterfaceEntries(),
+ config, headTarget, interfaceFor, LinkInterfaceField::Libraries, iface);
+ this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT,
+ this->Target->GetLinkInterfaceDirectEntries(),
+ config, headTarget, interfaceFor,
+ LinkInterfaceField::HeadInclude, iface);
+ this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE,
+ this->Target->GetLinkInterfaceDirectExcludeEntries(),
+ config, headTarget, interfaceFor,
+ LinkInterfaceField::HeadExclude, iface);
} else if (explicitLibrariesCMP0022OLD) {
// The interface libraries have been explicitly set in pre-CMP0022 style.
std::vector<BT<std::string>> entries;
entries.emplace_back(*explicitLibrariesCMP0022OLD);
this->ExpandLinkItems(linkIfacePropCMP0022OLD, cmMakeRange(entries),
- config, headTarget, interfaceFor, iface);
+ config, headTarget, interfaceFor,
+ LinkInterfaceField::Libraries, iface);
}
// If the link interface is explicit, do not fall back to the link impl.
@@ -7262,7 +7317,8 @@ void cmGeneratorTarget::ComputeLinkInterfaceLibraries(
cmLinkInterface ifaceNew;
this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES,
this->Target->GetLinkInterfaceEntries(), config,
- headTarget, interfaceFor, ifaceNew);
+ headTarget, interfaceFor,
+ LinkInterfaceField::Libraries, ifaceNew);
if (ifaceNew.Libraries != iface.Libraries) {
std::string oldLibraries = cmJoin(impl->Libraries, ";");
std::string newLibraries = cmJoin(ifaceNew.Libraries, ";");
@@ -7402,8 +7458,17 @@ const cmLinkInterface* cmGeneratorTarget::GetImportLinkInterface(
iface.LibrariesDone = true;
iface.Multiplicity = info->Multiplicity;
cmExpandList(info->Languages, iface.Languages);
+ this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT,
+ cmMakeRange(info->LibrariesHeadInclude), config,
+ headTarget, interfaceFor,
+ LinkInterfaceField::HeadInclude, iface);
+ this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE,
+ cmMakeRange(info->LibrariesHeadExclude), config,
+ headTarget, interfaceFor,
+ LinkInterfaceField::HeadExclude, iface);
this->ExpandLinkItems(info->LibrariesProp, cmMakeRange(info->Libraries),
- config, headTarget, interfaceFor, iface);
+ config, headTarget, interfaceFor,
+ LinkInterfaceField::Libraries, iface);
std::vector<std::string> deps = cmExpandedList(info->SharedDeps);
LookupLinkItemScope scope{ this->LocalGenerator };
for (std::string const& dep : deps) {
@@ -7496,6 +7561,14 @@ void cmGeneratorTarget::ComputeImportInfo(std::string const& desired_config,
}
}
}
+ for (BT<std::string> const& entry :
+ this->Target->GetLinkInterfaceDirectEntries()) {
+ info.LibrariesHeadInclude.emplace_back(entry);
+ }
+ for (BT<std::string> const& entry :
+ this->Target->GetLinkInterfaceDirectExcludeEntries()) {
+ info.LibrariesHeadExclude.emplace_back(entry);
+ }
if (this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
if (loc) {
info.LibName = *loc;
@@ -7929,6 +8002,112 @@ bool cmGeneratorTarget::IsNullImpliedByLinkLibraries(
return cm::contains(this->LinkImplicitNullProperties, p);
}
+namespace {
+class TransitiveLinkImpl
+{
+ cmGeneratorTarget const* Self;
+ std::string const& Config;
+ cmLinkImplementation& Impl;
+
+ std::set<cmLinkItem> Emitted;
+ std::set<cmLinkItem> Excluded;
+ std::unordered_set<cmGeneratorTarget const*> Followed;
+
+ void Follow(cmGeneratorTarget const* target);
+
+public:
+ TransitiveLinkImpl(cmGeneratorTarget const* self, std::string const& config,
+ cmLinkImplementation& impl)
+ : Self(self)
+ , Config(config)
+ , Impl(impl)
+ {
+ }
+
+ void Compute();
+};
+
+void TransitiveLinkImpl::Follow(cmGeneratorTarget const* target)
+{
+ if (!target || !this->Followed.insert(target).second ||
+ target->GetPolicyStatusCMP0022() == cmPolicies::OLD ||
+ target->GetPolicyStatusCMP0022() == cmPolicies::WARN) {
+ return;
+ }
+
+ // Get this target's usage requirements.
+ cmLinkInterfaceLibraries const* iface = target->GetLinkInterfaceLibraries(
+ this->Config, this->Self, LinkInterfaceFor::Usage);
+ if (!iface) {
+ return;
+ }
+ if (iface->HadContextSensitiveCondition) {
+ this->Impl.HadContextSensitiveCondition = true;
+ }
+
+ // Process 'INTERFACE_LINK_LIBRARIES_DIRECT' usage requirements.
+ for (cmLinkItem const& item : iface->HeadInclude) {
+ // Inject direct dependencies from the item's usage requirements
+ // before the item itself.
+ this->Follow(item.Target);
+
+ // Add the item itself, but at most once.
+ if (this->Emitted.insert(item).second) {
+ this->Impl.Libraries.emplace_back(item, /* checkCMP0027= */ false);
+ }
+ }
+
+ // Follow transitive dependencies.
+ for (cmLinkItem const& item : iface->Libraries) {
+ this->Follow(item.Target);
+ }
+
+ // Record exclusions from 'INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE'
+ // usage requirements.
+ for (cmLinkItem const& item : iface->HeadExclude) {
+ this->Excluded.insert(item);
+ }
+}
+
+void TransitiveLinkImpl::Compute()
+{
+ // Save the original items and start with an empty list.
+ std::vector<cmLinkImplItem> original = std::move(this->Impl.Libraries);
+
+ // Avoid injecting any original items as usage requirements.
+ // This gives LINK_LIBRARIES final control over the order
+ // if it explicitly lists everything.
+ this->Emitted.insert(original.cbegin(), original.cend());
+
+ // Process each original item.
+ for (cmLinkImplItem& item : original) {
+ // Inject direct dependencies listed in 'INTERFACE_LINK_LIBRARIES_DIRECT'
+ // usage requirements before the item itself.
+ this->Follow(item.Target);
+
+ // Add the item itself.
+ this->Impl.Libraries.emplace_back(std::move(item));
+ }
+
+ // Remove items listed in 'INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE'
+ // usage requirements found through any dependency above.
+ this->Impl.Libraries.erase(
+ std::remove_if(this->Impl.Libraries.begin(), this->Impl.Libraries.end(),
+ [this](cmLinkImplItem const& item) {
+ return this->Excluded.find(item) != this->Excluded.end();
+ }),
+ this->Impl.Libraries.end());
+}
+
+void ComputeLinkImplTransitive(cmGeneratorTarget const* self,
+ std::string const& config,
+ cmLinkImplementation& impl)
+{
+ TransitiveLinkImpl transitiveLinkImpl(self, config, impl);
+ transitiveLinkImpl.Compute();
+}
+}
+
void cmGeneratorTarget::ComputeLinkImplementationLibraries(
const std::string& config, cmOptionalLinkImplementation& impl,
cmGeneratorTarget const* head) const
@@ -8035,6 +8214,11 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
cge->GetMaxLanguageStandard(this, this->MaxLanguageStandards);
}
+ // Update the list of direct link dependencies from usage requirements.
+ if (head == this) {
+ ComputeLinkImplTransitive(this, config, impl);
+ }
+
// Get the list of configurations considered to be DEBUG.
std::vector<std::string> debugConfigs =
this->Makefile->GetCMakeInstance()->GetDebugConfigs();
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index 7cf1720..3e30913 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -513,7 +513,8 @@ public:
std::string const& config, std::string const& language) const;
std::vector<BT<std::string>>& ResolveLinkerWrapper(
- std::vector<BT<std::string>>& result, const std::string& language) const;
+ std::vector<BT<std::string>>& result, const std::string& language,
+ bool joinItems = false) const;
void GetStaticLibraryLinkOptions(std::vector<std::string>& result,
const std::string& config,
@@ -1006,6 +1007,8 @@ private:
std::string Languages;
std::string LibrariesProp;
std::vector<BT<std::string>> Libraries;
+ std::vector<BT<std::string>> LibrariesHeadInclude;
+ std::vector<BT<std::string>> LibrariesHeadExclude;
std::string SharedDeps;
};
@@ -1066,10 +1069,16 @@ private:
bool IsLinkLookupScope(std::string const& n,
cmLocalGenerator const*& lg) const;
+ enum class LinkInterfaceField
+ {
+ Libraries,
+ HeadExclude,
+ HeadInclude,
+ };
void ExpandLinkItems(std::string const& prop, cmBTStringRange entries,
std::string const& config,
const cmGeneratorTarget* headTarget,
- LinkInterfaceFor interfaceFor,
+ LinkInterfaceFor interfaceFor, LinkInterfaceField field,
cmLinkInterface& iface) const;
struct LookupLinkItemScope
diff --git a/Source/cmGhsMultiTargetGenerator.cxx b/Source/cmGhsMultiTargetGenerator.cxx
index 47cefae..a7796c9 100644
--- a/Source/cmGhsMultiTargetGenerator.cxx
+++ b/Source/cmGhsMultiTargetGenerator.cxx
@@ -36,11 +36,6 @@ cmGhsMultiTargetGenerator::cmGhsMultiTargetGenerator(cmGeneratorTarget* target)
static_cast<cmLocalGhsMultiGenerator*>(target->GetLocalGenerator()))
, Makefile(target->Target->GetMakefile())
, Name(target->GetName())
-#ifdef _WIN32
- , CmdWindowsShell(true)
-#else
- , CmdWindowsShell(false)
-#endif
{
// Store the configuration name that is being used
if (cmValue config = this->Makefile->GetDefinition("CMAKE_BUILD_TYPE")) {
@@ -112,12 +107,6 @@ void cmGhsMultiTargetGenerator::Generate()
return;
}
- // Tell the global generator the name of the project file
- this->GeneratorTarget->Target->SetProperty("GENERATOR_FILE_NAME",
- this->Name);
- this->GeneratorTarget->Target->SetProperty(
- "GENERATOR_FILE_NAME_EXT", GhsMultiGpj::GetGpjTag(this->TagType));
-
this->GenerateTarget();
}
@@ -126,7 +115,14 @@ void cmGhsMultiTargetGenerator::GenerateTarget()
// Open the target file in copy-if-different mode.
std::string fproj =
cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
- this->Name, cmGlobalGhsMultiGenerator::FILE_EXTENSION);
+ this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
+ '/', this->Name, cmGlobalGhsMultiGenerator::FILE_EXTENSION);
+
+ // Tell the global generator the name of the project file
+ this->GeneratorTarget->Target->SetProperty("GENERATOR_FILE_NAME", fproj);
+ this->GeneratorTarget->Target->SetProperty(
+ "GENERATOR_FILE_NAME_EXT", GhsMultiGpj::GetGpjTag(this->TagType));
+
cmGeneratedFileStream fout(fproj);
fout.SetCopyIfDifferent(true);
@@ -160,10 +156,16 @@ void cmGhsMultiTargetGenerator::WriteTargetSpecifics(std::ostream& fout,
{
std::string outpath;
+ /* Determine paths from the target project file to where the output artifacts
+ * need to be located.
+ */
if (this->TagType != GhsMultiGpj::SUBPROJECT) {
// set target binary file destination
- outpath = this->GeneratorTarget->GetDirectory(config);
- outpath = this->LocalGenerator->MaybeRelativeToCurBinDir(outpath);
+ std::string binpath = cmStrCat(
+ this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
+ this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget));
+ outpath = cmSystemTools::RelativePath(
+ binpath, this->GeneratorTarget->GetDirectory(config));
/* clang-format off */
fout << " :binDirRelative=\"" << outpath << "\"\n"
" -o \"" << this->TargetNameReal << "\"\n";
@@ -171,7 +173,7 @@ void cmGhsMultiTargetGenerator::WriteTargetSpecifics(std::ostream& fout,
}
// set target object file destination
- outpath = this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
+ outpath = ".";
fout << " :outputDirRelative=\"" << outpath << "\"\n";
}
@@ -187,6 +189,7 @@ void cmGhsMultiTargetGenerator::SetCompilerFlags(std::string const& config,
language, config);
this->LocalGenerator->AddVisibilityPresetFlags(
flags, this->GeneratorTarget, language);
+ this->LocalGenerator->AddColorDiagnosticsFlags(flags, language);
// Append old-style preprocessor definition flags.
if (this->Makefile->GetDefineFlags() != " ") {
@@ -316,19 +319,37 @@ void cmGhsMultiTargetGenerator::WriteTargetLinkLine(std::ostream& fout,
void cmGhsMultiTargetGenerator::WriteBuildEvents(std::ostream& fout)
{
- this->WriteBuildEventsHelper(
- fout, this->GeneratorTarget->GetPreBuildCommands(),
- std::string("prebuild"), std::string("preexecShell"));
+ this->WriteBuildEventsHelper(fout,
+ this->GeneratorTarget->GetPreBuildCommands(),
+ std::string("prebuild"),
+#ifdef _WIN32
+ std::string("preexecShell")
+#else
+ std::string("preexec")
+#endif
+ );
if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) {
- this->WriteBuildEventsHelper(
- fout, this->GeneratorTarget->GetPreLinkCommands(),
- std::string("prelink"), std::string("preexecShell"));
+ this->WriteBuildEventsHelper(fout,
+ this->GeneratorTarget->GetPreLinkCommands(),
+ std::string("prelink"),
+#ifdef _WIN32
+ std::string("preexecShell")
+#else
+ std::string("preexec")
+#endif
+ );
}
- this->WriteBuildEventsHelper(
- fout, this->GeneratorTarget->GetPostBuildCommands(),
- std::string("postbuild"), std::string("postexecShell"));
+ this->WriteBuildEventsHelper(fout,
+ this->GeneratorTarget->GetPostBuildCommands(),
+ std::string("postbuild"),
+#ifdef _WIN32
+ std::string("postexecShell")
+#else
+ std::string("postexec")
+#endif
+ );
}
void cmGhsMultiTargetGenerator::WriteBuildEventsHelper(
@@ -336,6 +357,13 @@ void cmGhsMultiTargetGenerator::WriteBuildEventsHelper(
std::string const& name, std::string const& cmd)
{
int cmdcount = 0;
+#ifdef _WIN32
+ std::string fext = ".bat";
+ std::string shell;
+#else
+ std::string fext = ".sh";
+ std::string shell = "/bin/sh ";
+#endif
for (cmCustomCommand const& cc : ccv) {
cmCustomCommandGenerator ccg(cc, this->ConfigName, this->LocalGenerator);
@@ -343,14 +371,14 @@ void cmGhsMultiTargetGenerator::WriteBuildEventsHelper(
std::string fname =
cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
- '/', this->Name, '_', name, cmdcount++,
- this->CmdWindowsShell ? ".bat" : ".sh");
+ '/', this->Name, '_', name, cmdcount++, fext);
+
cmGeneratedFileStream f(fname);
f.SetCopyIfDifferent(true);
this->WriteCustomCommandsHelper(f, ccg);
f.Close();
if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) {
- fout << " :" << cmd << "=\"" << fname << "\"\n";
+ fout << " :" << cmd << "=\"" << shell << fname << "\"\n";
} else {
fout << fname << "\n :outputName=\"" << fname << ".rule\"\n";
}
@@ -409,15 +437,15 @@ void cmGhsMultiTargetGenerator::WriteCustomCommandsHelper(
//
bool useCall = false;
- if (this->CmdWindowsShell) {
- std::string suffix;
- if (cmd.size() > 4) {
- suffix = cmSystemTools::LowerCase(cmd.substr(cmd.size() - 4));
- if (suffix == ".bat" || suffix == ".cmd") {
- useCall = true;
- }
+#ifdef _WIN32
+ std::string suffix;
+ if (cmd.size() > 4) {
+ suffix = cmSystemTools::LowerCase(cmd.substr(cmd.size() - 4));
+ if (suffix == ".bat" || suffix == ".cmd") {
+ useCall = true;
}
}
+#endif
cmSystemTools::ReplaceString(cmd, "/./", "/");
// Convert the command to a relative path only if the current
@@ -555,11 +583,12 @@ void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj)
// Open the filestream in copy-if-different mode.
std::string gname = sg;
cmsys::SystemTools::ReplaceString(gname, "\\", "_");
- std::string lpath = cmStrCat(
- this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget), '/',
- gname, cmGlobalGhsMultiGenerator::FILE_EXTENSION);
+ std::string lpath =
+ cmStrCat(gname, cmGlobalGhsMultiGenerator::FILE_EXTENSION);
std::string fpath = cmStrCat(
- this->LocalGenerator->GetCurrentBinaryDirectory(), '/', lpath);
+ this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
+ this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget), '/',
+ lpath);
cmGeneratedFileStream* f = new cmGeneratedFileStream(fpath);
f->SetCopyIfDifferent(true);
gfiles.push_back(f);
@@ -645,6 +674,11 @@ void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj)
}
}
int cmdcount = 0;
+#ifdef _WIN32
+ std::string fext = ".bat";
+#else
+ std::string fext = ".sh";
+#endif
for (auto& sf : customCommands) {
const cmCustomCommand* cc = sf->GetCustomCommand();
cmCustomCommandGenerator ccg(*cc, this->ConfigName,
@@ -655,8 +689,8 @@ void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj)
this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
'/', this->Name, "_cc", cmdcount++, '_',
- (sf->GetLocation()).GetName(),
- this->CmdWindowsShell ? ".bat" : ".sh");
+ (sf->GetLocation()).GetName(), fext);
+
cmGeneratedFileStream f(fname);
f.SetCopyIfDifferent(true);
this->WriteCustomCommandsHelper(f, ccg);
diff --git a/Source/cmGhsMultiTargetGenerator.h b/Source/cmGhsMultiTargetGenerator.h
index e9d7537..9289a72 100644
--- a/Source/cmGhsMultiTargetGenerator.h
+++ b/Source/cmGhsMultiTargetGenerator.h
@@ -78,6 +78,5 @@ private:
std::string TargetNameReal;
GhsMultiGpj::Types TagType;
std::string const Name;
- std::string ConfigName; /* CMAKE_BUILD_TYPE */
- bool const CmdWindowsShell; /* custom commands run in cmd.exe or /bin/sh */
+ std::string ConfigName; /* CMAKE_BUILD_TYPE */
};
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index 156ecce..baa54e5 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -19,6 +19,7 @@
#include "cmsys/Directory.hxx"
#include "cmsys/FStream.hxx"
+#include "cmsys/RegularExpression.hxx"
#if defined(_WIN32) && !defined(__CYGWIN__)
# include <windows.h>
@@ -2522,6 +2523,47 @@ bool cmGlobalGenerator::NameResolvesToFramework(
return false;
}
+// If the file has no extension it's either a raw executable or might
+// be a direct reference to a binary within a framework (bad practice!).
+// This is where we change the path to point to the framework directory.
+// .tbd files also can be located in SDK frameworks (they are
+// placeholders for actual libraries shipped with the OS)
+cm::optional<std::pair<std::string, std::string>>
+cmGlobalGenerator::SplitFrameworkPath(const std::string& path,
+ bool extendedFormat) const
+{
+ // Check for framework structure:
+ // (/path/to/)?FwName.framework
+ // or (/path/to/)?FwName.framework/FwName(.tbd)?
+ // or (/path/to/)?FwName.framework/Versions/*/FwName(.tbd)?
+ static cmsys::RegularExpression frameworkPath(
+ "((.+)/)?(.+)\\.framework(/Versions/[^/]+)?(/(.+))?$");
+
+ auto ext = cmSystemTools::GetFilenameLastExtension(path);
+ if ((ext.empty() || ext == ".tbd" || ext == ".framework") &&
+ frameworkPath.find(path)) {
+ auto name = frameworkPath.match(3);
+ auto libname =
+ cmSystemTools::GetFilenameWithoutExtension(frameworkPath.match(6));
+ if (!libname.empty() && name != libname) {
+ return cm::nullopt;
+ }
+ return std::pair<std::string, std::string>{ frameworkPath.match(2), name };
+ }
+
+ if (extendedFormat) {
+ // path format can be more flexible: (/path/to/)?fwName(.framework)?
+ auto fwDir = cmSystemTools::GetParentDirectory(path);
+ auto name = cmSystemTools::GetFilenameLastExtension(path) == ".framework"
+ ? cmSystemTools::GetFilenameWithoutExtension(path)
+ : cmSystemTools::GetFilenameName(path);
+
+ return std::pair<std::string, std::string>{ fwDir, name };
+ }
+
+ return cm::nullopt;
+}
+
bool cmGlobalGenerator::CheckCMP0037(std::string const& targetName,
std::string const& reason) const
{
diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h
index a43d4a6..a4b2ae3 100644
--- a/Source/cmGlobalGenerator.h
+++ b/Source/cmGlobalGenerator.h
@@ -367,6 +367,13 @@ public:
/** Determine if a name resolves to a framework on disk or a built target
that is a framework. */
bool NameResolvesToFramework(const std::string& libname) const;
+ /** Split a framework path to the directory and name of the framework
+ * returns std::nullopt if the path does not match with framework format
+ * when extendedFormat is true, required format is relaxed (i.e. extension
+ * `.framework' is optional). Used when FRAMEWORK link feature is
+ * specified */
+ cm::optional<std::pair<std::string, std::string>> SplitFrameworkPath(
+ const std::string& path, bool extendedFormat = false) const;
cmMakefile* FindMakefile(const std::string& start_dir) const;
cmLocalGenerator* FindLocalGenerator(cmDirectoryId const& id) const;
diff --git a/Source/cmGlobalGhsMultiGenerator.cxx b/Source/cmGlobalGhsMultiGenerator.cxx
index 5e51dd2..f405a04 100644
--- a/Source/cmGlobalGhsMultiGenerator.cxx
+++ b/Source/cmGlobalGhsMultiGenerator.cxx
@@ -2,14 +2,19 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmGlobalGhsMultiGenerator.h"
+#include <algorithm>
+#include <functional>
#include <map>
-#include <ostream>
+#include <sstream>
#include <utility>
#include <cm/memory>
#include <cm/string>
#include <cmext/algorithm>
+#include <cmext/memory>
+#include "cmCustomCommand.h"
+#include "cmCustomCommandLines.h"
#include "cmDocumentationEntry.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorTarget.h"
@@ -18,10 +23,13 @@
#include "cmLocalGhsMultiGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
+#include "cmPolicies.h"
+#include "cmSourceFile.h"
#include "cmState.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
+#include "cmTarget.h"
#include "cmValue.h"
#include "cmVersion.h"
#include "cmake.h"
@@ -32,6 +40,8 @@ const char* cmGlobalGhsMultiGenerator::DEFAULT_BUILD_PROGRAM = "gbuild";
#elif defined(_WIN32)
const char* cmGlobalGhsMultiGenerator::DEFAULT_BUILD_PROGRAM = "gbuild.exe";
#endif
+const char* cmGlobalGhsMultiGenerator::CHECK_BUILD_SYSTEM_TARGET =
+ "RERUN_CMAKE";
cmGlobalGhsMultiGenerator::cmGlobalGhsMultiGenerator(cmake* cm)
: cmGlobalGenerator(cm)
@@ -279,7 +289,7 @@ void cmGlobalGhsMultiGenerator::WriteTopLevelProject(std::ostream& fout,
{
this->WriteFileHeader(fout);
this->WriteMacros(fout, root);
- this->WriteHighLevelDirectives(root, fout);
+ this->WriteHighLevelDirectives(fout, root);
GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fout);
fout << "# Top Level Project File\n";
@@ -308,9 +318,13 @@ void cmGlobalGhsMultiGenerator::WriteTopLevelProject(std::ostream& fout,
}
void cmGlobalGhsMultiGenerator::WriteSubProjects(std::ostream& fout,
- std::string& all_target)
+ bool filterPredefined)
{
- fout << "CMakeFiles/" << all_target << " [Project]\n";
+ std::set<std::string> predefinedTargets;
+ predefinedTargets.insert(this->GetInstallTargetName());
+ predefinedTargets.insert(this->GetAllTargetName());
+ predefinedTargets.insert(std::string(CHECK_BUILD_SYSTEM_TARGET));
+
// All known targets
for (cmGeneratorTarget const* target : this->ProjectTargets) {
if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY ||
@@ -320,8 +334,13 @@ void cmGlobalGhsMultiGenerator::WriteSubProjects(std::ostream& fout,
target->GetName() != this->GetInstallTargetName())) {
continue;
}
- fout << "CMakeFiles/" << target->GetName() + ".tgt" + FILE_EXTENSION
- << " [Project]\n";
+ /* Check if the current target is a predefined CMake target */
+ bool predefinedTarget =
+ predefinedTargets.find(target->GetName()) != predefinedTargets.end();
+ if ((filterPredefined && predefinedTarget) ||
+ (!filterPredefined && !predefinedTarget)) {
+ fout << target->GetName() + ".tgt" + FILE_EXTENSION << " [Project]\n";
+ }
}
}
@@ -329,36 +348,22 @@ void cmGlobalGhsMultiGenerator::WriteProjectLine(
std::ostream& fout, cmGeneratorTarget const* target,
std::string& rootBinaryDir)
{
- cmValue projName = target->GetProperty("GENERATOR_FILE_NAME");
+ cmValue projFile = target->GetProperty("GENERATOR_FILE_NAME");
cmValue projType = target->GetProperty("GENERATOR_FILE_NAME_EXT");
- if (projName && projType) {
- cmLocalGenerator* lg = target->GetLocalGenerator();
- std::string dir = lg->GetCurrentBinaryDirectory();
- dir = cmSystemTools::ForceToRelativePath(rootBinaryDir, dir);
- if (dir == ".") {
- dir.clear();
- } else {
- if (dir.back() != '/') {
- dir += "/";
- }
- }
+ /* If either value is not valid then this particular target is an
+ * unsupported target type and should be skipped.
+ */
+ if (projFile && projType) {
+ std::string path = cmSystemTools::RelativePath(rootBinaryDir, projFile);
- std::string projFile = dir + *projName + FILE_EXTENSION;
- fout << projFile;
+ fout << path;
fout << ' ' << *projType << '\n';
- } else {
- /* Should never happen */
- std::string message =
- "The project file for target [" + target->GetName() + "] is missing.\n";
- cmSystemTools::Error(message);
- fout << "{comment} " << target->GetName() << " [missing project file]\n";
}
}
void cmGlobalGhsMultiGenerator::WriteTargets(cmLocalGenerator* root)
{
- std::string rootBinaryDir =
- cmStrCat(root->GetCurrentBinaryDirectory(), "/CMakeFiles");
+ std::string rootBinaryDir = root->GetCurrentBinaryDirectory();
// All known targets
for (cmGeneratorTarget const* target : this->ProjectTargets) {
@@ -391,62 +396,6 @@ void cmGlobalGhsMultiGenerator::WriteTargets(cmLocalGenerator* root)
}
}
-void cmGlobalGhsMultiGenerator::WriteAllTarget(
- cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators,
- std::string& all_target)
-{
- this->ProjectTargets.clear();
-
- // create target build file
- all_target = root->GetProjectName() + "." + this->GetAllTargetName() +
- ".tgt" + FILE_EXTENSION;
- std::string fname =
- root->GetCurrentBinaryDirectory() + "/CMakeFiles/" + all_target;
- cmGeneratedFileStream fbld(fname);
- fbld.SetCopyIfDifferent(true);
- this->WriteFileHeader(fbld);
- GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fbld);
-
- // Collect all targets under this root generator and the transitive
- // closure of their dependencies.
- TargetDependSet projectTargets;
- TargetDependSet originalTargets;
- this->GetTargetSets(projectTargets, originalTargets, root, generators);
- OrderedTargetDependSet sortedProjectTargets(projectTargets, "");
- std::vector<cmGeneratorTarget const*> defaultTargets;
- for (cmGeneratorTarget const* t : sortedProjectTargets) {
- /* save list of all targets in sorted order */
- this->ProjectTargets.push_back(t);
- }
- for (cmGeneratorTarget const* t : sortedProjectTargets) {
- if (!t->IsInBuildSystem()) {
- continue;
- }
- if (!this->IsExcluded(t->GetLocalGenerator(), t)) {
- defaultTargets.push_back(t);
- }
- }
- std::vector<cmGeneratorTarget const*> build;
- if (this->ComputeTargetBuildOrder(defaultTargets, build)) {
- std::string message = "The inter-target dependency graph for project [" +
- root->GetProjectName() + "] had a cycle.\n";
- cmSystemTools::Error(message);
- } else {
- // determine the targets for ALL target
- std::string rootBinaryDir =
- cmStrCat(root->GetCurrentBinaryDirectory(), "/CMakeFiles");
- for (cmGeneratorTarget const* target : build) {
- if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY ||
- target->GetType() == cmStateEnums::MODULE_LIBRARY ||
- target->GetType() == cmStateEnums::SHARED_LIBRARY) {
- continue;
- }
- this->WriteProjectLine(fbld, target, rootBinaryDir);
- }
- }
- fbld.Close();
-}
-
void cmGlobalGhsMultiGenerator::Generate()
{
std::string fname;
@@ -476,18 +425,36 @@ void cmGlobalGhsMultiGenerator::Generate()
this->WriteFileHeader(ftarget);
this->WriteCustomTargetBOD(ftarget);
ftarget.Close();
+
+ // create the stamp file when running CMake
+ if (!this->StampFile.empty()) {
+ cmGeneratedFileStream fstamp(this->StampFile);
+ fstamp.SetCopyIfDifferent(false);
+ fstamp.Close();
+ }
}
void cmGlobalGhsMultiGenerator::OutputTopLevelProject(
cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators)
{
std::string fname;
- std::string all_target;
if (generators.empty()) {
return;
}
+ // Collect all targets under this root generator and the transitive
+ // closure of their dependencies.
+ TargetDependSet projectTargets;
+ TargetDependSet originalTargets;
+ this->GetTargetSets(projectTargets, originalTargets, root, generators);
+ OrderedTargetDependSet sortedProjectTargets(projectTargets, "");
+ this->ProjectTargets.clear();
+ for (cmGeneratorTarget const* t : sortedProjectTargets) {
+ /* save list of all targets in sorted order */
+ this->ProjectTargets.push_back(t);
+ }
+
/* Name top-level projects as filename.top.gpj to avoid name clashes
* with target projects. This avoid the issue where the project has
* the same name as the executable target.
@@ -498,11 +465,9 @@ void cmGlobalGhsMultiGenerator::OutputTopLevelProject(
cmGeneratedFileStream top(fname);
top.SetCopyIfDifferent(true);
this->WriteTopLevelProject(top, root);
-
- this->WriteAllTarget(root, generators, all_target);
this->WriteTargets(root);
-
- this->WriteSubProjects(top, all_target);
+ this->WriteSubProjects(top, true);
+ this->WriteSubProjects(top, false);
top.Close();
}
@@ -510,59 +475,62 @@ std::vector<cmGlobalGenerator::GeneratedMakeCommand>
cmGlobalGhsMultiGenerator::GenerateBuildCommand(
const std::string& makeProgram, const std::string& projectName,
const std::string& projectDir, std::vector<std::string> const& targetNames,
- const std::string& /*config*/, int jobs, bool /*verbose*/,
+ const std::string& /*config*/, int jobs, bool verbose,
const cmBuildOptions& /*buildOptions*/,
std::vector<std::string> const& makeOptions)
{
- GeneratedMakeCommand makeCommand = {};
- std::string gbuild;
- if (cmValue gbuildCached =
- this->CMakeInstance->GetCacheDefinition("CMAKE_MAKE_PROGRAM")) {
- gbuild = *gbuildCached;
- }
- makeCommand.Add(this->SelectMakeProgram(makeProgram, gbuild));
+ GeneratedMakeCommand makeCommand;
+
+ makeCommand.Add(this->SelectMakeProgram(makeProgram));
if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) {
- makeCommand.Add("-parallel");
- if (jobs != cmake::DEFAULT_BUILD_PARALLEL_LEVEL) {
- makeCommand.Add(std::to_string(jobs));
+ if (jobs == cmake::DEFAULT_BUILD_PARALLEL_LEVEL) {
+ makeCommand.Add("-parallel");
+ } else {
+ makeCommand.Add(std::string("-parallel=") + std::to_string(jobs));
}
}
- makeCommand.Add(makeOptions.begin(), makeOptions.end());
-
- /* determine which top-project file to use */
+ /* determine the top-project file in the project directory */
std::string proj = projectName + ".top" + FILE_EXTENSION;
std::vector<std::string> files;
cmSystemTools::Glob(projectDir, ".*\\.top\\.gpj", files);
if (!files.empty()) {
- /* if multiple top-projects are found in build directory
- * then prefer projectName top-project.
- */
- if (!cm::contains(files, proj)) {
- proj = files.at(0);
- }
+ /* use the real top-level project in the directory */
+ proj = files.at(0);
}
-
makeCommand.Add("-top", proj);
+
+ /* determine targets to build */
+ bool build_all = false;
if (!targetNames.empty()) {
- if (cm::contains(targetNames, "clean")) {
- makeCommand.Add("-clean");
- } else {
- for (const auto& tname : targetNames) {
- if (!tname.empty()) {
+ for (const auto& tname : targetNames) {
+ if (!tname.empty()) {
+ if (tname == "clean") {
+ makeCommand.Add("-clean");
+ } else {
makeCommand.Add(tname + ".tgt.gpj");
}
+ } else {
+ build_all = true;
}
}
} else {
+ build_all = true;
+ }
+
+ if (build_all) {
/* transform name to default build */;
- std::string all = proj;
- all.replace(all.end() - 7, all.end(),
- std::string(this->GetAllTargetName()) + ".tgt.gpj");
+ std::string all = std::string(this->GetAllTargetName()) + ".tgt.gpj";
makeCommand.Add(all);
}
- return { makeCommand };
+
+ if (verbose) {
+ makeCommand.Add("-commands");
+ }
+ makeCommand.Add(makeOptions.begin(), makeOptions.end());
+
+ return { std::move(makeCommand) };
}
void cmGlobalGhsMultiGenerator::WriteMacros(std::ostream& fout,
@@ -579,7 +547,7 @@ void cmGlobalGhsMultiGenerator::WriteMacros(std::ostream& fout,
}
void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives(
- cmLocalGenerator* root, std::ostream& fout)
+ std::ostream& fout, cmLocalGenerator* root)
{
/* put primary target and customization files into project file */
cmValue const tgt = root->GetMakefile()->GetDefinition("GHS_PRIMARY_TARGET");
@@ -681,3 +649,147 @@ bool cmGlobalGhsMultiGenerator::VisitTarget(
/* already complete */
return false;
}
+
+bool cmGlobalGhsMultiGenerator::AddCheckTarget()
+{
+ // Skip the target if no regeneration is to be done.
+ if (this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION")) {
+ return false;
+ }
+
+ // Get the generators.
+ std::vector<std::unique_ptr<cmLocalGenerator>> const& generators =
+ this->LocalGenerators;
+ auto& lg =
+ cm::static_reference_cast<cmLocalGhsMultiGenerator>(generators[0]);
+
+ // The name of the output file for the custom command.
+ this->StampFile = lg.GetBinaryDirectory() + std::string("/CMakeFiles/") +
+ CHECK_BUILD_SYSTEM_TARGET;
+
+ // Add a custom rule to re-run CMake if any input files changed.
+ {
+ // Collect the input files used to generate all targets in this
+ // project.
+ std::vector<std::string> listFiles;
+ for (const auto& gen : generators) {
+ cm::append(listFiles, gen->GetMakefile()->GetListFiles());
+ }
+
+ // Add the cache file.
+ listFiles.push_back(cmStrCat(
+ this->GetCMakeInstance()->GetHomeOutputDirectory(), "/CMakeCache.txt"));
+
+ // Print not implemented warning.
+ if (this->GetCMakeInstance()->DoWriteGlobVerifyTarget()) {
+ std::ostringstream msg;
+ msg << "Any pre-check scripts, such as those generated for file(GLOB "
+ "CONFIGURE_DEPENDS), will not be run by gbuild.";
+ this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING,
+ msg.str());
+ }
+
+ // Sort the list of input files and remove duplicates.
+ std::sort(listFiles.begin(), listFiles.end(), std::less<std::string>());
+ auto newEnd = std::unique(listFiles.begin(), listFiles.end());
+ listFiles.erase(newEnd, listFiles.end());
+
+ // Create a rule to re-run CMake and create output file.
+ std::string argS = cmStrCat("-S", lg.GetSourceDirectory());
+ std::string argB = cmStrCat("-B", lg.GetBinaryDirectory());
+ cmCustomCommandLines commandLines = cmMakeSingleCommandLine(
+ { cmSystemTools::GetCMakeCommand(), argS, argB });
+
+ /* Create the target(Exclude from ALL_BUILD).
+ *
+ * The build tool, currently, does not support rereading the project files
+ * if they get updated. So do not run this target as part of ALL_BUILD.
+ */
+ auto cc = cm::make_unique<cmCustomCommand>();
+ cmTarget* tgt =
+ lg.AddUtilityCommand(CHECK_BUILD_SYSTEM_TARGET, true, std::move(cc));
+ auto ptr = cm::make_unique<cmGeneratorTarget>(tgt, &lg);
+ auto* gt = ptr.get();
+ lg.AddGeneratorTarget(std::move(ptr));
+
+ // Add the rule.
+ cc = cm::make_unique<cmCustomCommand>();
+ cc->SetOutputs(this->StampFile);
+ cc->SetDepends(listFiles);
+ cc->SetCommandLines(commandLines);
+ cc->SetComment("Checking Build System");
+ cc->SetCMP0116Status(cmPolicies::NEW);
+ cc->SetEscapeOldStyle(false);
+ cc->SetStdPipesUTF8(true);
+
+ if (cmSourceFile* file =
+ lg.AddCustomCommandToOutput(std::move(cc), true)) {
+ gt->AddSource(file->ResolveFullPath());
+ } else {
+ cmSystemTools::Error("Error adding rule for " + this->StampFile);
+ }
+ // Organize in the "predefined targets" folder:
+ if (this->UseFolderProperty()) {
+ tgt->SetProperty("FOLDER", this->GetPredefinedTargetsFolder());
+ }
+ }
+
+ return true;
+}
+
+void cmGlobalGhsMultiGenerator::AddAllTarget()
+{
+ // Add a special target that depends on ALL projects for easy build
+ // of one configuration only.
+ for (auto const& it : this->ProjectMap) {
+ std::vector<cmLocalGenerator*> const& gen = it.second;
+ // add the ALL_BUILD to the first local generator of each project
+ if (!gen.empty()) {
+ // Use no actual command lines so that the target itself is not
+ // considered always out of date.
+ auto cc = cm::make_unique<cmCustomCommand>();
+ cc->SetCMP0116Status(cmPolicies::NEW);
+ cc->SetEscapeOldStyle(false);
+ cc->SetComment("Build all projects");
+ cmTarget* allBuild = gen[0]->AddUtilityCommand(this->GetAllTargetName(),
+ true, std::move(cc));
+
+ gen[0]->AddGeneratorTarget(
+ cm::make_unique<cmGeneratorTarget>(allBuild, gen[0]));
+
+ // Organize in the "predefined targets" folder:
+ if (this->UseFolderProperty()) {
+ allBuild->SetProperty("FOLDER", this->GetPredefinedTargetsFolder());
+ }
+
+ // Now make all targets depend on the ALL_BUILD target
+ for (cmLocalGenerator const* i : gen) {
+ for (const auto& tgt : i->GetGeneratorTargets()) {
+ // Skip global or imported targets
+ if (tgt->GetType() == cmStateEnums::GLOBAL_TARGET ||
+ tgt->IsImported()) {
+ continue;
+ }
+ // Skip Exclude From All Targets
+ if (!this->IsExcluded(gen[0], tgt.get())) {
+ allBuild->AddUtility(tgt->GetName(), false);
+ }
+ }
+ }
+ }
+ }
+}
+
+void cmGlobalGhsMultiGenerator::AddExtraIDETargets()
+{
+ // Add a special target that depends on ALL projects.
+ this->AddAllTarget();
+
+ /* Add Custom Target to check if CMake needs to be rerun.
+ *
+ * The build tool, currently, does not support rereading the project files
+ * if they get updated. So do not make the other targets dependent on this
+ * check.
+ */
+ this->AddCheckTarget();
+}
diff --git a/Source/cmGlobalGhsMultiGenerator.h b/Source/cmGlobalGhsMultiGenerator.h
index 26ea3c7..aa68d3b 100644
--- a/Source/cmGlobalGhsMultiGenerator.h
+++ b/Source/cmGlobalGhsMultiGenerator.h
@@ -81,8 +81,6 @@ public:
// Write the common disclaimer text at the top of each build file.
void WriteFileHeader(std::ostream& fout);
- const char* GetInstallTargetName() const override { return "install"; }
-
protected:
void Generate() override;
std::vector<GeneratedMakeCommand> GenerateBuildCommand(
@@ -92,6 +90,7 @@ protected:
const cmBuildOptions& buildOptions = cmBuildOptions(),
std::vector<std::string> const& makeOptions =
std::vector<std::string>()) override;
+ void AddExtraIDETargets() override;
private:
void GetToolset(cmMakefile* mf, std::string& tsd, const std::string& ts);
@@ -101,20 +100,21 @@ private:
std::vector<cmLocalGenerator*>& generators);
void WriteTopLevelProject(std::ostream& fout, cmLocalGenerator* root);
void WriteMacros(std::ostream& fout, cmLocalGenerator* root);
- void WriteHighLevelDirectives(cmLocalGenerator* root, std::ostream& fout);
- void WriteSubProjects(std::ostream& fout, std::string& all_target);
+ void WriteHighLevelDirectives(std::ostream& fout, cmLocalGenerator* root);
+ void WriteSubProjects(std::ostream& fout, bool filterPredefined);
void WriteTargets(cmLocalGenerator* root);
void WriteProjectLine(std::ostream& fout, cmGeneratorTarget const* target,
std::string& rootBinaryDir);
void WriteCustomRuleBOD(std::ostream& fout);
void WriteCustomTargetBOD(std::ostream& fout);
- void WriteAllTarget(cmLocalGenerator* root,
- std::vector<cmLocalGenerator*>& generators,
- std::string& all_target);
+ bool AddCheckTarget();
+ void AddAllTarget();
+ std::string StampFile;
static std::string TrimQuotes(std::string str);
static const char* DEFAULT_BUILD_PROGRAM;
+ static const char* CHECK_BUILD_SYSTEM_TARGET;
bool ComputeTargetBuildOrder(cmGeneratorTarget const* tgt,
std::vector<cmGeneratorTarget const*>& build);
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index 4245037..bbc9c54 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -2495,8 +2495,7 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
snapshot.GetDirectory().SetCurrentBinary(dir_cur_bld);
auto mfd = cm::make_unique<cmMakefile>(this, snapshot);
auto lgd = this->CreateLocalGenerator(mfd.get());
- lgd->SetRelativePathTopSource(dir_top_src);
- lgd->SetRelativePathTopBinary(dir_top_bld);
+ lgd->SetRelativePathTop(dir_top_src, dir_top_bld);
this->Makefiles.push_back(std::move(mfd));
this->LocalGenerators.push_back(std::move(lgd));
}
diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx
index db54b86..35448ee 100644
--- a/Source/cmGlobalVisualStudio10Generator.cxx
+++ b/Source/cmGlobalVisualStudio10Generator.cxx
@@ -1286,6 +1286,14 @@ cmGlobalVisualStudio10Generator::GenerateBuildCommand(
return makeCommands;
}
+bool cmGlobalVisualStudio10Generator::IsInSolution(
+ const cmGeneratorTarget* gt) const
+{
+ return gt->IsInBuildSystem() &&
+ !(this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS16 &&
+ gt->GetName() == CMAKE_CHECK_BUILD_SYSTEM_TARGET);
+}
+
bool cmGlobalVisualStudio10Generator::Find64BitTools(cmMakefile* mf)
{
if (this->DefaultPlatformToolset == "v100") {
diff --git a/Source/cmGlobalVisualStudio10Generator.h b/Source/cmGlobalVisualStudio10Generator.h
index 4977a84..2203f71 100644
--- a/Source/cmGlobalVisualStudio10Generator.h
+++ b/Source/cmGlobalVisualStudio10Generator.h
@@ -118,6 +118,8 @@ public:
return this->WindowsTargetPlatformVersion;
}
+ bool IsInSolution(const cmGeneratorTarget* gt) const override;
+
/** Return true if building for WindowsCE */
bool TargetsWindowsCE() const override { return this->SystemIsWindowsCE; }
diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx
index 134937e..1c10fb3 100644
--- a/Source/cmGlobalVisualStudio7Generator.cxx
+++ b/Source/cmGlobalVisualStudio7Generator.cxx
@@ -359,7 +359,7 @@ void cmGlobalVisualStudio7Generator::WriteTargetConfigurations(
// loop over again and write out configurations for each target
// in the solution
for (cmGeneratorTarget const* target : projectTargets) {
- if (!target->IsInBuildSystem()) {
+ if (!this->IsInSolution(target)) {
continue;
}
cmValue expath = target->GetProperty("EXTERNAL_MSPROJECT");
@@ -396,7 +396,7 @@ void cmGlobalVisualStudio7Generator::WriteTargetsToSolution(
VisualStudioFolders.clear();
for (cmGeneratorTarget const* target : projectTargets) {
- if (!target->IsInBuildSystem()) {
+ if (!this->IsInSolution(target)) {
continue;
}
bool written = false;
@@ -458,22 +458,6 @@ void cmGlobalVisualStudio7Generator::WriteTargetsToSolution(
}
}
-void cmGlobalVisualStudio7Generator::WriteTargetDepends(
- std::ostream& fout, OrderedTargetDependSet const& projectTargets)
-{
- for (cmGeneratorTarget const* target : projectTargets) {
- if (!target->IsInBuildSystem()) {
- continue;
- }
- cmValue vcprojName = target->GetProperty("GENERATOR_FILE_NAME");
- if (vcprojName) {
- std::string dir =
- target->GetLocalGenerator()->GetCurrentSourceDirectory();
- this->WriteProjectDepends(fout, *vcprojName, dir, target);
- }
- }
-}
-
void cmGlobalVisualStudio7Generator::WriteFolders(std::ostream& fout)
{
cm::string_view const prefix = "CMAKE_FOLDER_GUID_";
diff --git a/Source/cmGlobalVisualStudio7Generator.h b/Source/cmGlobalVisualStudio7Generator.h
index 33f1063..a55cf45 100644
--- a/Source/cmGlobalVisualStudio7Generator.h
+++ b/Source/cmGlobalVisualStudio7Generator.h
@@ -148,8 +148,6 @@ protected:
virtual void WriteTargetsToSolution(
std::ostream& fout, cmLocalGenerator* root,
OrderedTargetDependSet const& projectTargets);
- virtual void WriteTargetDepends(
- std::ostream& fout, OrderedTargetDependSet const& projectTargets);
virtual void WriteTargetConfigurations(
std::ostream& fout, std::vector<std::string> const& configs,
OrderedTargetDependSet const& projectTargets);
diff --git a/Source/cmGlobalVisualStudio8Generator.cxx b/Source/cmGlobalVisualStudio8Generator.cxx
index dbd2de9..323ee67 100644
--- a/Source/cmGlobalVisualStudio8Generator.cxx
+++ b/Source/cmGlobalVisualStudio8Generator.cxx
@@ -392,7 +392,7 @@ void cmGlobalVisualStudio8Generator::WriteProjectDepends(
TargetDependSet const& unordered = this->GetTargetDirectDepends(gt);
OrderedTargetDependSet depends(unordered, std::string());
for (cmTargetDepend const& i : depends) {
- if (!i->IsInBuildSystem()) {
+ if (!this->IsInSolution(i)) {
continue;
}
std::string guid = this->GetGUID(i->GetName());
diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx
index 141b5eb..cddaaa4 100644
--- a/Source/cmGlobalVisualStudioGenerator.cxx
+++ b/Source/cmGlobalVisualStudioGenerator.cxx
@@ -837,6 +837,12 @@ bool cmGlobalVisualStudioGenerator::TargetIsFortranOnly(
return languages.size() == 1 && *languages.begin() == "Fortran";
}
+bool cmGlobalVisualStudioGenerator::IsInSolution(
+ const cmGeneratorTarget* gt) const
+{
+ return gt->IsInBuildSystem();
+}
+
bool cmGlobalVisualStudioGenerator::TargetCompare::operator()(
cmGeneratorTarget const* l, cmGeneratorTarget const* r) const
{
diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h
index cb1b14b..4f5f100 100644
--- a/Source/cmGlobalVisualStudioGenerator.h
+++ b/Source/cmGlobalVisualStudioGenerator.h
@@ -98,6 +98,9 @@ public:
// return true if target is fortran only
bool TargetIsFortranOnly(const cmGeneratorTarget* gt);
+ // return true if target should be included in solution.
+ virtual bool IsInSolution(const cmGeneratorTarget* gt) const;
+
/** Get the top-level registry key for this VS version. */
std::string GetRegistryBase();
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index 203addd..1e6624e 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -1154,47 +1154,25 @@ std::string GetSourcecodeValueFromFileExtension(
return sourcecode;
}
-// If the file has no extension it's either a raw executable or might
-// be a direct reference to a binary within a framework (bad practice!).
-// This is where we change the path to point to the framework directory.
-// .tbd files also can be located in SDK frameworks (they are
-// placeholders for actual libraries shipped with the OS)
-std::string GetLibraryOrFrameworkPath(const std::string& path)
+} // anonymous
+
+// Extracts the framework directory, if path matches the framework syntax
+// otherwise returns the path untouched
+std::string cmGlobalXCodeGenerator::GetLibraryOrFrameworkPath(
+ const std::string& path) const
{
- auto ext = cmSystemTools::GetFilenameLastExtension(path);
- if (ext.empty() || ext == ".tbd") {
- auto name = cmSystemTools::GetFilenameWithoutExtension(path);
- // Check for iOS framework structure:
- // FwName.framework/FwName (and also on macOS where FwName lib is a
- // symlink)
- auto parentDir = cmSystemTools::GetParentDirectory(path);
- auto parentName = cmSystemTools::GetFilenameWithoutExtension(parentDir);
- ext = cmSystemTools::GetFilenameLastExtension(parentDir);
- if (ext == ".framework" && name == parentName) {
- return parentDir;
- }
- // Check for macOS framework structure:
- // FwName.framework/Versions/*/FwName
- std::vector<std::string> components;
- cmSystemTools::SplitPath(path, components);
- if (components.size() > 3 &&
- components[components.size() - 3] == "Versions") {
- ext = cmSystemTools::GetFilenameLastExtension(
- components[components.size() - 4]);
- parentName = cmSystemTools::GetFilenameWithoutExtension(
- components[components.size() - 4]);
- if (ext == ".framework" && name == parentName) {
- components.erase(components.begin() + components.size() - 3,
- components.end());
- return cmSystemTools::JoinPath(components);
- }
+ auto fwItems = this->SplitFrameworkPath(path);
+ if (fwItems) {
+ if (fwItems->first.empty()) {
+ return cmStrCat(fwItems->second, ".framework");
+ } else {
+ return cmStrCat(fwItems->first, '/', fwItems->second, ".framework");
}
}
+
return path;
}
-} // anonymous
-
cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReferenceFromPath(
const std::string& fullpath, cmGeneratorTarget* target,
const std::string& lang, cmSourceFile* sf)
@@ -1217,7 +1195,7 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReferenceFromPath(
ext = ext.substr(1);
}
if (fileType.empty()) {
- path = GetLibraryOrFrameworkPath(path);
+ path = this->GetLibraryOrFrameworkPath(path);
ext = cmSystemTools::GetFilenameLastExtension(path);
if (!ext.empty()) {
ext = ext.substr(1);
@@ -3541,13 +3519,14 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
} else {
linkDir = libItem->Value.Value;
}
- linkDir = GetLibraryOrFrameworkPath(linkDir);
- bool isFramework = cmSystemTools::IsPathToFramework(linkDir);
- linkDir = cmSystemTools::GetParentDirectory(linkDir);
- if (isFramework) {
- if (std::find(frameworkSearchPaths.begin(), frameworkSearchPaths.end(),
- linkDir) == frameworkSearchPaths.end()) {
- frameworkSearchPaths.push_back(linkDir);
+ if (cmHasSuffix(libItem->GetFeatureName(), "FRAMEWORK"_s)) {
+ auto fwItems = this->SplitFrameworkPath(linkDir, true);
+ if (fwItems && !fwItems->first.empty()) {
+ linkDir = std::move(fwItems->first);
+ if (std::find(frameworkSearchPaths.begin(), frameworkSearchPaths.end(),
+ linkDir) == frameworkSearchPaths.end()) {
+ frameworkSearchPaths.push_back(linkDir);
+ }
}
} else {
if (std::find(linkSearchPaths.begin(), linkSearchPaths.end(), linkDir) ==
@@ -3555,7 +3534,7 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
linkSearchPaths.push_back(linkDir);
}
}
- // Add target dependency
+
if (libItem->Target && !libItem->Target->IsImported()) {
for (auto const& configName : this->CurrentConfigurationTypes) {
target->AddDependTarget(configName, libItem->Target->GetName());
@@ -3729,22 +3708,27 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
if (cmSystemTools::FileIsFullPath(cleanPath)) {
cleanPath = cmSystemTools::CollapseFullPath(cleanPath);
}
- const auto libPath = GetLibraryOrFrameworkPath(cleanPath);
- if (cmSystemTools::StringEndsWith(libPath.c_str(), ".framework")) {
- const auto fwName =
- cmSystemTools::GetFilenameWithoutExtension(libPath);
- const auto fwDir = cmSystemTools::GetParentDirectory(libPath);
- if (emitted.insert(fwDir).second) {
- // This is a search path we had not added before and it isn't an
- // implicit search path, so we need it
- libPaths.Add("-F " + this->XCodeEscapePath(fwDir));
+ bool isFramework =
+ cmHasSuffix(libName.GetFeatureName(), "FRAMEWORK"_s);
+ if (isFramework) {
+ const auto fwItems =
+ this->SplitFrameworkPath(cleanPath, isFramework);
+ if (!fwItems->first.empty() &&
+ emitted.insert(fwItems->first).second) {
+ // This is a search path we had not added before and it isn't
+ // an implicit search path, so we need it
+ libPaths.Add("-F " + this->XCodeEscapePath(fwItems->first));
}
- libPaths.Add("-framework " + this->XCodeEscapePath(fwName));
+ libPaths.Add(
+ libName.GetFormattedItem(this->XCodeEscapePath(fwItems->second))
+ .Value);
} else {
- libPaths.Add(this->XCodeEscapePath(cleanPath));
+ libPaths.Add(
+ libName.GetFormattedItem(this->XCodeEscapePath(cleanPath))
+ .Value);
}
if ((!libName.Target || libName.Target->IsImported()) &&
- IsLinkPhaseLibraryExtension(libPath)) {
+ (isFramework || IsLinkPhaseLibraryExtension(cleanPath))) {
// Create file reference for embedding
auto it = this->ExternalLibRefs.find(cleanPath);
if (it == this->ExternalLibRefs.end()) {
@@ -3912,8 +3896,8 @@ void cmGlobalXCodeGenerator::AddEmbeddedFrameworks(cmXCodeObject* target)
{
static const auto dstSubfolderSpec = "10";
- // Despite the name, by default Xcode uses "Embed Frameworks" build phase for
- // both frameworks and dynamic libraries
+ // Despite the name, by default Xcode uses "Embed Frameworks" build phase
+ // for both frameworks and dynamic libraries
this->AddEmbeddedObjects(target, "Embed Frameworks",
"XCODE_EMBED_FRAMEWORKS", dstSubfolderSpec,
NoActionOnCopyByDefault);
@@ -4711,10 +4695,12 @@ std::string cmGlobalXCodeGenerator::ConvertToRelativeForMake(
std::string cmGlobalXCodeGenerator::RelativeToSource(const std::string& p)
{
- // We force conversion because Xcode breakpoints do not work unless
- // they are in a file named relative to the source tree.
- return cmSystemTools::ForceToRelativePath(
- this->CurrentRootGenerator->GetCurrentSourceDirectory(), p);
+ std::string const& rootSrc =
+ this->CurrentRootGenerator->GetCurrentSourceDirectory();
+ if (cmSystemTools::IsSubDirectory(p, rootSrc)) {
+ return cmSystemTools::ForceToRelativePath(rootSrc, p);
+ }
+ return p;
}
std::string cmGlobalXCodeGenerator::RelativeToBinary(const std::string& p)
diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h
index ff6ffe8..98cebef 100644
--- a/Source/cmGlobalXCodeGenerator.h
+++ b/Source/cmGlobalXCodeGenerator.h
@@ -330,6 +330,8 @@ private:
{
}
+ std::string GetLibraryOrFrameworkPath(const std::string& path) const;
+
std::string GetObjectsDirectory(const std::string& projName,
const std::string& configName,
const cmGeneratorTarget* t,
diff --git a/Source/cmLinkItem.h b/Source/cmLinkItem.h
index e715659..262728b 100644
--- a/Source/cmLinkItem.h
+++ b/Source/cmLinkItem.h
@@ -70,6 +70,12 @@ struct cmLinkInterfaceLibraries
// Object files listed in the interface.
std::vector<cmLinkItem> Objects;
+ // Items to be included as if directly linked by the head target.
+ std::vector<cmLinkItem> HeadInclude;
+
+ // Items to be excluded from direct linking by the head target.
+ std::vector<cmLinkItem> HeadExclude;
+
// Whether the list depends on a genex referencing the head target.
bool HadHeadSensitiveCondition = false;
diff --git a/Source/cmLinkLibrariesCommand.cxx b/Source/cmLinkLibrariesCommand.cxx
index 2b8f836..ed89e91 100644
--- a/Source/cmLinkLibrariesCommand.cxx
+++ b/Source/cmLinkLibrariesCommand.cxx
@@ -35,5 +35,7 @@ bool cmLinkLibrariesCommand(std::vector<std::string> const& args,
mf.AppendProperty("LINK_LIBRARIES", *i);
}
+ mf.CheckProperty("LINK_LIBRARIES");
+
return true;
}
diff --git a/Source/cmLinkLineComputer.cxx b/Source/cmLinkLineComputer.cxx
index 5646368..290642b 100644
--- a/Source/cmLinkLineComputer.cxx
+++ b/Source/cmLinkLineComputer.cxx
@@ -75,14 +75,8 @@ void cmLinkLineComputer::ComputeLinkLibs(
BT<std::string> linkLib;
if (item.IsPath == cmComputeLinkInformation::ItemIsPath::Yes) {
- if (item.IsObject == cmComputeLinkInformation::ItemIsObject::Yes) {
- linkLib.Value += cli.GetObjLinkFileFlag();
- } else {
- linkLib.Value += cli.GetLibLinkFileFlag();
- }
- linkLib.Value += this->ConvertToOutputFormat(
- this->ConvertToLinkReference(item.Value.Value));
- linkLib.Backtrace = item.Value.Backtrace;
+ linkLib = item.GetFormattedItem(this->ConvertToOutputFormat(
+ this->ConvertToLinkReference(item.Value.Value)));
} else {
linkLib = item.Value;
}
diff --git a/Source/cmLinkLineDeviceComputer.cxx b/Source/cmLinkLineDeviceComputer.cxx
index 43f161b..71f9f80 100644
--- a/Source/cmLinkLineDeviceComputer.cxx
+++ b/Source/cmLinkLineDeviceComputer.cxx
@@ -118,8 +118,10 @@ void cmLinkLineDeviceComputer::ComputeLinkLibraries(
// can tolerate '.so' or '.dylib' it cannot tolerate '.so.1'.
if (cmHasLiteralSuffix(item.Value.Value, ".a") ||
cmHasLiteralSuffix(item.Value.Value, ".lib")) {
- linkLib.Value += this->ConvertToOutputFormat(
- this->ConvertToLinkReference(item.Value.Value));
+ linkLib.Value = item
+ .GetFormattedItem(this->ConvertToOutputFormat(
+ this->ConvertToLinkReference(item.Value.Value)))
+ .Value;
}
} else if (item.Value == "-framework") {
// This is the first part of '-framework Name' where the framework
@@ -127,7 +129,7 @@ void cmLinkLineDeviceComputer::ComputeLinkLibraries(
skipItemAfterFramework = true;
continue;
} else if (cmLinkItemValidForDevice(item.Value.Value)) {
- linkLib.Value += item.Value.Value;
+ linkLib.Value = item.Value.Value;
}
if (emitted.insert(linkLib.Value).second) {
diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx
index 3da266d..b90af08 100644
--- a/Source/cmListFileCache.cxx
+++ b/Source/cmListFileCache.cxx
@@ -40,6 +40,7 @@ struct cmListFileParser
cmListFileLexer* Lexer;
std::string FunctionName;
long FunctionLine;
+ long FunctionLineEnd;
std::vector<cmListFileArgument> FunctionArguments;
enum
{
@@ -146,7 +147,7 @@ bool cmListFileParser::Parse()
if (this->ParseFunction(token->text, token->line)) {
this->ListFile->Functions.emplace_back(
std::move(this->FunctionName), this->FunctionLine,
- std::move(this->FunctionArguments));
+ this->FunctionLineEnd, std::move(this->FunctionArguments));
} else {
return false;
}
@@ -259,6 +260,7 @@ bool cmListFileParser::ParseFunction(const char* name, long line)
}
} else if (token->type == cmListFileLexer_Token_ParenRight) {
if (parenDepth == 0) {
+ this->FunctionLineEnd = token->line;
return true;
}
parenDepth--;
@@ -539,11 +541,11 @@ std::ostream& operator<<(std::ostream& os, BT<std::string> const& s)
return os << s.Value;
}
-std::vector<BT<std::string>> ExpandListWithBacktrace(
- std::string const& list, cmListFileBacktrace const& bt)
+std::vector<BT<std::string>> cmExpandListWithBacktrace(
+ std::string const& list, cmListFileBacktrace const& bt, bool emptyArgs)
{
std::vector<BT<std::string>> result;
- std::vector<std::string> tmp = cmExpandedList(list);
+ std::vector<std::string> tmp = cmExpandedList(list, emptyArgs);
result.reserve(tmp.size());
for (std::string& i : tmp) {
result.emplace_back(std::move(i), bt);
diff --git a/Source/cmListFileCache.h b/Source/cmListFileCache.h
index 5d45027..c3da81b 100644
--- a/Source/cmListFileCache.h
+++ b/Source/cmListFileCache.h
@@ -51,9 +51,9 @@ struct cmListFileArgument
class cmListFileFunction
{
public:
- cmListFileFunction(std::string name, long line,
+ cmListFileFunction(std::string name, long line, long lineEnd,
std::vector<cmListFileArgument> args)
- : Impl{ std::make_shared<Implementation>(std::move(name), line,
+ : Impl{ std::make_shared<Implementation>(std::move(name), line, lineEnd,
std::move(args)) }
{
}
@@ -69,6 +69,7 @@ public:
}
long Line() const noexcept { return this->Impl->Line; }
+ long LineEnd() const noexcept { return this->Impl->LineEnd; }
std::vector<cmListFileArgument> const& Arguments() const noexcept
{
@@ -78,11 +79,12 @@ public:
private:
struct Implementation
{
- Implementation(std::string name, long line,
+ Implementation(std::string name, long line, long lineEnd,
std::vector<cmListFileArgument> args)
: OriginalName{ std::move(name) }
, LowerCaseName{ cmSystemTools::LowerCase(this->OriginalName) }
, Line{ line }
+ , LineEnd{ lineEnd }
, Arguments{ std::move(args) }
{
}
@@ -90,6 +92,7 @@ private:
std::string OriginalName;
std::string LowerCaseName;
long Line = 0;
+ long LineEnd = 0;
std::vector<cmListFileArgument> Arguments;
};
@@ -230,9 +233,10 @@ public:
friend bool operator==(T const& l, BTs<T> const& r) { return l == r.Value; }
};
-std::vector<BT<std::string>> ExpandListWithBacktrace(
+std::vector<BT<std::string>> cmExpandListWithBacktrace(
std::string const& list,
- cmListFileBacktrace const& bt = cmListFileBacktrace());
+ cmListFileBacktrace const& bt = cmListFileBacktrace(),
+ bool emptyArgs = false);
struct cmListFile
{
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index 2adb232..4fa7d4f 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -1609,6 +1609,7 @@ std::vector<BT<std::string>> cmLocalGenerator::GetTargetCompileFlags(
this->AddCMP0018Flags(compileFlags, target, lang, config);
this->AddVisibilityPresetFlags(compileFlags, target, lang);
+ this->AddColorDiagnosticsFlags(compileFlags, lang);
this->AppendFlags(compileFlags, mf->GetDefineFlags());
this->AppendFlags(compileFlags,
this->GetFrameworkFlags(lang, config, target));
@@ -2354,6 +2355,29 @@ void cmLocalGenerator::AddPositionIndependentFlags(std::string& flags,
}
}
+void cmLocalGenerator::AddColorDiagnosticsFlags(std::string& flags,
+ const std::string& lang)
+{
+ cmValue diag = this->Makefile->GetDefinition("CMAKE_COLOR_DIAGNOSTICS");
+ if (diag.IsSet()) {
+ std::string colorFlagName;
+ if (diag.IsOn()) {
+ colorFlagName =
+ cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_COLOR_DIAGNOSTICS");
+ } else {
+ colorFlagName =
+ cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_COLOR_DIAGNOSTICS_OFF");
+ }
+
+ std::vector<std::string> options;
+ this->Makefile->GetDefExpandList(colorFlagName, options);
+
+ for (std::string const& option : options) {
+ this->AppendFlagEscape(flags, option);
+ }
+ }
+}
+
void cmLocalGenerator::AddConfigVariableFlags(std::string& flags,
const std::string& var,
const std::string& config)
@@ -3199,7 +3223,7 @@ void cmLocalGenerator::AppendDefines(std::set<std::string>& defines,
std::string const& defines_list) const
{
std::set<BT<std::string>> tmp;
- this->AppendDefines(tmp, ExpandListWithBacktrace(defines_list));
+ this->AppendDefines(tmp, cmExpandListWithBacktrace(defines_list));
for (BT<std::string> const& i : tmp) {
defines.emplace(i.Value);
}
@@ -3214,7 +3238,7 @@ void cmLocalGenerator::AppendDefines(std::set<BT<std::string>>& defines,
}
// Expand the list of definitions.
- this->AppendDefines(defines, ExpandListWithBacktrace(defines_list));
+ this->AppendDefines(defines, cmExpandListWithBacktrace(defines_list));
}
void cmLocalGenerator::AppendDefines(
diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h
index 115a54a..436a29e 100644
--- a/Source/cmLocalGenerator.h
+++ b/Source/cmLocalGenerator.h
@@ -159,6 +159,7 @@ public:
cmGeneratorTarget const* target,
const std::string& lang,
const std::string& config);
+ void AddColorDiagnosticsFlags(std::string& flags, const std::string& lang);
//! Append flags to a string.
virtual void AppendFlags(std::string& flags,
const std::string& newFlags) const;
diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx
index 0f8cdca..97a275a 100644
--- a/Source/cmLocalUnixMakefileGenerator3.cxx
+++ b/Source/cmLocalUnixMakefileGenerator3.cxx
@@ -133,7 +133,11 @@ void cmLocalUnixMakefileGenerator3::Generate()
// Record whether some options are enabled to avoid checking many
// times later.
if (!this->GetGlobalGenerator()->GetCMakeInstance()->GetIsInTryCompile()) {
- this->ColorMakefile = this->Makefile->IsOn("CMAKE_COLOR_MAKEFILE");
+ if (this->Makefile->IsSet("CMAKE_COLOR_MAKEFILE")) {
+ this->ColorMakefile = this->Makefile->IsOn("CMAKE_COLOR_MAKEFILE");
+ } else {
+ this->ColorMakefile = this->Makefile->IsOn("CMAKE_COLOR_DIAGNOSTICS");
+ }
}
this->SkipPreprocessedSourceRules =
this->Makefile->IsOn("CMAKE_SKIP_PREPROCESSED_SOURCE_RULES");
@@ -1508,13 +1512,12 @@ bool cmLocalUnixMakefileGenerator3::ScanDependencies(
}
// Setup relative path top directories.
- if (cmValue relativePathTopSource =
- mf->GetDefinition("CMAKE_RELATIVE_PATH_TOP_SOURCE")) {
- this->SetRelativePathTopSource(*relativePathTopSource);
- }
- if (cmValue relativePathTopBinary =
- mf->GetDefinition("CMAKE_RELATIVE_PATH_TOP_BINARY")) {
- this->SetRelativePathTopBinary(*relativePathTopBinary);
+ cmValue relativePathTopSource =
+ mf->GetDefinition("CMAKE_RELATIVE_PATH_TOP_SOURCE");
+ cmValue relativePathTopBinary =
+ mf->GetDefinition("CMAKE_RELATIVE_PATH_TOP_BINARY");
+ if (relativePathTopSource && relativePathTopBinary) {
+ this->SetRelativePathTop(*relativePathTopSource, *relativePathTopBinary);
}
} else {
cmSystemTools::Error("Directory Information file not found");
@@ -1849,7 +1852,7 @@ void cmLocalUnixMakefileGenerator3::ClearDependencies(cmMakefile* mf,
cmSystemTools::Touch(DepTimestamp.GenericString(), true);
// clear the dependencies files generated by the compiler
- std::vector<std::string> dependencies = cmExpandedList(depsFiles);
+ std::vector<std::string> dependencies = cmExpandedList(depsFiles, true);
cmDependsCompiler depsManager;
depsManager.SetVerbose(verbose);
depsManager.ClearDependencies(dependencies);
diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx
index ed7e888..f65add1 100644
--- a/Source/cmLocalVisualStudio7Generator.cxx
+++ b/Source/cmLocalVisualStudio7Generator.cxx
@@ -1294,7 +1294,9 @@ void cmLocalVisualStudio7GeneratorInternals::OutputLibraries(
for (auto const& lib : libs) {
if (lib.IsPath == cmComputeLinkInformation::ItemIsPath::Yes) {
std::string rel = lg->MaybeRelativeToCurBinDir(lib.Value.Value);
- fout << lg->ConvertToXMLOutputPath(rel) << " ";
+ rel = lg->ConvertToXMLOutputPath(rel);
+ fout << (lib.HasFeature() ? lib.GetFormattedItem(rel).Value : rel)
+ << " ";
} else if (!lib.Target ||
lib.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
fout << lib.Value.Value << " ";
diff --git a/Source/cmLocalVisualStudioGenerator.cxx b/Source/cmLocalVisualStudioGenerator.cxx
index 93f01ed..2703c7b 100644
--- a/Source/cmLocalVisualStudioGenerator.cxx
+++ b/Source/cmLocalVisualStudioGenerator.cxx
@@ -252,7 +252,7 @@ std::string cmLocalVisualStudioGenerator::FinishConstructScript(
// Store the script in a string.
std::string script;
- if (useLocal && projectType == VsProjectType::csproj) {
+ if (useLocal && projectType != VsProjectType::vcxproj) {
// This label is not provided by MSBuild for C# projects.
script += newline;
script += this->GetReportErrorLabel();
diff --git a/Source/cmMacroCommand.cxx b/Source/cmMacroCommand.cxx
index 154df63..ef12487 100644
--- a/Source/cmMacroCommand.cxx
+++ b/Source/cmMacroCommand.cxx
@@ -116,7 +116,7 @@ bool cmMacroHelperCommand::operator()(
newLFFArgs.push_back(std::move(arg));
}
cmListFileFunction newLFF{ func.OriginalName(), func.Line(),
- std::move(newLFFArgs) };
+ func.LineEnd(), std::move(newLFFArgs) };
cmExecutionStatus status(makefile);
if (!makefile.ExecuteCommand(newLFF, status) || status.GetNestedError()) {
// The error message should have already included the call stack
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 94d3be6..f0a96a8 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -291,6 +291,9 @@ void cmMakefile::PrintCommandTrace(
builder["indentation"] = "";
val["file"] = full_path;
val["line"] = static_cast<Json::Value::Int64>(lff.Line());
+ if (lff.Line() != lff.LineEnd()) {
+ val["line_end"] = static_cast<Json::Value::Int64>(lff.LineEnd());
+ }
if (deferId) {
val["defer"] = *deferId;
}
@@ -302,6 +305,8 @@ void cmMakefile::PrintCommandTrace(
val["time"] = cmSystemTools::GetTime();
val["frame"] =
static_cast<Json::Value::UInt64>(this->ExecutionStatusStack.size());
+ val["global_frame"] =
+ static_cast<Json::Value::UInt64>(this->RecursionDepth);
msg << Json::writeString(builder, val);
#endif
break;
@@ -453,6 +458,11 @@ bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff,
return result;
}
+bool cmMakefile::IsImportedTargetGlobalScope() const
+{
+ return this->CurrentImportedTargetScope == ImportedTargetScope::Global;
+}
+
class cmMakefile::IncludeScope
{
public:
@@ -1663,6 +1673,7 @@ void cmMakefile::Configure()
this->Backtrace);
cmListFileFunction project{ "project",
0,
+ 0,
{ { "Project", cmListFileArgument::Unquoted,
0 },
{ "__CMAKE_INJECTED_PROJECT_COMMAND__",
@@ -3976,6 +3987,31 @@ std::vector<std::string> cmMakefile::GetPropertyKeys() const
return this->StateSnapshot.GetDirectory().GetPropertyKeys();
}
+void cmMakefile::CheckProperty(const std::string& prop) const
+{
+ // Certain properties need checking.
+ if (prop == "LINK_LIBRARIES") {
+ if (cmValue value = this->GetProperty(prop)) {
+ // Look for <LINK_LIBRARY:> internal pattern
+ static cmsys::RegularExpression linkPattern(
+ "(^|;)(</?LINK_(LIBRARY|GROUP):[^;>]*>)(;|$)");
+ if (!linkPattern.find(value)) {
+ return;
+ }
+
+ // Report an error.
+ this->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("Property ", prop, " contains the invalid item \"",
+ linkPattern.match(2), "\". The ", prop,
+ " property may contain the generator-expression \"$<LINK_",
+ linkPattern.match(3),
+ ":...>\" which may be used to specify how the libraries are "
+ "linked."));
+ }
+ }
+}
+
cmTarget* cmMakefile::FindLocalNonAliasTarget(const std::string& name) const
{
auto i = this->Targets.find(name);
@@ -4395,12 +4431,14 @@ bool cmMakefile::SetPolicy(cmPolicies::PolicyID id,
}
// Deprecate old policies.
- if (status == cmPolicies::OLD && id <= cmPolicies::CMP0094 &&
+ if (status == cmPolicies::OLD && id <= cmPolicies::CMP0097 &&
!(this->GetCMakeInstance()->GetIsInTryCompile() &&
(
// Policies set by cmCoreTryCompile::TryCompileCode.
id == cmPolicies::CMP0065 || id == cmPolicies::CMP0083 ||
- id == cmPolicies::CMP0091))) {
+ id == cmPolicies::CMP0091)) &&
+ (!this->IsSet("CMAKE_WARN_DEPRECATED") ||
+ this->IsOn("CMAKE_WARN_DEPRECATED"))) {
this->IssueMessage(MessageType::DEPRECATION_WARNING,
cmPolicies::GetPolicyDeprecatedWarning(id));
}
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h
index f425697..6d44e79 100644
--- a/Source/cmMakefile.h
+++ b/Source/cmMakefile.h
@@ -787,6 +787,7 @@ public:
cmValue GetProperty(const std::string& prop, bool chain) const;
bool GetPropertyAsBool(const std::string& prop) const;
std::vector<std::string> GetPropertyKeys() const;
+ void CheckProperty(const std::string& prop) const;
//! Initialize a makefile from its parent
void InitializeFromParent(cmMakefile* parent);
@@ -859,6 +860,44 @@ public:
void PushLoopBlockBarrier();
void PopLoopBlockBarrier();
+ bool IsImportedTargetGlobalScope() const;
+
+ enum class ImportedTargetScope
+ {
+ Local,
+ Global,
+ };
+
+ /** Helper class to manage whether imported packages
+ * should be globally scoped based off the find package command
+ */
+ class SetGlobalTargetImportScope
+ {
+ public:
+ SetGlobalTargetImportScope(cmMakefile* mk, ImportedTargetScope const scope)
+ : Makefile(mk)
+ {
+ if (scope == ImportedTargetScope::Global &&
+ !this->Makefile->IsImportedTargetGlobalScope()) {
+ this->Makefile->CurrentImportedTargetScope = scope;
+ this->Set = true;
+ } else {
+ this->Set = false;
+ }
+ }
+ ~SetGlobalTargetImportScope()
+ {
+ if (this->Set) {
+ this->Makefile->CurrentImportedTargetScope =
+ ImportedTargetScope::Local;
+ }
+ }
+
+ private:
+ cmMakefile* Makefile;
+ bool Set;
+ };
+
/** Helper class to push and pop scopes automatically. */
class ScopePushPop
{
@@ -1123,4 +1162,5 @@ private:
std::set<std::string> WarnedCMP0074;
bool IsSourceFileTryCompile;
mutable bool SuppressSideEffects;
+ ImportedTargetScope CurrentImportedTargetScope = ImportedTargetScope::Local;
};
diff --git a/Source/cmOutputConverter.cxx b/Source/cmOutputConverter.cxx
index b143170..e62e0cd 100644
--- a/Source/cmOutputConverter.cxx
+++ b/Source/cmOutputConverter.cxx
@@ -34,6 +34,7 @@ cmOutputConverter::cmOutputConverter(cmStateSnapshot const& snapshot)
assert(this->StateSnapshot.IsValid());
this->ComputeRelativePathTopSource();
this->ComputeRelativePathTopBinary();
+ this->ComputeRelativePathTopRelation();
}
void cmOutputConverter::ComputeRelativePathTopSource()
@@ -69,6 +70,22 @@ void cmOutputConverter::ComputeRelativePathTopBinary()
this->RelativePathTopBinary = snapshot.GetDirectory().GetCurrentBinary();
}
+void cmOutputConverter::ComputeRelativePathTopRelation()
+{
+ if (cmSystemTools::ComparePath(this->RelativePathTopSource,
+ this->RelativePathTopBinary)) {
+ this->RelativePathTopRelation = TopRelation::InSource;
+ } else if (cmSystemTools::IsSubDirectory(this->RelativePathTopBinary,
+ this->RelativePathTopSource)) {
+ this->RelativePathTopRelation = TopRelation::BinInSrc;
+ } else if (cmSystemTools::IsSubDirectory(this->RelativePathTopSource,
+ this->RelativePathTopBinary)) {
+ this->RelativePathTopRelation = TopRelation::SrcInBin;
+ } else {
+ this->RelativePathTopRelation = TopRelation::Separate;
+ }
+}
+
std::string const& cmOutputConverter::GetRelativePathTopSource() const
{
return this->RelativePathTopSource;
@@ -79,27 +96,45 @@ std::string const& cmOutputConverter::GetRelativePathTopBinary() const
return this->RelativePathTopBinary;
}
-void cmOutputConverter::SetRelativePathTopSource(std::string const& top)
-{
- this->RelativePathTopSource = top;
-}
-
-void cmOutputConverter::SetRelativePathTopBinary(std::string const& top)
+void cmOutputConverter::SetRelativePathTop(std::string const& topSource,
+ std::string const& topBinary)
{
- this->RelativePathTopBinary = top;
+ this->RelativePathTopSource = topSource;
+ this->RelativePathTopBinary = topBinary;
+ this->ComputeRelativePathTopRelation();
}
std::string cmOutputConverter::MaybeRelativeTo(
std::string const& local_path, std::string const& remote_path) const
{
- bool bothInBinary =
- PathEqOrSubDir(local_path, this->RelativePathTopBinary) &&
+ bool localInBinary = PathEqOrSubDir(local_path, this->RelativePathTopBinary);
+ bool remoteInBinary =
PathEqOrSubDir(remote_path, this->RelativePathTopBinary);
- bool bothInSource =
- PathEqOrSubDir(local_path, this->RelativePathTopSource) &&
+ bool localInSource = PathEqOrSubDir(local_path, this->RelativePathTopSource);
+ bool remoteInSource =
PathEqOrSubDir(remote_path, this->RelativePathTopSource);
+ switch (this->RelativePathTopRelation) {
+ case TopRelation::Separate:
+ // Checks are independent.
+ break;
+ case TopRelation::BinInSrc:
+ localInSource = localInSource && !localInBinary;
+ remoteInSource = remoteInSource && !remoteInBinary;
+ break;
+ case TopRelation::SrcInBin:
+ localInBinary = localInBinary && !localInSource;
+ remoteInBinary = remoteInBinary && !remoteInSource;
+ break;
+ case TopRelation::InSource:
+ // Checks are identical.
+ break;
+ };
+
+ bool const bothInBinary = localInBinary && remoteInBinary;
+ bool const bothInSource = localInSource && remoteInSource;
+
if (bothInBinary || bothInSource) {
return cmSystemTools::ForceToRelativePath(local_path, remote_path);
}
diff --git a/Source/cmOutputConverter.h b/Source/cmOutputConverter.h
index 335442d..d19bccc 100644
--- a/Source/cmOutputConverter.h
+++ b/Source/cmOutputConverter.h
@@ -29,8 +29,8 @@ public:
std::string const& GetRelativePathTopSource() const;
std::string const& GetRelativePathTopBinary() const;
- void SetRelativePathTopSource(std::string const& top);
- void SetRelativePathTopBinary(std::string const& top);
+ void SetRelativePathTop(std::string const& topSource,
+ std::string const& topBinary);
enum OutputFormat
{
@@ -147,8 +147,17 @@ private:
// safely by the build tools.
std::string RelativePathTopSource;
std::string RelativePathTopBinary;
+ enum class TopRelation
+ {
+ Separate,
+ BinInSrc,
+ SrcInBin,
+ InSource,
+ };
+ TopRelation RelativePathTopRelation = TopRelation::Separate;
void ComputeRelativePathTopSource();
void ComputeRelativePathTopBinary();
+ void ComputeRelativePathTopRelation();
std::string MaybeRelativeTo(std::string const& local_path,
std::string const& remote_path) const;
};
diff --git a/Source/cmPlaceholderExpander.cxx b/Source/cmPlaceholderExpander.cxx
new file mode 100644
index 0000000..118017e
--- /dev/null
+++ b/Source/cmPlaceholderExpander.cxx
@@ -0,0 +1,54 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmPlaceholderExpander.h"
+
+#include <cctype>
+
+std::string& cmPlaceholderExpander::ExpandVariables(std::string& s)
+{
+ std::string::size_type start = s.find('<');
+ // no variables to expand
+ if (start == std::string::npos) {
+ return s;
+ }
+ std::string::size_type pos = 0;
+ std::string expandedInput;
+ while (start != std::string::npos && start < s.size() - 2) {
+ std::string::size_type end = s.find('>', start);
+ // if we find a < with no > we are done
+ if (end == std::string::npos) {
+ s = expandedInput;
+ return s;
+ }
+ char c = s[start + 1];
+ // if the next char after the < is not A-Za-z then
+ // skip it and try to find the next < in the string
+ if (!isalpha(c)) {
+ start = s.find('<', start + 1);
+ } else {
+ // extract the var
+ std::string var = s.substr(start + 1, end - start - 1);
+ std::string replace = this->ExpandVariable(var);
+ expandedInput += s.substr(pos, start - pos);
+
+ // Prevent consecutive whitespace in the output if the rule variable
+ // expands to an empty string.
+ bool consecutive = replace.empty() && start > 0 && s[start - 1] == ' ' &&
+ end + 1 < s.size() && s[end + 1] == ' ';
+ if (consecutive) {
+ expandedInput.pop_back();
+ }
+
+ expandedInput += replace;
+
+ // move to next one
+ start = s.find('<', start + var.size() + 2);
+ pos = end + 1;
+ }
+ }
+ // add the rest of the input
+ expandedInput += s.substr(pos, s.size() - pos);
+ s = expandedInput;
+
+ return s;
+}
diff --git a/Source/cmPlaceholderExpander.h b/Source/cmPlaceholderExpander.h
new file mode 100644
index 0000000..24225cc
--- /dev/null
+++ b/Source/cmPlaceholderExpander.h
@@ -0,0 +1,19 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <string>
+
+class cmPlaceholderExpander
+{
+public:
+ virtual ~cmPlaceholderExpander() = default;
+
+ std::string& ExpandVariables(std::string& string);
+
+protected:
+ virtual std::string ExpandVariable(std::string const& variable) = 0;
+};
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index 99e2eb6..7d4012d 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -388,7 +388,9 @@ class cmMakefile;
22, 0, cmPolicies::WARN) \
SELECT(POLICY, CMP0129, \
"Compiler id for MCST LCC compilers is now LCC, not GNU.", 3, 23, 0, \
- cmPolicies::WARN)
+ cmPolicies::WARN) \
+ SELECT(POLICY, CMP0130, "while() diagnoses condition evaluation errors.", \
+ 3, 24, 0, cmPolicies::WARN)
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
#define CM_FOR_EACH_POLICY_ID(POLICY) \
diff --git a/Source/cmRulePlaceholderExpander.cxx b/Source/cmRulePlaceholderExpander.cxx
index 4cee09d..b63d11c 100644
--- a/Source/cmRulePlaceholderExpander.cxx
+++ b/Source/cmRulePlaceholderExpander.cxx
@@ -2,7 +2,6 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmRulePlaceholderExpander.h"
-#include <cctype>
#include <utility>
#include "cmOutputConverter.h"
@@ -20,195 +19,194 @@ cmRulePlaceholderExpander::cmRulePlaceholderExpander(
{
}
-std::string cmRulePlaceholderExpander::ExpandRuleVariable(
- cmOutputConverter* outputConverter, std::string const& variable,
- const RuleVariables& replaceValues)
+std::string cmRulePlaceholderExpander::ExpandVariable(
+ std::string const& variable)
{
- if (replaceValues.LinkFlags) {
+ if (this->ReplaceValues->LinkFlags) {
if (variable == "LINK_FLAGS") {
- return replaceValues.LinkFlags;
+ return this->ReplaceValues->LinkFlags;
}
}
- if (replaceValues.Manifests) {
+ if (this->ReplaceValues->Manifests) {
if (variable == "MANIFESTS") {
- return replaceValues.Manifests;
+ return this->ReplaceValues->Manifests;
}
}
- if (replaceValues.Flags) {
+ if (this->ReplaceValues->Flags) {
if (variable == "FLAGS") {
- return replaceValues.Flags;
+ return this->ReplaceValues->Flags;
}
}
- if (replaceValues.Source) {
+ if (this->ReplaceValues->Source) {
if (variable == "SOURCE") {
- return replaceValues.Source;
+ return this->ReplaceValues->Source;
}
}
- if (replaceValues.DynDepFile) {
+ if (this->ReplaceValues->DynDepFile) {
if (variable == "DYNDEP_FILE") {
- return replaceValues.DynDepFile;
+ return this->ReplaceValues->DynDepFile;
}
}
- if (replaceValues.PreprocessedSource) {
+ if (this->ReplaceValues->PreprocessedSource) {
if (variable == "PREPROCESSED_SOURCE") {
- return replaceValues.PreprocessedSource;
+ return this->ReplaceValues->PreprocessedSource;
}
}
- if (replaceValues.AssemblySource) {
+ if (this->ReplaceValues->AssemblySource) {
if (variable == "ASSEMBLY_SOURCE") {
- return replaceValues.AssemblySource;
+ return this->ReplaceValues->AssemblySource;
}
}
- if (replaceValues.Object) {
+ if (this->ReplaceValues->Object) {
if (variable == "OBJECT") {
- return replaceValues.Object;
+ return this->ReplaceValues->Object;
}
}
- if (replaceValues.ObjectDir) {
+ if (this->ReplaceValues->ObjectDir) {
if (variable == "OBJECT_DIR") {
- return replaceValues.ObjectDir;
+ return this->ReplaceValues->ObjectDir;
}
}
- if (replaceValues.ObjectFileDir) {
+ if (this->ReplaceValues->ObjectFileDir) {
if (variable == "OBJECT_FILE_DIR") {
- return replaceValues.ObjectFileDir;
+ return this->ReplaceValues->ObjectFileDir;
}
}
- if (replaceValues.Objects) {
+ if (this->ReplaceValues->Objects) {
if (variable == "OBJECTS") {
- return replaceValues.Objects;
+ return this->ReplaceValues->Objects;
}
}
- if (replaceValues.ObjectsQuoted) {
+ if (this->ReplaceValues->ObjectsQuoted) {
if (variable == "OBJECTS_QUOTED") {
- return replaceValues.ObjectsQuoted;
+ return this->ReplaceValues->ObjectsQuoted;
}
}
- if (replaceValues.CudaCompileMode) {
+ if (this->ReplaceValues->CudaCompileMode) {
if (variable == "CUDA_COMPILE_MODE") {
- return replaceValues.CudaCompileMode;
+ return this->ReplaceValues->CudaCompileMode;
}
}
- if (replaceValues.AIXExports) {
+ if (this->ReplaceValues->AIXExports) {
if (variable == "AIX_EXPORTS") {
- return replaceValues.AIXExports;
+ return this->ReplaceValues->AIXExports;
}
}
- if (replaceValues.ISPCHeader) {
+ if (this->ReplaceValues->ISPCHeader) {
if (variable == "ISPC_HEADER") {
- return replaceValues.ISPCHeader;
+ return this->ReplaceValues->ISPCHeader;
}
}
- if (replaceValues.Defines && variable == "DEFINES") {
- return replaceValues.Defines;
+ if (this->ReplaceValues->Defines && variable == "DEFINES") {
+ return this->ReplaceValues->Defines;
}
- if (replaceValues.Includes && variable == "INCLUDES") {
- return replaceValues.Includes;
+ if (this->ReplaceValues->Includes && variable == "INCLUDES") {
+ return this->ReplaceValues->Includes;
}
- if (replaceValues.SwiftLibraryName) {
+ if (this->ReplaceValues->SwiftLibraryName) {
if (variable == "SWIFT_LIBRARY_NAME") {
- return replaceValues.SwiftLibraryName;
+ return this->ReplaceValues->SwiftLibraryName;
}
}
- if (replaceValues.SwiftModule) {
+ if (this->ReplaceValues->SwiftModule) {
if (variable == "SWIFT_MODULE") {
- return replaceValues.SwiftModule;
+ return this->ReplaceValues->SwiftModule;
}
}
- if (replaceValues.SwiftModuleName) {
+ if (this->ReplaceValues->SwiftModuleName) {
if (variable == "SWIFT_MODULE_NAME") {
- return replaceValues.SwiftModuleName;
+ return this->ReplaceValues->SwiftModuleName;
}
}
- if (replaceValues.SwiftOutputFileMap) {
+ if (this->ReplaceValues->SwiftOutputFileMap) {
if (variable == "SWIFT_OUTPUT_FILE_MAP") {
- return replaceValues.SwiftOutputFileMap;
+ return this->ReplaceValues->SwiftOutputFileMap;
}
}
- if (replaceValues.SwiftSources) {
+ if (this->ReplaceValues->SwiftSources) {
if (variable == "SWIFT_SOURCES") {
- return replaceValues.SwiftSources;
+ return this->ReplaceValues->SwiftSources;
}
}
- if (replaceValues.TargetPDB) {
+ if (this->ReplaceValues->TargetPDB) {
if (variable == "TARGET_PDB") {
- return replaceValues.TargetPDB;
+ return this->ReplaceValues->TargetPDB;
}
}
- if (replaceValues.TargetCompilePDB) {
+ if (this->ReplaceValues->TargetCompilePDB) {
if (variable == "TARGET_COMPILE_PDB") {
- return replaceValues.TargetCompilePDB;
+ return this->ReplaceValues->TargetCompilePDB;
}
}
- if (replaceValues.DependencyFile) {
+ if (this->ReplaceValues->DependencyFile) {
if (variable == "DEP_FILE") {
- return replaceValues.DependencyFile;
+ return this->ReplaceValues->DependencyFile;
}
}
- if (replaceValues.DependencyTarget) {
+ if (this->ReplaceValues->DependencyTarget) {
if (variable == "DEP_TARGET") {
- return replaceValues.DependencyTarget;
+ return this->ReplaceValues->DependencyTarget;
}
}
- if (replaceValues.Fatbinary) {
+ if (this->ReplaceValues->Fatbinary) {
if (variable == "FATBINARY") {
- return replaceValues.Fatbinary;
+ return this->ReplaceValues->Fatbinary;
}
}
- if (replaceValues.RegisterFile) {
+ if (this->ReplaceValues->RegisterFile) {
if (variable == "REGISTER_FILE") {
- return replaceValues.RegisterFile;
+ return this->ReplaceValues->RegisterFile;
}
}
- if (replaceValues.Target) {
+ if (this->ReplaceValues->Target) {
if (variable == "TARGET_QUOTED") {
- std::string targetQuoted = replaceValues.Target;
+ std::string targetQuoted = this->ReplaceValues->Target;
if (!targetQuoted.empty() && targetQuoted.front() != '\"') {
targetQuoted = '\"';
- targetQuoted += replaceValues.Target;
+ targetQuoted += this->ReplaceValues->Target;
targetQuoted += '\"';
}
return targetQuoted;
}
if (variable == "TARGET_UNQUOTED") {
- std::string unquoted = replaceValues.Target;
+ std::string unquoted = this->ReplaceValues->Target;
std::string::size_type sz = unquoted.size();
if (sz > 2 && unquoted.front() == '\"' && unquoted.back() == '\"') {
unquoted = unquoted.substr(1, sz - 2);
}
return unquoted;
}
- if (replaceValues.LanguageCompileFlags) {
+ if (this->ReplaceValues->LanguageCompileFlags) {
if (variable == "LANGUAGE_COMPILE_FLAGS") {
- return replaceValues.LanguageCompileFlags;
+ return this->ReplaceValues->LanguageCompileFlags;
}
}
- if (replaceValues.Target) {
+ if (this->ReplaceValues->Target) {
if (variable == "TARGET") {
- return replaceValues.Target;
+ return this->ReplaceValues->Target;
}
}
if (variable == "TARGET_IMPLIB") {
return this->TargetImpLib;
}
if (variable == "TARGET_VERSION_MAJOR") {
- if (replaceValues.TargetVersionMajor) {
- return replaceValues.TargetVersionMajor;
+ if (this->ReplaceValues->TargetVersionMajor) {
+ return this->ReplaceValues->TargetVersionMajor;
}
return "0";
}
if (variable == "TARGET_VERSION_MINOR") {
- if (replaceValues.TargetVersionMinor) {
- return replaceValues.TargetVersionMinor;
+ if (this->ReplaceValues->TargetVersionMinor) {
+ return this->ReplaceValues->TargetVersionMinor;
}
return "0";
}
- if (replaceValues.Target) {
+ if (this->ReplaceValues->Target) {
if (variable == "TARGET_BASE") {
// Strip the last extension off the target name.
- std::string targetBase = replaceValues.Target;
+ std::string targetBase = this->ReplaceValues->Target;
std::string::size_type pos = targetBase.rfind('.');
if (pos != std::string::npos) {
return targetBase.substr(0, pos);
@@ -220,54 +218,54 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable(
if (variable == "TARGET_SONAME" || variable == "SONAME_FLAG" ||
variable == "TARGET_INSTALLNAME_DIR") {
// All these variables depend on TargetSOName
- if (replaceValues.TargetSOName) {
+ if (this->ReplaceValues->TargetSOName) {
if (variable == "TARGET_SONAME") {
- return replaceValues.TargetSOName;
+ return this->ReplaceValues->TargetSOName;
}
- if (variable == "SONAME_FLAG" && replaceValues.SONameFlag) {
- return replaceValues.SONameFlag;
+ if (variable == "SONAME_FLAG" && this->ReplaceValues->SONameFlag) {
+ return this->ReplaceValues->SONameFlag;
}
- if (replaceValues.TargetInstallNameDir &&
+ if (this->ReplaceValues->TargetInstallNameDir &&
variable == "TARGET_INSTALLNAME_DIR") {
- return replaceValues.TargetInstallNameDir;
+ return this->ReplaceValues->TargetInstallNameDir;
}
}
return "";
}
- if (replaceValues.LinkLibraries) {
+ if (this->ReplaceValues->LinkLibraries) {
if (variable == "LINK_LIBRARIES") {
- return replaceValues.LinkLibraries;
+ return this->ReplaceValues->LinkLibraries;
}
}
- if (replaceValues.Language) {
+ if (this->ReplaceValues->Language) {
if (variable == "LANGUAGE") {
- return replaceValues.Language;
+ return this->ReplaceValues->Language;
}
}
- if (replaceValues.CMTargetName) {
+ if (this->ReplaceValues->CMTargetName) {
if (variable == "TARGET_NAME") {
- return replaceValues.CMTargetName;
+ return this->ReplaceValues->CMTargetName;
}
}
- if (replaceValues.CMTargetType) {
+ if (this->ReplaceValues->CMTargetType) {
if (variable == "TARGET_TYPE") {
- return replaceValues.CMTargetType;
+ return this->ReplaceValues->CMTargetType;
}
}
- if (replaceValues.Output) {
+ if (this->ReplaceValues->Output) {
if (variable == "OUTPUT") {
- return replaceValues.Output;
+ return this->ReplaceValues->Output;
}
}
if (variable == "CMAKE_COMMAND") {
- return outputConverter->ConvertToOutputFormat(
+ return this->OutputConverter->ConvertToOutputFormat(
cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
}
auto compIt = this->Compilers.find(variable);
if (compIt != this->Compilers.end()) {
- std::string ret = outputConverter->ConvertToOutputForExisting(
+ std::string ret = this->OutputConverter->ConvertToOutputForExisting(
this->VariableMappings["CMAKE_" + compIt->second + "_COMPILER"]);
std::string const& compilerArg1 =
this->VariableMappings["CMAKE_" + compIt->second + "_COMPILER_ARG1"];
@@ -286,11 +284,12 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable(
this->VariableMappings["CMAKE_" + compIt->second +
"_COMPILE_OPTIONS_SYSROOT"];
- if (compIt->second == replaceValues.Language && replaceValues.Launcher) {
+ if (compIt->second == this->ReplaceValues->Language &&
+ 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.
- ret = cmStrCat(replaceValues.Launcher, " ", ret);
+ ret = cmStrCat(this->ReplaceValues->Launcher, " ", ret);
}
// if there are required arguments to the compiler add it
@@ -308,13 +307,14 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable(
!compilerOptionExternalToolchain.empty()) {
ret += " ";
ret += compilerOptionExternalToolchain;
- ret += outputConverter->EscapeForShell(compilerExternalToolchain, true);
+ ret +=
+ this->OutputConverter->EscapeForShell(compilerExternalToolchain, true);
}
std::string sysroot;
// Some platforms may use separate sysroots for compiling and linking.
// If we detect link flags, then we pass the link sysroot instead.
// FIXME: Use a more robust way to detect link line expansion.
- if (replaceValues.LinkFlags) {
+ if (this->ReplaceValues->LinkFlags) {
sysroot = this->LinkerSysroot;
} else {
sysroot = this->CompilerSysroot;
@@ -322,7 +322,7 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable(
if (!sysroot.empty() && !compilerOptionSysroot.empty()) {
ret += " ";
ret += compilerOptionSysroot;
- ret += outputConverter->EscapeForShell(sysroot, true);
+ ret += this->OutputConverter->EscapeForShell(sysroot, true);
}
return ret;
}
@@ -331,13 +331,13 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable(
if (mapIt != this->VariableMappings.end()) {
if (variable.find("_FLAG") == std::string::npos) {
std::string ret =
- outputConverter->ConvertToOutputForExisting(mapIt->second);
+ this->OutputConverter->ConvertToOutputForExisting(mapIt->second);
- if (replaceValues.Launcher && variable == "CMAKE_LINKER") {
+ 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(replaceValues.Launcher, " ", ret);
+ ret = cmStrCat(this->ReplaceValues->Launcher, " ", ret);
}
return ret;
@@ -351,47 +351,8 @@ void cmRulePlaceholderExpander::ExpandRuleVariables(
cmOutputConverter* outputConverter, std::string& s,
const RuleVariables& replaceValues)
{
- std::string::size_type start = s.find('<');
- // no variables to expand
- if (start == std::string::npos) {
- return;
- }
- std::string::size_type pos = 0;
- std::string expandedInput;
- while (start != std::string::npos && start < s.size() - 2) {
- std::string::size_type end = s.find('>', start);
- // if we find a < with no > we are done
- if (end == std::string::npos) {
- return;
- }
- char c = s[start + 1];
- // if the next char after the < is not A-Za-z then
- // skip it and try to find the next < in the string
- if (!isalpha(c)) {
- start = s.find('<', start + 1);
- } else {
- // extract the var
- std::string var = s.substr(start + 1, end - start - 1);
- std::string replace =
- this->ExpandRuleVariable(outputConverter, var, replaceValues);
- expandedInput += s.substr(pos, start - pos);
-
- // Prevent consecutive whitespace in the output if the rule variable
- // expands to an empty string.
- bool consecutive = replace.empty() && start > 0 && s[start - 1] == ' ' &&
- end + 1 < s.size() && s[end + 1] == ' ';
- if (consecutive) {
- expandedInput.pop_back();
- }
+ this->OutputConverter = outputConverter;
+ this->ReplaceValues = &replaceValues;
- expandedInput += replace;
-
- // move to next one
- start = s.find('<', start + var.size() + 2);
- pos = end + 1;
- }
- }
- // add the rest of the input
- expandedInput += s.substr(pos, s.size() - pos);
- s = expandedInput;
+ this->ExpandVariables(s);
}
diff --git a/Source/cmRulePlaceholderExpander.h b/Source/cmRulePlaceholderExpander.h
index 852954f..23ec405 100644
--- a/Source/cmRulePlaceholderExpander.h
+++ b/Source/cmRulePlaceholderExpander.h
@@ -8,9 +8,11 @@
#include <map>
#include <string>
+#include "cmPlaceholderExpander.h"
+
class cmOutputConverter;
-class cmRulePlaceholderExpander
+class cmRulePlaceholderExpander : public cmPlaceholderExpander
{
public:
cmRulePlaceholderExpander(
@@ -76,16 +78,16 @@ public:
std::string& string,
const RuleVariables& replaceValues);
- // Expand rule variables in a single string
- std::string ExpandRuleVariable(cmOutputConverter* outputConverter,
- std::string const& variable,
- const RuleVariables& replaceValues);
-
private:
+ std::string ExpandVariable(std::string const& variable) override;
+
std::string TargetImpLib;
std::map<std::string, std::string> Compilers;
std::map<std::string, std::string> VariableMappings;
std::string CompilerSysroot;
std::string LinkerSysroot;
+
+ cmOutputConverter* OutputConverter = nullptr;
+ RuleVariables const* ReplaceValues = nullptr;
};
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index effb837..a5dfa4c 100644
--- a/Source/cmSystemTools.cxx
+++ b/Source/cmSystemTools.cxx
@@ -3314,12 +3314,22 @@ cmsys::Status cmSystemTools::CreateSymlink(std::string const& origName,
uv_fs_t req;
int flags = 0;
#if defined(_WIN32)
- if (cmsys::SystemTools::FileIsDirectory(origName)) {
- flags |= UV_FS_SYMLINK_DIR;
+ bool const isDir = cmsys::SystemTools::FileIsDirectory(origName);
+ if (isDir) {
+ flags |= UV_FS_SYMLINK_JUNCTION;
}
#endif
int err = uv_fs_symlink(nullptr, &req, origName.c_str(), newName.c_str(),
flags, nullptr);
+#if defined(_WIN32)
+ if (err && uv_fs_get_system_error(&req) == ERROR_NOT_SUPPORTED && isDir) {
+ // Try fallback to symlink for network (requires additional permissions).
+ flags ^= UV_FS_SYMLINK_JUNCTION | UV_FS_SYMLINK_DIR;
+ err = uv_fs_symlink(nullptr, &req, origName.c_str(), newName.c_str(),
+ flags, nullptr);
+ }
+#endif
+
cmsys::Status status;
if (err) {
#if defined(_WIN32)
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index e1a9667..4ca1b9b 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -203,6 +203,8 @@ public:
std::vector<BT<std::string>> LinkDirectoriesEntries;
std::vector<BT<std::string>> LinkImplementationPropertyEntries;
std::vector<BT<std::string>> LinkInterfacePropertyEntries;
+ std::vector<BT<std::string>> LinkInterfaceDirectPropertyEntries;
+ std::vector<BT<std::string>> LinkInterfaceDirectExcludePropertyEntries;
std::vector<BT<std::string>> HeaderSetsEntries;
std::vector<BT<std::string>> InterfaceHeaderSetsEntries;
std::vector<std::pair<cmTarget::TLLSignature, cmListFileContext>>
@@ -1142,6 +1144,16 @@ cmBTStringRange cmTarget::GetLinkInterfaceEntries() const
return cmMakeRange(this->impl->LinkInterfacePropertyEntries);
}
+cmBTStringRange cmTarget::GetLinkInterfaceDirectEntries() const
+{
+ return cmMakeRange(this->impl->LinkInterfaceDirectPropertyEntries);
+}
+
+cmBTStringRange cmTarget::GetLinkInterfaceDirectExcludeEntries() const
+{
+ return cmMakeRange(this->impl->LinkInterfaceDirectExcludePropertyEntries);
+}
+
cmBTStringRange cmTarget::GetHeaderSetsEntries() const
{
return cmMakeRange(this->impl->HeaderSetsEntries);
@@ -1186,6 +1198,8 @@ MAKE_PROP(HEADER_SET);
MAKE_PROP(HEADER_SETS);
MAKE_PROP(INTERFACE_HEADER_SETS);
MAKE_PROP(INTERFACE_LINK_LIBRARIES);
+MAKE_PROP(INTERFACE_LINK_LIBRARIES_DIRECT);
+MAKE_PROP(INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE);
#undef MAKE_PROP
}
@@ -1317,6 +1331,19 @@ void cmTarget::StoreProperty(const std::string& prop, ValueType value)
cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
this->impl->LinkInterfacePropertyEntries.emplace_back(value, lfbt);
}
+ } else if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT) {
+ this->impl->LinkInterfaceDirectPropertyEntries.clear();
+ if (value) {
+ cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
+ this->impl->LinkInterfaceDirectPropertyEntries.emplace_back(value, lfbt);
+ }
+ } else if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE) {
+ this->impl->LinkInterfaceDirectExcludePropertyEntries.clear();
+ if (value) {
+ cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
+ this->impl->LinkInterfaceDirectExcludePropertyEntries.emplace_back(value,
+ lfbt);
+ }
} else if (prop == propSOURCES) {
this->impl->SourceEntries.clear();
if (value) {
@@ -1575,6 +1602,17 @@ void cmTarget::AppendProperty(const std::string& prop,
cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
this->impl->LinkInterfacePropertyEntries.emplace_back(value, lfbt);
}
+ } else if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT) {
+ if (!value.empty()) {
+ cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
+ this->impl->LinkInterfaceDirectPropertyEntries.emplace_back(value, lfbt);
+ }
+ } else if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE) {
+ if (!value.empty()) {
+ cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
+ this->impl->LinkInterfaceDirectExcludePropertyEntries.emplace_back(value,
+ lfbt);
+ }
} else if (prop == "SOURCES") {
cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
this->impl->SourceEntries.emplace_back(value, lfbt);
@@ -1745,69 +1783,94 @@ void cmTarget::InsertPrecompileHeader(BT<std::string> const& entry)
this->impl->PrecompileHeadersEntries.push_back(entry);
}
-static void cmTargetCheckLINK_INTERFACE_LIBRARIES(const std::string& prop,
- const std::string& value,
- cmMakefile* context,
- bool imported)
+namespace {
+void CheckLinkLibraryPattern(const std::string& property,
+ const std::string& value, cmMakefile* context)
{
- // Look for link-type keywords in the value.
- static cmsys::RegularExpression keys("(^|;)(debug|optimized|general)(;|$)");
- if (!keys.find(value)) {
+ // Look for <LINK_LIBRARY:> and </LINK_LIBRARY:> internal tags
+ static cmsys::RegularExpression linkPattern(
+ "(^|;)(</?LINK_(LIBRARY|GROUP):[^;>]*>)(;|$)");
+ if (!linkPattern.find(value)) {
return;
}
+ // Report an error.
+ context->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat(
+ "Property ", property, " contains the invalid item \"",
+ linkPattern.match(2), "\". The ", property,
+ " property may contain the generator-expression \"$<LINK_",
+ linkPattern.match(3),
+ ":...>\" which may be used to specify how the libraries are linked."));
+}
+
+void CheckLINK_INTERFACE_LIBRARIES(const std::string& prop,
+ const std::string& value,
+ cmMakefile* context, bool imported)
+{
// Support imported and non-imported versions of the property.
const char* base = (imported ? "IMPORTED_LINK_INTERFACE_LIBRARIES"
: "LINK_INTERFACE_LIBRARIES");
- // Report an error.
- std::ostringstream e;
- e << "Property " << prop << " may not contain link-type keyword \""
- << keys.match(2) << "\". "
- << "The " << base << " property has a per-configuration "
- << "version called " << base << "_<CONFIG> which may be "
- << "used to specify per-configuration rules.";
- if (!imported) {
- e << " "
- << "Alternatively, an IMPORTED library may be created, configured "
- << "with a per-configuration location, and then named in the "
- << "property value. "
- << "See the add_library command's IMPORTED mode for details."
- << "\n"
- << "If you have a list of libraries that already contains the "
- << "keyword, use the target_link_libraries command with its "
- << "LINK_INTERFACE_LIBRARIES mode to set the property. "
- << "The command automatically recognizes link-type keywords and sets "
- << "the LINK_INTERFACE_LIBRARIES and LINK_INTERFACE_LIBRARIES_DEBUG "
- << "properties accordingly.";
- }
- context->IssueMessage(MessageType::FATAL_ERROR, e.str());
-}
-
-static void cmTargetCheckINTERFACE_LINK_LIBRARIES(const std::string& value,
- cmMakefile* context)
-{
// Look for link-type keywords in the value.
static cmsys::RegularExpression keys("(^|;)(debug|optimized|general)(;|$)");
- if (!keys.find(value)) {
- return;
+ if (keys.find(value)) {
+ // Report an error.
+ std::ostringstream e;
+ e << "Property " << prop << " may not contain link-type keyword \""
+ << keys.match(2) << "\". "
+ << "The " << base << " property has a per-configuration "
+ << "version called " << base << "_<CONFIG> which may be "
+ << "used to specify per-configuration rules.";
+ if (!imported) {
+ e << " "
+ << "Alternatively, an IMPORTED library may be created, configured "
+ << "with a per-configuration location, and then named in the "
+ << "property value. "
+ << "See the add_library command's IMPORTED mode for details."
+ << "\n"
+ << "If you have a list of libraries that already contains the "
+ << "keyword, use the target_link_libraries command with its "
+ << "LINK_INTERFACE_LIBRARIES mode to set the property. "
+ << "The command automatically recognizes link-type keywords and sets "
+ << "the LINK_INTERFACE_LIBRARIES and LINK_INTERFACE_LIBRARIES_DEBUG "
+ << "properties accordingly.";
+ }
+ context->IssueMessage(MessageType::FATAL_ERROR, e.str());
}
- // Report an error.
- std::ostringstream e;
+ CheckLinkLibraryPattern(base, value, context);
+}
+
+void CheckLINK_LIBRARIES(const std::string& value, cmMakefile* context)
+{
+ CheckLinkLibraryPattern("LINK_LIBRARIES", value, context);
+}
+
+void CheckINTERFACE_LINK_LIBRARIES(const std::string& value,
+ cmMakefile* context)
+{
+ // Look for link-type keywords in the value.
+ static cmsys::RegularExpression keys("(^|;)(debug|optimized|general)(;|$)");
+ if (keys.find(value)) {
+ // Report an error.
+ std::ostringstream e;
+
+ e << "Property INTERFACE_LINK_LIBRARIES may not contain link-type "
+ "keyword \""
+ << keys.match(2)
+ << "\". The INTERFACE_LINK_LIBRARIES "
+ "property may contain configuration-sensitive generator-expressions "
+ "which may be used to specify per-configuration rules.";
- e << "Property INTERFACE_LINK_LIBRARIES may not contain link-type "
- "keyword \""
- << keys.match(2)
- << "\". The INTERFACE_LINK_LIBRARIES "
- "property may contain configuration-sensitive generator-expressions "
- "which may be used to specify per-configuration rules.";
+ context->IssueMessage(MessageType::FATAL_ERROR, e.str());
+ }
- context->IssueMessage(MessageType::FATAL_ERROR, e.str());
+ CheckLinkLibraryPattern("INTERFACE_LINK_LIBRARIES", value, context);
}
-static void cmTargetCheckIMPORTED_GLOBAL(const cmTarget* target,
- cmMakefile* context)
+void CheckIMPORTED_GLOBAL(const cmTarget* target, cmMakefile* context)
{
const auto& targets = context->GetOwnedImportedTargets();
auto it =
@@ -1823,6 +1886,7 @@ static void cmTargetCheckIMPORTED_GLOBAL(const cmTarget* target,
context->IssueMessage(MessageType::FATAL_ERROR, e.str());
}
}
+}
void cmTarget::CheckProperty(const std::string& prop,
cmMakefile* context) const
@@ -1830,22 +1894,23 @@ void cmTarget::CheckProperty(const std::string& prop,
// Certain properties need checking.
if (cmHasLiteralPrefix(prop, "LINK_INTERFACE_LIBRARIES")) {
if (cmValue value = this->GetProperty(prop)) {
- cmTargetCheckLINK_INTERFACE_LIBRARIES(prop, *value, context, false);
+ CheckLINK_INTERFACE_LIBRARIES(prop, *value, context, false);
}
- }
- if (cmHasLiteralPrefix(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES")) {
+ } else if (cmHasLiteralPrefix(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES")) {
if (cmValue value = this->GetProperty(prop)) {
- cmTargetCheckLINK_INTERFACE_LIBRARIES(prop, *value, context, true);
+ CheckLINK_INTERFACE_LIBRARIES(prop, *value, context, true);
}
- }
- if (prop == "INTERFACE_LINK_LIBRARIES") {
+ } else if (prop == "LINK_LIBRARIES") {
if (cmValue value = this->GetProperty(prop)) {
- cmTargetCheckINTERFACE_LINK_LIBRARIES(*value, context);
+ CheckLINK_LIBRARIES(*value, context);
}
- }
- if (prop == "IMPORTED_GLOBAL") {
+ } else if (prop == "INTERFACE_LINK_LIBRARIES") {
+ if (cmValue value = this->GetProperty(prop)) {
+ CheckINTERFACE_LINK_LIBRARIES(*value, context);
+ }
+ } else if (prop == "IMPORTED_GLOBAL") {
if (this->IsImported()) {
- cmTargetCheckIMPORTED_GLOBAL(this, context);
+ CheckIMPORTED_GLOBAL(this, context);
}
}
}
@@ -1885,6 +1950,8 @@ cmValue cmTarget::GetProperty(const std::string& prop) const
propHEADER_SETS,
propINTERFACE_HEADER_SETS,
propINTERFACE_LINK_LIBRARIES,
+ propINTERFACE_LINK_LIBRARIES_DIRECT,
+ propINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE,
};
if (specialProps.count(prop)) {
if (prop == propC_STANDARD || prop == propCXX_STANDARD ||
@@ -1914,6 +1981,25 @@ cmValue cmTarget::GetProperty(const std::string& prop) const
output = cmJoin(this->impl->LinkInterfacePropertyEntries, ";");
return cmValue(output);
}
+ if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT) {
+ if (this->impl->LinkInterfaceDirectPropertyEntries.empty()) {
+ return nullptr;
+ }
+
+ static std::string output;
+ output = cmJoin(this->impl->LinkInterfaceDirectPropertyEntries, ";");
+ return cmValue(output);
+ }
+ if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE) {
+ if (this->impl->LinkInterfaceDirectExcludePropertyEntries.empty()) {
+ return nullptr;
+ }
+
+ static std::string output;
+ output =
+ cmJoin(this->impl->LinkInterfaceDirectExcludePropertyEntries, ";");
+ return cmValue(output);
+ }
// the type property returns what type the target is
if (prop == propTYPE) {
return cmValue(cmState::GetTargetTypeName(this->GetType()));
diff --git a/Source/cmTarget.h b/Source/cmTarget.h
index 1bbd0b0..0cdd2fc 100644
--- a/Source/cmTarget.h
+++ b/Source/cmTarget.h
@@ -269,6 +269,8 @@ public:
cmBTStringRange GetLinkImplementationEntries() const;
cmBTStringRange GetLinkInterfaceEntries() const;
+ cmBTStringRange GetLinkInterfaceDirectEntries() const;
+ cmBTStringRange GetLinkInterfaceDirectExcludeEntries() const;
cmBTStringRange GetHeaderSetsEntries() const;
diff --git a/Source/cmTargetLinkLibrariesCommand.cxx b/Source/cmTargetLinkLibrariesCommand.cxx
index e10d0b5..e1a6c78 100644
--- a/Source/cmTargetLinkLibrariesCommand.cxx
+++ b/Source/cmTargetLinkLibrariesCommand.cxx
@@ -379,6 +379,9 @@ bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args,
target->SetProperty("LINK_INTERFACE_LIBRARIES", "");
}
+ target->CheckProperty("LINK_LIBRARIES", &mf);
+ target->CheckProperty("INTERFACE_LINK_LIBRARIES", &mf);
+
return true;
}
diff --git a/Source/cmVariableWatchCommand.cxx b/Source/cmVariableWatchCommand.cxx
index fd5402c..24394d9 100644
--- a/Source/cmVariableWatchCommand.cxx
+++ b/Source/cmVariableWatchCommand.cxx
@@ -58,7 +58,7 @@ void cmVariableWatchCommandVariableAccessed(const std::string& variable,
{ stack, cmListFileArgument::Quoted, fakeLineNo }
};
- cmListFileFunction newLFF{ data->Command, fakeLineNo,
+ cmListFileFunction newLFF{ data->Command, fakeLineNo, fakeLineNo,
std::move(newLFFArgs) };
cmExecutionStatus status(*makefile);
if (!makefile->ExecuteCommand(newLFF, status)) {
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 8ffb664..276eccf 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -50,6 +50,24 @@
#include "cmValue.h"
#include "cmVisualStudioGeneratorOptions.h"
+namespace {
+std::string getProjectFileExtension(VsProjectType projectType)
+{
+ switch (projectType) {
+ case VsProjectType::csproj:
+ return ".csproj";
+ case VsProjectType::proj:
+ return ".proj";
+ case VsProjectType::vcxproj:
+ return ".vcxproj";
+ // Valid inputs shouldn't reach here. This default is needed so that all
+ // paths return value (C4715).
+ default:
+ return "";
+ }
+}
+}
+
struct cmIDEFlagTable;
static void ConvertToWindowsSlash(std::string& s);
@@ -235,29 +253,6 @@ static bool cmVS10IsTargetsFile(std::string const& path)
return cmSystemTools::Strucmp(ext.c_str(), ".targets") == 0;
}
-static VsProjectType computeProjectType(cmGeneratorTarget const* t)
-{
- if (t->IsCSharpOnly()) {
- return VsProjectType::csproj;
- }
- return VsProjectType::vcxproj;
-}
-
-static std::string computeProjectFileExtension(VsProjectType projectType)
-{
- switch (projectType) {
- case VsProjectType::csproj:
- return ".csproj";
- default:
- return ".vcxproj";
- }
-}
-
-static std::string computeProjectFileExtension(cmGeneratorTarget const* t)
-{
- return computeProjectFileExtension(computeProjectType(t));
-}
-
cmVisualStudio10TargetGenerator::cmVisualStudio10TargetGenerator(
cmGeneratorTarget* target, cmGlobalVisualStudio10Generator* gg)
: GeneratorTarget(target)
@@ -352,10 +347,10 @@ std::ostream& cmVisualStudio10TargetGenerator::Elem::WriteString(
void cmVisualStudio10TargetGenerator::Generate()
{
- this->ProjectType = computeProjectType(this->GeneratorTarget);
+ this->ProjectType = this->ComputeProjectType(this->GeneratorTarget);
this->Managed = this->ProjectType == VsProjectType::csproj;
const std::string ProjectFileExtension =
- computeProjectFileExtension(this->ProjectType);
+ getProjectFileExtension(this->ProjectType);
if (this->ProjectType == VsProjectType::csproj &&
this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY) {
@@ -416,10 +411,12 @@ void cmVisualStudio10TargetGenerator::Generate()
char magic[] = { char(0xEF), char(0xBB), char(0xBF) };
BuildFileStream.write(magic, 3);
- if (this->ProjectType == VsProjectType::csproj &&
- this->GeneratorTarget->IsDotNetSdkTarget() &&
- this->GlobalGenerator->GetVersion() >=
- cmGlobalVisualStudioGenerator::VSVersion::VS16) {
+ if (this->ProjectType == VsProjectType::proj) {
+ this->WriteZeroCheckProj(BuildFileStream);
+ } else if (this->ProjectType == VsProjectType::csproj &&
+ this->GeneratorTarget->IsDotNetSdkTarget() &&
+ this->GlobalGenerator->GetVersion() >=
+ cmGlobalVisualStudioGenerator::VSVersion::VS16) {
this->WriteSdkStyleProjectFile(BuildFileStream);
} else {
this->WriteClassicMsBuildProjectFile(BuildFileStream);
@@ -681,6 +678,8 @@ void cmVisualStudio10TargetGenerator::WriteClassicMsBuildProjectFile(
.Attribute("Project", VS10_CSharp_DEFAULT_PROPS)
.Attribute("Condition", "Exists('" VS10_CSharp_DEFAULT_PROPS "')");
break;
+ default:
+ break;
}
this->WriteProjectConfigurationValues(e0);
@@ -737,6 +736,8 @@ void cmVisualStudio10TargetGenerator::WriteClassicMsBuildProjectFile(
case VsProjectType::csproj:
props = VS10_CSharp_USER_PROPS;
break;
+ default:
+ break;
}
if (cmValue p = this->GeneratorTarget->GetProperty("VS_USER_PROPS")) {
props = *p;
@@ -779,6 +780,8 @@ void cmVisualStudio10TargetGenerator::WriteClassicMsBuildProjectFile(
Elem(e0, "Import").Attribute("Project", VS10_CSharp_TARGETS);
}
break;
+ default:
+ break;
}
this->WriteTargetSpecificReferences(e0);
@@ -944,6 +947,45 @@ void cmVisualStudio10TargetGenerator::WriteSdkStyleProjectFile(
this->WriteProjectReferences(e0);
}
+void cmVisualStudio10TargetGenerator::WriteZeroCheckProj(
+ cmGeneratedFileStream& BuildFileStream)
+{
+ // ZERO_CHECK.proj is an XML file without any imports or targets. This is a
+ // ProjectReference for other targets and therefore, it needs to follow the
+ // ProjectReference protocol as documented here:
+ // https://github.com/dotnet/msbuild/blob/main/documentation/ProjectReference-Protocol.md
+ //
+ // We implement MSBuild target Build from WriteCustomCommand which calls
+ // WriteZeroCheckBuildTarget after setting up the command generator. MSBuild
+ // target Clean is a no-op as we do all the work for ZERO_CHECK on Build.
+ // MSBuild target GetTargetPath is needed and is no-op.
+ // MSBuild targets GetNativeManifest and GetCopyToOutputDirectoryItems are
+ // needed for MSBuild versions below 15.7 and are no-op. MSBuild target
+ // BeforeBuild is needed for supporting GLOBs.
+ BuildFileStream << "<?xml version=\"1.0\" encoding=\""
+ << this->GlobalGenerator->Encoding() << "\"?>";
+ {
+ Elem e0(BuildFileStream, "Project");
+ e0.Attribute("DefaultTargets", "Build");
+ e0.Attribute("ToolsVersion", this->GlobalGenerator->GetToolsVersion());
+ e0.Attribute("xmlns",
+ "http://schemas.microsoft.com/developer/msbuild/2003");
+
+ this->WriteCustomCommands(e0);
+
+ for (const char* targetName :
+ { "Clean", "GetTargetPath", "GetNativeManifest",
+ "GetCopyToOutputDirectoryItems" }) {
+ {
+ Elem e1(e0, "Target");
+ e1.Attribute("Name", targetName);
+ }
+ }
+
+ this->WriteZeroCheckBeforeBuildTarget(e0);
+ }
+}
+
void cmVisualStudio10TargetGenerator::WriteCommonPropertyGroupGlobals(Elem& e1)
{
e1.Attribute("Label", "Globals");
@@ -1659,11 +1701,16 @@ void cmVisualStudio10TargetGenerator::WriteCustomRule(
}
}
}
+ if (this->ProjectType == VsProjectType::proj) {
+ this->WriteZeroCheckBuildTarget(e0, command, source);
+ return;
+ }
+
cmLocalVisualStudio7Generator* lg = this->LocalGenerator;
std::unique_ptr<Elem> spe1;
std::unique_ptr<Elem> spe2;
- if (this->ProjectType != VsProjectType::csproj) {
+ if (this->ProjectType == VsProjectType::vcxproj) {
spe1 = cm::make_unique<Elem>(e0, "ItemGroup");
spe2 = cm::make_unique<Elem>(*spe1, "CustomBuild");
this->WriteSource(*spe2, source);
@@ -1861,7 +1908,7 @@ void cmVisualStudio10TargetGenerator::WriteGroups()
// Write out group file
std::string path = cmStrCat(
this->LocalGenerator->GetCurrentBinaryDirectory(), '/', this->Name,
- computeProjectFileExtension(this->GeneratorTarget), ".filters");
+ this->ComputeProjectFileExtension(this->GeneratorTarget), ".filters");
cmGeneratedFileStream fout(path);
fout.SetCopyIfDifferent(true);
char magic[] = { char(0xEF), char(0xBB), char(0xBF) };
@@ -3016,6 +3063,134 @@ void cmVisualStudio10TargetGenerator::OutputLinkIncremental(
}
}
+void cmVisualStudio10TargetGenerator::WriteZeroCheckBuildTarget(
+ cmVisualStudio10TargetGenerator::Elem& e0, const cmCustomCommand& command,
+ const cmSourceFile* source)
+{
+ cmLocalVisualStudio7Generator* lg = this->LocalGenerator;
+
+ Elem e1(e0, "Target");
+ e1.Attribute("Name", "Build");
+
+ std::string noConfig{};
+ cmCustomCommandGenerator ccg{ command, noConfig, lg, true };
+ std::string comment = lg->ConstructComment(ccg);
+ comment = cmVS10EscapeComment(comment);
+ std::string script = lg->ConstructScript(ccg);
+ bool symbolic = false;
+ // input files for custom command
+ std::stringstream additional_inputs;
+ {
+ const char* sep = "";
+ if (this->ProjectType == VsProjectType::proj) {
+ // List explicitly the path to primary input.
+ std::string sourceFullPath = source->GetFullPath();
+ ConvertToWindowsSlash(sourceFullPath);
+ additional_inputs << sourceFullPath;
+ sep = ";";
+ }
+
+ // Avoid listing an input more than once.
+ std::set<std::string> unique_inputs;
+ // The source is either implicitly an input or has been added above.
+ unique_inputs.insert(source->GetFullPath());
+
+ for (std::string const& d : ccg.GetDepends()) {
+ std::string dep;
+ if (lg->GetRealDependency(d, noConfig, dep)) {
+ if (!unique_inputs.insert(dep).second) {
+ // already listed
+ continue;
+ }
+ ConvertToWindowsSlash(dep);
+ additional_inputs << sep << dep;
+ sep = ";";
+ if (!symbolic) {
+ if (cmSourceFile* sf = this->Makefile->GetSource(
+ dep, cmSourceFileLocationKind::Known)) {
+ symbolic = sf->GetPropertyAsBool("SYMBOLIC");
+ }
+ }
+ }
+ }
+ }
+ // output files for custom command
+ std::stringstream outputs;
+ {
+ const char* sep = "";
+ for (std::string const& o : ccg.GetOutputs()) {
+ std::string out = o;
+ ConvertToWindowsSlash(out);
+ outputs << sep << out;
+ sep = ";";
+ if (!symbolic) {
+ if (cmSourceFile* sf =
+ this->Makefile->GetSource(o, cmSourceFileLocationKind::Known)) {
+ symbolic = sf->GetPropertyAsBool("SYMBOLIC");
+ }
+ }
+ }
+ }
+ script += lg->FinishConstructScript(this->ProjectType);
+
+ e1.Attribute("Inputs", cmVS10EscapeAttr(additional_inputs.str()));
+ e1.Attribute("Outputs", cmVS10EscapeAttr(outputs.str()));
+
+ e1.SetHasElements();
+
+ if (!comment.empty()) {
+ Elem(e1, "Message").Attribute("Text", comment);
+ }
+ Elem(e1, "Exec").Attribute("Command", script);
+}
+
+void cmVisualStudio10TargetGenerator::WriteZeroCheckBeforeBuildTarget(
+ cmVisualStudio10TargetGenerator::Elem& e0)
+{
+ const auto& commands = this->GeneratorTarget->GetPreBuildCommands();
+ if (commands.empty()) {
+ return;
+ }
+
+ {
+ Elem e1(e0, "Target");
+ e1.Attribute("Name", "BeforeBuild");
+ e1.Attribute("BeforeTargets", "Build");
+
+ cmLocalVisualStudio7Generator* lg = this->LocalGenerator;
+ std::string script;
+ const char* pre = "";
+ std::string comment;
+ for (cmCustomCommand const& cc : commands) {
+ cmCustomCommandGenerator ccg(cc, std::string{}, lg);
+ if (!ccg.HasOnlyEmptyCommandLines()) {
+ comment += pre;
+ comment += lg->ConstructComment(ccg);
+ script += pre;
+ pre = "\n";
+ script += lg->ConstructScript(ccg);
+ }
+ }
+
+ if (script.empty()) {
+ return;
+ }
+
+ script += lg->FinishConstructScript(this->ProjectType);
+ comment = cmVS10EscapeComment(comment);
+ std::string strippedComment = comment;
+ strippedComment.erase(
+ std::remove(strippedComment.begin(), strippedComment.end(), '\t'),
+ strippedComment.end());
+
+ e1.SetHasElements();
+ if (!comment.empty() && !strippedComment.empty()) {
+ Elem(e1, "Message").Attribute("Text", comment);
+ }
+ Elem(e1, "Exec").Attribute("Command", script);
+ }
+}
+
std::vector<std::string> cmVisualStudio10TargetGenerator::GetIncludes(
std::string const& config, std::string const& lang) const
{
@@ -3057,6 +3232,8 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions(
cm::make_unique<Options>(this->LocalGenerator, Options::CSharpCompiler,
gg->GetCSharpFlagTable());
break;
+ default:
+ break;
}
Options& clOptions = *pOptions;
@@ -3182,6 +3359,8 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions(
return def.find('=') != std::string::npos;
});
break;
+ default:
+ break;
}
clOptions.AddDefines(targetDefines);
@@ -3309,7 +3488,9 @@ void cmVisualStudio10TargetGenerator::WriteClOptions(
} else if (this->MSTools) {
cmsys::RegularExpression clangToolset("v[0-9]+_clang_.*");
const char* toolset = this->GlobalGenerator->GetPlatformToolset();
- if (toolset && clangToolset.find(toolset)) {
+ cmValue noCompileBatching =
+ this->GeneratorTarget->GetProperty("VS_NO_COMPILE_BATCHING");
+ if (noCompileBatching.IsOn() || (toolset && clangToolset.find(toolset))) {
e2.Element("ObjectFileName", "$(IntDir)%(filename).obj");
} else {
e2.Element("ObjectFileName", "$(IntDir)");
@@ -3688,7 +3869,8 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions(
this->LocalGenerator->MaybeRelativeToCurBinDir(l.Value.Value);
ConvertToWindowsSlash(path);
if (!cmVS10IsTargetsFile(l.Value.Value)) {
- libVec.push_back(path);
+ libVec.push_back(l.HasFeature() ? l.GetFormattedItem(path).Value
+ : path);
}
} else {
libVec.push_back(l.Value.Value);
@@ -4305,6 +4487,9 @@ void cmVisualStudio10TargetGenerator::AddLibraries(
this->AdditionalUsingDirectories[config].insert(
cmSystemTools::GetFilenamePath(location));
break;
+ default:
+ // In .proj files, we wouldn't be referencing libraries.
+ break;
}
}
}
@@ -4326,7 +4511,8 @@ void cmVisualStudio10TargetGenerator::AddLibraries(
if (cmVS10IsTargetsFile(l.Value.Value)) {
vsTargetVec.push_back(path);
} else {
- libVec.push_back(path);
+ libVec.push_back(l.HasFeature() ? l.GetFormattedItem(path).Value
+ : path);
}
} else if (!l.Target ||
l.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
@@ -4533,7 +4719,7 @@ void cmVisualStudio10TargetGenerator::WriteProjectReferences(Elem& e0)
path = *p;
} else {
path = cmStrCat(lg->GetCurrentBinaryDirectory(), '/', dt->GetName(),
- computeProjectFileExtension(dt));
+ this->ComputeProjectFileExtension(dt));
}
ConvertToWindowsSlash(path);
Elem e2(e1, "ProjectReference");
@@ -5387,6 +5573,26 @@ std::string cmVisualStudio10TargetGenerator::GetCMakeFilePath(
return path;
}
+std::string cmVisualStudio10TargetGenerator::ComputeProjectFileExtension(
+ cmGeneratorTarget const* t) const
+{
+ return getProjectFileExtension(this->ComputeProjectType(t));
+}
+
+VsProjectType cmVisualStudio10TargetGenerator::ComputeProjectType(
+ cmGeneratorTarget const* t) const
+{
+ if (this->GlobalGenerator->GetVersion() >=
+ cmGlobalVisualStudioGenerator::VSVersion::VS16 &&
+ t->GetName() == CMAKE_CHECK_BUILD_SYSTEM_TARGET) {
+ return VsProjectType::proj;
+ }
+ if (t->IsCSharpOnly()) {
+ return VsProjectType::csproj;
+ }
+ return VsProjectType::vcxproj;
+}
+
void cmVisualStudio10TargetGenerator::WriteStdOutEncodingUtf8(Elem& e1)
{
if (this->GlobalGenerator->IsUtf8EncodingSupported()) {
diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h
index 8d777a3..7a0b8da 100644
--- a/Source/cmVisualStudio10TargetGenerator.h
+++ b/Source/cmVisualStudio10TargetGenerator.h
@@ -264,6 +264,13 @@ private:
void WriteClassicMsBuildProjectFile(cmGeneratedFileStream& BuildFileStream);
void WriteSdkStyleProjectFile(cmGeneratedFileStream& BuildFileStream);
+ void WriteZeroCheckProj(cmGeneratedFileStream& BuildFileStream);
+ void WriteZeroCheckBuildTarget(cmVisualStudio10TargetGenerator::Elem& e0,
+ const cmCustomCommand& command,
+ const cmSourceFile* source);
+ void WriteZeroCheckBeforeBuildTarget(
+ cmVisualStudio10TargetGenerator::Elem& e0);
+
void WriteCommonPropertyGroupGlobals(
cmVisualStudio10TargetGenerator::Elem& e1);
@@ -275,4 +282,7 @@ private:
void ParseSettingsProperty(const std::string& settingsPropertyValue,
ConfigToSettings& toolSettings);
std::string GetCMakeFilePath(const char* name) const;
+
+ std::string ComputeProjectFileExtension(cmGeneratorTarget const* t) const;
+ VsProjectType ComputeProjectType(cmGeneratorTarget const* t) const;
};
diff --git a/Source/cmVisualStudioGeneratorOptions.cxx b/Source/cmVisualStudioGeneratorOptions.cxx
index 9045a4d..d5d08b8 100644
--- a/Source/cmVisualStudioGeneratorOptions.cxx
+++ b/Source/cmVisualStudioGeneratorOptions.cxx
@@ -182,7 +182,8 @@ void cmVisualStudioGeneratorOptions::FixCudaCodeGeneration()
// First entries for the -arch=<arch> [-code=<code>,...] pair.
if (!arch.empty()) {
std::string arch_name = arch[0];
- if (arch_name == "all" || arch_name == "all-major") {
+ if (arch_name == "all" || arch_name == "all-major" ||
+ arch_name == "native") {
AppendFlagString("AdditionalOptions", "-arch=" + arch_name);
return;
}
diff --git a/Source/cmVsProjectType.h b/Source/cmVsProjectType.h
index 8899267..04053a0 100644
--- a/Source/cmVsProjectType.h
+++ b/Source/cmVsProjectType.h
@@ -7,5 +7,6 @@
enum class VsProjectType
{
vcxproj,
- csproj
+ csproj,
+ proj,
};
diff --git a/Source/cmWhileCommand.cxx b/Source/cmWhileCommand.cxx
index a93a81f..68d5a9a 100644
--- a/Source/cmWhileCommand.cxx
+++ b/Source/cmWhileCommand.cxx
@@ -17,6 +17,8 @@
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmOutputConverter.h"
+#include "cmPolicies.h"
+#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmake.h"
@@ -79,9 +81,8 @@ bool cmWhileFunctionBlocker::Replay(std::vector<cmListFileFunction> functions,
return out;
};
- // FIXME(#23296): For compatibility with older versions of CMake, we
- // tolerate condition errors that evaluate to false. We should add
- // a policy to enforce such errors.
+ // For compatibility with projects that do not set CMP0130 to NEW,
+ // we tolerate condition errors that evaluate to false.
bool enforceError = true;
std::string errorString;
MessageType messageType;
@@ -110,14 +111,38 @@ bool cmWhileFunctionBlocker::Replay(std::vector<cmListFileFunction> functions,
}
}
+ if (!errorString.empty() && !enforceError) {
+ // This error should only be enforced if CMP0130 is NEW.
+ switch (mf.GetPolicyStatus(cmPolicies::CMP0130)) {
+ case cmPolicies::WARN:
+ // Convert the error to a warning and enforce it.
+ messageType = MessageType::AUTHOR_WARNING;
+ enforceError = true;
+ break;
+ case cmPolicies::OLD:
+ // OLD behavior is to silently ignore the error.
+ break;
+ case cmPolicies::REQUIRED_ALWAYS:
+ case cmPolicies::REQUIRED_IF_USED:
+ case cmPolicies::NEW:
+ // NEW behavior is to enforce the error.
+ enforceError = true;
+ break;
+ }
+ }
+
if (!errorString.empty() && enforceError) {
- std::string err = "had incorrect arguments:\n ";
+ std::string err = "while() given incorrect arguments:\n ";
for (auto const& i : expandedArguments) {
err += " ";
err += cmOutputConverter::EscapeForCMake(i.GetValue());
}
err += "\n";
err += errorString;
+ if (mf.GetPolicyStatus(cmPolicies::CMP0130) == cmPolicies::WARN) {
+ err =
+ cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0130), '\n', err);
+ }
mf.GetCMakeInstance()->IssueMessage(messageType, err, whileBT);
if (messageType == MessageType::FATAL_ERROR) {
cmSystemTools::SetFatalErrorOccured();
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index 7de488b..a602458 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -1158,6 +1158,12 @@ void cmake::SetArgs(const std::vector<std::string>& args)
// iterate each argument
std::string const& arg = args[i];
+ if (scriptMode && arg == "--") {
+ // Stop processing CMake args and avoid possible errors
+ // when arbitrary args are given to CMake script.
+ break;
+ }
+
// Generator flag has special handling for when to print help
// so it becomes the exception
if (generatorCommand.matches(arg)) {
@@ -1455,7 +1461,7 @@ void cmake::PrintTraceFormatVersion()
Json::StreamWriterBuilder builder;
builder["indentation"] = "";
version["major"] = 1;
- version["minor"] = 1;
+ version["minor"] = 2;
val["version"] = version;
msg = Json::writeString(builder, val);
#endif
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx
index 32c01e5..f1c1bdc 100644
--- a/Source/cmcmd.cxx
+++ b/Source/cmcmd.cxx
@@ -97,7 +97,8 @@ void CMakeCommandUsage(std::string const& program)
<< "Available commands: \n"
<< " capabilities - Report capabilities built into cmake "
"in JSON format\n"
- << " cat <files>... - concat the files and print them to the standard output\n"
+ << " cat [--] <files>... - concat the files and print them to the "
+ "standard output\n"
<< " chdir dir cmd [args...] - run command in a given directory\n"
<< " compare_files [--ignore-eol] file1 file2\n"
<< " - check if file1 is same as file2\n"
@@ -110,7 +111,7 @@ void CMakeCommandUsage(std::string const& program)
<< " echo [<string>...] - displays arguments as text\n"
<< " echo_append [<string>...] - displays arguments as text but no new "
"line\n"
- << " env [--unset=NAME]... [NAME=VALUE]... COMMAND [ARG]...\n"
+ << " env [--unset=NAME ...] [NAME=VALUE ...] [--] <command> [<arg>...]\n"
<< " - run command in a modified environment\n"
<< " environment - display the current environment\n"
<< " make_directory <dir>... - create parent and <dir> directories\n"
@@ -125,8 +126,9 @@ void CMakeCommandUsage(std::string const& program)
<< " remove_directory <dir>... - remove directories and their contents (deprecated: use rm instead)\n"
<< " rename oldname newname - rename a file or directory "
"(on one volume)\n"
- << " rm [-rRf] <file/dir>... - remove files or directories, use -f to "
- "force it, r or R to remove directories and their contents recursively\n"
+ << " rm [-rRf] [--] <file/dir>... - remove files or directories, use -f "
+ "to force it, r or R to remove directories and their contents "
+ "recursively\n"
<< " sleep <number>... - sleep for given number of seconds\n"
<< " tar [cxt][vf][zjJ] file.tar [file/dir1 file/dir2 ...]\n"
<< " - create or extract a tar or zip archive\n"
@@ -793,6 +795,12 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
auto ae = args.cend();
for (; ai != ae; ++ai) {
std::string const& a = *ai;
+ if (a == "--") {
+ // Stop parsing options/environment variables; the next argument
+ // should be the command.
+ ++ai;
+ break;
+ }
if (cmHasLiteralPrefix(a, "--unset=")) {
// Unset environment variable.
cmSystemTools::UnPutEnv(a.substr(8));
@@ -1051,9 +1059,12 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
// Command to concat files into one
if (args[1] == "cat" && args.size() >= 3) {
int return_value = 0;
+ bool doing_options = true;
for (auto const& arg : cmMakeRange(args).advance(2)) {
- if (cmHasLiteralPrefix(arg, "-")) {
- if (arg != "--") {
+ if (doing_options && cmHasLiteralPrefix(arg, "-")) {
+ if (arg == "--") {
+ doing_options = false;
+ } else {
cmSystemTools::Error(arg + ": option not handled");
return_value = 1;
}
@@ -1275,8 +1286,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
// FIXME: With advanced add_subdirectory usage, these are
// not necessarily the same as the generator originally used.
// We should pass all these directories through an info file.
- lgd->SetRelativePathTopSource(homeDir);
- lgd->SetRelativePathTopBinary(homeOutDir);
+ lgd->SetRelativePathTop(homeDir, homeOutDir);
// Actually scan dependencies.
return lgd->UpdateDependencies(depInfo, verbose, color) ? 0 : 2;
@@ -1558,8 +1568,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
// FIXME: With advanced add_subdirectory usage, these are
// not necessarily the same as the generator originally used.
// We should pass all these directories through an info file.
- lgd->SetRelativePathTopSource(homeDir);
- lgd->SetRelativePathTopBinary(homeOutDir);
+ lgd->SetRelativePathTop(homeDir, homeOutDir);
return cmTransformDepfile(format, *lgd, args[8], args[9]) ? 0 : 2;
}
diff --git a/Source/ctest.cxx b/Source/ctest.cxx
index cad27fa..363f473 100644
--- a/Source/ctest.cxx
+++ b/Source/ctest.cxx
@@ -44,6 +44,9 @@ static const char* cmDocumentationOptions[][2] = {
{ "--test-output-size-failed <size>",
"Limit the output for failed tests "
"to <size> bytes" },
+ { "--test-output-truncation <mode>",
+ "Truncate 'tail' (default), 'middle' or 'head' of test output once "
+ "maximum output size is reached" },
{ "-F", "Enable failover." },
{ "-j <jobs>, --parallel <jobs>",
"Run the tests in parallel using the "
diff --git a/Source/kwsys/ProcessUNIX.c b/Source/kwsys/ProcessUNIX.c
index 19bf982..22cbf06 100644
--- a/Source/kwsys/ProcessUNIX.c
+++ b/Source/kwsys/ProcessUNIX.c
@@ -1906,7 +1906,7 @@ static void kwsysProcessDestroy(kwsysProcess* cp)
(errno == EINTR)) {
}
if (result > 0) {
- /* This child has termianted. */
+ /* This child has terminated. */
cp->ForkPIDs[i] = 0;
if (--cp->CommandsLeft == 0) {
/* All children have terminated. Close the signal pipe
diff --git a/Source/kwsys/ProcessWin32.c b/Source/kwsys/ProcessWin32.c
index e97973e..17e1507 100644
--- a/Source/kwsys/ProcessWin32.c
+++ b/Source/kwsys/ProcessWin32.c
@@ -2119,7 +2119,7 @@ static void kwsysProcessSetExitExceptionByIndex(kwsysProcess* cp, int code,
KWSYSPE_CASE(Fault, "In-page error");
break;
case STATUS_INVALID_HANDLE:
- KWSYSPE_CASE(Fault, "Invalid hanlde");
+ KWSYSPE_CASE(Fault, "Invalid handle");
break;
case STATUS_NONCONTINUABLE_EXCEPTION:
KWSYSPE_CASE(Fault, "Noncontinuable exception");