summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Help/command/target_link_libraries.rst85
-rw-r--r--Help/release/dev/link-objects-first.rst8
-rw-r--r--Source/cmComputeLinkDepends.cxx36
-rw-r--r--Source/cmComputeLinkDepends.h5
-rw-r--r--Source/cmComputeTargetDepends.cxx13
-rw-r--r--Source/cmGeneratorTarget.cxx38
-rw-r--r--Source/cmGeneratorTarget.h1
-rw-r--r--Source/cmLinkItem.h6
-rw-r--r--Tests/ObjectLibrary/CMakeLists.txt2
-rw-r--r--Tests/ObjectLibrary/LinkObjects/CMakeLists.txt45
-rw-r--r--Tests/ObjectLibrary/LinkObjects/a_dep.c7
-rw-r--r--Tests/ObjectLibrary/LinkObjects/a_obj.c8
-rw-r--r--Tests/ObjectLibrary/LinkObjects/b_dep.c7
-rw-r--r--Tests/ObjectLibrary/LinkObjects/b_obj.c8
-rw-r--r--Tests/ObjectLibrary/LinkObjects/c_dep.c7
-rw-r--r--Tests/ObjectLibrary/LinkObjects/c_obj.c8
-rw-r--r--Tests/ObjectLibrary/LinkObjects/d_dep.c7
-rw-r--r--Tests/ObjectLibrary/LinkObjects/d_obj.c8
-rw-r--r--Tests/ObjectLibrary/LinkObjects/e_dep.c7
-rw-r--r--Tests/ObjectLibrary/LinkObjects/e_lib.c5
-rw-r--r--Tests/ObjectLibrary/LinkObjects/e_obj.c8
-rw-r--r--Tests/ObjectLibrary/LinkObjects/main.c24
22 files changed, 338 insertions, 5 deletions
diff --git a/Help/command/target_link_libraries.rst b/Help/command/target_link_libraries.rst
index 872e264..ac231bc 100644
--- a/Help/command/target_link_libraries.rst
+++ b/Help/command/target_link_libraries.rst
@@ -289,6 +289,91 @@ treated as :ref:`Interface Libraries`, but when they appear in
a target's :prop_tgt:`LINK_LIBRARIES` property their object files
will be included in the link too.
+.. _`Linking Object Libraries via $<TARGET_OBJECTS>`:
+
+Linking Object Libraries via $<TARGET_OBJECTS>
+""""""""""""""""""""""""""""""""""""""""""""""
+
+.. versionadded:: 3.21
+
+The object files associated with an object library may be referenced
+by the :genex:`$<TARGET_OBJECTS>` generator expression. Such object
+files are placed on the link line *before* all libraries, regardless
+of their relative order. Additionally, an ordering dependency will be
+added to the build sysstem to make sure the object library is up-to-date
+before the dependent target links. For example, the code
+
+.. code-block:: cmake
+
+ add_library(obj3 OBJECT obj3.c)
+ target_compile_definitions(obj3 PUBLIC OBJ3)
+
+ add_executable(main3 main3.c)
+ target_link_libraries(main3 PRIVATE a3 $<TARGET_OBJECTS:obj3> b3)
+
+links executable ``main3`` with object files from ``main3.c``
+and ``obj3.c`` followed by the ``a3`` and ``b3`` libraries.
+``main3.c`` is *not* compiled with usage requirements from ``obj3``,
+such as ``-DOBJ3``.
+
+This approach can be used to achieve transitive inclusion of object
+files in link lines as usage requirements. Continuing the above
+example, the code
+
+.. code-block:: cmake
+
+ add_library(iface_obj3 INTERFACE)
+ target_link_libraries(iface_obj3 INTERFACE obj3 $<TARGET_OBJECTS:obj3>)
+
+creates an interface library ``iface_obj3`` that forwards the ``obj3``
+usage requirements and adds the ``obj3`` object files to dependents'
+link lines. The code
+
+.. code-block:: cmake
+
+ add_executable(use_obj3 use_obj3.c)
+ target_link_libraries(use_obj3 PRIVATE iface_obj3)
+
+compiles ``use_obj3.c`` with ``-DOBJ3`` and links executable ``use_obj3``
+with object files from ``use_obj3.c`` and ``obj3.c``.
+
+This also works transitively through a static library. Since a static
+library does not link, it does not consume the object files from
+object libraries referenced this way. Instead, the object files
+become transitive link dependencies of the static library.
+Continuing the above example, the code
+
+.. code-block:: cmake
+
+ add_library(static3 STATIC static3.c)
+ target_link_libraries(static3 PRIVATE iface_obj3)
+
+ add_executable(use_static3 use_static3.c)
+ target_link_libraries(use_static3 PRIVATE static3)
+
+compiles ``static3.c`` with ``-DOBJ3`` and creates ``libstatic3.a``
+using only its own object file. ``use_static3.c`` is compiled *without*
+``-DOBJ3`` because the usage requirement is not transitive through
+the private dependency of ``static3``. However, the link dependencies
+of ``static3`` are propagated, including the ``iface_obj3`` reference
+to ``$<TARGET_OBJECTS:obj3>``. The ``use_static3`` executable is
+created with object files from ``use_static3.c`` and ``obj3.c``, and
+linked to library ``libstatic3.a``.
+
+When using this approach, it is the project's responsibility to avoid
+linking multiple dependent binaries to ``iface_obj3``, because they will
+all get the ``obj3`` object files on their link lines.
+
+.. note::
+
+ Referencing :genex:`$<TARGET_OBJECTS>` in ``target_link_libraries``
+ calls worked in versions of CMake prior to 3.21 for some cases,
+ but was not fully supported:
+
+ * It did not place the object files before libraries on link lines.
+ * It did not add an ordering dependency on the object library.
+ * It did not work in Xcode with multiple architectures.
+
Cyclic Dependencies of Static Libraries
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/Help/release/dev/link-objects-first.rst b/Help/release/dev/link-objects-first.rst
new file mode 100644
index 0000000..0c622e7
--- /dev/null
+++ b/Help/release/dev/link-objects-first.rst
@@ -0,0 +1,8 @@
+link-objects-first
+------------------
+
+* :command:`target_link_libraries` calls referencing object libraries
+ via the :genex:`TARGET_OBJECTS` generator expression now place the
+ object files before all libraries on the link line, regardless of
+ their specified order. See documentation on
+ :ref:`Linking Object Libraries via \$\<TARGET_OBJECTS\>` for details.
diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx
index 5341e8d..4a6518fd 100644
--- a/Source/cmComputeLinkDepends.cxx
+++ b/Source/cmComputeLinkDepends.cxx
@@ -263,6 +263,12 @@ cmComputeLinkDepends::Compute()
this->FinalLinkEntries.push_back(e);
}
}
+ // Place explicitly linked object files in the front. The linker will
+ // always use them anyway, and they may depend on symbols from libraries.
+ // Append in reverse order since we reverse the final order below.
+ for (int i : cmReverseRange(this->ObjectEntries)) {
+ this->FinalLinkEntries.emplace_back(this->EntryList[i]);
+ }
// Reverse the resulting order since we iterated in reverse.
std::reverse(this->FinalLinkEntries.begin(), this->FinalLinkEntries.end());
@@ -328,6 +334,27 @@ int cmComputeLinkDepends::AddLinkEntry(cmLinkItem const& item)
return index;
}
+void cmComputeLinkDepends::AddLinkObject(cmLinkItem const& item)
+{
+ // Check if the item entry has already been added.
+ auto lei = this->LinkEntryIndex.find(item);
+ if (lei != this->LinkEntryIndex.end()) {
+ return;
+ }
+
+ // Allocate a spot for the item entry.
+ lei = this->AllocateLinkEntry(item);
+
+ // Initialize the item entry.
+ int index = lei->second;
+ LinkEntry& entry = this->EntryList[index];
+ entry.Item = BT<std::string>(item.AsStr(), item.Backtrace);
+ entry.IsObject = true;
+
+ // Record explicitly linked object files separately.
+ this->ObjectEntries.emplace_back(index);
+}
+
void cmComputeLinkDepends::FollowLinkEntry(BFSEntry qe)
{
// Get this entry representation.
@@ -343,6 +370,7 @@ void cmComputeLinkDepends::FollowLinkEntry(BFSEntry qe)
entry.Target->GetType() == cmStateEnums::INTERFACE_LIBRARY;
// This target provides its own link interface information.
this->AddLinkEntries(depender_index, iface->Libraries);
+ this->AddLinkObjects(iface->Objects);
if (isIface) {
return;
@@ -487,6 +515,7 @@ void cmComputeLinkDepends::AddDirectLinkEntries()
cmLinkImplementation const* impl =
this->Target->GetLinkImplementation(this->Config);
this->AddLinkEntries(-1, impl->Libraries);
+ this->AddLinkObjects(impl->Objects);
for (cmLinkItem const& wi : impl->WrongConfigLibraries) {
this->CheckWrongConfigItem(wi);
}
@@ -546,6 +575,13 @@ void cmComputeLinkDepends::AddLinkEntries(int depender_index,
}
}
+void cmComputeLinkDepends::AddLinkObjects(std::vector<cmLinkItem> const& objs)
+{
+ for (cmLinkItem const& obj : objs) {
+ this->AddLinkObject(obj);
+ }
+}
+
cmLinkItem cmComputeLinkDepends::ResolveLinkItem(int depender_index,
const std::string& name)
{
diff --git a/Source/cmComputeLinkDepends.h b/Source/cmComputeLinkDepends.h
index 00fbd59..72316f1 100644
--- a/Source/cmComputeLinkDepends.h
+++ b/Source/cmComputeLinkDepends.h
@@ -66,10 +66,12 @@ private:
std::map<cmLinkItem, int>::iterator AllocateLinkEntry(
cmLinkItem const& item);
int AddLinkEntry(cmLinkItem const& item);
+ void AddLinkObject(cmLinkItem const& item);
void AddVarLinkEntries(int depender_index, const char* value);
void AddDirectLinkEntries();
template <typename T>
void AddLinkEntries(int depender_index, std::vector<T> const& libs);
+ void AddLinkObjects(std::vector<cmLinkItem> const& objs);
cmLinkItem ResolveLinkItem(int depender_index, const std::string& name);
// One entry for each unique item.
@@ -154,6 +156,9 @@ private:
std::set<cmGeneratorTarget const*> OldWrongConfigItems;
void CheckWrongConfigItem(cmLinkItem const& item);
+ // Record of explicitly linked object files.
+ std::vector<int> ObjectEntries;
+
int ComponentOrderId;
cmTargetLinkLibraryType LinkType;
bool HasConfig;
diff --git a/Source/cmComputeTargetDepends.cxx b/Source/cmComputeTargetDepends.cxx
index 0f23417..76712f4 100644
--- a/Source/cmComputeTargetDepends.cxx
+++ b/Source/cmComputeTargetDepends.cxx
@@ -20,6 +20,7 @@
#include "cmProperty.h"
#include "cmRange.h"
#include "cmSourceFile.h"
+#include "cmSourceFileLocationKind.h"
#include "cmState.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
@@ -227,6 +228,12 @@ void cmComputeTargetDepends::CollectTargetDepends(int depender_index)
this->AddInterfaceDepends(depender_index, lib, it, emitted);
}
}
+ for (cmLinkItem const& obj : impl->Objects) {
+ if (cmSourceFile const* o = depender->Makefile->GetSource(
+ obj.AsStr(), cmSourceFileLocationKind::Known)) {
+ this->AddObjectDepends(depender_index, o, emitted);
+ }
+ }
}
// Add dependencies on object libraries not otherwise handled above.
@@ -274,6 +281,12 @@ void cmComputeTargetDepends::AddInterfaceDepends(
this->AddInterfaceDepends(depender_index, libBT, config, emitted);
}
}
+ for (cmLinkItem const& obj : iface->Objects) {
+ if (cmSourceFile const* o = depender->Makefile->GetSource(
+ obj.AsStr(), cmSourceFileLocationKind::Known)) {
+ this->AddObjectDepends(depender_index, o, emitted);
+ }
+ }
}
}
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 51742cf..5deb2df 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -6275,8 +6275,8 @@ cm::optional<cmLinkItem> cmGeneratorTarget::LookupLinkItem(
void cmGeneratorTarget::ExpandLinkItems(
std::string const& prop, std::string const& value, std::string const& config,
cmGeneratorTarget const* headTarget, bool usage_requirements_only,
- std::vector<cmLinkItem>& items, bool& hadHeadSensitiveCondition,
- bool& hadContextSensitiveCondition,
+ std::vector<cmLinkItem>& items, std::vector<cmLinkItem>& objects,
+ bool& hadHeadSensitiveCondition, bool& hadContextSensitiveCondition,
bool& hadLinkLanguageSensitiveCondition) const
{
// Keep this logic in sync with ComputeLinkImplementationLibraries.
@@ -6294,9 +6294,22 @@ void cmGeneratorTarget::ExpandLinkItems(
cmExpandList(cge->Evaluate(this->LocalGenerator, config, headTarget,
&dagChecker, this, headTarget->LinkerLanguage),
libs);
+ cmMakefile const* mf = this->LocalGenerator->GetMakefile();
for (std::string const& lib : libs) {
if (cm::optional<cmLinkItem> maybeItem =
this->LookupLinkItem(lib, cge->GetBacktrace())) {
+ if (!maybeItem->Target) {
+ // Report explicitly linked object files separately.
+ std::string const& maybeObj = maybeItem->AsStr();
+ if (cmSystemTools::FileIsFullPath(maybeObj)) {
+ cmSourceFile const* sf =
+ mf->GetSource(maybeObj, cmSourceFileLocationKind::Known);
+ if (sf && sf->GetPropertyAsBool("EXTERNAL_OBJECT")) {
+ objects.emplace_back(std::move(*maybeItem));
+ continue;
+ }
+ }
+ }
items.emplace_back(std::move(*maybeItem));
}
}
@@ -6804,7 +6817,7 @@ void cmGeneratorTarget::ComputeLinkInterfaceLibraries(
// The interface libraries have been explicitly set.
this->ExpandLinkItems(linkIfaceProp, *explicitLibraries, config,
headTarget, usage_requirements_only, iface.Libraries,
- iface.HadHeadSensitiveCondition,
+ iface.Objects, iface.HadHeadSensitiveCondition,
iface.HadContextSensitiveCondition,
iface.HadLinkLanguageSensitiveCondition);
return;
@@ -6828,6 +6841,7 @@ void cmGeneratorTarget::ComputeLinkInterfaceLibraries(
// Compare the link implementation fallback link interface to the
// preferred new link interface property and warn if different.
std::vector<cmLinkItem> ifaceLibs;
+ std::vector<cmLinkItem> ifaceObjects;
static const std::string newProp = "INTERFACE_LINK_LIBRARIES";
if (cmProp newExplicitLibraries = this->GetProperty(newProp)) {
bool hadHeadSensitiveConditionDummy = false;
@@ -6835,7 +6849,7 @@ void cmGeneratorTarget::ComputeLinkInterfaceLibraries(
bool hadLinkLanguageSensitiveConditionDummy = false;
this->ExpandLinkItems(newProp, *newExplicitLibraries, config,
headTarget, usage_requirements_only, ifaceLibs,
- hadHeadSensitiveConditionDummy,
+ ifaceObjects, hadHeadSensitiveConditionDummy,
hadContextSensitiveConditionDummy,
hadLinkLanguageSensitiveConditionDummy);
}
@@ -6903,7 +6917,7 @@ const cmLinkInterface* cmGeneratorTarget::GetImportLinkInterface(
cmExpandList(info->Languages, iface.Languages);
this->ExpandLinkItems(info->LibrariesProp, info->Libraries, config,
headTarget, usage_requirements_only, iface.Libraries,
- iface.HadHeadSensitiveCondition,
+ iface.Objects, iface.HadHeadSensitiveCondition,
iface.HadContextSensitiveCondition,
iface.HadLinkLanguageSensitiveCondition);
std::vector<std::string> deps = cmExpandedList(info->SharedDeps);
@@ -7425,6 +7439,7 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
cmGeneratorTarget const* head) const
{
cmLocalGenerator const* lg = this->LocalGenerator;
+ cmMakefile const* mf = lg->GetMakefile();
cmStringRange entryRange = this->Target->GetLinkImplementationEntries();
cmBacktraceRange btRange = this->Target->GetLinkImplementationBacktraces();
cmBacktraceRange::const_iterator btIt = btRange.begin();
@@ -7500,6 +7515,19 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
// The entry is meant for this configuration.
cmLinkItem item = this->ResolveLinkItem(name, *btIt, lg);
+ if (!item.Target) {
+ // Report explicitly linked object files separately.
+ std::string const& maybeObj = item.AsStr();
+ if (cmSystemTools::FileIsFullPath(maybeObj)) {
+ cmSourceFile const* sf =
+ mf->GetSource(maybeObj, cmSourceFileLocationKind::Known);
+ if (sf && sf->GetPropertyAsBool("EXTERNAL_OBJECT")) {
+ impl.Objects.emplace_back(std::move(item));
+ continue;
+ }
+ }
+ }
+
impl.Libraries.emplace_back(std::move(item), evaluated != *le);
}
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index dbc47b8..be36f3f 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -1036,6 +1036,7 @@ private:
const cmGeneratorTarget* headTarget,
bool usage_requirements_only,
std::vector<cmLinkItem>& items,
+ std::vector<cmLinkItem>& objects,
bool& hadHeadSensitiveCondition,
bool& hadContextSensitiveCondition,
bool& hadLinkLanguageSensitiveCondition) const;
diff --git a/Source/cmLinkItem.h b/Source/cmLinkItem.h
index 5a90e7e..db44938 100644
--- a/Source/cmLinkItem.h
+++ b/Source/cmLinkItem.h
@@ -50,6 +50,9 @@ struct cmLinkImplementationLibraries
// Libraries linked directly in this configuration.
std::vector<cmLinkImplItem> Libraries;
+ // Object files linked directly in this configuration.
+ std::vector<cmLinkItem> Objects;
+
// Libraries linked directly in other configurations.
// Needed only for OLD behavior of CMP0003.
std::vector<cmLinkItem> WrongConfigLibraries;
@@ -63,6 +66,9 @@ struct cmLinkInterfaceLibraries
// Libraries listed in the interface.
std::vector<cmLinkItem> Libraries;
+ // Object files listed in the interface.
+ std::vector<cmLinkItem> Objects;
+
// Whether the list depends on a genex referencing the head target.
bool HadHeadSensitiveCondition = false;
diff --git a/Tests/ObjectLibrary/CMakeLists.txt b/Tests/ObjectLibrary/CMakeLists.txt
index 74f34e4..06167a8 100644
--- a/Tests/ObjectLibrary/CMakeLists.txt
+++ b/Tests/ObjectLibrary/CMakeLists.txt
@@ -74,4 +74,6 @@ target_link_libraries(UseABstaticObjs ABstatic)
add_subdirectory(ExportLanguages)
+add_subdirectory(LinkObjects)
+
add_subdirectory(Transitive)
diff --git a/Tests/ObjectLibrary/LinkObjects/CMakeLists.txt b/Tests/ObjectLibrary/LinkObjects/CMakeLists.txt
new file mode 100644
index 0000000..899be45
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/CMakeLists.txt
@@ -0,0 +1,45 @@
+add_executable(LinkObjects main.c)
+
+# Link TARGET_OBJECTS through LINK_LIBRARIES.
+add_library(LinkObjectsAObj OBJECT a_obj.c)
+add_library(LinkObjectsADep STATIC a_dep.c)
+target_compile_definitions(LinkObjectsAObj PUBLIC OBJA)
+target_link_libraries(LinkObjects PRIVATE LinkObjectsADep $<TARGET_OBJECTS:LinkObjectsAObj>)
+
+# Link TARGET_OBJECTS through INTERFACE_LINK_LIBRARIES with usage requirements.
+add_library(LinkObjectsB INTERFACE)
+add_library(LinkObjectsBObj OBJECT b_obj.c)
+add_library(LinkObjectsBDep STATIC b_dep.c)
+target_compile_definitions(LinkObjectsBObj PUBLIC OBJB)
+target_link_libraries(LinkObjectsB INTERFACE LinkObjectsBObj $<TARGET_OBJECTS:LinkObjectsBObj>)
+target_link_libraries(LinkObjectsBObj PRIVATE LinkObjectsBDep)
+target_link_libraries(LinkObjects PRIVATE LinkObjectsB)
+
+# Link TARGET_OBJECTS through INTERFACE_LINK_LIBRARIES without usage requirements.
+add_library(LinkObjectsC INTERFACE)
+add_library(LinkObjectsCObj OBJECT c_obj.c)
+add_library(LinkObjectsCDep STATIC c_dep.c)
+target_compile_definitions(LinkObjectsCObj PUBLIC OBJC)
+target_link_libraries(LinkObjectsC INTERFACE LinkObjectsCDep $<TARGET_OBJECTS:LinkObjectsCObj>)
+target_link_libraries(LinkObjectsCObj PRIVATE LinkObjectsCDep)
+target_link_libraries(LinkObjects PRIVATE LinkObjectsC)
+
+# Link TARGET_OBJECTS through both LINK_LIBRARIES and INTERFACE_LINK_LIBRARIES, deduplicated.
+add_library(LinkObjectsD INTERFACE)
+add_library(LinkObjectsDObj OBJECT d_obj.c)
+add_library(LinkObjectsDDep STATIC d_dep.c)
+target_compile_definitions(LinkObjectsDObj PUBLIC OBJD)
+target_link_libraries(LinkObjectsD INTERFACE LinkObjectsDObj $<TARGET_OBJECTS:LinkObjectsDObj>)
+target_link_libraries(LinkObjectsDObj PRIVATE LinkObjectsDDep)
+target_link_libraries(LinkObjects PRIVATE $<TARGET_OBJECTS:LinkObjectsDObj> LinkObjectsD)
+
+# Link TARGET_OBJECTS through STATIC library private dependency.
+add_library(LinkObjectsE INTERFACE)
+add_library(LinkObjectsEObj OBJECT e_obj.c)
+add_library(LinkObjectsEDep STATIC e_dep.c)
+add_library(LinkObjectsEStatic STATIC e_lib.c)
+target_compile_definitions(LinkObjectsEObj PUBLIC OBJE)
+target_link_libraries(LinkObjectsE INTERFACE LinkObjectsEObj $<TARGET_OBJECTS:LinkObjectsEObj>)
+target_link_libraries(LinkObjectsEObj PRIVATE LinkObjectsEDep)
+target_link_libraries(LinkObjectsEStatic PRIVATE LinkObjectsE)
+target_link_libraries(LinkObjects PRIVATE LinkObjectsEStatic)
diff --git a/Tests/ObjectLibrary/LinkObjects/a_dep.c b/Tests/ObjectLibrary/LinkObjects/a_dep.c
new file mode 100644
index 0000000..0128740
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/a_dep.c
@@ -0,0 +1,7 @@
+#ifdef OBJA
+# error "OBJA is defined, but should not be"
+#endif
+int a_dep(void)
+{
+ return 0;
+}
diff --git a/Tests/ObjectLibrary/LinkObjects/a_obj.c b/Tests/ObjectLibrary/LinkObjects/a_obj.c
new file mode 100644
index 0000000..5e79c60
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/a_obj.c
@@ -0,0 +1,8 @@
+#ifndef OBJA
+# error "OBJA is not defined, but should be"
+#endif
+extern int a_dep(void);
+int a_obj(void)
+{
+ return a_dep();
+}
diff --git a/Tests/ObjectLibrary/LinkObjects/b_dep.c b/Tests/ObjectLibrary/LinkObjects/b_dep.c
new file mode 100644
index 0000000..ad5d367
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/b_dep.c
@@ -0,0 +1,7 @@
+#ifdef OBJB
+# error "OBJB is defined, but should not be"
+#endif
+int b_dep(void)
+{
+ return 0;
+}
diff --git a/Tests/ObjectLibrary/LinkObjects/b_obj.c b/Tests/ObjectLibrary/LinkObjects/b_obj.c
new file mode 100644
index 0000000..d0f426a
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/b_obj.c
@@ -0,0 +1,8 @@
+#ifndef OBJB
+# error "OBJB is not defined, but should be"
+#endif
+extern int b_dep(void);
+int b_obj(void)
+{
+ return b_dep();
+}
diff --git a/Tests/ObjectLibrary/LinkObjects/c_dep.c b/Tests/ObjectLibrary/LinkObjects/c_dep.c
new file mode 100644
index 0000000..1d99ab8
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/c_dep.c
@@ -0,0 +1,7 @@
+#ifdef OBJC
+# error "OBJC is defined, but should not be"
+#endif
+int c_dep(void)
+{
+ return 0;
+}
diff --git a/Tests/ObjectLibrary/LinkObjects/c_obj.c b/Tests/ObjectLibrary/LinkObjects/c_obj.c
new file mode 100644
index 0000000..08fa5f5
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/c_obj.c
@@ -0,0 +1,8 @@
+#ifndef OBJC
+# error "OBJC is not defined, but should be"
+#endif
+extern int c_dep(void);
+int c_obj(void)
+{
+ return c_dep();
+}
diff --git a/Tests/ObjectLibrary/LinkObjects/d_dep.c b/Tests/ObjectLibrary/LinkObjects/d_dep.c
new file mode 100644
index 0000000..cf09314
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/d_dep.c
@@ -0,0 +1,7 @@
+#ifdef OBJD
+# error "OBJD is defined, but should not be"
+#endif
+int d_dep(void)
+{
+ return 0;
+}
diff --git a/Tests/ObjectLibrary/LinkObjects/d_obj.c b/Tests/ObjectLibrary/LinkObjects/d_obj.c
new file mode 100644
index 0000000..d14ce66
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/d_obj.c
@@ -0,0 +1,8 @@
+#ifndef OBJD
+# error "OBJD is not defined, but should be"
+#endif
+extern int d_dep(void);
+int d_obj(void)
+{
+ return d_dep();
+}
diff --git a/Tests/ObjectLibrary/LinkObjects/e_dep.c b/Tests/ObjectLibrary/LinkObjects/e_dep.c
new file mode 100644
index 0000000..7fb70c1
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/e_dep.c
@@ -0,0 +1,7 @@
+#ifdef OBJE
+# error "OBJE is defined, but should not be"
+#endif
+int e_dep(void)
+{
+ return 0;
+}
diff --git a/Tests/ObjectLibrary/LinkObjects/e_lib.c b/Tests/ObjectLibrary/LinkObjects/e_lib.c
new file mode 100644
index 0000000..9bb3a44
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/e_lib.c
@@ -0,0 +1,5 @@
+extern int e_obj(void);
+int e_lib(void)
+{
+ return e_obj();
+}
diff --git a/Tests/ObjectLibrary/LinkObjects/e_obj.c b/Tests/ObjectLibrary/LinkObjects/e_obj.c
new file mode 100644
index 0000000..02624eb
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/e_obj.c
@@ -0,0 +1,8 @@
+#ifndef OBJE
+# error "OBJE is not defined, but should be"
+#endif
+extern int e_dep(void);
+int e_obj(void)
+{
+ return e_dep();
+}
diff --git a/Tests/ObjectLibrary/LinkObjects/main.c b/Tests/ObjectLibrary/LinkObjects/main.c
new file mode 100644
index 0000000..c09c4f1
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/main.c
@@ -0,0 +1,24 @@
+#ifdef OBJA
+# error "OBJA is defined, but should not be"
+#endif
+#ifndef OBJB
+# error "OBJB is not defined, but should be"
+#endif
+#ifdef OBJC
+# error "OBJC is defined, but should not be"
+#endif
+#ifndef OBJD
+# error "OBJD is not defined, but should be"
+#endif
+#ifdef OBJE
+# error "OBJE is defined, but should not be"
+#endif
+extern int a_obj(void);
+extern int b_obj(void);
+extern int c_obj(void);
+extern int d_obj(void);
+extern int e_lib(void);
+int main(void)
+{
+ return a_obj() + b_obj() + c_obj() + d_obj() + e_lib();
+}