Commit 370bf554 authored by Stephen Kelly's avatar Stephen Kelly

Add the ALIAS target concept for libraries and executables.

* The ALIAS name must match a validity regex.
* Executables and libraries may be aliased.
* An ALIAS acts immutable. It can not be used as the lhs
  of target_link_libraries or other commands.
* An ALIAS can be used with add_custom_command, add_custom_target,
  and add_test in the same way regular targets can.
* The target of an ALIAS can be retrieved with the ALIASED_TARGET
  target property.
* An ALIAS does not appear in the generated buildsystem. It
  is kept separate from cmMakefile::Targets for that reason.
* A target may have multiple aliases.
* An ALIAS target may not itself have an alias.
* An IMPORTED target may not have an alias.
* An ALIAS may not be exported or imported.
parent b341bf21
......@@ -24,6 +24,13 @@ bool cmAddDependenciesCommand
}
std::string target_name = args[0];
if(this->Makefile->IsAlias(target_name.c_str()))
{
cmOStringStream e;
e << "Cannot add target-level dependencies to alias target \""
<< target_name << "\".\n";
this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
}
if(cmTarget* target = this->Makefile->FindTargetToUse(target_name.c_str()))
{
std::vector<std::string>::const_iterator s = args.begin();
......
......@@ -30,6 +30,7 @@ bool cmAddExecutableCommand
bool excludeFromAll = false;
bool importTarget = false;
bool importGlobal = false;
bool isAlias = false;
while ( s != args.end() )
{
if (*s == "WIN32")
......@@ -57,6 +58,11 @@ bool cmAddExecutableCommand
++s;
importGlobal = true;
}
else if(*s == "ALIAS")
{
++s;
isAlias = true;
}
else
{
break;
......@@ -83,6 +89,72 @@ bool cmAddExecutableCommand
}
return false;
}
if (isAlias)
{
if(!cmGeneratorExpression::IsValidTargetName(exename.c_str()))
{
this->SetError(("Invalid name for ALIAS: " + exename).c_str());
return false;
}
if(excludeFromAll)
{
this->SetError("EXCLUDE_FROM_ALL with ALIAS makes no sense.");
return false;
}
if(importTarget || importGlobal)
{
this->SetError("IMPORTED with ALIAS is not allowed.");
return false;
}
if(args.size() != 3)
{
cmOStringStream e;
e << "ALIAS requires exactly one target argument.";
this->SetError(e.str().c_str());
return false;
}
const char *aliasedName = s->c_str();
if(this->Makefile->IsAlias(aliasedName))
{
cmOStringStream e;
e << "cannot create ALIAS target \"" << exename
<< "\" because target \"" << aliasedName << "\" is itself an ALIAS.";
this->SetError(e.str().c_str());
return false;
}
cmTarget *aliasedTarget =
this->Makefile->FindTargetToUse(aliasedName, true);
if(!aliasedTarget)
{
cmOStringStream e;
e << "cannot create ALIAS target \"" << exename
<< "\" because target \"" << aliasedName << "\" does not already "
"exist.";
this->SetError(e.str().c_str());
return false;
}
cmTarget::TargetType type = aliasedTarget->GetType();
if(type != cmTarget::EXECUTABLE)
{
cmOStringStream e;
e << "cannot create ALIAS target \"" << exename
<< "\" because target \"" << aliasedName << "\" is not an "
"executable.";
this->SetError(e.str().c_str());
return false;
}
if(aliasedTarget->IsImported())
{
cmOStringStream e;
e << "cannot create ALIAS target \"" << exename
<< "\" because target \"" << aliasedName << "\" is IMPORTED.";
this->SetError(e.str().c_str());
return false;
}
this->Makefile->AddAlias(exename.c_str(), aliasedTarget);
return true;
}
// Handle imported target creation.
if(importTarget)
......
......@@ -107,6 +107,19 @@ public:
"(and its per-configuration version IMPORTED_LOCATION_<CONFIG>) "
"which specifies the location of the main executable file on disk. "
"See documentation of the IMPORTED_* properties for more information."
"\n"
"The signature\n"
" add_executable(<name> ALIAS <target>)\n"
"creates an alias, such that <name> can be used to refer to <target> "
"in subsequent commands. The <name> does not appear in the generated "
"buildsystem as a make target. The <target> may not be an IMPORTED "
"target or an ALIAS. Alias targets can be used as linkable targets, "
"targets to read properties from, executables for custom commands and "
"custom targets. They can also be tested for existance with the "
"regular if(TARGET) subcommand. The <name> may not be used to modify "
"properties of <target>, that is, it may not be used as the operand of "
"set_property, set_target_properties, target_link_libraries etc. An "
"ALIAS target may not be installed of exported."
;
}
......
......@@ -43,6 +43,7 @@ bool cmAddLibraryCommand
// the type of library. Otherwise, it is treated as a source or
// source list name. There may be two keyword arguments, check for them
bool haveSpecifiedType = false;
bool isAlias = false;
while ( s != args.end() )
{
std::string libType = *s;
......@@ -76,6 +77,11 @@ bool cmAddLibraryCommand
type = cmTarget::UNKNOWN_LIBRARY;
haveSpecifiedType = true;
}
else if(libType == "ALIAS")
{
++s;
isAlias = true;
}
else if(*s == "EXCLUDE_FROM_ALL")
{
++s;
......@@ -96,6 +102,80 @@ bool cmAddLibraryCommand
break;
}
}
if (isAlias)
{
if(!cmGeneratorExpression::IsValidTargetName(libName.c_str()))
{
this->SetError(("Invalid name for ALIAS: " + libName).c_str());
return false;
}
if(excludeFromAll)
{
this->SetError("EXCLUDE_FROM_ALL with ALIAS makes no sense.");
return false;
}
if(importTarget || importGlobal)
{
this->SetError("IMPORTED with ALIAS is not allowed.");
return false;
}
if(args.size() != 3)
{
cmOStringStream e;
e << "ALIAS requires exactly one target argument.";
this->SetError(e.str().c_str());
return false;
}
const char *aliasedName = s->c_str();
if(this->Makefile->IsAlias(aliasedName))
{
cmOStringStream e;
e << "cannot create ALIAS target \"" << libName
<< "\" because target \"" << aliasedName << "\" is itself an ALIAS.";
this->SetError(e.str().c_str());
return false;
}
cmTarget *aliasedTarget =
this->Makefile->FindTargetToUse(aliasedName, true);
if(!aliasedTarget)
{
cmOStringStream e;
e << "cannot create ALIAS target \"" << libName
<< "\" because target \"" << aliasedName << "\" does not already "
"exist.";
this->SetError(e.str().c_str());
return false;
}
cmTarget::TargetType aliasedType = aliasedTarget->GetType();
if(aliasedType != cmTarget::SHARED_LIBRARY
&& aliasedType != cmTarget::STATIC_LIBRARY
&& aliasedType != cmTarget::MODULE_LIBRARY
&& aliasedType != cmTarget::OBJECT_LIBRARY)
{
cmOStringStream e;
e << "cannot create ALIAS target \"" << libName
<< "\" because target \"" << aliasedName << "\" is not a library.";
this->SetError(e.str().c_str());
return false;
}
if(aliasedTarget->IsImported())
{
cmOStringStream e;
e << "cannot create ALIAS target \"" << libName
<< "\" because target \"" << aliasedName << "\" is IMPORTED.";
this->SetError(e.str().c_str());
return false;
}
this->Makefile->AddAlias(libName.c_str(), aliasedTarget);
return true;
}
if(importTarget && excludeFromAll)
{
this->SetError("excludeFromAll with IMPORTED target makes no sense.");
return false;
}
/* ideally we should check whether for the linker language of the target
CMAKE_${LANG}_CREATE_SHARED_LIBRARY is defined and if not default to
......
......@@ -138,6 +138,19 @@ public:
"Some native build systems may not like targets that have only "
"object files, so consider adding at least one real source file "
"to any target that references $<TARGET_OBJECTS:objlib>."
"\n"
"The signature\n"
" add_library(<name> ALIAS <target>)\n"
"creates an alias, such that <name> can be used to refer to <target> "
"in subsequent commands. The <name> does not appear in the generated "
"buildsystem as a make target. The <target> may not be an IMPORTED "
"target or an ALIAS. Alias targets can be used as linkable targets, "
"targets to read properties from. They can also be tested for "
"existance with the "
"regular if(TARGET) subcommand. The <name> may not be used to modify "
"properties of <target>, that is, it may not be used as the operand of "
"set_property, set_target_properties, target_link_libraries etc. An "
"ALIAS target may not be installed of exported."
;
}
......
......@@ -114,6 +114,15 @@ bool cmExportCommand
currentTarget != this->Targets.GetVector().end();
++currentTarget)
{
if (this->Makefile->IsAlias(currentTarget->c_str()))
{
cmOStringStream e;
e << "given ALIAS target \"" << *currentTarget
<< "\" which may not be exported.";
this->SetError(e.str().c_str());
return false;
}
if(cmTarget* target =
this->Makefile->GetLocalGenerator()->
GetGlobalGenerator()->FindTarget(0, currentTarget->c_str()))
......
......@@ -747,6 +747,18 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
"Target name not supported.");
return std::string();
}
if(propertyName == "ALIASED_TARGET")
{
if(context->Makefile->IsAlias(targetName.c_str()))
{
if(cmTarget* tgt =
context->Makefile->FindTargetToUse(targetName.c_str()))
{
return tgt->GetName();
}
}
return "";
}
target = context->Makefile->FindTargetToUse(
targetName.c_str());
......
......@@ -288,6 +288,18 @@ bool cmGetPropertyCommand::HandleTargetMode()
return false;
}
if(this->PropertyName == "ALIASED_TARGET")
{
if(this->Makefile->IsAlias(this->Name.c_str()))
{
if(cmTarget* target =
this->Makefile->FindTargetToUse(this->Name.c_str()))
{
return this->StoreResult(target->GetName());
}
}
return false;
}
if(cmTarget* target = this->Makefile->FindTargetToUse(this->Name.c_str()))
{
return this->StoreResult(target->GetProperty(this->PropertyName.c_str()));
......
......@@ -22,17 +22,30 @@ bool cmGetTargetPropertyCommand
}
std::string var = args[0].c_str();
const char* targetName = args[1].c_str();
const char *prop = 0;
if(cmTarget* tgt = this->Makefile->FindTargetToUse(targetName))
if(args[2] == "ALIASED_TARGET")
{
cmTarget& target = *tgt;
const char *prop = target.GetProperty(args[2].c_str());
if (prop)
if(this->Makefile->IsAlias(targetName))
{
this->Makefile->AddDefinition(var.c_str(), prop);
return true;
if(cmTarget* target =
this->Makefile->FindTargetToUse(targetName))
{
prop = target->GetName();
}
}
}
else if(cmTarget* tgt = this->Makefile->FindTargetToUse(targetName))
{
cmTarget& target = *tgt;
prop = target.GetProperty(args[2].c_str());
}
if (prop)
{
this->Makefile->AddDefinition(var.c_str(), prop);
return true;
}
this->Makefile->AddDefinition(var.c_str(), (var+"-NOTFOUND").c_str());
return true;
}
......
......@@ -1769,10 +1769,22 @@ cmLocalGenerator* cmGlobalGenerator::FindLocalGenerator(const char* start_dir)
return 0;
}
//----------------------------------------------------------------------------
void cmGlobalGenerator::AddAlias(const char *name, cmTarget *tgt)
{
this->AliasTargets[name] = tgt;
}
//----------------------------------------------------------------------------
bool cmGlobalGenerator::IsAlias(const char *name)
{
return this->AliasTargets.find(name) != this->AliasTargets.end();
}
//----------------------------------------------------------------------------
cmTarget*
cmGlobalGenerator::FindTarget(const char* project, const char* name)
cmGlobalGenerator::FindTarget(const char* project, const char* name,
bool excludeAliases)
{
// if project specific
if(project)
......@@ -1780,7 +1792,8 @@ cmGlobalGenerator::FindTarget(const char* project, const char* name)
std::vector<cmLocalGenerator*>* gens = &this->ProjectMap[project];
for(unsigned int i = 0; i < gens->size(); ++i)
{
cmTarget* ret = (*gens)[i]->GetMakefile()->FindTarget(name);
cmTarget* ret = (*gens)[i]->GetMakefile()->FindTarget(name,
excludeAliases);
if(ret)
{
return ret;
......@@ -1790,6 +1803,15 @@ cmGlobalGenerator::FindTarget(const char* project, const char* name)
// if all projects/directories
else
{
if (!excludeAliases)
{
std::map<cmStdString, cmTarget*>::iterator ai
= this->AliasTargets.find(name);
if (ai != this->AliasTargets.end())
{
return ai->second;
}
}
std::map<cmStdString,cmTarget *>::iterator i =
this->TotalTargets.find ( name );
if ( i != this->TotalTargets.end() )
......
......@@ -196,7 +196,11 @@ public:
void FindMakeProgram(cmMakefile*);
///! Find a target by name by searching the local generators.
cmTarget* FindTarget(const char* project, const char* name);
cmTarget* FindTarget(const char* project, const char* name,
bool excludeAliases = false);
void AddAlias(const char *name, cmTarget *tgt);
bool IsAlias(const char *name);
/** Determine if a name resolves to a framework on disk or a built target
that is a framework. */
......@@ -347,6 +351,7 @@ protected:
// All targets in the entire project.
std::map<cmStdString,cmTarget *> TotalTargets;
std::map<cmStdString,cmTarget *> AliasTargets;
std::map<cmStdString,cmTarget *> ImportedTargets;
std::vector<cmGeneratorExpressionEvaluationFile*> EvaluationFiles;
......
......@@ -362,6 +362,15 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
targetIt!=targetList.GetVector().end();
++targetIt)
{
if (this->Makefile->IsAlias(targetIt->c_str()))
{
cmOStringStream e;
e << "TARGETS given target \"" << (*targetIt)
<< "\" which is an alias.";
this->SetError(e.str().c_str());
return false;
}
// Lookup this target in the current directory.
if(cmTarget* target=this->Makefile->FindTarget(targetIt->c_str()))
{
......
......@@ -41,6 +41,7 @@
#include <stack>
#include <ctype.h> // for isspace
#include <assert.h>
class cmMakefile::Internals
{
......@@ -1437,6 +1438,14 @@ void cmMakefile::AddLinkDirectoryForTarget(const char *target,
cmTargets::iterator i = this->Targets.find(target);
if ( i != this->Targets.end())
{
if(this->IsAlias(target))
{
cmOStringStream e;
e << "ALIAS target \"" << target << "\" "
<< "may not be linked into another target.";
this->IssueMessage(cmake::FATAL_ERROR, e.str().c_str());
return;
}
i->second.AddLinkDirectory( d );
}
else
......@@ -1923,6 +1932,12 @@ void cmMakefile::AddGlobalLinkInformation(const char* name, cmTarget& target)
}
void cmMakefile::AddAlias(const char* lname, cmTarget *tgt)
{
this->AliasTargets[lname] = tgt;
this->LocalGenerator->GetGlobalGenerator()->AddAlias(lname, tgt);
}
cmTarget* cmMakefile::AddLibrary(const char* lname, cmTarget::TargetType type,
const std::vector<std::string> &srcs,
bool excludeFromAll)
......@@ -3758,8 +3773,17 @@ const char* cmMakefile::GetFeature(const char* feature, const char* config)
return 0;
}
cmTarget* cmMakefile::FindTarget(const char* name)
cmTarget* cmMakefile::FindTarget(const char* name, bool excludeAliases)
{
if (!excludeAliases)
{
std::map<std::string, cmTarget*>::iterator i
= this->AliasTargets.find(name);
if (i != this->AliasTargets.end())
{
return i->second;
}
}
cmTargets& tgts = this->GetTargets();
cmTargets::iterator i = tgts.find ( name );
......@@ -4184,7 +4208,7 @@ cmMakefile::AddImportedTarget(const char* name, cmTarget::TargetType type,
}
//----------------------------------------------------------------------------
cmTarget* cmMakefile::FindTargetToUse(const char* name)
cmTarget* cmMakefile::FindTargetToUse(const char* name, bool excludeAliases)
{
// Look for an imported target. These take priority because they
// are more local in scope and do not have to be globally unique.
......@@ -4196,15 +4220,25 @@ cmTarget* cmMakefile::FindTargetToUse(const char* name)
}
// Look for a target built in this directory.
if(cmTarget* t = this->FindTarget(name))
if(cmTarget* t = this->FindTarget(name, excludeAliases))
{
return t;
}
// Look for a target built in this project.
return this->LocalGenerator->GetGlobalGenerator()->FindTarget(0, name);
return this->LocalGenerator->GetGlobalGenerator()->FindTarget(0, name,
excludeAliases);
}
//----------------------------------------------------------------------------
bool cmMakefile::IsAlias(const char *name)
{
if (this->AliasTargets.find(name) != this->AliasTargets.end())
return true;
return this->GetLocalGenerator()->GetGlobalGenerator()->IsAlias(name);
}
//----------------------------------------------------------------------------
cmGeneratorTarget* cmMakefile::FindGeneratorTargetToUse(const char* name)
{
cmTarget *t = this->FindTargetToUse(name);
......@@ -4215,6 +4249,14 @@ cmGeneratorTarget* cmMakefile::FindGeneratorTargetToUse(const char* name)
bool cmMakefile::EnforceUniqueName(std::string const& name, std::string& msg,
bool isCustom)
{
if(this->IsAlias(name.c_str()))
{
cmOStringStream e;
e << "cannot create target \"" << name
<< "\" because an alias with the same name already exists.";
msg = e.str();
return false;
}
if(cmTarget* existing = this->FindTargetToUse(name.c_str()))
{
// The name given conflicts with an existing target. Produce an
......
......@@ -337,6 +337,7 @@ public:
cmTarget* AddLibrary(const char *libname, cmTarget::TargetType type,
const std::vector<std::string> &srcs,
bool excludeFromAll = false);
void AddAlias(const char *libname, cmTarget *tgt);
#if defined(CMAKE_BUILD_WITH_CMAKE)
/**
......@@ -536,11 +537,12 @@ public:
this->GeneratorTargets = targets;
}
cmTarget* FindTarget(const char* name);
cmTarget* FindTarget(const char* name, bool excludeAliases = false);
/** Find a target to use in place of the given name. The target
returned may be imported or built within the project. */
cmTarget* FindTargetToUse(const char* name);
cmTarget* FindTargetToUse(const char* name, bool excludeAliases = false);
bool IsAlias(const char *name);
cmGeneratorTarget* FindGeneratorTargetToUse(const char* name);
/**
......@@ -902,6 +904,7 @@ protected:
// libraries, classes, and executables
cmTargets Targets;
std::map<std::string, cmTarget*> AliasTargets;
cmGeneratorTargetsType GeneratorTargets;
std::vector<cmSourceFile*> SourceFiles;
......
......@@ -244,6 +244,11 @@ bool cmSetPropertyCommand::HandleTargetMode()
for(std::set<cmStdString>::const_iterator ni = this->Names.begin();
ni != this->Names.end(); ++ni)
{
if (this->Makefile->IsAlias(ni->c_str()))
{
this->SetError("can not be used on an ALIAS target.");
return false;
}
if(cmTarget* target = this->Makefile->FindTargetToUse(ni->c_str()))
{
// Handle the current target.
......
......@@ -72,6 +72,11 @@ bool cmSetTargetPropertiesCommand
int i;
for(i = 0; i < numFiles; ++i)
{
if (this->Makefile->IsAlias(args[i].c_str()))
{
this->SetError("can not be used on an ALIAS target.");
return false;
}
bool ret = cmSetTargetPropertiesCommand::SetOneTarget
(args[i].c_str(),propertyPairs,this->Makefile);
if (!ret)
......
......@@ -968,6 +968,12 @@ void cmTarget::DefineProperties(cmake *cm)
"Per-configuration target file base name.",
"This is the configuration-specific version of OUTPUT_NAME.");
cm->DefineProperty
("ALIASED_TARGET", cmProperty::TARGET,
"Name of target aliased by this target.",
"If this is an ALIAS target, this property contains the name of the "
"target aliased.");
cm->DefineProperty
("<CONFIG>_OUTPUT_NAME", cmProperty::TARGET,
"Old per-configuration target file base name.",
......
......@@ -31,6 +31,11 @@ bool cmTargetLinkLibrariesCommand
return false;
}
if (this->Makefile->IsAlias(args[0].c_str()))
{
this->SetError("can not be used on an ALIAS target.");
return false;
}
// Lookup the target for which libraries are specified.
this->Target =
this->Makefile->GetCMakeInstance()
......
......@@ -26,6 +26,11 @@ bool cmTargetPropCommandBase
}
// Lookup the target for which libraries are specified.
if (this->Makefile->IsAlias(args[0].c_str()))
{
this->SetError("can not be used on an ALIAS target.");
return false;
}
this->Target =
this->Makefile->GetCMakeInstance()
->GetGlobalGenerator()->FindTarget(0, args[0].c_str());
......
cmake_minimum_required(VERSION 2.8.11)
project(AliasTarget)
add_library(foo SHARED empty.cpp)
add_library(PREFIX::Foo ALIAS foo)
add_library(Another::Alias ALIAS foo)
add_library(objects OBJECT object.cpp)
add_library(Alias::Objects ALIAS objects)
target_compile_definitions(foo PUBLIC FOO_DEFINE)
add_library(bar SHARED empty.cpp)
target_compile_definitions(bar PUBLIC BAR_DEFINE)
target_link_libraries(foo LINK_PUBLIC $<$<STREQUAL:$<TARGET_PROPERTY:PREFIX::Foo,ALIASED_TARGET>,foo>:bar>)
add_executable(AliasTarget commandgenerator.cpp $<TARGET_OBJECTS:Alias::Objects>)
add_executable(PREFIX::AliasTarget ALIAS AliasTarget)
add_executable(Generator::Command ALIAS AliasTarget)
add_custom_command(OUTPUT commandoutput.h COMMAND Generator::Command)
add_library(bat SHARED bat.cpp "${CMAKE_CURRENT_BINARY_DIR}/commandoutput.h")
target_link_libraries(bat PREFIX::Foo)
target_include_directories(bat PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
add_executable(targetgenerator targetgenerator.cpp)
add_executable(Generator::Target ALIAS targetgenerator)
add_custom_target(usealias Generator::Target)
add_dependencies(bat usealias)
if (NOT TARGET Another::Alias)
message(SEND_ERROR "Another::Alias is not considered a target.")
endif()
get_target_property(_alt PREFIX::Foo ALIASED_TARGET)
if (NOT ${_alt} STREQUAL foo)