Commit 44f0d2d9 authored by Brad King's avatar Brad King 💬 Committed by Kitware Robot
Browse files

Merge topic 'objlib-extend'

eec93bce Allow OBJECT libraries to be installed, exported, and imported
93c89bc7 Genex: Allow TARGET_OBJECTS to be used everywhere
ac0cf7ff Genex: Reject TARGET_OBJECTS on non-object libraries earlier
8577978c Tests: ExportImport C code should use explicit (void) in prototypes
26cfd039 cmInstallTargetGenerator: Re-order GenerateScriptForConfig logic
25f3f22a cmGlobalGenerator: Add method to check if object file location is known
d596c550 cmGeneratorTarget: Add method to get the object file directory
930042f2

 cmGeneratorTarget: Factor out a GetTargetObjectNames method
...

Acked-by: Kitware Robot's avatarKitware Robot <kwrobot@kitware.com>
Merge-request: !712
parents 9db9bb27 eec93bce
......@@ -64,7 +64,7 @@ Imported Libraries
::
add_library(<name> <SHARED|STATIC|MODULE|UNKNOWN> IMPORTED
add_library(<name> <SHARED|STATIC|MODULE|OBJECT|UNKNOWN> IMPORTED
[GLOBAL])
An :ref:`IMPORTED library target <Imported Targets>` references a library
......@@ -106,10 +106,9 @@ may contain only sources that compile, header files, and other files
that would not affect linking of a normal library (e.g. ``.txt``).
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. 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>``.
cannot be linked. 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>``.
Alias Libraries
^^^^^^^^^^^^^^^
......
......@@ -73,7 +73,7 @@ Installing Targets
::
install(TARGETS targets... [EXPORT <export-name>]
[[ARCHIVE|LIBRARY|RUNTIME|FRAMEWORK|BUNDLE|
[[ARCHIVE|LIBRARY|RUNTIME|OBJECTS|FRAMEWORK|BUNDLE|
PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE]
[DESTINATION <dir>]
[PERMISSIONS permissions...]
......@@ -86,10 +86,10 @@ Installing Targets
)
The ``TARGETS`` form specifies rules for installing targets from a
project. There are five kinds of target files that may be installed:
``ARCHIVE``, ``LIBRARY``, ``RUNTIME``, ``FRAMEWORK``, and ``BUNDLE``.
Executables are treated as ``RUNTIME`` targets, except that those
marked with the ``MACOSX_BUNDLE`` property are treated as ``BUNDLE``
project. There are six kinds of target files that may be installed:
``ARCHIVE``, ``LIBRARY``, ``RUNTIME``, ``OBJECTS``, ``FRAMEWORK``, and
``BUNDLE``. Executables are treated as ``RUNTIME`` targets, except that
those marked with the ``MACOSX_BUNDLE`` property are treated as ``BUNDLE``
targets on OS X. Static libraries are treated as ``ARCHIVE`` targets,
except that those marked with the ``FRAMEWORK`` property are treated
as ``FRAMEWORK`` targets on OS X.
......@@ -99,10 +99,11 @@ targets, except that those marked with the ``FRAMEWORK`` property are
treated as ``FRAMEWORK`` targets on OS X. For DLL platforms the DLL
part of a shared library is treated as a ``RUNTIME`` target and the
corresponding import library is treated as an ``ARCHIVE`` target.
All Windows-based systems including Cygwin are DLL platforms.
The ``ARCHIVE``, ``LIBRARY``, ``RUNTIME``, and ``FRAMEWORK`` arguments
change the type of target to which the subsequent properties apply.
If none is given the installation properties apply to all target
All Windows-based systems including Cygwin are DLL platforms. Object
libraries are always treated as ``OBJECTS`` targets.
The ``ARCHIVE``, ``LIBRARY``, ``RUNTIME``, ``OBJECTS``, and ``FRAMEWORK``
arguments change the type of target to which the subsequent properties
apply. If none is given the installation properties apply to all target
types. If only one is given then only targets of that type will be
installed (which can be used to install just a DLL or just an import
library).
......@@ -165,8 +166,8 @@ the ``mySharedLib`` DLL will be installed to ``<prefix>/bin`` and
The ``EXPORT`` option associates the installed target files with an
export called ``<export-name>``. It must appear before any ``RUNTIME``,
``LIBRARY``, or ``ARCHIVE`` options. To actually install the export
file itself, call ``install(EXPORT)``, documented below.
``LIBRARY``, ``ARCHIVE``, or ``OBJECTS`` options. To actually install the
export file itself, call ``install(EXPORT)``, documented below.
Installing a target with the :prop_tgt:`EXCLUDE_FROM_ALL` target property
set to ``TRUE`` has undefined behavior.
......
......@@ -125,10 +125,10 @@ The object files collection can be used as source inputs to other targets:
add_executable(test_exe $<TARGET_OBJECTS:archive> test.cpp)
``OBJECT`` libraries may only be used locally as sources in a buildsystem --
they may not be installed, exported, or used in the right hand side of
``OBJECT`` libraries may not be used in the right hand side of
:command:`target_link_libraries`. They also may not be used as the ``TARGET``
in a use of the :command:`add_custom_command(TARGET)` command signature.
in a use of the :command:`add_custom_command(TARGET)` command signature. They
may be installed, and will be exported as an INTERFACE library.
Although object libraries may not be named directly in calls to
the :command:`target_link_libraries` command, they can be "linked"
......@@ -136,6 +136,12 @@ indirectly by using an :ref:`Interface Library <Interface Libraries>`
whose :prop_tgt:`INTERFACE_SOURCES` target property is set to name
``$<TARGET_OBJECTS:objlib>``.
Although object libraries may not be used as the ``TARGET``
in a use of the :command:`add_custom_command(TARGET)` command signature,
the list of objects can be used by :command:`add_custom_command(OUTPUT)` or
:command:`file(GENERATE)` by using ``$<TARGET_OBJECTS:objlib>``.
Build Specification and Usage Requirements
==========================================
......
......@@ -290,9 +290,7 @@ Available output expressions are:
Content of ``...`` converted to a C identifier.
``$<TARGET_OBJECTS:objLib>``
List of objects resulting from build of ``objLib``. ``objLib`` must be an
object of type ``OBJECT_LIBRARY``. This expression may only be used in
the sources of :command:`add_library` and :command:`add_executable`
commands.
object of type ``OBJECT_LIBRARY``.
``$<SHELL_PATH:...>``
Content of ``...`` converted to shell path style. For example, slashes are
converted to backslashes in Windows shells and drive letters are converted
......
......@@ -194,6 +194,8 @@ Properties on Targets
/prop_tgt/IMPORTED_LOCATION
/prop_tgt/IMPORTED_NO_SONAME_CONFIG
/prop_tgt/IMPORTED_NO_SONAME
/prop_tgt/IMPORTED_OBJECTS_CONFIG
/prop_tgt/IMPORTED_OBJECTS
/prop_tgt/IMPORTED
/prop_tgt/IMPORTED_SONAME_CONFIG
/prop_tgt/IMPORTED_SONAME
......
IMPORTED_OBJECTS
----------------
:ref:`;-list <CMake Language Lists>` of absolute paths to the object
files on disk for an :ref:`imported <Imported targets>`
:ref:`object library <object libraries>`.
Ignored for non-imported targets.
Projects may skip ``IMPORTED_OBJECTS`` if the configuration-specific
property :prop_tgt:`IMPORTED_OBJECTS_<CONFIG>` is set instead.
IMPORTED_OBJECTS_<CONFIG>
-------------------------
<CONFIG>-specific version of :prop_tgt:`IMPORTED_OBJECTS` property.
Configuration names correspond to those provided by the project from
which the target is imported.
add_custom_command-TARGET_OBJECTS
---------------------------------
* The :command:`add_custom_command` command learned to evaluate the
``TARGET_OBJECTS``
:manual:`generator expression <cmake-generator-expressions(7)>`.
add_library-TARGET_OBJECTS
--------------------------
* The :command:`add_library` command ``IMPORTED`` option learned to support
:ref:`Object Libraries`.
file-GENERATE-TARGET_OBJECTS
----------------------------
* The :command:`file(GENERATE)` subcommand learned to evaluate the
``TARGET_OBJECTS``
:manual:`generator expression <cmake-generator-expressions(7)>`.
install-TARGET_OBJECTS
----------------------
* The :command:`install(TARGETS)` command learned a new ``OBJECTS`` option to
specify where to install :ref:`Object Libraries`.
* The :command:`install(EXPORT)` command learned how to export
:ref:`Object Libraries`.
......@@ -297,10 +297,15 @@ bool cmAddLibraryCommand::InitialPass(std::vector<std::string> const& args,
return false;
}
if (type == cmStateEnums::OBJECT_LIBRARY) {
this->Makefile->IssueMessage(
cmake::FATAL_ERROR,
"The OBJECT library type may not be used for IMPORTED libraries.");
return true;
std::string reason;
if (!this->Makefile->GetGlobalGenerator()->HasKnownObjectFileLocation(
&reason)) {
this->Makefile->IssueMessage(
cmake::FATAL_ERROR,
"The OBJECT library type may not be used for IMPORTED libraries" +
reason + ".");
return true;
}
}
if (type == cmStateEnums::INTERFACE_LIBRARY) {
if (!cmGeneratorExpression::IsValidTargetName(libName)) {
......
......@@ -2,6 +2,7 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmExportBuildFileGenerator.h"
#include "cmAlgorithms.h"
#include "cmExportSet.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorTarget.h"
......@@ -21,6 +22,8 @@
#include <sstream>
#include <utility>
class cmSourceFile;
cmExportBuildFileGenerator::cmExportBuildFileGenerator()
{
this->LG = CM_NULLPTR;
......@@ -171,27 +174,48 @@ void cmExportBuildFileGenerator::SetImportLocationProperty(
// Get the makefile in which to lookup target information.
cmMakefile* mf = target->Makefile;
// Add the main target file.
{
std::string prop = "IMPORTED_LOCATION";
if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
std::string prop = "IMPORTED_OBJECTS";
prop += suffix;
std::string value;
if (target->IsAppBundleOnApple()) {
value = target->GetFullPath(config, false);
} else {
value = target->GetFullPath(config, false, true);
// Compute all the object files inside this target and setup
// IMPORTED_OBJECTS as a list of object files
std::vector<cmSourceFile const*> objectSources;
target->GetObjectSources(objectSources, config);
std::string const obj_dir = target->GetObjectDirectory(config);
std::vector<std::string> objects;
for (std::vector<cmSourceFile const*>::const_iterator si =
objectSources.begin();
si != objectSources.end(); ++si) {
const std::string& obj = target->GetObjectName(*si);
objects.push_back(obj_dir + obj);
}
properties[prop] = value;
}
// Add the import library for windows DLLs.
if (target->HasImportLibrary() &&
mf->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) {
std::string prop = "IMPORTED_IMPLIB";
prop += suffix;
std::string value = target->GetFullPath(config, true);
target->GetImplibGNUtoMS(value, value, "${CMAKE_IMPORT_LIBRARY_SUFFIX}");
properties[prop] = value;
// Store the property.
properties[prop] = cmJoin(objects, ";");
} else {
// Add the main target file.
{
std::string prop = "IMPORTED_LOCATION";
prop += suffix;
std::string value;
if (target->IsAppBundleOnApple()) {
value = target->GetFullPath(config, false);
} else {
value = target->GetFullPath(config, false, true);
}
properties[prop] = value;
}
// Add the import library for windows DLLs.
if (target->HasImportLibrary() &&
mf->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) {
std::string prop = "IMPORTED_IMPLIB";
prop += suffix;
std::string value = target->GetFullPath(config, true);
target->GetImplibGNUtoMS(value, value, "${CMAKE_IMPORT_LIBRARY_SUFFIX}");
properties[prop] = value;
}
}
}
......
......@@ -149,11 +149,15 @@ bool cmExportCommand::InitialPass(std::vector<std::string> const& args,
if (cmTarget* target = gg->FindTarget(*currentTarget)) {
if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
std::ostringstream e;
e << "given OBJECT library \"" << *currentTarget
<< "\" which may not be exported.";
this->SetError(e.str());
return false;
std::string reason;
if (!this->Makefile->GetGlobalGenerator()
->HasKnownObjectFileLocation(&reason)) {
std::ostringstream e;
e << "given OBJECT library \"" << *currentTarget
<< "\" which may not be exported" << reason << ".";
this->SetError(e.str());
return false;
}
}
if (target->GetType() == cmStateEnums::UTILITY) {
this->SetError("given custom target \"" + *currentTarget +
......
......@@ -441,6 +441,11 @@ void getCompatibleInterfaceProperties(cmGeneratorTarget* target,
std::set<std::string>& ifaceProperties,
const std::string& config)
{
if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
// object libraries have no link information, so nothing to compute
return;
}
cmComputeLinkInformation* info = target->GetLinkInformation(config);
if (!info) {
......@@ -927,6 +932,9 @@ void cmExportFileGenerator::GenerateImportTargetCode(
case cmStateEnums::UNKNOWN_LIBRARY:
os << "add_library(" << targetName << " UNKNOWN IMPORTED)\n";
break;
case cmStateEnums::OBJECT_LIBRARY:
os << "add_library(" << targetName << " OBJECT IMPORTED)\n";
break;
case cmStateEnums::INTERFACE_LIBRARY:
os << "add_library(" << targetName << " INTERFACE IMPORTED)\n";
break;
......
......@@ -331,6 +331,8 @@ void cmExportInstallFileGenerator::GenerateImportTargetsConfig(
properties, importedLocations);
this->SetImportLocationProperty(config, suffix, te->RuntimeGenerator,
properties, importedLocations);
this->SetImportLocationProperty(config, suffix, te->ObjectsGenerator,
properties, importedLocations);
this->SetImportLocationProperty(config, suffix, te->FrameworkGenerator,
properties, importedLocations);
this->SetImportLocationProperty(config, suffix, te->BundleGenerator,
......@@ -397,6 +399,23 @@ void cmExportInstallFileGenerator::SetImportLocationProperty(
// Store the property.
properties[prop] = value;
importedLocations.insert(prop);
} else if (itgen->GetTarget()->GetType() == cmStateEnums::OBJECT_LIBRARY) {
// Construct the property name.
std::string prop = "IMPORTED_OBJECTS";
prop += suffix;
// Compute all the object files inside this target and setup
// IMPORTED_OBJECTS as a list of object files
std::vector<std::string> objects;
itgen->GetInstallObjectNames(config, objects);
for (std::vector<std::string>::iterator i = objects.begin();
i != objects.end(); ++i) {
*i = value + *i;
}
// Store the property.
properties[prop] = cmJoin(objects, ";");
importedLocations.insert(prop);
} else {
// Construct the property name.
std::string prop = "IMPORTED_LOCATION";
......
......@@ -64,8 +64,10 @@ void cmGeneratorExpressionEvaluationFile::Generate(
return;
}
std::ostringstream e;
e << "Evaluation file to be written multiple times for different "
"configurations or languages with different content:\n "
e << "Evaluation file to be written multiple times with different "
"content. "
"This is generally caused by the content evaluating the "
"configuration type, language, or location of object files:\n "
<< outputFileName;
lg->IssueMessage(cmake::FATAL_ERROR, e.str());
return;
......
......@@ -33,8 +33,6 @@
#include <string.h>
#include <utility>
class cmSourceFile;
std::string cmGeneratorExpressionNode::EvaluateDependentExpression(
std::string const& prop, cmLocalGenerator* lg,
cmGeneratorExpressionContext* context, cmGeneratorTarget const* headTarget,
......@@ -1228,15 +1226,6 @@ static const struct TargetObjectsNode : public cmGeneratorExpressionNode
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const
CM_OVERRIDE
{
if (!context->EvaluateForBuildsystem) {
std::ostringstream e;
e << "The evaluation of the TARGET_OBJECTS generator expression "
"is only suitable for consumption by CMake. It is not suitable "
"for writing out elsewhere.";
reportError(context, content->GetOriginalExpression(), e.str());
return std::string();
}
std::string tgtName = parameters.front();
cmGeneratorTarget* gt = context->LG->FindGeneratorTargetToUse(tgtName);
if (!gt) {
......@@ -1253,39 +1242,60 @@ static const struct TargetObjectsNode : public cmGeneratorExpressionNode
reportError(context, content->GetOriginalExpression(), e.str());
return std::string();
}
if (!context->EvaluateForBuildsystem) {
cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
std::string reason;
if (!gg->HasKnownObjectFileLocation(&reason)) {
std::ostringstream e;
e << "The evaluation of the TARGET_OBJECTS generator expression "
"is only suitable for consumption by CMake (limited"
<< reason << "). "
"It is not suitable for writing out elsewhere.";
reportError(context, content->GetOriginalExpression(), e.str());
return std::string();
}
}
std::vector<cmSourceFile const*> objectSources;
gt->GetObjectSources(objectSources, context->Config);
std::map<cmSourceFile const*, std::string> mapping;
std::vector<std::string> objects;
for (std::vector<cmSourceFile const*>::const_iterator it =
objectSources.begin();
it != objectSources.end(); ++it) {
mapping[*it];
}
if (gt->IsImported()) {
const char* loc = CM_NULLPTR;
const char* imp = CM_NULLPTR;
std::string suffix;
if (gt->Target->GetMappedConfig(context->Config, &loc, &imp, suffix)) {
cmSystemTools::ExpandListArgument(loc, objects);
}
context->HadContextSensitiveCondition = true;
} else {
gt->GetTargetObjectNames(context->Config, objects);
std::string obj_dir;
if (context->EvaluateForBuildsystem) {
// Use object file directory with buildsystem placeholder.
obj_dir = gt->ObjectDirectory;
// Here we assume that the set of object files produced
// by an object library does not vary with configuration
// and do not set HadContextSensitiveCondition to true.
} else {
// Use object file directory with per-config location.
obj_dir = gt->GetObjectDirectory(context->Config);
context->HadContextSensitiveCondition = true;
}
gt->LocalGenerator->ComputeObjectFilenames(mapping, gt);
for (std::vector<std::string>::iterator oi = objects.begin();
oi != objects.end(); ++oi) {
*oi = obj_dir + *oi;
}
}
// Create the cmSourceFile instances in the referencing directory.
cmMakefile* mf = context->LG->GetMakefile();
std::string obj_dir = gt->ObjectDirectory;
std::string result;
const char* sep = "";
for (std::vector<cmSourceFile const*>::const_iterator it =
objectSources.begin();
it != objectSources.end(); ++it) {
// Find the object file name corresponding to this source file.
std::map<cmSourceFile const*, std::string>::const_iterator map_it =
mapping.find(*it);
// It must exist because we populated the mapping just above.
assert(!map_it->second.empty());
result += sep;
std::string objFile = obj_dir + map_it->second;
mf->AddTargetObject(tgtName, objFile);
result += objFile;
sep = ";";
for (std::vector<std::string>::iterator oi = objects.begin();
oi != objects.end(); ++oi) {
mf->AddTargetObject(tgtName, *oi);
}
return result;
return cmJoin(objects, ";");
}
} targetObjectsNode;
......
......@@ -3274,6 +3274,46 @@ std::string cmGeneratorTarget::GetPDBName(const std::string& config) const
return prefix + base + ".pdb";
}
std::string cmGeneratorTarget::GetObjectDirectory(
std::string const& config) const
{
std::string obj_dir =
this->GlobalGenerator->ExpandCFGIntDir(this->ObjectDirectory, config);
#if defined(__APPLE__)
// find and replace $(PROJECT_NAME) xcode placeholder
const std::string projectName = this->LocalGenerator->GetProjectName();
cmSystemTools::ReplaceString(obj_dir, "$(PROJECT_NAME)", projectName);
#endif
return obj_dir;
}
void cmGeneratorTarget::GetTargetObjectNames(
std::string const& config, std::vector<std::string>& objects) const
{
std::vector<cmSourceFile const*> objectSources;
this->GetObjectSources(objectSources, config);
std::map<cmSourceFile const*, std::string> mapping;
for (std::vector<cmSourceFile const*>::const_iterator it =
objectSources.begin();
it != objectSources.end(); ++it) {
mapping[*it];
}
this->LocalGenerator->ComputeObjectFilenames(mapping, this);
for (std::vector<cmSourceFile const*>::const_iterator it =
objectSources.begin();
it != objectSources.end(); ++it) {
// Find the object file name corresponding to this source file.
std::map<cmSourceFile const*, std::string>::const_iterator map_it =
mapping.find(*it);
// It must exist because we populated the mapping just above.
assert(!map_it->second.empty());
objects.push_back(map_it->second);
}
}
bool cmGeneratorTarget::StrictTargetComparison::operator()(
cmGeneratorTarget const* t1, cmGeneratorTarget const* t2) const
{
......
......@@ -209,6 +209,11 @@ public:
bool realname) const;
std::string NormalGetRealName(const std::string& config) const;
/** Get the names of an object library's object files underneath
its object file directory. */
void GetTargetObjectNames(std::string const& config,
std::vector<std::string>& objects) const;
/** What hierarchy level should the reported directory contain */
enum BundleDirectoryLevel
{
......@@ -364,6 +369,10 @@ public:
time config name placeholder if needed for the generator. */
std::string ObjectDirectory;
/** Full path with trailing slash to the top-level directory
holding object files for the given configuration. */
std::string GetObjectDirectory(std::string const& config) const;
void GetAppleArchs(const std::string& config,
std::vector<std::string>& archVec) const;
......@@ -534,7 +543,7 @@ public:
std::string GetPDBDirectory(const std::string& config) const;
///! Return the preferred linker language for this target
std::string GetLinkerLanguage(const std::string& config = "") const;
std::string GetLinkerLanguage(const std::string& config) const;
/** Does this target have a GNU implib to convert to MS format? */
bool HasImplibGNUtoMS() const;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment