Commit 31c0bc02 authored by David Cole's avatar David Cole Committed by Kitware Robot
Browse files

Merge topic 'object-library'

93d5509b Merge branch 'ninja-object-library' into object-library
821037cf Merge branch 'xcode-object-library' into object-library
eb24c99c Merge branch 'object-library' into xcode-object-library
63d1be8b Xcode: Honor $<TARGET_OBJECTS:...> source expressions
020ba387 Merge branch 'object-library' into xcode-object-library
e8ea6158 Build object library targets in Xcode
8045e171 Pre-compute object file names before Xcode generation
247a1324 Allow txt files as ExtraSources in object library targets
b0635996 Add a default source group for object files.
be01f3b0 Xcode: Re-factor some existing methods into "FromPath" variants
2693dbe0 Merge branch 'object-library' into ninja-object-library
51997cb6 Ninja: Honor $<TARGET_OBJECTS:...> source expressions
23ec258f Merge branch 'object-library' into ninja-object-library
61124de4 Build object library targets in Ninja
f5b06cda Pre-compute object file names before Ninja generation
a2514f15 Simplify cmNinjaTargetGenerator using cmGeneratorTarget
...
parents b06f7c07 93d5509b
......@@ -185,6 +185,8 @@ SET(SRCS
cmGeneratedFileStream.cxx
cmGeneratorExpression.cxx
cmGeneratorExpression.h
cmGeneratorTarget.cxx
cmGeneratorTarget.h
cmGlobalGenerator.cxx
cmGlobalGenerator.h
cmGlobalUnixMakefileGenerator3.cxx
......
......@@ -64,6 +64,12 @@ bool cmAddLibraryCommand
type = cmTarget::MODULE_LIBRARY;
haveSpecifiedType = true;
}
else if(libType == "OBJECT")
{
++s;
type = cmTarget::OBJECT_LIBRARY;
haveSpecifiedType = true;
}
else if(libType == "UNKNOWN")
{
++s;
......@@ -118,6 +124,14 @@ bool cmAddLibraryCommand
this->SetError("called with IMPORTED argument but no library type.");
return false;
}
if(type == cmTarget::OBJECT_LIBRARY)
{
this->Makefile->IssueMessage(
cmake::FATAL_ERROR,
"The OBJECT library type may not be used for IMPORTED libraries."
);
return true;
}
// Make sure the target does not already exist.
if(this->Makefile->FindTargetToUse(libName.c_str()))
......
......@@ -112,6 +112,26 @@ public:
"(and its per-configuration version IMPORTED_LOCATION_<CONFIG>) "
"which specifies the location of the main library file on disk. "
"See documentation of the IMPORTED_* properties for more information."
"\n"
"The signature\n"
" add_library(<name> OBJECT <src>...)\n"
"creates a special \"object library\" target. "
"An object library compiles source files but does not archive or link "
"their object files into a library. "
"Instead other targets created by add_library or add_executable may "
"reference the objects using an expression of the form "
"$<TARGET_OBJECTS:objlib> as a source, where \"objlib\" is the "
"object library name. "
"For example:\n"
" add_library(... $<TARGET_OBJECTS:objlib> ...)\n"
" add_executable(... $<TARGET_OBJECTS:objlib> ...)\n"
"will include objlib's object files in a library and an executable "
"along with those compiled from their own sources. "
"Object libraries may contain only sources (and headers) that compile "
"to object files. "
"They may contain custom commands generating such sources, but not "
"PRE_BUILD, PRE_LINK, or POST_BUILD commands. "
"Object libraries cannot be imported, exported, installed, or linked."
;
}
......
......@@ -633,6 +633,19 @@ cmTarget* cmComputeLinkDepends::FindTargetToLink(int depender_index,
tgt = 0;
}
if(tgt && tgt->GetType() == cmTarget::OBJECT_LIBRARY)
{
cmOStringStream e;
e << "Target \"" << this->Target->GetName() << "\" links to "
"OBJECT library \"" << tgt->GetName() << "\" but this is not "
"allowed. "
"One may link only to STATIC or SHARED libraries, or to executables "
"with the ENABLE_EXPORTS property set.";
this->CMakeInstance->IssueMessage(cmake::FATAL_ERROR, e.str(),
this->Target->GetBacktrace());
tgt = 0;
}
// Return the target found, if any.
return tgt;
}
......
......@@ -124,6 +124,14 @@ bool cmExportCommand
{
targets.push_back(target);
}
else if(target->GetType() == cmTarget::OBJECT_LIBRARY)
{
cmOStringStream e;
e << "given OBJECT library \"" << *currentTarget
<< "\" which may not be exported.";
this->SetError(e.str().c_str());
return false;
}
else
{
cmOStringStream e;
......
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2000-2012 Kitware, Inc., Insight Software Consortium
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#include "cmGeneratorTarget.h"
#include "cmTarget.h"
#include "cmMakefile.h"
#include "cmLocalGenerator.h"
#include "cmGlobalGenerator.h"
#include "cmSourceFile.h"
//----------------------------------------------------------------------------
cmGeneratorTarget::cmGeneratorTarget(cmTarget* t): Target(t)
{
this->Makefile = this->Target->GetMakefile();
this->LocalGenerator = this->Makefile->GetLocalGenerator();
this->GlobalGenerator = this->LocalGenerator->GetGlobalGenerator();
this->ClassifySources();
this->LookupObjectLibraries();
}
//----------------------------------------------------------------------------
void cmGeneratorTarget::ClassifySources()
{
bool isObjLib = this->Target->GetType() == cmTarget::OBJECT_LIBRARY;
std::vector<cmSourceFile*> badObjLib;
std::vector<cmSourceFile*> const& sources = this->Target->GetSourceFiles();
for(std::vector<cmSourceFile*>::const_iterator si = sources.begin();
si != sources.end(); ++si)
{
cmSourceFile* sf = *si;
cmTarget::SourceFileFlags tsFlags =
this->Target->GetTargetSourceFileFlags(sf);
if(sf->GetCustomCommand())
{
this->CustomCommands.push_back(sf);
}
else if(tsFlags.Type != cmTarget::SourceFileTypeNormal)
{
this->OSXContent.push_back(sf);
if(isObjLib) { badObjLib.push_back(sf); }
}
else if(sf->GetPropertyAsBool("HEADER_FILE_ONLY"))
{
this->HeaderSources.push_back(sf);
}
else if(sf->GetPropertyAsBool("EXTERNAL_OBJECT"))
{
this->ExternalObjects.push_back(sf);
if(isObjLib) { badObjLib.push_back(sf); }
}
else if(cmSystemTools::LowerCase(sf->GetExtension()) == "def")
{
this->ModuleDefinitionFile = sf->GetFullPath();
if(isObjLib) { badObjLib.push_back(sf); }
}
else if(this->GlobalGenerator->IgnoreFile(sf->GetExtension().c_str()))
{
// We only get here if a source file is not an external object
// and has an extension that is listed as an ignored file type.
// No message or diagnosis should be given.
}
else if(sf->GetLanguage())
{
this->ObjectSources.push_back(sf);
}
else
{
this->ExtraSources.push_back(sf);
if(isObjLib && cmSystemTools::LowerCase(sf->GetExtension()) != "txt")
{
badObjLib.push_back(sf);
}
}
}
if(!badObjLib.empty())
{
cmOStringStream e;
e << "OBJECT library \"" << this->Target->GetName() << "\" contains:\n";
for(std::vector<cmSourceFile*>::iterator i = badObjLib.begin();
i != badObjLib.end(); ++i)
{
e << " " << (*i)->GetLocation().GetName() << "\n";
}
e << "but may contain only headers and sources that compile.";
this->GlobalGenerator->GetCMakeInstance()
->IssueMessage(cmake::FATAL_ERROR, e.str(),
this->Target->GetBacktrace());
}
}
//----------------------------------------------------------------------------
void cmGeneratorTarget::LookupObjectLibraries()
{
std::vector<std::string> const& objLibs =
this->Target->GetObjectLibraries();
for(std::vector<std::string>::const_iterator oli = objLibs.begin();
oli != objLibs.end(); ++oli)
{
std::string const& objLibName = *oli;
if(cmTarget* objLib = this->Makefile->FindTargetToUse(objLibName.c_str()))
{
if(objLib->GetType() == cmTarget::OBJECT_LIBRARY)
{
if(this->Target->GetType() != cmTarget::EXECUTABLE &&
this->Target->GetType() != cmTarget::STATIC_LIBRARY &&
this->Target->GetType() != cmTarget::SHARED_LIBRARY &&
this->Target->GetType() != cmTarget::MODULE_LIBRARY)
{
this->GlobalGenerator->GetCMakeInstance()
->IssueMessage(cmake::FATAL_ERROR,
"Only executables and non-OBJECT libraries may "
"reference target objects.",
this->Target->GetBacktrace());
return;
}
this->Target->AddUtility(objLib->GetName());
this->ObjectLibraries.push_back(objLib);
}
else
{
cmOStringStream e;
e << "Objects of target \"" << objLibName
<< "\" referenced but is not an OBJECT library.";
this->GlobalGenerator->GetCMakeInstance()
->IssueMessage(cmake::FATAL_ERROR, e.str(),
this->Target->GetBacktrace());
return;
}
}
else
{
cmOStringStream e;
e << "Objects of target \"" << objLibName
<< "\" referenced but no such target exists.";
this->GlobalGenerator->GetCMakeInstance()
->IssueMessage(cmake::FATAL_ERROR, e.str(),
this->Target->GetBacktrace());
return;
}
}
}
//----------------------------------------------------------------------------
void cmGeneratorTarget::UseObjectLibraries(std::vector<std::string>& objs)
{
for(std::vector<cmTarget*>::const_iterator
ti = this->ObjectLibraries.begin();
ti != this->ObjectLibraries.end(); ++ti)
{
cmTarget* objLib = *ti;
cmGeneratorTarget* ogt =
this->GlobalGenerator->GetGeneratorTarget(objLib);
for(std::vector<cmSourceFile*>::const_iterator
si = ogt->ObjectSources.begin();
si != ogt->ObjectSources.end(); ++si)
{
std::string obj = ogt->ObjectDirectory;
obj += ogt->Objects[*si];
objs.push_back(obj);
}
}
}
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2000-2012 Kitware, Inc., Insight Software Consortium
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#ifndef cmGeneratorTarget_h
#define cmGeneratorTarget_h
#include "cmStandardIncludes.h"
class cmCustomCommand;
class cmGlobalGenerator;
class cmLocalGenerator;
class cmMakefile;
class cmSourceFile;
class cmTarget;
class cmGeneratorTarget
{
public:
cmGeneratorTarget(cmTarget*);
cmTarget* Target;
cmMakefile* Makefile;
cmLocalGenerator* LocalGenerator;
cmGlobalGenerator* GlobalGenerator;
/** Sources classified by purpose. */
std::vector<cmSourceFile*> CustomCommands;
std::vector<cmSourceFile*> ExtraSources;
std::vector<cmSourceFile*> HeaderSources;
std::vector<cmSourceFile*> ObjectSources;
std::vector<cmSourceFile*> ExternalObjects;
std::vector<cmSourceFile*> OSXContent;
std::string ModuleDefinitionFile;
std::map<cmSourceFile const*, std::string> Objects;
std::set<cmSourceFile const*> ExplicitObjectName;
/** Full path with trailing slash to the top-level directory
holding object files for this target. Includes the build
time config name placeholder if needed for the generator. */
std::string ObjectDirectory;
std::vector<cmTarget*> ObjectLibraries;
void UseObjectLibraries(std::vector<std::string>& objs);
private:
void ClassifySources();
void LookupObjectLibraries();
cmGeneratorTarget(cmGeneratorTarget const&);
void operator=(cmGeneratorTarget const&);
};
#endif
......@@ -24,6 +24,7 @@
#include "cmExportInstallFileGenerator.h"
#include "cmComputeTargetDepends.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorTarget.h"
#include <cmsys/Directory.hxx>
......@@ -74,6 +75,7 @@ cmGlobalGenerator::~cmGlobalGenerator()
delete this->ExtraGenerator;
}
this->ClearGeneratorTargets();
this->ClearExportSets();
}
......@@ -807,6 +809,7 @@ bool cmGlobalGenerator::IsDependedOn(const char* project,
void cmGlobalGenerator::Configure()
{
this->FirstTimeProgress = 0.0f;
this->ClearGeneratorTargets();
this->ClearExportSets();
// Delete any existing cmLocalGenerators
unsigned int i;
......@@ -947,6 +950,9 @@ void cmGlobalGenerator::Generate()
this->LocalGenerators[i]->GenerateTargetManifest();
}
// Create per-target generator information.
this->CreateGeneratorTargets();
// Compute the inter-target dependencies.
if(!this->ComputeTargetDepends())
{
......@@ -1056,6 +1062,55 @@ void cmGlobalGenerator::CreateAutomocTargets()
#endif
}
//----------------------------------------------------------------------------
void cmGlobalGenerator::CreateGeneratorTargets()
{
// Construct per-target generator information.
for(unsigned int i=0; i < this->LocalGenerators.size(); ++i)
{
cmTargets& targets =
this->LocalGenerators[i]->GetMakefile()->GetTargets();
for(cmTargets::iterator ti = targets.begin();
ti != targets.end(); ++ti)
{
cmTarget* t = &ti->second;
cmGeneratorTarget* gt = new cmGeneratorTarget(t);
this->GeneratorTargets[t] = gt;
this->ComputeTargetObjects(gt);
}
}
}
//----------------------------------------------------------------------------
void cmGlobalGenerator::ClearGeneratorTargets()
{
for(GeneratorTargetsType::iterator i = this->GeneratorTargets.begin();
i != this->GeneratorTargets.end(); ++i)
{
delete i->second;
}
this->GeneratorTargets.clear();
}
//----------------------------------------------------------------------------
cmGeneratorTarget* cmGlobalGenerator::GetGeneratorTarget(cmTarget* t) const
{
GeneratorTargetsType::const_iterator ti = this->GeneratorTargets.find(t);
if(ti == this->GeneratorTargets.end())
{
this->CMakeInstance->IssueMessage(
cmake::INTERNAL_ERROR, "Missing cmGeneratorTarget instance!",
cmListFileBacktrace());
return 0;
}
return ti->second;
}
//----------------------------------------------------------------------------
void cmGlobalGenerator::ComputeTargetObjects(cmGeneratorTarget*) const
{
// Implemented in generator subclasses that need this.
}
void cmGlobalGenerator::CheckLocalGenerators()
{
......@@ -1714,7 +1769,7 @@ void cmGlobalGenerator::SetCMakeInstance(cmake* cm)
void cmGlobalGenerator::CreateDefaultGlobalTargets(cmTargets* targets)
{
cmMakefile* mf = this->LocalGenerators[0]->GetMakefile();
const char* cmakeCfgIntDir = this->GetCMakeCFGInitDirectory();
const char* cmakeCfgIntDir = this->GetCMakeCFGIntDir();
const char* cmakeCommand = mf->GetRequiredDefinition("CMAKE_COMMAND");
// CPack
......
......@@ -19,6 +19,7 @@
#include "cmTargetDepend.h" // For cmTargetDependSet
#include "cmSystemTools.h" // for cmSystemTools::OutputOption
class cmake;
class cmGeneratorTarget;
class cmMakefile;
class cmLocalGenerator;
class cmExternalMakefileProjectGenerator;
......@@ -183,7 +184,7 @@ public:
const char* GetLanguageOutputExtension(cmSourceFile const&);
///! What is the configurations directory variable called?
virtual const char* GetCMakeCFGInitDirectory() { return "."; }
virtual const char* GetCMakeCFGIntDir() const { return "."; }
/** Get whether the generator should use a script for link commands. */
bool GetUseLinkScript() const { return this->UseLinkScript; }
......@@ -251,6 +252,9 @@ public:
// via a target_link_libraries or add_dependencies
TargetDependSet const& GetTargetDirectDepends(cmTarget & target);
/** Get per-target generator information. */
cmGeneratorTarget* GetGeneratorTarget(cmTarget*) const;
const std::map<cmStdString, std::vector<cmLocalGenerator*> >& GetProjectMap()
const {return this->ProjectMap;}
......@@ -370,6 +374,13 @@ private:
typedef std::map<cmTarget *, TargetDependSet> TargetDependMap;
TargetDependMap TargetDependencies;
// Per-target generator information.
typedef std::map<cmTarget*, cmGeneratorTarget*> GeneratorTargetsType;
GeneratorTargetsType GeneratorTargets;
void CreateGeneratorTargets();
void ClearGeneratorTargets();
virtual void ComputeTargetObjects(cmGeneratorTarget* gt) const;
// Cache directory content and target files to be built.
struct DirectoryContent: public std::set<cmStdString>
{
......
......@@ -14,6 +14,7 @@
#include "cmLocalNinjaGenerator.h"
#include "cmMakefile.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorTarget.h"
#include "cmVersion.h"
const char* cmGlobalNinjaGenerator::NINJA_BUILD_FILE = "build.ninja";
......@@ -499,6 +500,34 @@ bool cmGlobalNinjaGenerator::HasRule(const std::string &name)
return (rule != this->Rules.end());
}
//----------------------------------------------------------------------------
// Private virtual overrides
// TODO: Refactor to combine with cmGlobalUnixMakefileGenerator3 impl.
void cmGlobalNinjaGenerator::ComputeTargetObjects(cmGeneratorTarget* gt) const
{
cmTarget* target = gt->Target;
// Compute full path to object file directory for this target.
std::string dir_max;
dir_max += gt->Makefile->GetCurrentOutputDirectory();
dir_max += "/";
dir_max += gt->LocalGenerator->GetTargetDirectory(*target);
dir_max += "/";
gt->ObjectDirectory = dir_max;
// Compute the name of each object file.
for(std::vector<cmSourceFile*>::iterator
si = gt->ObjectSources.begin();
si != gt->ObjectSources.end(); ++si)
{
cmSourceFile* sf = *si;
std::string objectName = gt->LocalGenerator
->GetObjectFileNameWithoutTarget(*sf, dir_max);
gt->Objects[sf] = objectName;
}
}
//----------------------------------------------------------------------------
// Private methods
......@@ -635,6 +664,7 @@ cmGlobalNinjaGenerator
target->GetFullPath(configName).c_str()));
break;
case cmTarget::OBJECT_LIBRARY:
case cmTarget::UTILITY: {
std::string path = ng->ConvertToNinjaPath(
target->GetMakefile()->GetStartOutputDirectory());
......
......@@ -18,6 +18,7 @@
class cmLocalGenerator;
class cmGeneratedFileStream;
class cmGeneratorTarget;
/**
* \class cmGlobalNinjaGenerator
......@@ -235,6 +236,11 @@ protected:
/// @see cmGlobalGenerator::CheckALLOW_DUPLICATE_CUSTOM_TARGETS()
virtual bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() { return true; }
private:
/// @see cmGlobalGenerator::ComputeTargetObjects
virtual void ComputeTargetObjects(cmGeneratorTarget* gt) const;
private:
// In order to access the AddDependencyToAll() functions and co.
friend class cmLocalNinjaGenerator;
......
......@@ -17,6 +17,7 @@
#include "cmGeneratedFileStream.h"
#include "cmSourceFile.h"
#include "cmTarget.h"
#include "cmGeneratorTarget.h"
cmGlobalUnixMakefileGenerator3::cmGlobalUnixMakefileGenerator3()
{
......@@ -70,6 +71,38 @@ void cmGlobalUnixMakefileGenerator3
"default make target. A \"make install\" target is also provided.";
}
//----------------------------------------------------------------------------
void
cmGlobalUnixMakefileGenerator3
::ComputeTargetObjects(cmGeneratorTarget* gt) const
{
cmTarget* target = gt->Target;
cmLocalUnixMakefileGenerator3* lg =
static_cast<cmLocalUnixMakefileGenerator3*>(gt->LocalGenerator);
// Compute full path to object file directory for this target.
std::string dir_max;
dir_max += gt->Makefile->GetCurrentOutputDirectory();
dir_max += "/";
dir_max += gt->LocalGenerator->GetTargetDirectory(*target);
dir_max += "/";
gt->ObjectDirectory = dir_max;
// Compute the name of each object file.
for(std::vector<cmSourceFile*>::iterator
si = gt->ObjectSources.begin();
si != gt->ObjectSources.end(); ++si)
{
cmSourceFile* sf = *si;
bool hasSourceExtension = true;
std::string objectName = gt->LocalGenerator
->GetObjectFileNameWithoutTarget(*sf, dir_max,
&hasSourceExtension);
gt->Objects[sf] = objectName;
lg->AddLocalObjectFile(target, sf, objectName, hasSourceExtension);
}
}
//----------------------------------------------------------------------------
std::string EscapeJSON(const std::string& s) {
std::string result;
......@@ -378,6 +411,7 @@ void cmGlobalUnixMakefileGenerator3
(l->second.GetType() == cmTarget::STATIC_LIBRARY) ||
(l->second.GetType() == cmTarget::SHARED_LIBRARY) ||
(l->second.GetType() == cmTarget::MODULE_LIBRARY) ||
(l->second.GetType() == cmTarget::OBJECT_LIBRARY) ||
(l->second.GetType() == cmTarget::UTILITY))
{
std::string tname = lg->GetRelativeTargetDirectory(l->second);
......@@ -413,6 +447,7 @@ cmGlobalUnixMakefileGenerator3
(l->second.GetType() == cmTarget::STATIC_LIBRARY) ||
(l->second.GetType() == cmTarget::SHARED_LIBRARY) ||
(l->second.GetType() == cmTarget::MODULE_LIBRARY) ||
(l->second.GetType() == cmTarget::OBJECT_LIBRARY) ||
(l->second.GetType() == cmTarget::UTILITY))