diff options
26 files changed, 817 insertions, 118 deletions
diff --git a/Source/cmDocumentGeneratorExpressions.h b/Source/cmDocumentGeneratorExpressions.h index 445fd0e..b8889ac 100644 --- a/Source/cmDocumentGeneratorExpressions.h +++ b/Source/cmDocumentGeneratorExpressions.h @@ -26,6 +26,16 @@ "strings which contain a '>' for example.\n" \ " $<COMMA> = A literal ','. Used to compare " \ "strings which contain a ',' for example.\n" \ + " $<TARGET_NAME:...> = Marks ... as being the name of a " \ + "target. This is required if exporting targets to multiple " \ + "dependent export sets. The '...' must be a literal name of a " \ + "target- it may not contain generator expressions.\n" \ + " $<INSTALL_INTERFACE:...> = content of \"...\" when the property " \ + "is exported using install(EXPORT), and empty otherwise.\n" \ + " $<BUILD_INTERFACE:...> = content of \"...\" when the property " \ + "is exported using export(), or when the target is used by another " \ + "target in the same buildsystem. Expands to the empty string " \ + "otherwise.\n" \ " $<TARGET_FILE:tgt> = main file (.exe, .so.1.2, .a)\n" \ " $<TARGET_LINKER_FILE:tgt> = file used to link (.a, .lib, .so)\n" \ " $<TARGET_SONAME_FILE:tgt> = file with soname (.so.3)\n" \ diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx index cd6a7ab..9533319 100644 --- a/Source/cmExportBuildFileGenerator.cxx +++ b/Source/cmExportBuildFileGenerator.cxx @@ -22,6 +22,7 @@ cmExportBuildFileGenerator::cmExportBuildFileGenerator() //---------------------------------------------------------------------------- bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os) { + std::vector<cmTarget*> allTargets; { std::string expectedTargets; std::string sep; @@ -31,20 +32,10 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os) { expectedTargets += sep + this->Namespace + (*tei)->GetName(); sep = " "; - } - - this->GenerateExpectedTargetsCode(os, expectedTargets); - } - - // Create all the imported targets. - for(std::vector<cmTarget*>::const_iterator - tei = this->Exports->begin(); - tei != this->Exports->end(); ++tei) - { cmTarget* te = *tei; if(this->ExportedTargets.insert(te).second) { - this->GenerateImportTargetCode(os, te); + allTargets.push_back(te); } else { @@ -58,6 +49,33 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os) } } + this->GenerateExpectedTargetsCode(os, expectedTargets); + } + + std::vector<std::string> missingTargets; + + // Create all the imported targets. + for(std::vector<cmTarget*>::const_iterator + tei = allTargets.begin(); + tei != allTargets.end(); ++tei) + { + cmTarget* te = *tei; + this->GenerateImportTargetCode(os, te); + + ImportPropertyMap properties; + + this->PopulateInterfaceProperty("INTERFACE_INCLUDE_DIRECTORIES", te, + cmGeneratorExpression::BuildInterface, + properties, missingTargets); + this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", te, + cmGeneratorExpression::BuildInterface, + properties, missingTargets); + + this->GenerateInterfaceProperties(te, os, properties); + } + + this->GenerateMissingTargetsCheckCode(os, missingTargets); + // Generate import file content for each configuration. for(std::vector<std::string>::const_iterator ci = this->Configurations.begin(); diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index 3f738cc..2b133be 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -125,6 +125,201 @@ void cmExportFileGenerator::GenerateImportConfig(std::ostream& os, } //---------------------------------------------------------------------------- +void cmExportFileGenerator::PopulateInterfaceProperty(const char *propName, + const char *outputName, + cmTarget *target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap &properties, + std::vector<std::string> &missingTargets) +{ + const char *input = target->GetProperty(propName); + if (input) + { + if (!*input) + { + // Set to empty + properties[outputName] = ""; + return; + } + + std::string prepro = cmGeneratorExpression::Preprocess(input, + preprocessRule); + if (!prepro.empty()) + { + this->ResolveTargetsInGeneratorExpressions(prepro, target, + missingTargets); + properties[outputName] = prepro; + } + } +} + +//---------------------------------------------------------------------------- +void cmExportFileGenerator::PopulateInterfaceProperty(const char *propName, + cmTarget *target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap &properties, + std::vector<std::string> &missingTargets) +{ + this->PopulateInterfaceProperty(propName, propName, target, preprocessRule, + properties, missingTargets); +} + +//---------------------------------------------------------------------------- +void cmExportFileGenerator::GenerateInterfaceProperties(cmTarget *target, + std::ostream& os, + const ImportPropertyMap &properties) +{ + if (!properties.empty()) + { + std::string targetName = this->Namespace; + targetName += target->GetName(); + os << "SET_TARGET_PROPERTIES(" << targetName << " PROPERTIES\n"; + for(ImportPropertyMap::const_iterator pi = properties.begin(); + pi != properties.end(); ++pi) + { + os << " " << pi->first << " \"" << pi->second << "\"\n"; + } + os << ")\n\n"; + } +} + +//---------------------------------------------------------------------------- +void +cmExportFileGenerator::ResolveTargetsInGeneratorExpressions( + std::string &input, + cmTarget* target, + std::vector<std::string> &missingTargets) +{ + std::string::size_type pos = 0; + std::string::size_type lastPos = pos; + + cmMakefile *mf = target->GetMakefile(); + std::string errorString; + + while((pos = input.find("$<TARGET_PROPERTY:", lastPos)) != input.npos) + { + std::string::size_type nameStartPos = pos + + sizeof("$<TARGET_PROPERTY:") - 1; + std::string::size_type closePos = input.find(">", nameStartPos); + std::string::size_type commaPos = input.find(",", nameStartPos); + std::string::size_type nextOpenPos = input.find("$<", nameStartPos); + if (commaPos == input.npos // Implied 'this' target + || closePos == input.npos // Imcomplete expression. + || closePos < commaPos // Implied 'this' target + || nextOpenPos < commaPos) // Non-literal + { + lastPos = nameStartPos; + continue; + } + + const std::string targetName = input.substr(nameStartPos, + commaPos - nameStartPos); + + pos = nameStartPos; // We're not going to replace the entire expression, + // but only the target parameter. + if (cmTarget *tgt = mf->FindTargetToUse(targetName.c_str())) + { + if(tgt->IsImported()) + { + pos += targetName.size(); + } + else if(this->ExportedTargets.find(tgt) != this->ExportedTargets.end()) + { + input.replace(pos, targetName.size(), + this->Namespace + targetName); + pos += this->Namespace.size() + targetName.size(); + } + else + { + std::string namespacedTarget; + this->HandleMissingTarget(namespacedTarget, missingTargets, + mf, target, tgt); + if (!namespacedTarget.empty()) + { + input.replace(pos, targetName.size(), namespacedTarget); + pos += namespacedTarget.size(); + } + } + } + else + { + errorString = "$<TARGET_PROPERTY:" + targetName + ",prop> requires " + "its first parameter to be a reachable target."; + } + lastPos = pos; + if (!errorString.empty()) + { + break; + } + } + if (!errorString.empty()) + { + mf->IssueMessage(cmake::FATAL_ERROR, errorString); + return; + } + + pos = 0; + lastPos = pos; + while((pos = input.find("$<TARGET_NAME:", lastPos)) != input.npos) + { + std::string::size_type nameStartPos = pos + sizeof("$<TARGET_NAME:") - 1; + std::string::size_type endPos = input.find(">", nameStartPos); + if (endPos == input.npos) + { + errorString = "$<TARGET_NAME:...> expression incomplete"; + } + const std::string targetName = input.substr(nameStartPos, + endPos - nameStartPos); + if(targetName.find("$<", lastPos) != input.npos) + { + errorString = "$<TARGET_NAME:...> requires its parameter to be a " + "literal."; + } + if (cmTarget *tgt = mf->FindTargetToUse(targetName.c_str())) + { + if(tgt->IsImported()) + { + input.replace(pos, sizeof("$<TARGET_NAME:") + targetName.size(), + targetName); + pos += sizeof("$<TARGET_NAME:") + targetName.size(); + } + else if(this->ExportedTargets.find(tgt) != this->ExportedTargets.end()) + { + input.replace(pos, sizeof("$<TARGET_NAME:") + targetName.size(), + this->Namespace + targetName); + pos += sizeof("$<TARGET_NAME:") + targetName.size(); + } + else + { + std::string namespacedTarget; + this->HandleMissingTarget(namespacedTarget, missingTargets, + mf, target, tgt); + if (!namespacedTarget.empty()) + { + input.replace(pos, sizeof("$<TARGET_NAME:") + targetName.size(), + namespacedTarget); + pos += sizeof("$<TARGET_NAME:") + targetName.size(); + } + } + } + else + { + errorString = "$<TARGET_NAME:...> requires its parameter to be a " + "reachable target."; + } + lastPos = pos; + if (!errorString.empty()) + { + break; + } + } + if (!errorString.empty()) + { + mf->IssueMessage(cmake::FATAL_ERROR, errorString); + } +} + +//---------------------------------------------------------------------------- void cmExportFileGenerator ::SetImportDetailProperties(const char* config, std::string const& suffix, diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h index 4a75c52..7c58ad8 100644 --- a/Source/cmExportFileGenerator.h +++ b/Source/cmExportFileGenerator.h @@ -13,6 +13,7 @@ #define cmExportFileGenerator_h #include "cmCommand.h" +#include "cmGeneratorExpression.h" /** \class cmExportFileGenerator * \brief Generate a file exporting targets from a build or install tree. @@ -93,6 +94,17 @@ protected: cmMakefile* mf, cmTarget* depender, cmTarget* dependee) = 0; + void PopulateInterfaceProperty(const char *, + cmTarget *target, + cmGeneratorExpression::PreprocessContext, + ImportPropertyMap &properties, + std::vector<std::string> &missingTargets); + void GenerateInterfaceProperties(cmTarget *target, std::ostream& os, + const ImportPropertyMap &properties); + + void ResolveTargetsInGeneratorExpressions(std::string &input, + cmTarget* target, + std::vector<std::string> &missingTargets); // The namespace in which the exports are placed in the generated file. std::string Namespace; @@ -109,6 +121,13 @@ protected: // The set of targets included in the export. std::set<cmTarget*> ExportedTargets; + +private: + void PopulateInterfaceProperty(const char *, const char *, + cmTarget *target, + cmGeneratorExpression::PreprocessContext, + ImportPropertyMap &properties, + std::vector<std::string> &missingTargets); }; #endif diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx index 6ba7d9f..bc953c9 100644 --- a/Source/cmExportInstallFileGenerator.cxx +++ b/Source/cmExportInstallFileGenerator.cxx @@ -39,6 +39,7 @@ std::string cmExportInstallFileGenerator::GetConfigImportFileGlob() //---------------------------------------------------------------------------- bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) { + std::vector<cmTarget*> allTargets; { std::string expectedTargets; std::string sep; @@ -48,20 +49,10 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) { expectedTargets += sep + this->Namespace + (*tei)->Target->GetName(); sep = " "; - } - - this->GenerateExpectedTargetsCode(os, expectedTargets); - } - - // Create all the imported targets. - for(std::vector<cmTargetExport*>::const_iterator - tei = this->IEGen->GetExportSet()->GetTargetExports()->begin(); - tei != this->IEGen->GetExportSet()->GetTargetExports()->end(); ++tei) - { cmTargetExport const* te = *tei; if(this->ExportedTargets.insert(te->Target).second) { - this->GenerateImportTargetCode(os, te->Target); + allTargets.push_back(te->Target); } else { @@ -75,6 +66,35 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) } } + this->GenerateExpectedTargetsCode(os, expectedTargets); + } + + std::vector<std::string> missingTargets; + + // Create all the imported targets. + for(std::vector<cmTarget*>::const_iterator + tei = allTargets.begin(); + tei != allTargets.end(); ++tei) + { + cmTarget* te = *tei; + this->GenerateImportTargetCode(os, te); + + ImportPropertyMap properties; + + this->PopulateInterfaceProperty("INTERFACE_INCLUDE_DIRECTORIES", + te, + cmGeneratorExpression::InstallInterface, + properties, missingTargets); + this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", + te, + cmGeneratorExpression::InstallInterface, + properties, missingTargets); + + this->GenerateInterfaceProperties(te, os, properties); + } + + this->GenerateMissingTargetsCheckCode(os, missingTargets); + // Now load per-configuration properties for them. os << "# Load information for each installed configuration.\n" << "GET_FILENAME_COMPONENT(_DIR \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n" diff --git a/Source/cmGeneratorExpression.cxx b/Source/cmGeneratorExpression.cxx index 3fd4a5f..6d003e1 100644 --- a/Source/cmGeneratorExpression.cxx +++ b/Source/cmGeneratorExpression.cxx @@ -53,7 +53,22 @@ cmGeneratorExpression::~cmGeneratorExpression() //---------------------------------------------------------------------------- const char *cmCompiledGeneratorExpression::Evaluate( cmMakefile* mf, const char* config, bool quiet, - cmTarget *target, + cmTarget *headTarget, + cmGeneratorExpressionDAGChecker *dagChecker) const +{ + return this->Evaluate(mf, + config, + quiet, + headTarget, + headTarget, + dagChecker); +} + +//---------------------------------------------------------------------------- +const char *cmCompiledGeneratorExpression::Evaluate( + cmMakefile* mf, const char* config, bool quiet, + cmTarget *headTarget, + cmTarget *currentTarget, cmGeneratorExpressionDAGChecker *dagChecker) const { if (!this->NeedsParsing) @@ -73,7 +88,8 @@ const char *cmCompiledGeneratorExpression::Evaluate( context.Config = config; context.Quiet = quiet; context.HadError = false; - context.Target = target; + context.HeadTarget = headTarget; + context.CurrentTarget = currentTarget ? currentTarget : headTarget; context.Backtrace = this->Backtrace; for ( ; it != end; ++it) @@ -123,15 +139,9 @@ cmCompiledGeneratorExpression::~cmCompiledGeneratorExpression() } } -std::string cmGeneratorExpression::Preprocess(const std::string &input, - PreprocessContext context) +//---------------------------------------------------------------------------- +static std::string stripAllGeneratorExpressions(const std::string &input) { - if (context != StripAllGeneratorExpressions) - { - assert(!"cmGeneratorExpression::Preprocess called with invalid args"); - return std::string(); - } - std::string result; std::string::size_type pos = 0; std::string::size_type lastPos = pos; @@ -170,3 +180,81 @@ std::string cmGeneratorExpression::Preprocess(const std::string &input, result += input.substr(lastPos); return result; } + +//---------------------------------------------------------------------------- +static std::string stripExportInterface(const std::string &input, + cmGeneratorExpression::PreprocessContext context) +{ + std::string result; + + std::string::size_type pos = 0; + std::string::size_type lastPos = pos; + while((pos = input.find("$<BUILD_INTERFACE:", lastPos)) != input.npos + || (pos = input.find("$<INSTALL_INTERFACE:", lastPos)) != input.npos) + { + result += input.substr(lastPos, pos - lastPos); + const bool gotInstallInterface = input[pos + 2] == 'I'; + pos += gotInstallInterface ? sizeof("$<INSTALL_INTERFACE:") - 1 + : sizeof("$<BUILD_INTERFACE:") - 1; + int nestingLevel = 1; + const char *c = input.c_str() + pos; + const char * const cStart = c; + for ( ; *c; ++c) + { + if(c[0] == '$' && c[1] == '<') + { + ++nestingLevel; + ++c; + continue; + } + if(c[0] == '>') + { + --nestingLevel; + if (nestingLevel != 0) + { + continue; + } + if(context == cmGeneratorExpression::BuildInterface + && !gotInstallInterface) + { + result += input.substr(pos, c - cStart); + } + else if(context == cmGeneratorExpression::InstallInterface + && gotInstallInterface) + { + result += input.substr(pos, c - cStart); + } + break; + } + } + const std::string::size_type traversed = (c - cStart) + 1; + if (!*c) + { + result += std::string(gotInstallInterface ? "$<INSTALL_INTERFACE:" + : "$<BUILD_INTERFACE:") + + input.substr(pos, traversed); + } + pos += traversed; + lastPos = pos; + } + result += input.substr(lastPos); + + return result; +} + +//---------------------------------------------------------------------------- +std::string cmGeneratorExpression::Preprocess(const std::string &input, + PreprocessContext context) +{ + if (context == StripAllGeneratorExpressions) + { + return stripAllGeneratorExpressions(input); + } + else if (context == BuildInterface || context == InstallInterface) + { + return stripExportInterface(input, context); + } + + assert(!"cmGeneratorExpression::Preprocess called with invalid args"); + return std::string(); +} diff --git a/Source/cmGeneratorExpression.h b/Source/cmGeneratorExpression.h index 15e637c..dcdfefb 100644 --- a/Source/cmGeneratorExpression.h +++ b/Source/cmGeneratorExpression.h @@ -51,7 +51,9 @@ public: cmsys::auto_ptr<cmCompiledGeneratorExpression> Parse(const char* input); enum PreprocessContext { - StripAllGeneratorExpressions + StripAllGeneratorExpressions, + BuildInterface, + InstallInterface }; static std::string Preprocess(const std::string &input, @@ -69,8 +71,13 @@ class cmCompiledGeneratorExpression public: const char* Evaluate(cmMakefile* mf, const char* config, bool quiet = false, - cmTarget *target = 0, + cmTarget *headTarget = 0, + cmTarget *currentTarget = 0, cmGeneratorExpressionDAGChecker *dagChecker = 0) const; + const char* Evaluate(cmMakefile* mf, const char* config, + bool quiet, + cmTarget *headTarget, + cmGeneratorExpressionDAGChecker *dagChecker) const; /** Get set of targets found during evaluations. */ std::set<cmTarget*> const& GetTargets() const diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx index bfb0ddf..2e5b5ae 100644 --- a/Source/cmGeneratorExpressionDAGChecker.cxx +++ b/Source/cmGeneratorExpressionDAGChecker.cxx @@ -24,13 +24,14 @@ cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker( : Parent(parent), Target(target), Property(property), Content(content), Backtrace(backtrace) { - this->IsDAG = this->isDAG(); + this->CheckResult = this->checkGraph(); } //---------------------------------------------------------------------------- -bool cmGeneratorExpressionDAGChecker::check() const +cmGeneratorExpressionDAGChecker::Result +cmGeneratorExpressionDAGChecker::check() const { - return this->IsDAG; + return this->CheckResult; } //---------------------------------------------------------------------------- @@ -38,7 +39,7 @@ void cmGeneratorExpressionDAGChecker::reportError( cmGeneratorExpressionContext *context, const std::string &expr) { - if (this->IsDAG) + if (this->CheckResult == DAG) { return; } @@ -57,7 +58,7 @@ void cmGeneratorExpressionDAGChecker::reportError( e << "Error evaluating generator expression:\n" << " " << expr << "\n" << "Self reference on target \"" - << context->Target->GetName() << "\".\n"; + << context->HeadTarget->GetName() << "\".\n"; context->Makefile->GetCMakeInstance() ->IssueMessage(cmake::FATAL_ERROR, e.str().c_str(), parent->Backtrace); @@ -91,16 +92,17 @@ void cmGeneratorExpressionDAGChecker::reportError( } //---------------------------------------------------------------------------- -bool cmGeneratorExpressionDAGChecker::isDAG() const +cmGeneratorExpressionDAGChecker::Result +cmGeneratorExpressionDAGChecker::checkGraph() const { const cmGeneratorExpressionDAGChecker *parent = this->Parent; while (parent) { if (this->Target == parent->Target && this->Property == parent->Property) { - return false; + return parent->Parent ? CYCLIC_REFERENCE : SELF_REFERENCE; } parent = parent->Parent; } - return true; + return DAG; } diff --git a/Source/cmGeneratorExpressionDAGChecker.h b/Source/cmGeneratorExpressionDAGChecker.h index ffc84f8..48f26ed 100644 --- a/Source/cmGeneratorExpressionDAGChecker.h +++ b/Source/cmGeneratorExpressionDAGChecker.h @@ -25,12 +25,18 @@ struct cmGeneratorExpressionDAGChecker const GeneratorExpressionContent *content, cmGeneratorExpressionDAGChecker *parent); - bool check() const; + enum Result { + DAG, + SELF_REFERENCE, + CYCLIC_REFERENCE + }; + + Result check() const; void reportError(cmGeneratorExpressionContext *context, const std::string &expr); private: - bool isDAG() const; + Result checkGraph() const; private: const cmGeneratorExpressionDAGChecker * const Parent; @@ -38,7 +44,7 @@ private: const std::string Property; const GeneratorExpressionContent * const Content; const cmListFileBacktrace Backtrace; - bool IsDAG; + Result CheckResult; }; #endif diff --git a/Source/cmGeneratorExpressionEvaluator.cxx b/Source/cmGeneratorExpressionEvaluator.cxx index d86ae54..82becaf 100644 --- a/Source/cmGeneratorExpressionEvaluator.cxx +++ b/Source/cmGeneratorExpressionEvaluator.cxx @@ -49,6 +49,8 @@ struct cmGeneratorExpressionNode virtual bool GeneratesContent() const { return true; } + virtual bool RequiresLiteralInput() const { return false; } + virtual bool AcceptsSingleArbitraryContentParameter() const { return false; } @@ -98,6 +100,12 @@ static const struct OneNode : public cmGeneratorExpressionNode } oneNode; //---------------------------------------------------------------------------- +static const struct OneNode buildInterfaceNode; + +//---------------------------------------------------------------------------- +static const struct ZeroNode installInterfaceNode; + +//---------------------------------------------------------------------------- #define BOOLEAN_OP_NODE(OPNAME, OP, SUCCESS_VALUE, FAILURE_VALUE) \ static const struct OP ## Node : public cmGeneratorExpressionNode \ { \ @@ -259,11 +267,34 @@ static const struct ConfigurationTestNode : public cmGeneratorExpressionNode return parameters.front().empty() ? "1" : "0"; } - return cmsysString_strcasecmp(parameters.begin()->c_str(), - context->Config) == 0 ? "1" : "0"; + if (cmsysString_strcasecmp(parameters.begin()->c_str(), + context->Config) == 0) + { + return "1"; + } + + if (context->CurrentTarget + && context->CurrentTarget->IsImported()) + { + const char* loc = 0; + const char* imp = 0; + std::string suffix; + return context->CurrentTarget->GetMappedConfig(context->Config, + &loc, + &imp, + suffix) ? "1" : "0"; + } + return "0"; } } configurationTestNode; + +//---------------------------------------------------------------------------- +static const char* targetPropertyTransitiveWhitelist[] = { + "INTERFACE_INCLUDE_DIRECTORIES" + , "INTERFACE_COMPILE_DEFINITIONS" +}; + //---------------------------------------------------------------------------- static const struct TargetPropertyNode : public cmGeneratorExpressionNode { @@ -291,7 +322,7 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode cmsys::RegularExpression propertyNameValidator; propertyNameValidator.compile("^[A-Za-z0-9_]+$"); - cmTarget* target = context->Target; + cmTarget* target = context->HeadTarget; std::string propertyName = *parameters.begin(); if (!target && parameters.size() == 1) @@ -372,18 +403,67 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode content, dagCheckerParent); - if (!dagChecker.check()) + switch (dagChecker.check()) { + case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: dagChecker.reportError(context, content->GetOriginalExpression()); return std::string(); + case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: + // No error. We just skip cyclic references. + return std::string(); + case cmGeneratorExpressionDAGChecker::DAG: + break; } const char *prop = target->GetProperty(propertyName.c_str()); - return prop ? prop : ""; + if (!prop) + { + return std::string(); + } + + for (size_t i = 0; + i < (sizeof(targetPropertyTransitiveWhitelist) / + sizeof(*targetPropertyTransitiveWhitelist)); + ++i) + { + if (targetPropertyTransitiveWhitelist[i] == propertyName) + { + cmGeneratorExpression ge(context->Backtrace); + return ge.Parse(prop)->Evaluate(context->Makefile, + context->Config, + context->Quiet, + context->HeadTarget, + target, + &dagChecker); + } + } + return prop; } } targetPropertyNode; //---------------------------------------------------------------------------- +static const struct TargetNameNode : public cmGeneratorExpressionNode +{ + TargetNameNode() {} + + virtual bool GeneratesContent() const { return true; } + + virtual bool AcceptsSingleArbitraryContentParameter() const { return true; } + virtual bool RequiresLiteralInput() const { return true; } + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *, + const GeneratorExpressionContent *, + cmGeneratorExpressionDAGChecker *) const + { + return parameters.front(); + } + + virtual int NumExpectedParameters() const { return 1; } + +} targetNameNode; + +//---------------------------------------------------------------------------- template<bool linker, bool soname> struct TargetFilesystemArtifactResultCreator { @@ -608,6 +688,12 @@ cmGeneratorExpressionNode* GetNode(const std::string &identifier) return &commaNode; else if (identifier == "TARGET_PROPERTY") return &targetPropertyNode; + else if (identifier == "TARGET_NAME") + return &targetNameNode; + else if (identifier == "BUILD_INTERFACE") + return &buildInterfaceNode; + else if (identifier == "INSTALL_INTERFACE") + return &installInterfaceNode; return 0; } @@ -697,6 +783,15 @@ std::string GeneratorExpressionContent::Evaluate( = pit->end(); for ( ; it != end; ++it) { + if (node->RequiresLiteralInput()) + { + if ((*it)->GetType() != cmGeneratorExpressionEvaluator::Text) + { + reportError(context, this->GetOriginalExpression(), + "$<" + identifier + "> expression requires literal input."); + return std::string(); + } + } result += (*it)->Evaluate(context, dagChecker); if (context->HadError) { @@ -704,6 +799,12 @@ std::string GeneratorExpressionContent::Evaluate( } } } + if (node->RequiresLiteralInput()) + { + std::vector<std::string> parameters; + parameters.push_back(result); + return node->Evaluate(parameters, context, this, dagChecker); + } return result; } diff --git a/Source/cmGeneratorExpressionEvaluator.h b/Source/cmGeneratorExpressionEvaluator.h index d904b02..59804ff 100644 --- a/Source/cmGeneratorExpressionEvaluator.h +++ b/Source/cmGeneratorExpressionEvaluator.h @@ -26,7 +26,9 @@ struct cmGeneratorExpressionContext std::set<cmTarget*> Targets; cmMakefile *Makefile; const char *Config; - cmTarget *Target; + cmTarget *HeadTarget; // The target whose property is being evaluated. + cmTarget *CurrentTarget; // The dependent of HeadTarget which appears + // directly or indirectly in the property. bool Quiet; bool HadError; }; diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 58fab80..ca53a39 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -720,6 +720,28 @@ void cmTarget::DefineProperties(cmake *cm) "for the named configuration."); cm->DefineProperty + ("INTERFACE_INCLUDE_DIRECTORIES", cmProperty::TARGET, + "List of public include directories for a library.", + "Targets may populate this property to publish the include directories " + "required to compile against the headers for the target. Consuming " + "targets can add entries to their own INCLUDE_DIRECTORIES property such " + "as $<TARGET_PROPERTY:foo,INTERFACE_INCLUDE_DIRECTORIES> to use the " + "include directories specified in the interface of 'foo'." + "\n" + CM_DOCUMENT_COMMAND_GENERATOR_EXPRESSIONS); + + cm->DefineProperty + ("INTERFACE_COMPILE_DEFINITIONS", cmProperty::TARGET, + "List of public compile definitions for a library.", + "Targets may populate this property to publish the compile definitions " + "required to compile against the headers for the target. Consuming " + "targets can add entries to their own COMPILE_DEFINITIONS property such " + "as $<TARGET_PROPERTY:foo,INTERFACE_COMPILE_DEFINITIONS> to use the " + "compile definitions specified in the interface of 'foo'." + "\n" + CM_DOCUMENT_COMMAND_GENERATOR_EXPRESSIONS); + + cm->DefineProperty ("LINK_INTERFACE_MULTIPLICITY", cmProperty::TARGET, "Repetition count for STATIC libraries with cyclic dependencies.", "When linking to a STATIC library target with cyclic dependencies the " @@ -4365,27 +4387,15 @@ cmTarget::GetImportInfo(const char* config) return &i->second; } -//---------------------------------------------------------------------------- -void cmTarget::ComputeImportInfo(std::string const& desired_config, - ImportInfo& info) +bool cmTarget::GetMappedConfig(std::string const& desired_config, + const char** loc, + const char** imp, + std::string& suffix) { - // This method finds information about an imported target from its - // properties. The "IMPORTED_" namespace is reserved for properties - // defined by the project exporting the target. - - // Initialize members. - info.NoSOName = false; - // Track the configuration-specific property suffix. - std::string suffix = "_"; + suffix = "_"; suffix += desired_config; - // On a DLL platform there may be only IMPORTED_IMPLIB for a shared - // library or an executable with exports. - bool allowImp = this->HasImportLibrary(); - - // Look for a mapping from the current project's configuration to - // the imported project's configuration. std::vector<std::string> mappedConfigs; { std::string mapProp = "MAP_IMPORTED_CONFIG_"; @@ -4396,26 +4406,29 @@ void cmTarget::ComputeImportInfo(std::string const& desired_config, } } + // If we needed to find one of the mapped configurations but did not + // On a DLL platform there may be only IMPORTED_IMPLIB for a shared + // library or an executable with exports. + bool allowImp = this->HasImportLibrary(); + // If a mapping was found, check its configurations. - const char* loc = 0; - const char* imp = 0; for(std::vector<std::string>::const_iterator mci = mappedConfigs.begin(); - !loc && !imp && mci != mappedConfigs.end(); ++mci) + !*loc && !*imp && mci != mappedConfigs.end(); ++mci) { // Look for this configuration. std::string mcUpper = cmSystemTools::UpperCase(mci->c_str()); std::string locProp = "IMPORTED_LOCATION_"; locProp += mcUpper; - loc = this->GetProperty(locProp.c_str()); + *loc = this->GetProperty(locProp.c_str()); if(allowImp) { std::string impProp = "IMPORTED_IMPLIB_"; impProp += mcUpper; - imp = this->GetProperty(impProp.c_str()); + *imp = this->GetProperty(impProp.c_str()); } // If it was found, use it for all properties below. - if(loc || imp) + if(*loc || *imp) { suffix = "_"; suffix += mcUpper; @@ -4425,45 +4438,45 @@ void cmTarget::ComputeImportInfo(std::string const& desired_config, // If we needed to find one of the mapped configurations but did not // then the target is not found. The project does not want any // other configuration. - if(!mappedConfigs.empty() && !loc && !imp) + if(!mappedConfigs.empty() && !*loc && !*imp) { - return; + return false; } // If we have not yet found it then there are no mapped // configurations. Look for an exact-match. - if(!loc && !imp) + if(!*loc && !*imp) { std::string locProp = "IMPORTED_LOCATION"; locProp += suffix; - loc = this->GetProperty(locProp.c_str()); + *loc = this->GetProperty(locProp.c_str()); if(allowImp) { std::string impProp = "IMPORTED_IMPLIB"; impProp += suffix; - imp = this->GetProperty(impProp.c_str()); + *imp = this->GetProperty(impProp.c_str()); } } // If we have not yet found it then there are no mapped // configurations and no exact match. - if(!loc && !imp) + if(!*loc && !*imp) { // The suffix computed above is not useful. suffix = ""; // Look for a configuration-less location. This may be set by // manually-written code. - loc = this->GetProperty("IMPORTED_LOCATION"); + *loc = this->GetProperty("IMPORTED_LOCATION"); if(allowImp) { - imp = this->GetProperty("IMPORTED_IMPLIB"); + *imp = this->GetProperty("IMPORTED_IMPLIB"); } } // If we have not yet found it then the project is willing to try // any available configuration. - if(!loc && !imp) + if(!*loc && !*imp) { std::vector<std::string> availableConfigs; if(const char* iconfigs = this->GetProperty("IMPORTED_CONFIGURATIONS")) @@ -4472,24 +4485,45 @@ void cmTarget::ComputeImportInfo(std::string const& desired_config, } for(std::vector<std::string>::const_iterator aci = availableConfigs.begin(); - !loc && !imp && aci != availableConfigs.end(); ++aci) + !*loc && !*imp && aci != availableConfigs.end(); ++aci) { suffix = "_"; suffix += cmSystemTools::UpperCase(*aci); std::string locProp = "IMPORTED_LOCATION"; locProp += suffix; - loc = this->GetProperty(locProp.c_str()); + *loc = this->GetProperty(locProp.c_str()); if(allowImp) { std::string impProp = "IMPORTED_IMPLIB"; impProp += suffix; - imp = this->GetProperty(impProp.c_str()); + *imp = this->GetProperty(impProp.c_str()); } } } - // If we have not yet found it then the target is not available. - if(!loc && !imp) + if(!*loc && !*imp) + { + return false; + } + + return true; +} + +//---------------------------------------------------------------------------- +void cmTarget::ComputeImportInfo(std::string const& desired_config, + ImportInfo& info) +{ + // This method finds information about an imported target from its + // properties. The "IMPORTED_" namespace is reserved for properties + // defined by the project exporting the target. + + // Initialize members. + info.NoSOName = false; + + const char* loc = 0; + const char* imp = 0; + std::string suffix; + if (!this->GetMappedConfig(desired_config, &loc, &imp, suffix)) { return; } diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 3f36050..52d5ca6 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -404,6 +404,11 @@ public: // Get the properties cmPropertyMap &GetProperties() { return this->Properties; }; + bool GetMappedConfig(std::string const& desired_config, + const char** loc, + const char** imp, + std::string& suffix); + // Define the properties static void DefineProperties(cmake *cm); diff --git a/Tests/ExportImport/Export/CMakeLists.txt b/Tests/ExportImport/Export/CMakeLists.txt index e19ab88..569845a 100644 --- a/Tests/ExportImport/Export/CMakeLists.txt +++ b/Tests/ExportImport/Export/CMakeLists.txt @@ -90,9 +90,82 @@ set_property(TARGET testLibCycleA PROPERTY LINK_INTERFACE_MULTIPLICITY 3) # Test exporting dependent libraries into different exports add_library(testLibRequired testLibRequired.c) add_library(testLibDepends testLibDepends.c) +set_property(TARGET testLibDepends APPEND PROPERTY + INCLUDE_DIRECTORIES + $<TARGET_PROPERTY:testLibRequired,INTERFACE_INCLUDE_DIRECTORIES> +) +set_property(TARGET testLibDepends APPEND PROPERTY + COMPILE_DEFINITIONS + $<TARGET_PROPERTY:testLibRequired,INTERFACE_COMPILE_DEFINITIONS> +) +set_property(TARGET testLibDepends APPEND PROPERTY + INTERFACE_INCLUDE_DIRECTORIES + $<TARGET_PROPERTY:testLibRequired,INTERFACE_INCLUDE_DIRECTORIES> +) +set_property(TARGET testLibDepends APPEND PROPERTY + INTERFACE_COMPILE_DEFINITIONS + $<TARGET_PROPERTY:testLibRequired,INTERFACE_COMPILE_DEFINITIONS> +) target_link_libraries(testLibDepends testLibRequired) -install(TARGETS testLibRequired EXPORT RequiredExp DESTINATION lib ) +macro(add_include_lib _libName) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_libName}.c" "// no content\n") + add_library(${_libName} "${CMAKE_CURRENT_BINARY_DIR}/${_libName}.c") + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${_libName}") + set_property(TARGET ${_libName} APPEND PROPERTY + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/${_libName}") + if (NOT "${ARGV1}" STREQUAL "NO_HEADER") + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_libName}/${_libName}.h" "// no content\n") + endif() +endmacro() + +add_include_lib(testLibIncludeRequired1) +add_include_lib(testLibIncludeRequired2) +add_include_lib(testLibIncludeRequired3 NO_HEADER) +# Generate testLibIncludeRequired4 in the testLibIncludeRequired3 directory +# with an error. If the includes from testLibIncludeRequired3 appear first, +# the error will be hit. +# Below, the '3' library appears before the '4' library +# but we are testing that the INSTALL_INTERFACE causes it not to be used +# at build time. +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/testLibIncludeRequired3/testLibIncludeRequired4.h" "#error Should not be included\n") +add_include_lib(testLibIncludeRequired4) +add_include_lib(testLibIncludeRequired5 NO_HEADER) +# Generate testLibIncludeRequired6 in the testLibIncludeRequired5 directory +# with an error. If the includes from testLibIncludeRequired5 appear first, +# the error will be hit. +# Below, the '5' library appears before the '6' library +# but we are testing that when the installed IMPORTED target is used, from +# the Import side of this unit test, the '6' include from the '5' directory +# will not be used because it is in the BUILD_INTERFACE only. +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/testLibIncludeRequired5/testLibIncludeRequired6.h" "#error Should not be included\n") +add_include_lib(testLibIncludeRequired6) + +set_property(TARGET testLibRequired APPEND PROPERTY + INTERFACE_INCLUDE_DIRECTORIES + $<TARGET_PROPERTY:testLibIncludeRequired1,INTERFACE_INCLUDE_DIRECTORIES> + $<TARGET_PROPERTY:$<1:$<TARGET_NAME:testLibIncludeRequired2>>,INTERFACE_INCLUDE_DIRECTORIES> + $<INSTALL_INTERFACE:$<TARGET_PROPERTY:testLibIncludeRequired3,INTERFACE_INCLUDE_DIRECTORIES>> + $<BUILD_INTERFACE:$<TARGET_PROPERTY:testLibIncludeRequired4,INTERFACE_INCLUDE_DIRECTORIES>> + $<BUILD_INTERFACE:$<TARGET_PROPERTY:testLibIncludeRequired5,INTERFACE_INCLUDE_DIRECTORIES>> + $<INSTALL_INTERFACE:$<TARGET_PROPERTY:testLibIncludeRequired6,INTERFACE_INCLUDE_DIRECTORIES>> +) + +set_property(TARGET testLibRequired APPEND PROPERTY + INTERFACE_COMPILE_DEFINITIONS + testLibRequired_IFACE_DEFINE + $<BUILD_INTERFACE:BuildOnly_DEFINE> + $<INSTALL_INTERFACE:InstallOnly_DEFINE> +) + +install(TARGETS testLibRequired + testLibIncludeRequired1 + testLibIncludeRequired2 + testLibIncludeRequired3 + testLibIncludeRequired4 + testLibIncludeRequired5 + testLibIncludeRequired6 + EXPORT RequiredExp DESTINATION lib ) install(EXPORT RequiredExp NAMESPACE Req:: FILE testLibRequiredConfig.cmake DESTINATION lib/cmake/testLibRequired) install(TARGETS testLibDepends EXPORT DependsExp DESTINATION lib ) diff --git a/Tests/ExportImport/Export/testLibDepends.c b/Tests/ExportImport/Export/testLibDepends.c index 2849b33..fb5a002 100644 --- a/Tests/ExportImport/Export/testLibDepends.c +++ b/Tests/ExportImport/Export/testLibDepends.c @@ -1,4 +1,20 @@ +#include "testLibIncludeRequired1.h" +#include "testLibIncludeRequired2.h" +#include "testLibIncludeRequired4.h" + +#ifndef testLibRequired_IFACE_DEFINE +#error Expected testLibRequired_IFACE_DEFINE +#endif + +#ifndef BuildOnly_DEFINE +#error Expected BuildOnly_DEFINE +#endif + +#ifdef InstallOnly_DEFINE +#error Unexpected InstallOnly_DEFINE +#endif + extern int testLibRequired(void); int testLibDepends(void) { return testLibRequired(); } diff --git a/Tests/ExportImport/Import/A/CMakeLists.txt b/Tests/ExportImport/Import/A/CMakeLists.txt index 8841792..b77562e 100644 --- a/Tests/ExportImport/Import/A/CMakeLists.txt +++ b/Tests/ExportImport/Import/A/CMakeLists.txt @@ -152,3 +152,18 @@ check_function_exists(testLib1 HAVE_TESTLIB1_FUNCTION) if (NOT HAVE_TESTLIB1_FUNCTION) message(SEND_ERROR "Using imported target testLib2 in check_function_exists() failed !") endif() + +#----------------------------------------------------------------------------- +# Test that dependent imported targets have usable +# INTERFACE_COMPILE_DEFINITIONS and INTERFACE_INCLUDE_DIRECTORIES + +add_library(deps_iface deps_iface.cpp) +target_link_libraries(deps_iface testLibsDepends) +set_property(TARGET deps_iface APPEND PROPERTY + COMPILE_DEFINITIONS + $<TARGET_PROPERTY:testLibDepends,INTERFACE_COMPILE_DEFINITIONS> +) +set_property(TARGET deps_iface APPEND PROPERTY + INCLUDE_DIRECTORIES + $<TARGET_PROPERTY:testLibDepends,INTERFACE_INCLUDE_DIRECTORIES> +) diff --git a/Tests/ExportImport/Import/A/deps_iface.cpp b/Tests/ExportImport/Import/A/deps_iface.cpp new file mode 100644 index 0000000..7190b92 --- /dev/null +++ b/Tests/ExportImport/Import/A/deps_iface.cpp @@ -0,0 +1,24 @@ + +#include "testLibIncludeRequired1.h" +#include "testLibIncludeRequired2.h" +#include "testLibIncludeRequired6.h" + +#ifndef testLibRequired_IFACE_DEFINE +#error Expected testLibRequired_IFACE_DEFINE +#endif + +#ifdef BuildOnly_DEFINE +#error Unexpected BuildOnly_DEFINE +#endif + +#ifndef InstallOnly_DEFINE +#error Expected InstallOnly_DEFINE +#endif + +extern int testLibDepends(void); + + +int main(int,char **) +{ + return testLibDepends(); +} diff --git a/Tests/GeneratorExpression/CMakeLists.txt b/Tests/GeneratorExpression/CMakeLists.txt index 3a92d81..ecbbedf 100644 --- a/Tests/GeneratorExpression/CMakeLists.txt +++ b/Tests/GeneratorExpression/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required (VERSION 2.8.8) project(GeneratorExpression NONE) -add_custom_target(check ALL +add_custom_target(check-part1 ALL COMMAND ${CMAKE_COMMAND} -Dtest_0=$<0:nothing> -Dtest_0_with_comma=$<0:-Wl,--no-undefined> @@ -57,6 +57,13 @@ add_custom_target(check ALL -Dtest_colons_3=$<1:Qt5::Core> -Dtest_colons_4=$<1:C:\\CMake> -Dtest_colons_5=$<1:C:/CMake> + -P ${CMAKE_CURRENT_SOURCE_DIR}/check-part1.cmake + COMMAND ${CMAKE_COMMAND} -E echo "check done (part 1 of 2)" + VERBATIM + ) + +add_custom_target(check-part2 ALL + COMMAND ${CMAKE_COMMAND} -Dtest_incomplete_1=$< -Dtest_incomplete_2=$<something -Dtest_incomplete_3=$<something: @@ -78,7 +85,11 @@ add_custom_target(check ALL -Dtest_incomplete_19=$<1:some,thing$<ANGLE-R> -Dtest_incomplete_20=$<CONFIGURATION$<ANGLE-R> -Dtest_incomplete_21=$<BOOL:something$<ANGLE-R> - -P ${CMAKE_CURRENT_SOURCE_DIR}/check.cmake - COMMAND ${CMAKE_COMMAND} -E echo "check done" + -Dtest_build_interface=$<BUILD_INTERFACE:build> + -Dtest_install_interface=$<INSTALL_INTERFACE:install> + -Dtest_target_name_1=$<TARGET_NAME:tgt,ok> + -Dtest_target_name_2=$<TARGET_NAME:tgt:ok> + -P ${CMAKE_CURRENT_SOURCE_DIR}/check-part2.cmake + COMMAND ${CMAKE_COMMAND} -E echo "check done (part 2 of 2)" VERBATIM ) diff --git a/Tests/GeneratorExpression/check-common.cmake b/Tests/GeneratorExpression/check-common.cmake new file mode 100644 index 0000000..8ffebd7 --- /dev/null +++ b/Tests/GeneratorExpression/check-common.cmake @@ -0,0 +1,5 @@ +macro(check var val) + if(NOT "${${var}}" STREQUAL "${val}") + message(SEND_ERROR "${var} is \"${${var}}\", not \"${val}\"") + endif() +endmacro() diff --git a/Tests/GeneratorExpression/check.cmake b/Tests/GeneratorExpression/check-part1.cmake index af436de..7abfa82 100644 --- a/Tests/GeneratorExpression/check.cmake +++ b/Tests/GeneratorExpression/check-part1.cmake @@ -1,8 +1,5 @@ -macro(check var val) - if(NOT "${${var}}" STREQUAL "${val}") - message(SEND_ERROR "${var} is \"${${var}}\", not \"${val}\"") - endif() -endmacro() + +include(${CMAKE_CURRENT_LIST_DIR}/check-common.cmake) message(STATUS "config=[${config}]") check(test_0 "") @@ -57,24 +54,3 @@ check(test_colons_2 "::") check(test_colons_3 "Qt5::Core") check(test_colons_4 "C:\\\\CMake") check(test_colons_5 "C:/CMake") -check(test_incomplete_1 "$<") -check(test_incomplete_2 "$<something") -check(test_incomplete_3 "$<something:") -check(test_incomplete_4 "$<something:,") -check(test_incomplete_5 "$something:,>") -check(test_incomplete_6 "<something:,>") -check(test_incomplete_7 "$<something::") -check(test_incomplete_8 "$<something:,") -check(test_incomplete_9 "$<something:,,") -check(test_incomplete_10 "$<something:,:") -check(test_incomplete_11 "$<something,,") -check(test_incomplete_12 "$<,,") -check(test_incomplete_13 "$<somespecialthing") -check(test_incomplete_14 "$<>") -check(test_incomplete_15 "$<some$<thing") -check(test_incomplete_16 "$<BOOL:something") -check(test_incomplete_17 "some$thing") -check(test_incomplete_18 "$<1:some,thing") -check(test_incomplete_19 "$<1:some,thing>") -check(test_incomplete_20 "$<CONFIGURATION>") -check(test_incomplete_21 "$<BOOL:something>") diff --git a/Tests/GeneratorExpression/check-part2.cmake b/Tests/GeneratorExpression/check-part2.cmake new file mode 100644 index 0000000..8855a97 --- /dev/null +++ b/Tests/GeneratorExpression/check-part2.cmake @@ -0,0 +1,28 @@ + +include(${CMAKE_CURRENT_LIST_DIR}/check-common.cmake) + +check(test_incomplete_1 "$<") +check(test_incomplete_2 "$<something") +check(test_incomplete_3 "$<something:") +check(test_incomplete_4 "$<something:,") +check(test_incomplete_5 "$something:,>") +check(test_incomplete_6 "<something:,>") +check(test_incomplete_7 "$<something::") +check(test_incomplete_8 "$<something:,") +check(test_incomplete_9 "$<something:,,") +check(test_incomplete_10 "$<something:,:") +check(test_incomplete_11 "$<something,,") +check(test_incomplete_12 "$<,,") +check(test_incomplete_13 "$<somespecialthing") +check(test_incomplete_14 "$<>") +check(test_incomplete_15 "$<some$<thing") +check(test_incomplete_16 "$<BOOL:something") +check(test_incomplete_17 "some$thing") +check(test_incomplete_18 "$<1:some,thing") +check(test_incomplete_19 "$<1:some,thing>") +check(test_incomplete_20 "$<CONFIGURATION>") +check(test_incomplete_21 "$<BOOL:something>") +check(test_build_interface "build") +check(test_install_interface "") +check(test_target_name_1 "tgt,ok") +check(test_target_name_2 "tgt:ok") diff --git a/Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt b/Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt index 3044abd..21159e0 100644 --- a/Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt +++ b/Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt @@ -37,6 +37,37 @@ include_directories("sing$<1:/ting>") include_directories("$<1:${CMAKE_CURRENT_BINARY_DIR}/arguments;${CMAKE_CURRENT_BINARY_DIR}/list>") +create_header(fee) +create_header(fiy) +create_header(foh) +create_header(fum) + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/lib1.cpp" "#include \"fee.h\"\n") +add_library(lib1 "${CMAKE_CURRENT_BINARY_DIR}/lib1.cpp") +set_property(TARGET lib1 APPEND PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/fee") +set_property(TARGET lib1 APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/fiy") +set_property(TARGET lib1 APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>:${CMAKE_CURRENT_BINARY_DIR}/foh>") + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/lib2.cpp" "#include \"fiy.h\"\n") +add_library(lib2 "${CMAKE_CURRENT_BINARY_DIR}/lib2.cpp") +set_property(TARGET lib2 APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/fum;$<TARGET_PROPERTY:lib1,INTERFACE_INCLUDE_DIRECTORIES>") +set_property(TARGET lib2 APPEND PROPERTY INCLUDE_DIRECTORIES "$<TARGET_PROPERTY:lib1,INTERFACE_INCLUDE_DIRECTORIES>") + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/main3.cpp" "#include \"fiy.h\"\n#include \"foh.h\"\n#include \"fum.h\"\nint main(int,char**) { return 0; }\n") +add_executable(exe3 "${CMAKE_CURRENT_BINARY_DIR}/main3.cpp") +set_property(TARGET exe3 APPEND PROPERTY INCLUDE_DIRECTORIES "$<TARGET_PROPERTY:lib2,INTERFACE_INCLUDE_DIRECTORIES>") + +# Test cycles +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/lib3.cpp" "#include \"fiy.h\"\n#include \"foh.h\"\n") +add_library(lib3 "${CMAKE_CURRENT_BINARY_DIR}/lib3.cpp") +set_property(TARGET lib3 APPEND PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/fiy;$<TARGET_PROPERTY:lib4,INTERFACE_INCLUDE_DIRECTORIES>") +set_property(TARGET lib3 APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/fiy;$<TARGET_PROPERTY:lib4,INTERFACE_INCLUDE_DIRECTORIES>") + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/lib4.cpp" "#include \"fiy.h\"\n#include \"foh.h\"\n") +add_library(lib4 "${CMAKE_CURRENT_BINARY_DIR}/lib4.cpp") +set_property(TARGET lib4 APPEND PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/foh;$<TARGET_PROPERTY:lib3,INTERFACE_INCLUDE_DIRECTORIES>") +set_property(TARGET lib4 APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/foh;$<TARGET_PROPERTY:lib3,INTERFACE_INCLUDE_DIRECTORIES>") + add_library(somelib::withcolons UNKNOWN IMPORTED) set_property(TARGET somelib::withcolons PROPERTY IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/target") set_property(TARGET somelib::withcolons PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/target") diff --git a/Tests/RunCMake/GeneratorExpression/BadTargetName-result.txt b/Tests/RunCMake/GeneratorExpression/BadTargetName-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/BadTargetName-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GeneratorExpression/BadTargetName-stderr.txt b/Tests/RunCMake/GeneratorExpression/BadTargetName-stderr.txt new file mode 100644 index 0000000..969393a --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/BadTargetName-stderr.txt @@ -0,0 +1,8 @@ +CMake Error at BadTargetName.cmake:1 \(add_custom_target\): + Error evaluating generator expression: + + \$<TARGET_NAME:\$<1:tgt>> + + \$<TARGET_NAME> expression requires literal input. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\)$ diff --git a/Tests/RunCMake/GeneratorExpression/BadTargetName.cmake b/Tests/RunCMake/GeneratorExpression/BadTargetName.cmake new file mode 100644 index 0000000..e125cab --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/BadTargetName.cmake @@ -0,0 +1,3 @@ +add_custom_target(check ALL COMMAND check + $<TARGET_NAME:$<1:tgt>> + VERBATIM) diff --git a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake index 992ba79..8a69675 100644 --- a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake +++ b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake @@ -6,3 +6,4 @@ run_cmake(BadAND) run_cmake(BadNOT) run_cmake(BadStrEqual) run_cmake(BadZero) +run_cmake(BadTargetName) |