Merge topic 'Genex-LINK_LANGUAGE'

461efa7b51 Genex: Add $<LINK_LANGUAGE:...> and $<LINK_LANG_AND_ID:...>

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !4244
This commit is contained in:
Brad King 2020-02-27 16:18:18 +00:00 committed by Kitware Robot
commit 2a9cf88917
182 changed files with 1619 additions and 55 deletions

View File

@ -259,6 +259,109 @@ Variable Queries
add_executable(myapp main.cpp)
target_link_libraries(myapp myapp_c myapp_cxx)
.. _`Boolean LINK_LANGUAGE Generator Expression`:
``$<LINK_LANG_AND_ID:language,compiler_ids>``
``1`` when the language used for link step matches ``language`` and the
CMake's compiler id of the language linker matches any one of the entries
in ``compiler_ids``, otherwise ``0``. This expression is a short form for the
combination of ``$<LINK_LANGUAGE:language>`` and
``$<LANG_COMPILER_ID:compiler_ids>``. This expression may be used to specify
link libraries, link options, link directories and link dependencies of a
particular language and linker combination in a target. For example:
.. code-block:: cmake
add_library(libC_Clang ...)
add_library(libCXX_Clang ...)
add_library(libC_Intel ...)
add_library(libCXX_Intel ...)
add_executable(myapp main.c)
if (CXX_CONFIG)
target_sources(myapp PRIVATE file.cxx)
endif()
target_link_libraries(myapp
PRIVATE $<$<LINK_LANG_AND_ID:CXX,Clang,AppleClang>:libCXX_Clang>
$<$<LINK_LANG_AND_ID:C,Clang,AppleClang>:libC_Clang>
$<$<LINK_LANG_AND_ID:CXX,Intel>:libCXX_Intel>
$<$<LINK_LANG_AND_ID:C,Intel>:libC_Intel>)
This specifies the use of different link libraries based on both the
compiler id and link language. This example will have target ``libCXX_Clang``
as link dependency when ``Clang`` or ``AppleClang`` is the ``CXX``
linker, and ``libCXX_Intel`` when ``Intel`` is the ``CXX`` linker.
Likewise when the ``C`` linker is ``Clang`` or ``AppleClang``, target
``libC_Clang`` will be added as link dependency and ``libC_Intel`` when
``Intel`` is the ``C`` linker.
See :ref:`the note related to
<Constraints LINK_LANGUAGE Generator Expression>`
``$<LINK_LANGUAGE:language>`` for constraints about the usage of this
generator expression.
``$<LINK_LANGUAGE:languages>``
``1`` when the language used for link step matches any of the entries
in ``languages``, otherwise ``0``. This expression may be used to specify
link libraries, link options, link directories and link dependencies of a
particular language in a target. For example:
.. code-block:: cmake
add_library(api_C ...)
add_library(api_CXX ...)
add_library(api INTERFACE)
target_link_options(api INTERFACE $<$<LINK_LANGUAGE:C>:-opt_c>
$<$<LINK_LANGUAGE:CXX>:-opt_cxx>)
target_link_libraries(api INTERFACE $<$<LINK_LANGUAGE:C>:api_C>
$<$<LINK_LANGUAGE:CXX>:api_CXX>)
add_executable(myapp1 main.c)
target_link_options(myapp1 PRIVATE api)
add_executable(myapp2 main.cpp)
target_link_options(myapp2 PRIVATE api)
This specifies to use the ``api`` target for linking targets ``myapp1`` and
``myapp2``. In practice, ``myapp1`` will link with target ``api_C`` and
option ``-opt_c`` because it will use ``C`` as link language. And ``myapp2``
will link with ``api_CXX`` and option ``-opt_cxx`` because ``CXX`` will be
the link language.
.. _`Constraints LINK_LANGUAGE Generator Expression`:
.. note::
To determine the link language of a target, it is required to collect,
transitively, all the targets which will be linked to it. So, for link
libraries properties, a double evaluation will be done. During the first
evaluation, ``$<LINK_LANGUAGE:..>`` expressions will always return ``0``.
The link language computed after this first pass will be used to do the
second pass. To avoid inconsistency, it is required that the second pass
do not change the link language. Moreover, to avoid unexpected
side-effects, it is required to specify complete entities as part of the
``$<LINK_LANGUAGE:..>`` expression. For example:
.. code-block:: cmake
add_library(lib STATIC file.cxx)
add_library(libother STATIC file.c)
# bad usage
add_executable(myapp1 main.c)
target_link_libraries(myapp1 PRIVATE lib$<$<LINK_LANGUAGE:C>:other>)
# correct usage
add_executable(myapp2 main.c)
target_link_libraries(myapp2 PRIVATE $<$<LINK_LANGUAGE:C>:libother>)
In this example, for ``myapp1``, the first pass will, unexpectedly,
determine that the link language is ``CXX`` because the evaluation of the
generator expression will be an empty string so ``myapp1`` will depends on
target ``lib`` which is ``C++``. On the contrary, for ``myapp2``, the first
evaluation will give ``C`` as link language, so the second pass will
correctly add target ``libother`` as link dependency.
String-Valued Generator Expressions
===================================
@ -450,6 +553,17 @@ Variable Queries
<Boolean COMPILE_LANGUAGE Generator Expression>`
``$<COMPILE_LANGUAGE:language>``
for notes about the portability of this generator expression.
``$<LINK_LANGUAGE>``
The link language of target when evaluating link options.
See :ref:`the related boolean expression
<Boolean LINK_LANGUAGE Generator Expression>` ``$<LINK_LANGUAGE:language>``
for notes about the portability of this generator expression.
.. note::
This generator expression is not supported by the link libraries
properties to avoid side-effects due to the double evaluation of
these properties.
Target-Dependent Queries
------------------------

View File

@ -0,0 +1,5 @@
genex-LINK_LANGUAGE
===================
* The ``$<LINK_LANGUAGE:...>`` and ``$<LINK_LANG_AND_ID:...>``
:manual:`generator expressions <cmake-generator-expressions(7)>` were added.

View File

@ -103,6 +103,8 @@ const std::string& cmCompiledGeneratorExpression::EvaluateWithContext(
if (!context.HadError) {
this->HadContextSensitiveCondition = context.HadContextSensitiveCondition;
this->HadHeadSensitiveCondition = context.HadHeadSensitiveCondition;
this->HadLinkLanguageSensitiveCondition =
context.HadLinkLanguageSensitiveCondition;
this->SourceSensitiveTargets = context.SourceSensitiveTargets;
}
@ -119,6 +121,7 @@ cmCompiledGeneratorExpression::cmCompiledGeneratorExpression(
, Quiet(false)
, HadContextSensitiveCondition(false)
, HadHeadSensitiveCondition(false)
, HadLinkLanguageSensitiveCondition(false)
{
cmGeneratorExpressionLexer l;
std::vector<cmGeneratorExpressionToken> tokens = l.Tokenize(this->Input);

View File

@ -137,6 +137,10 @@ public:
{
return this->HadHeadSensitiveCondition;
}
bool GetHadLinkLanguageSensitiveCondition() const
{
return this->HadLinkLanguageSensitiveCondition;
}
std::set<cmGeneratorTarget const*> GetSourceSensitiveTargets() const
{
return this->SourceSensitiveTargets;
@ -178,6 +182,7 @@ private:
mutable std::string Output;
mutable bool HadContextSensitiveCondition;
mutable bool HadHeadSensitiveCondition;
mutable bool HadLinkLanguageSensitiveCondition;
mutable std::set<cmGeneratorTarget const*> SourceSensitiveTargets;
};

View File

@ -19,6 +19,7 @@ cmGeneratorExpressionContext::cmGeneratorExpressionContext(
, HadError(false)
, HadContextSensitiveCondition(false)
, HadHeadSensitiveCondition(false)
, HadLinkLanguageSensitiveCondition(false)
, EvaluateForBuildsystem(evaluateForBuildsystem)
{
}

View File

@ -40,6 +40,7 @@ struct cmGeneratorExpressionContext
bool HadError;
bool HadContextSensitiveCondition;
bool HadHeadSensitiveCondition;
bool HadLinkLanguageSensitiveCondition;
bool EvaluateForBuildsystem;
};

View File

@ -6,6 +6,10 @@
#include <sstream>
#include <utility>
#include <cm/string_view>
#include "cm_static_string_view.hxx"
#include "cmGeneratorExpressionContext.h"
#include "cmGeneratorExpressionEvaluator.h"
#include "cmGeneratorTarget.h"
@ -170,6 +174,21 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingPICExpression()
return top->Property == "INTERFACE_POSITION_INDEPENDENT_CODE";
}
bool cmGeneratorExpressionDAGChecker::EvaluatingLinkExpression()
{
const cmGeneratorExpressionDAGChecker* top = this;
const cmGeneratorExpressionDAGChecker* parent = this->Parent;
while (parent) {
top = parent;
parent = parent->Parent;
}
cm::string_view property(top->Property);
return property == "LINK_DIRECTORIES"_s || property == "LINK_OPTIONS"_s ||
property == "LINK_DEPENDS"_s;
}
bool cmGeneratorExpressionDAGChecker::EvaluatingLinkLibraries(
cmGeneratorTarget const* tgt)
{
@ -180,18 +199,17 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingLinkLibraries(
parent = parent->Parent;
}
const char* prop = top->Property.c_str();
cm::string_view prop(top->Property);
if (tgt) {
return top->Target == tgt && strcmp(prop, "LINK_LIBRARIES") == 0;
return top->Target == tgt && prop == "LINK_LIBRARIES"_s;
}
return (strcmp(prop, "LINK_LIBRARIES") == 0 ||
strcmp(prop, "LINK_INTERFACE_LIBRARIES") == 0 ||
strcmp(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES") == 0 ||
cmHasLiteralPrefix(prop, "LINK_INTERFACE_LIBRARIES_") ||
cmHasLiteralPrefix(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES_")) ||
strcmp(prop, "INTERFACE_LINK_LIBRARIES") == 0;
return prop == "LINK_LIBRARIES"_s || prop == "LINK_INTERFACE_LIBRARIES"_s ||
prop == "IMPORTED_LINK_INTERFACE_LIBRARIES"_s ||
cmHasLiteralPrefix(prop, "LINK_INTERFACE_LIBRARIES_") ||
cmHasLiteralPrefix(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES_") ||
prop == "INTERFACE_LINK_LIBRARIES"_s;
}
cmGeneratorTarget const* cmGeneratorExpressionDAGChecker::TopTarget() const

View File

@ -68,6 +68,7 @@ struct cmGeneratorExpressionDAGChecker
bool EvaluatingGenexExpression();
bool EvaluatingPICExpression();
bool EvaluatingLinkExpression();
bool EvaluatingLinkLibraries(cmGeneratorTarget const* tgt = nullptr);
#define DECLARE_TRANSITIVE_PROPERTY_METHOD(METHOD) bool METHOD() const;

View File

@ -62,6 +62,9 @@ std::string cmGeneratorExpressionNode::EvaluateDependentExpression(
if (cge->GetHadHeadSensitiveCondition()) {
context->HadHeadSensitiveCondition = true;
}
if (cge->GetHadLinkLanguageSensitiveCondition()) {
context->HadLinkLanguageSensitiveCondition = true;
}
return result;
}
@ -1039,6 +1042,150 @@ static const struct CompileLanguageAndIdNode : public cmGeneratorExpressionNode
}
} languageAndIdNode;
static const struct LinkLanguageNode : public cmGeneratorExpressionNode
{
LinkLanguageNode() {} // NOLINT(modernize-use-equals-default)
int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
std::string Evaluate(
const std::vector<std::string>& parameters,
cmGeneratorExpressionContext* context,
const GeneratorExpressionContent* content,
cmGeneratorExpressionDAGChecker* dagChecker) const override
{
if (!context->HeadTarget || !dagChecker ||
!(dagChecker->EvaluatingLinkExpression() ||
dagChecker->EvaluatingLinkLibraries())) {
reportError(context, content->GetOriginalExpression(),
"$<LINK_LANGUAGE:...> may only be used with binary targets "
"to specify link libraries, link directories, link options "
"and link depends.");
return std::string();
}
if (dagChecker->EvaluatingLinkLibraries() && parameters.empty()) {
reportError(
context, content->GetOriginalExpression(),
"$<LINK_LANGUAGE> is not supported in link libraries expression.");
return std::string();
}
cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
std::string genName = gg->GetName();
if (genName.find("Makefiles") == std::string::npos &&
genName.find("Ninja") == std::string::npos &&
genName.find("Visual Studio") == std::string::npos &&
genName.find("Xcode") == std::string::npos &&
genName.find("Watcom WMake") == std::string::npos) {
reportError(context, content->GetOriginalExpression(),
"$<LINK_LANGUAGE:...> not supported for this generator.");
return std::string();
}
if (dagChecker->EvaluatingLinkLibraries()) {
context->HadHeadSensitiveCondition = true;
context->HadLinkLanguageSensitiveCondition = true;
}
if (parameters.empty()) {
return context->Language;
}
for (auto& param : parameters) {
if (context->Language == param) {
return "1";
}
}
return "0";
}
} linkLanguageNode;
namespace {
struct LinkerId
{
static std::string Evaluate(const std::vector<std::string>& parameters,
cmGeneratorExpressionContext* context,
const GeneratorExpressionContent* content,
const std::string& lang)
{
std::string const& linkerId =
context->LG->GetMakefile()->GetSafeDefinition("CMAKE_" + lang +
"_COMPILER_ID");
if (parameters.empty()) {
return linkerId;
}
if (linkerId.empty()) {
return parameters.front().empty() ? "1" : "0";
}
static cmsys::RegularExpression linkerIdValidator("^[A-Za-z0-9_]*$");
for (auto& param : parameters) {
if (!linkerIdValidator.find(param)) {
reportError(context, content->GetOriginalExpression(),
"Expression syntax not recognized.");
return std::string();
}
if (param == linkerId) {
return "1";
}
}
return "0";
}
};
}
static const struct LinkLanguageAndIdNode : public cmGeneratorExpressionNode
{
LinkLanguageAndIdNode() {} // NOLINT(modernize-use-equals-default)
int NumExpectedParameters() const override { return TwoOrMoreParameters; }
std::string Evaluate(
const std::vector<std::string>& parameters,
cmGeneratorExpressionContext* context,
const GeneratorExpressionContent* content,
cmGeneratorExpressionDAGChecker* dagChecker) const override
{
if (!context->HeadTarget || !dagChecker ||
!(dagChecker->EvaluatingLinkExpression() ||
dagChecker->EvaluatingLinkLibraries())) {
reportError(
context, content->GetOriginalExpression(),
"$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets "
"to specify link libraries, link directories, link options, and link "
"depends.");
return std::string();
}
cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
std::string genName = gg->GetName();
if (genName.find("Makefiles") == std::string::npos &&
genName.find("Ninja") == std::string::npos &&
genName.find("Visual Studio") == std::string::npos &&
genName.find("Xcode") == std::string::npos &&
genName.find("Watcom WMake") == std::string::npos) {
reportError(
context, content->GetOriginalExpression(),
"$<LINK_LANG_AND_ID:lang,id> not supported for this generator.");
return std::string();
}
if (dagChecker->EvaluatingLinkLibraries()) {
context->HadHeadSensitiveCondition = true;
context->HadLinkLanguageSensitiveCondition = true;
}
const std::string& lang = context->Language;
if (lang == parameters.front()) {
std::vector<std::string> idParameter((parameters.cbegin() + 1),
parameters.cend());
return LinkerId::Evaluate(idParameter, context, content, lang);
}
return "0";
}
} linkLanguageAndIdNode;
std::string getLinkedTargetsContent(
cmGeneratorTarget const* target, std::string const& prop,
cmGeneratorExpressionContext* context,
@ -2314,6 +2461,8 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
{ "LINK_ONLY", &linkOnlyNode },
{ "COMPILE_LANG_AND_ID", &languageAndIdNode },
{ "COMPILE_LANGUAGE", &languageNode },
{ "LINK_LANG_AND_ID", &linkLanguageAndIdNode },
{ "LINK_LANGUAGE", &linkLanguageNode },
{ "SHELL_PATH", &shellPathNode }
};

View File

@ -17,6 +17,7 @@
#include <cm/memory>
#include <cm/string_view>
#include <cmext/algorithm>
#include "cmsys/RegularExpression.hxx"
@ -308,6 +309,13 @@ cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg)
this->SourceEntries, true);
this->PolicyMap = t->GetPolicyMap();
// Get hard-coded linker language
if (this->Target->GetProperty("HAS_CXX")) {
this->LinkerLanguage = "CXX";
} else {
this->LinkerLanguage = this->Target->GetSafeProperty("LINKER_LANGUAGE");
}
}
cmGeneratorTarget::~cmGeneratorTarget() = default;
@ -2223,11 +2231,12 @@ public:
cmTargetCollectLinkLanguages(cmGeneratorTarget const* target,
std::string config,
std::unordered_set<std::string>& languages,
cmGeneratorTarget const* head)
cmGeneratorTarget const* head, bool secondPass)
: Config(std::move(config))
, Languages(languages)
, HeadTarget(head)
, Target(target)
, SecondPass(secondPass)
{
this->Visited.insert(target);
}
@ -2269,11 +2278,14 @@ public:
if (!this->Visited.insert(item.Target).second) {
return;
}
cmLinkInterface const* iface =
item.Target->GetLinkInterface(this->Config, this->HeadTarget);
cmLinkInterface const* iface = item.Target->GetLinkInterface(
this->Config, this->HeadTarget, this->SecondPass);
if (!iface) {
return;
}
if (iface->HadLinkLanguageSensitiveCondition) {
this->HadLinkLanguageSensitiveCondition = true;
}
for (std::string const& language : iface->Languages) {
this->Languages.insert(language);
@ -2284,12 +2296,19 @@ public:
}
}
bool GetHadLinkLanguageSensitiveCondition()
{
return HadLinkLanguageSensitiveCondition;
}
private:
std::string Config;
std::unordered_set<std::string>& Languages;
cmGeneratorTarget const* HeadTarget;
const cmGeneratorTarget* Target;
std::set<cmGeneratorTarget const*> Visited;
bool SecondPass;
bool HadLinkLanguageSensitiveCondition = false;
};
cmGeneratorTarget::LinkClosure const* cmGeneratorTarget::GetLinkClosure(
@ -2320,7 +2339,7 @@ public:
{
this->GG = this->Target->GetLocalGenerator()->GetGlobalGenerator();
}
void Consider(const char* lang)
void Consider(const std::string& lang)
{
int preference = this->GG->GetLinkerPreference(lang);
if (preference > this->Preference) {
@ -2353,40 +2372,36 @@ public:
}
};
void cmGeneratorTarget::ComputeLinkClosure(const std::string& config,
LinkClosure& lc) const
bool cmGeneratorTarget::ComputeLinkClosure(const std::string& config,
LinkClosure& lc,
bool secondPass) const
{
// Get languages built in this target.
std::unordered_set<std::string> languages;
cmLinkImplementation const* impl = this->GetLinkImplementation(config);
cmLinkImplementation const* impl =
this->GetLinkImplementation(config, secondPass);
assert(impl);
for (std::string const& li : impl->Languages) {
languages.insert(li);
}
languages.insert(impl->Languages.cbegin(), impl->Languages.cend());
// Add interface languages from linked targets.
cmTargetCollectLinkLanguages cll(this, config, languages, this);
// cmTargetCollectLinkLanguages cll(this, config, languages, this,
// secondPass);
cmTargetCollectLinkLanguages cll(this, config, languages, this, secondPass);
for (cmLinkImplItem const& lib : impl->Libraries) {
cll.Visit(lib);
}
// Store the transitive closure of languages.
for (std::string const& lang : languages) {
lc.Languages.push_back(lang);
}
cm::append(lc.Languages, languages);
// Choose the language whose linker should be used.
if (this->GetProperty("HAS_CXX")) {
lc.LinkerLanguage = "CXX";
} else if (const char* linkerLang = this->GetProperty("LINKER_LANGUAGE")) {
lc.LinkerLanguage = linkerLang;
} else {
if (secondPass || lc.LinkerLanguage.empty()) {
// Find the language with the highest preference value.
cmTargetSelectLinker tsl(this);
// First select from the languages compiled directly in this target.
for (std::string const& l : impl->Languages) {
tsl.Consider(l.c_str());
tsl.Consider(l);
}
// Now consider languages that propagate from linked targets.
@ -2394,12 +2409,50 @@ void cmGeneratorTarget::ComputeLinkClosure(const std::string& config,
std::string propagates =
"CMAKE_" + lang + "_LINKER_PREFERENCE_PROPAGATES";
if (this->Makefile->IsOn(propagates)) {
tsl.Consider(lang.c_str());
tsl.Consider(lang);
}
}
lc.LinkerLanguage = tsl.Choose();
}
return impl->HadLinkLanguageSensitiveCondition ||
cll.GetHadLinkLanguageSensitiveCondition();
}
void cmGeneratorTarget::ComputeLinkClosure(const std::string& config,
LinkClosure& lc) const
{
bool secondPass = false;
{
LinkClosure linkClosure;
linkClosure.LinkerLanguage = this->LinkerLanguage;
// Get languages built in this target.
secondPass = this->ComputeLinkClosure(config, linkClosure, false);
this->LinkerLanguage = linkClosure.LinkerLanguage;
if (!secondPass) {
lc = std::move(linkClosure);
}
}
if (secondPass) {
LinkClosure linkClosure;
this->ComputeLinkClosure(config, linkClosure, secondPass);
lc = std::move(linkClosure);
// linker language must not be changed between the two passes
if (this->LinkerLanguage != lc.LinkerLanguage) {
std::ostringstream e;
e << "Evaluation of $<LINK_LANGUAGE:...> or $<LINK_LAND_AND_ID:...> "
"changes\nthe linker language for target \""
<< this->GetName() << "\" (from '" << this->LinkerLanguage << "' to '"
<< lc.LinkerLanguage << "') which is invalid.";
cmSystemTools::Error(e.str());
}
}
}
void cmGeneratorTarget::GetFullNameComponents(
@ -5492,7 +5545,8 @@ void cmGeneratorTarget::LookupLinkItems(std::vector<std::string> const& names,
void cmGeneratorTarget::ExpandLinkItems(
std::string const& prop, std::string const& value, std::string const& config,
cmGeneratorTarget const* headTarget, bool usage_requirements_only,
std::vector<cmLinkItem>& items, bool& hadHeadSensitiveCondition) const
std::vector<cmLinkItem>& items, bool& hadHeadSensitiveCondition,
bool& hadLinkLanguageSensitiveCondition) const
{
// Keep this logic in sync with ComputeLinkImplementationLibraries.
cmGeneratorExpression ge;
@ -5504,19 +5558,28 @@ void cmGeneratorTarget::ExpandLinkItems(
}
std::vector<std::string> libs;
std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(value);
cmExpandList(
cge->Evaluate(this->LocalGenerator, config, headTarget, &dagChecker, this),
libs);
cmExpandList(cge->Evaluate(this->LocalGenerator, config, headTarget,
&dagChecker, this, headTarget->LinkerLanguage),
libs);
this->LookupLinkItems(libs, cge->GetBacktrace(), items);
hadHeadSensitiveCondition = cge->GetHadHeadSensitiveCondition();
hadLinkLanguageSensitiveCondition =
cge->GetHadLinkLanguageSensitiveCondition();
}
cmLinkInterface const* cmGeneratorTarget::GetLinkInterface(
const std::string& config, cmGeneratorTarget const* head) const
{
return this->GetLinkInterface(config, head, false);
}
cmLinkInterface const* cmGeneratorTarget::GetLinkInterface(
const std::string& config, cmGeneratorTarget const* head,
bool secondPass) const
{
// Imported targets have their own link interface.
if (this->IsImported()) {
return this->GetImportLinkInterface(config, head, false);
return this->GetImportLinkInterface(config, head, false, secondPass);
}
// Link interfaces are not supported for executables that do not
@ -5529,6 +5592,10 @@ cmLinkInterface const* cmGeneratorTarget::GetLinkInterface(
// Lookup any existing link interface for this configuration.
cmHeadToLinkInterfaceMap& hm = this->GetHeadToLinkInterfaceMap(config);
if (secondPass) {
hm.erase(head);
}
// If the link interface does not depend on the head target
// then return the one we computed first.
if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) {
@ -5543,7 +5610,7 @@ cmLinkInterface const* cmGeneratorTarget::GetLinkInterface(
if (!iface.AllDone) {
iface.AllDone = true;
if (iface.Exists) {
this->ComputeLinkInterface(config, iface, head);
this->ComputeLinkInterface(config, iface, head, secondPass);
}
}
@ -5553,6 +5620,13 @@ cmLinkInterface const* cmGeneratorTarget::GetLinkInterface(
void cmGeneratorTarget::ComputeLinkInterface(
const std::string& config, cmOptionalLinkInterface& iface,
cmGeneratorTarget const* headTarget) const
{
this->ComputeLinkInterface(config, iface, headTarget, false);
}
void cmGeneratorTarget::ComputeLinkInterface(
const std::string& config, cmOptionalLinkInterface& iface,
cmGeneratorTarget const* headTarget, bool secondPass) const
{
if (iface.Explicit) {
if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
@ -5565,7 +5639,8 @@ void cmGeneratorTarget::ComputeLinkInterface(
emitted.insert(lib);
}
if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
cmLinkImplementation const* impl = this->GetLinkImplementation(config);
cmLinkImplementation const* impl =
this->GetLinkImplementation(config, secondPass);
for (cmLinkImplItem const& lib : impl->Libraries) {
if (emitted.insert(lib).second) {
if (lib.Target) {
@ -5595,7 +5670,7 @@ void cmGeneratorTarget::ComputeLinkInterface(
if (this->LinkLanguagePropagatesToDependents()) {
// Targets using this archive need its language runtime libraries.
if (cmLinkImplementation const* impl =
this->GetLinkImplementation(config)) {
this->GetLinkImplementation(config, secondPass)) {
iface.Languages = impl->Languages;
}
}
@ -5991,7 +6066,8 @@ void cmGeneratorTarget::ComputeLinkInterfaceLibraries(
// The interface libraries have been explicitly set.
this->ExpandLinkItems(linkIfaceProp, explicitLibraries, config, headTarget,
usage_requirements_only, iface.Libraries,
iface.HadHeadSensitiveCondition);
iface.HadHeadSensitiveCondition,
iface.HadLinkLanguageSensitiveCondition);
} else if (!cmp0022NEW)
// If CMP0022 is NEW then the plain tll signature sets the
// INTERFACE_LINK_LIBRARIES, so if we get here then the project
@ -6011,9 +6087,11 @@ void cmGeneratorTarget::ComputeLinkInterfaceLibraries(
static const std::string newProp = "INTERFACE_LINK_LIBRARIES";
if (const char* newExplicitLibraries = this->GetProperty(newProp)) {
bool hadHeadSensitiveConditionDummy = false;
bool hadLinkLanguageSensitiveConditionDummy = false;
this->ExpandLinkItems(newProp, newExplicitLibraries, config,
headTarget, usage_requirements_only, ifaceLibs,
hadHeadSensitiveConditionDummy);
hadHeadSensitiveConditionDummy,
hadLinkLanguageSensitiveConditionDummy);
}
if (ifaceLibs != iface.Libraries) {
std::string oldLibraries = cmJoin(impl->Libraries, ";");
@ -6050,7 +6128,7 @@ void cmGeneratorTarget::ComputeLinkInterfaceLibraries(
const cmLinkInterface* cmGeneratorTarget::GetImportLinkInterface(
const std::string& config, cmGeneratorTarget const* headTarget,
bool usage_requirements_only) const
bool usage_requirements_only, bool secondPass) const
{
cmGeneratorTarget::ImportInfo const* info = this->GetImportInfo(config);
if (!info) {
@ -6063,6 +6141,10 @@ const cmLinkInterface* cmGeneratorTarget::GetImportLinkInterface(
? this->GetHeadToLinkInterfaceUsageRequirementsMap(config)
: this->GetHeadToLinkInterfaceMap(config));
if (secondPass) {
hm.erase(headTarget);
}
// If the link interface does not depend on the head target
// then return the one we computed first.
if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) {
@ -6076,7 +6158,8 @@ const cmLinkInterface* cmGeneratorTarget::GetImportLinkInterface(
cmExpandList(info->Languages, iface.Languages);
this->ExpandLinkItems(info->LibrariesProp, info->Libraries, config,
headTarget, usage_requirements_only, iface.Libraries,
iface.HadHeadSensitiveCondition);
iface.HadHeadSensitiveCondition,
iface.HadLinkLanguageSensitiveCondition);
std::vector<std::string> deps = cmExpandedList(info->SharedDeps);
this->LookupLinkItems(deps, cmListFileBacktrace(), iface.SharedDeps);
}
@ -6280,6 +6363,12 @@ cmGeneratorTarget::GetHeadToLinkInterfaceUsageRequirementsMap(
const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation(
const std::string& config) const
{
return this->GetLinkImplementation(config, false);
}
const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation(
const std::string& config, bool secondPass) const
{
// There is no link implementation for imported targets.
if (this->IsImported()) {
@ -6288,6 +6377,9 @@ const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation(
std::string CONFIG = cmSystemTools::UpperCase(config);
cmOptionalLinkImplementation& impl = this->LinkImplMap[CONFIG][this];
if (secondPass) {
impl = cmOptionalLinkImplementation();
}
if (!impl.LibrariesDone) {
impl.LibrariesDone = true;
this->ComputeLinkImplementationLibraries(config, impl, this);
@ -6585,11 +6677,15 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
cmGeneratorExpression ge(*btIt);
std::unique_ptr<cmCompiledGeneratorExpression> const cge = ge.Parse(*le);
std::string const& evaluated =
cge->Evaluate(this->LocalGenerator, config, head, &dagChecker);
cge->Evaluate(this->LocalGenerator, config, head, &dagChecker, nullptr,
this->LinkerLanguage);
cmExpandList(evaluated, llibs);
if (cge->GetHadHeadSensitiveCondition()) {
impl.HadHeadSensitiveCondition = true;
}
if (cge->GetHadLinkLanguageSensitiveCondition()) {
impl.HadLinkLanguageSensitiveCondition = true;
}
for (std::string const& lib : llibs) {
if (this->IsLinkLookupScope(lib, lg)) {

View File

@ -357,7 +357,6 @@ public:
};
LinkClosure const* GetLinkClosure(const std::string& config) const;
void ComputeLinkClosure(const std::string& config, LinkClosure& lc) const;
cmLinkImplementation const* GetLinkImplementation(
const std::string& config) const;
@ -816,6 +815,7 @@ private:
std::string& outPrefix, std::string& outBase,
std::string& outSuffix) const;
mutable std::string LinkerLanguage;
using LinkClosureMapType = std::map<std::string, LinkClosure>;
mutable LinkClosureMapType LinkClosureMap;
@ -850,6 +850,10 @@ private:
void CheckPropertyCompatibility(cmComputeLinkInformation& info,
const std::string& config) const;
void ComputeLinkClosure(const std::string& config, LinkClosure& lc) const;
bool ComputeLinkClosure(const std::string& config, LinkClosure& lc,
bool secondPass) const;
struct LinkImplClosure : public std::vector<cmGeneratorTarget const*>
{
bool Done = false;
@ -868,6 +872,17 @@ private:
std::string GetLinkInterfaceDependentStringAsBoolProperty(
const std::string& p, const std::string& config) const;
friend class cmTargetCollectLinkLanguages;
cmLinkInterface const* GetLinkInterface(const std::string& config,
const cmGeneratorTarget* headTarget,
bool secondPass) const;
void ComputeLinkInterface(const std::string& config,
cmOptionalLinkInterface& iface,
const cmGeneratorTarget* head,
bool secondPass) const;
cmLinkImplementation const* GetLinkImplementation(const std::string& config,
bool secondPass) const;
// Cache import information from properties for each configuration.
struct ImportInfo
{
@ -894,9 +909,10 @@ private:
the link dependencies of this target. */
std::string CheckCMP0004(std::string const& item) const;
cmLinkInterface const* GetImportLinkInterface(
const std::string& config, const cmGeneratorTarget* head,
bool usage_requirements_only) const;
cmLinkInterface const* GetImportLinkInterface(const std::string& config,
const cmGeneratorTarget* head,
bool usage_requirements_only,
bool secondPass = false) const;
using KindedSourcesMapType = std::map<std::string, KindedSources>;
mutable KindedSourcesMapType KindedSourcesMap;
@ -940,7 +956,8 @@ private:
const cmGeneratorTarget* headTarget,
bool usage_requirements_only,
std::vector<cmLinkItem>& items,
bool& hadHeadSensitiveCondition) const;
bool& hadHeadSensitiveCondition,
bool& hadLinkLanguageSensitiveCondition) const;
void LookupLinkItems(std::vector<std::string> const& names,
cmListFileBacktrace const& bt,
std::vector<cmLinkItem>& items) const;

View File

@ -81,6 +81,9 @@ struct cmLinkInterface : public cmLinkInterfaceLibraries
std::vector<cmLinkItem> WrongConfigLibraries;
bool ImplementationIsInterface = false;
// Whether the list depends on a link language genex.
bool HadLinkLanguageSensitiveCondition = false;
};
struct cmOptionalLinkInterface : public cmLinkInterface
@ -100,6 +103,9 @@ struct cmLinkImplementation : public cmLinkImplementationLibraries
{
// Languages whose runtime libraries must be linked.
std::vector<std::string> Languages;
// Whether the list depends on a link language genex.
bool HadLinkLanguageSensitiveCondition = false;
};
// Cache link implementation computation from each configuration.

View File

@ -366,6 +366,28 @@ is not newer than dependency
${linkdep2}
is not newer than dependency
${TEST_LINK_DEPENDS}
")
endif()
set(linkdep3 ${BuildDepends_BINARY_DIR}/Project/linkdep3${CMAKE_EXECUTABLE_SUFFIX})
if(${linkdep3} IS_NEWER_THAN ${TEST_LINK_DEPENDS})
message("$<LINK_LANGUAGE> in LINK_DEPENDS worked")
else()
message(SEND_ERROR "$<LINK_LANGUAGE> in LINK_DEPENDS failed. Executable
${linkdep3}
is not newer than dependency
${TEST_LINK_DEPENDS}
")
endif()
set(linkdep4 ${BuildDepends_BINARY_DIR}/Project/linkdep4${CMAKE_EXECUTABLE_SUFFIX})
if(${linkdep4} IS_NEWER_THAN ${TEST_LINK_DEPENDS})
message("$<LINK_LANGUAGE> in INTERFACE_LINK_DEPENDS worked")
else()
message(SEND_ERROR "$<LINK_LANGUAGE> in INTERFACE_LINK_DEPENDS failed. Executable
${linkdep4}
is not newer than dependency
${TEST_LINK_DEPENDS}
")
endif()
endif()

View File

@ -118,6 +118,14 @@ if(TEST_LINK_DEPENDS)
set_property(TARGET foo_interface PROPERTY INTERFACE_LINK_DEPENDS $<1:${TEST_LINK_DEPENDS}>)
add_executable(linkdep2 linkdep.cxx)
target_link_libraries(linkdep2 PRIVATE foo_interface)
add_executable(linkdep3 linkdep.cxx)
set_property(TARGET linkdep3 PROPERTY LINK_DEPENDS $<$<LINK_LANGUAGE:CXX>:${TEST_LINK_DEPENDS}>)
add_library(foo_interface2 INTERFACE)
set_property(TARGET foo_interface2 PROPERTY INTERFACE_LINK_DEPENDS $<$<LINK_LANGUAGE:CXX>:${TEST_LINK_DEPENDS}>)
add_executable(linkdep4 linkdep.cxx)
target_link_libraries(linkdep4 PRIVATE foo_interface2)
endif()
add_library(link_depends_no_shared_lib SHARED link_depends_no_shared_lib.c

View File

@ -0,0 +1,9 @@
CMake Error at LINK_LANGUAGE-add_custom_command.cmake:2 \(add_custom_command\):
Error evaluating generator expression:
\$<LINK_LANGUAGE>
\$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
libraries, link directories, link options and link depends.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,4 @@
add_custom_target(drive)
add_custom_command(TARGET drive PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E echo $<LINK_LANGUAGE>
)

View File

@ -0,0 +1,9 @@
CMake Error at LINK_LANGUAGE-add_custom_target.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<LINK_LANGUAGE>
\$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
libraries, link directories, link options and link depends.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,3 @@
add_custom_target(drive
COMMAND ${CMAKE_COMMAND} -E echo $<LINK_LANGUAGE>
)

View File

@ -0,0 +1,9 @@
CMake Error at LINK_LANGUAGE-add_executable.cmake:1 \(add_executable\):
Error evaluating generator expression:
\$<LINK_LANGUAGE>
\$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
libraries, link directories, link options and link depends.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1 @@
add_executable(empty empty.$<LINK_LANGUAGE>)

View File

@ -0,0 +1,9 @@
CMake Error at LINK_LANGUAGE-add_library.cmake:1 \(add_library\):
Error evaluating generator expression:
\$<LINK_LANGUAGE>
\$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
libraries, link directories, link options and link depends.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1 @@
add_library(empty empty.$<LINK_LANGUAGE>)

View File

@ -0,0 +1,9 @@
CMake Error at LINK_LANGUAGE-add_test.cmake:5 \(add_test\):
Error evaluating generator expression:
\$<LINK_LANGUAGE>
\$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
libraries, link directories, link options and link depends.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,5 @@
include(CTest)
enable_testing()
add_test(NAME dummy COMMAND ${CMAKE_COMMAND} -E echo $<LINK_LANGUAGE>)

View File

@ -0,0 +1,9 @@
CMake Error at LINK_LANGUAGE-file_generate.cmake:3 \(file\):
Error evaluating generator expression:
\$<LINK_LANGUAGE>
\$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
libraries, link directories, link options and link depends.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,6 @@
enable_language(CXX C)
file(GENERATE
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/output.txt
CONTENT "LANG_IS_$<LINK_LANGUAGE>\n"
)

View File

@ -0,0 +1,10 @@
CMake Error:
Error evaluating generator expression:
\$<LINK_LANGUAGE>
\$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
libraries, link directories, link options and link depends.
CMake Generate step failed. Build files cannot be regenerated correctly.

View File

@ -0,0 +1,5 @@
install(FILES
empty.$<LINK_LANGUAGE>
DESTINATION src
)

View File

@ -0,0 +1,9 @@
CMake Error at LINK_LANGUAGE-target_sources.cmake:2 \(target_sources\):
Error evaluating generator expression:
\$<LINK_LANGUAGE>
\$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
libraries, link directories, link options and link depends.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,2 @@
add_library(empty)
target_sources(empty PRIVATE empty.$<LINK_LANGUAGE>)

View File

@ -0,0 +1,4 @@
enable_language(C)
add_executable(empty empty.c)
target_link_options(empty PRIVATE $<$<LINK_LANGUAGE:CXX>:$<TARGET_EXISTS:too,many,parameters>>)

View File

@ -0,0 +1,9 @@
CMake Error at LINK_LANGUAGE-wrong-usage1.cmake:4 \(target_compile_definitions\):
Error evaluating generator expression:
\$<LINK_LANGUAGE:C>
\$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
libraries, link directories, link options and link depends.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,4 @@
enable_language(C)
add_executable(empty empty.c)
target_compile_definitions(empty PRIVATE $<$<LINK_LANGUAGE:C>:DEF>)

View File

@ -0,0 +1,9 @@
CMake Error at LINK_LANGUAGE-wrong-usage2.cmake:4 \(target_compile_options\):
Error evaluating generator expression:
\$<LINK_LANGUAGE:C>
\$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
libraries, link directories, link options and link depends.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,4 @@
enable_language(C)
add_executable(empty empty.c)
target_compile_options(empty PRIVATE $<$<LINK_LANGUAGE:C>:-OPT>)

View File

@ -0,0 +1,9 @@
CMake Error at LINK_LANGUAGE-wrong-usage3.cmake:4 \(target_include_directories\):
Error evaluating generator expression:
\$<LINK_LANGUAGE:C>
\$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
libraries, link directories, link options and link depends.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,4 @@
enable_language(C)
add_executable(empty empty.c)
target_include_directories(empty PRIVATE $<$<LINK_LANGUAGE:C>:/DIR>)

View File

@ -0,0 +1,8 @@
CMake Error at LINK_LANGUAGE-wrong-usage4.cmake:7 \(target_link_libraries\):
Error evaluating generator expression:
\$<LINK_LANGUAGE>
\$<LINK_LANGUAGE> is not supported in link libraries expression.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,7 @@
enable_language(C)
add_library(libC empty.c)
add_executable(empty empty.c)
target_link_libraries(empty PRIVATE lib$<LINK_LANGUAGE>)

View File

@ -0,0 +1,9 @@
CMake Error at LINK_LANG_AND_ID-add_custom_command.cmake:2 \(add_custom_command\):
Error evaluating generator expression:
\$<LINK_LANG_AND_ID:LANG,ID>
\$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets to specify
link libraries, link directories, link options, and link depends.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,4 @@
add_custom_target(drive)
add_custom_command(TARGET drive PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E echo $<LINK_LANG_AND_ID:LANG,ID>
)

View File

@ -0,0 +1,9 @@
CMake Error at LINK_LANG_AND_ID-add_custom_target.cmake:2 \(add_custom_target\):
Error evaluating generator expression:
\$<LINK_LANG_AND_ID:LANG,ID>
\$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets to specify
link libraries, link directories, link options, and link depends.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,4 @@
add_custom_target(drive
COMMAND ${CMAKE_COMMAND} -E echo $<LINK_LANG_AND_ID:LANG,ID>
)

View File

@ -0,0 +1,9 @@
CMake Error at LINK_LANG_AND_ID-add_executable.cmake:1 \(add_executable\):
Error evaluating generator expression:
\$<LINK_LANG_AND_ID:C,MSVC>
\$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets to specify
link libraries, link directories, link options, and link depends.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,5 @@
add_executable(empty main.c
$<$<LINK_LANG_AND_ID:C,MSVC>:empty.c>
$<$<LINK_LANG_AND_ID:C,GNU>:empty2.c>
$<$<LINK_LANG_AND_ID:C,Clang>:empty3.c>
)

View File

@ -0,0 +1,9 @@
CMake Error at LINK_LANG_AND_ID-add_library.cmake:2 \(add_library\):
Error evaluating generator expression:
\$<LINK_LANG_AND_ID:C,MSVC>
\$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets to specify
link libraries, link directories, link options, and link depends.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,2 @@
add_library(empty empty.$<LINK_LANG_AND_ID:C,MSVC>)

View File

@ -0,0 +1,9 @@
CMake Error at LINK_LANG_AND_ID-add_test.cmake:5 \(add_test\):
Error evaluating generator expression:
\$<LINK_LANG_AND_ID:CXX,GNU>
\$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets to specify
link libraries, link directories, link options, and link depends.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,5 @@
include(CTest)
enable_testing()
add_test(NAME dummy COMMAND ${CMAKE_COMMAND} -E echo $<LINK_LANG_AND_ID:CXX,GNU>)

View File

@ -0,0 +1,9 @@
CMake Error at LINK_LANG_AND_ID-file_generate.cmake:3 \(file\):
Error evaluating generator expression:
\$<LINK_LANG_AND_ID:C,GNU>
\$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets to specify
link libraries, link directories, link options, and link depends.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,6 @@
enable_language(CXX C)
file(GENERATE
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/output.txt
CONTENT "LANG_IS_$<$<LINK_LANG_AND_ID:C,GNU>:C>\n"
)

View File

@ -0,0 +1,10 @@
CMake Error:
Error evaluating generator expression:
\$<LINK_LANG_AND_ID:C,MSVC>
\$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets to specify
link libraries, link directories, link options, and link depends.
CMake Generate step failed. Build files cannot be regenerated correctly.

View File

@ -0,0 +1,5 @@
install(FILES
empty.$<LINK_LANG_AND_ID:C,MSVC>
DESTINATION src
)

View File

@ -0,0 +1,8 @@
CMake Error at LINK_LANG_AND_ID-target_sources.cmake:2 \(target_sources\):
Error evaluating generator expression:
\$<LINK_LANG_AND_ID>
\$<LINK_LANG_AND_ID> expression requires at least two parameters.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,2 @@
add_library(empty)
target_sources(empty PRIVATE empty.$<LINK_LANG_AND_ID>)

View File

@ -0,0 +1,4 @@
enable_language(C)
add_executable(empty empty.c)
target_link_options(empty PRIVATE $<$<LINK_LANG_AND_ID:CXX,GNU>:$<TARGET_EXISTS:too,many,parameters>>)

View File

@ -0,0 +1,9 @@
CMake Error at LINK_LANG_AND_ID-wrong-usage1.cmake:4 \(target_compile_definitions\):
Error evaluating generator expression:
\$<LINK_LANG_AND_ID:C,GNU>
\$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets to specify
link libraries, link directories, link options, and link depends.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,4 @@
enable_language(C)
add_executable(empty empty.c)
target_compile_definitions(empty PRIVATE $<$<LINK_LANG_AND_ID:C,GNU>:DEF>)

View File

@ -0,0 +1,9 @@
CMake Error at LINK_LANG_AND_ID-wrong-usage2.cmake:4 \(target_compile_options\):
Error evaluating generator expression:
\$<LINK_LANG_AND_ID:C,GNU>
\$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets to specify
link libraries, link directories, link options, and link depends.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,4 @@
enable_language(C)
add_executable(empty empty.c)
target_compile_options(empty PRIVATE $<$<LINK_LANG_AND_ID:C,GNU>:-OPT>)

View File

@ -0,0 +1,9 @@
CMake Error at LINK_LANG_AND_ID-wrong-usage3.cmake:4 \(target_include_directories\):
Error evaluating generator expression:
\$<LINK_LANG_AND_ID:C,GNU>
\$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets to specify
link libraries, link directories, link options, and link depends.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,4 @@
enable_language(C)
add_executable(empty empty.c)
target_include_directories(empty PRIVATE $<$<LINK_LANG_AND_ID:C,GNU>:/DIR>)

View File

@ -39,6 +39,31 @@ run_cmake(COMPILE_LANG_AND_ID-add_executable)
run_cmake(COMPILE_LANG_AND_ID-add_library)
run_cmake(COMPILE_LANG_AND_ID-add_test)
run_cmake(COMPILE_LANG_AND_ID-unknown-lang)
run_cmake(LINK_LANGUAGE-add_custom_target)
run_cmake(LINK_LANGUAGE-add_custom_command)
run_cmake(LINK_LANGUAGE-install)
run_cmake(LINK_LANGUAGE-target_sources)
run_cmake(LINK_LANGUAGE-add_executable)
run_cmake(LINK_LANGUAGE-add_library)
run_cmake(LINK_LANGUAGE-add_test)
run_cmake(LINK_LANGUAGE-unknown-lang)
run_cmake(LINK_LANGUAGE-wrong-usage1)
run_cmake(LINK_LANGUAGE-wrong-usage2)
run_cmake(LINK_LANGUAGE-wrong-usage3)
run_cmake(LINK_LANGUAGE-wrong-usage4)
run_cmake(LINK_LANGUAGE-file_generate)
run_cmake(LINK_LANG_AND_ID-add_custom_target)
run_cmake(LINK_LANG_AND_ID-add_custom_command)
run_cmake(LINK_LANG_AND_ID-install)
run_cmake(LINK_LANG_AND_ID-target_sources)
run_cmake(LINK_LANG_AND_ID-add_executable)
run_cmake(LINK_LANG_AND_ID-add_library)
run_cmake(LINK_LANG_AND_ID-add_test)
run_cmake(LINK_LANG_AND_ID-unknown-lang)
run_cmake(LINK_LANG_AND_ID-wrong-usage1)
run_cmake(LINK_LANG_AND_ID-wrong-usage2)
run_cmake(LINK_LANG_AND_ID-wrong-usage3)
run_cmake(LINK_LANG_AND_ID-file_generate)
run_cmake(TARGET_FILE-recursion)
run_cmake(OUTPUT_NAME-recursion)
run_cmake(TARGET_FILE_PREFIX)

View File

@ -0,0 +1 @@
1

View File

@ -1,4 +1,4 @@
CMake Error at LINK_LANGUAGE-genex.cmake:[0-9]+ \(target_link_libraries\):
CMake Error at LINKER_LANGUAGE-genex.cmake:[0-9]+ \(target_link_libraries\):
Error evaluating generator expression:
\$<TARGET_PROPERTY:LINKER_LANGUAGE>

View File

@ -1,7 +1,7 @@
include(RunCMake)
run_cmake(NoLangSHARED)
run_cmake(LINK_LANGUAGE-genex)
run_cmake(LINKER_LANGUAGE-genex)
run_cmake(link-libraries-TARGET_FILE-genex)
run_cmake(link-libraries-TARGET_FILE-genex-ok)

View File

@ -0,0 +1,16 @@
enable_language(C)
set(obj "${CMAKE_C_OUTPUT_EXTENSION}")
if(BORLAND)
set(pre -)
endif()
add_link_options ($<$<LINK_LANGUAGE:C>:${pre}BADFLAG_LANG_C${obj}>
$<$<LINK_LANGUAGE:CXX>:${pre}BADFLAG_LANG_CXX${obj}>)
add_library(LinkOptions_shared SHARED LinkOptionsLib.c)
add_library(LinkOptions_mod MODULE LinkOptionsLib.c)
add_executable(LinkOptions_exe LinkOptionsExe.c)

View File

@ -11,5 +11,8 @@ endif()
file(READ "${RunCMake_TEST_BINARY_DIR}/LINKER.txt" linker_flag)
if (NOT actual_stdout MATCHES "${linker_flag}")
set (RunCMake_TEST_FAILED "LINKER: was not expanded correctly.")
if (RunCMake_TEST_FAILED)
string (APPEND RunCMake_TEST_FAILED "\n")
endif()
string (APPEND RunCMake_TEST_FAILED "LINKER: was not expanded correctly.")
endif()

View File

@ -3,5 +3,8 @@ if (NOT actual_stdout MATCHES "BADFLAG_EXECUTABLE_RELEASE")
set (RunCMake_TEST_FAILED "Not found expected 'BADFLAG_EXECUTABLE_RELEASE'.")
endif()
if (actual_stdout MATCHES "BADFLAG_(SHARED|MODULE)_RELEASE")
set (RunCMake_TEST_FAILED "Found unexpected 'BADFLAG_(SHARED|MODULE)_RELEASE'.")
if (RunCMake_TEST_FAILED)
string (APPEND RunCMake_TEST_FAILED "\n")
endif()
string (APPEND RunCMake_TEST_FAILED "Found unexpected 'BADFLAG_(SHARED|MODULE)_RELEASE'.")
endif()

View File

@ -3,5 +3,8 @@ if (NOT actual_stdout MATCHES "BADFLAG_MODULE_RELEASE")
set (RunCMake_TEST_FAILED "Not found expected 'BADFLAG_MODULE_RELEASE'.")
endif()
if (actual_stdout MATCHES "BADFLAG_(SHARED|EXECUTABLE)_RELEASE")
set (RunCMake_TEST_FAILED "Found unexpected 'BADFLAG_(SHARED|EXECUTABLE)_RELEASE'.")
if (RunCMake_TEST_FAILED)
string (APPEND RunCMake_TEST_FAILED "\n")
endif()
string (APPEND RunCMake_TEST_FAILED "Found unexpected 'BADFLAG_(SHARED|EXECUTABLE)_RELEASE'.")
endif()

View File

@ -3,5 +3,8 @@ if (NOT actual_stdout MATCHES "BADFLAG_SHARED_RELEASE")
set (RunCMake_TEST_FAILED "Not found expected 'BADFLAG_SHARED_RELEASE'.")
endif()
if (actual_stdout MATCHES "BADFLAG_(MODULE|EXECUTABLE)_RELEASE")
set (RunCMake_TEST_FAILED "Found unexpected 'BADFLAG_(MODULE|EXECUTABLE)_RELEASE'.")
if (RunCMake_TEST_FAILED)
string (APPEND RunCMake_TEST_FAILED "\n")
endif()
string (APPEND RunCMake_TEST_FAILED "Found unexpected 'BADFLAG_(MODULE|EXECUTABLE)_RELEASE'.")
endif()

View File

@ -0,0 +1,7 @@
#if defined(_WIN32)
__declspec(dllexport)
#endif
int flags_lib(void)
{
return 0;
}

View File

@ -23,6 +23,21 @@ if (NOT CMAKE_C_COMPILER_ID STREQUAL "Intel")
run_cmake_target(LINK_OPTIONS mod LinkOptions_mod --config Release)
run_cmake_target(LINK_OPTIONS exe LinkOptions_exe --config Release)
run_cmake(genex_LINK_LANGUAGE)
run_cmake_target(genex_LINK_LANGUAGE shared_c LinkOptions_shared_c --config Release)
run_cmake_target(genex_LINK_LANGUAGE shared_cxx LinkOptions_shared_cxx --config Release)
run_cmake_target(genex_LINK_LANGUAGE mod LinkOptions_mod --config Release)
run_cmake_target(genex_LINK_LANGUAGE exe LinkOptions_exe --config Release)
run_cmake(genex_LINK_LANG_AND_ID)
run_cmake_target(genex_LINK_LANG_AND_ID shared_c LinkOptions_shared_c --config Release)
run_cmake_target(genex_LINK_LANG_AND_ID shared_cxx LinkOptions_shared_cxx --config Release)
run_cmake_target(genex_LINK_LANG_AND_ID mod LinkOptions_mod --config Release)
run_cmake_target(genex_LINK_LANG_AND_ID exe LinkOptions_exe --config Release)
unset(RunCMake_TEST_OPTIONS)
unset(RunCMake_TEST_OUTPUT_MERGE)
endif()

View File

@ -0,0 +1,2 @@
include ("${CMAKE_CURRENT_LIST_DIR}/genex_LINK_LANGUAGE-validation.cmake")

View File

@ -0,0 +1,2 @@
include ("${CMAKE_CURRENT_LIST_DIR}/genex_LINK_LANGUAGE-validation.cmake")

Some files were not shown because too many files have changed in this diff Show More