From 7abd3137b74543cf698ac6c7290696d82042e7de Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 19 Sep 2024 13:10:04 -0400 Subject: Linking: Optionally reorder direct dependencies from LINK_LIBRARIES Traditionally CMake generates link lines by starting with the direct link dependencies specified by `LINK_LIBRARIES` in their original order and then appending indirect dependencies that the direct dependencies do not express. This gives projects control over ordering among independent entries, which can be important when intermixing flags and libraries, or when multiple libraries provide the same symbol. However, it may also result in inefficient link lines. Add support for an alternative strategy that can reorder direct link dependencies to produce more efficient link lines. This is useful for projects that cannot easily specify their targets' direct dependencies in an order that satisfies indirect dependencies. Add a `CMAKE_LINK_LIBRARIES_STRATEGY` variable and corresponding `LINK_LIBRARIES_STRATEGY` target property to select a strategy. Fixes: #26271 --- Help/command/target_link_libraries.rst | 5 ++ Help/manual/cmake-properties.7.rst | 1 + Help/manual/cmake-variables.7.rst | 1 + Help/prop_tgt/LINK_LIBRARIES.rst | 5 ++ Help/prop_tgt/LINK_LIBRARIES_STRATEGY.rst | 11 ++++ Help/release/dev/link-strategy.rst | 7 +++ Help/variable/CMAKE_LINK_LIBRARIES_STRATEGY.rst | 68 ++++++++++++++++++++++ Source/cmComputeLinkDepends.cxx | 22 ++++++- Source/cmComputeLinkDepends.h | 10 +++- Source/cmComputeLinkInformation.cxx | 20 ++++++- Source/cmTarget.cxx | 1 + Tests/RunCMake/CMakeLists.txt | 1 + .../Basic-PRESERVE_ORDER-run-stdout-reverse.txt | 1 + .../Basic-PRESERVE_ORDER-run-stdout.txt | 1 + .../Basic-PRESERVE_ORDER-stderr.txt | 7 +++ .../Basic-PRESERVE_ORDER.cmake | 2 + .../Basic-REORDER-run-stdout.txt | 1 + .../LinkLibrariesStrategy/Basic-REORDER-stderr.txt | 6 ++ .../LinkLibrariesStrategy/Basic-REORDER.cmake | 2 + .../LinkLibrariesStrategy/Basic-common.cmake | 15 +++++ Tests/RunCMake/LinkLibrariesStrategy/Basic.c | 11 ++++ Tests/RunCMake/LinkLibrariesStrategy/BasicA.c | 4 ++ Tests/RunCMake/LinkLibrariesStrategy/BasicB.c | 5 ++ Tests/RunCMake/LinkLibrariesStrategy/BasicC.c | 5 ++ Tests/RunCMake/LinkLibrariesStrategy/BasicX.c | 7 +++ .../RunCMake/LinkLibrariesStrategy/CMakeLists.txt | 3 + .../Duplicate-PRESERVE_ORDER-stderr.txt | 15 +++++ .../Duplicate-PRESERVE_ORDER.cmake | 2 + .../Duplicate-REORDER-stderr.txt | 9 +++ .../LinkLibrariesStrategy/Duplicate-REORDER.cmake | 2 + .../LinkLibrariesStrategy/Duplicate-common.cmake | 12 ++++ Tests/RunCMake/LinkLibrariesStrategy/Duplicate.c | 4 ++ Tests/RunCMake/LinkLibrariesStrategy/Inspect.cmake | 12 ++++ .../LinkLibrariesStrategy/RunCMakeTest.cmake | 36 ++++++++++++ .../LinkLibrariesStrategy/Unknown-result.txt | 1 + .../LinkLibrariesStrategy/Unknown-stderr.txt | 4 ++ Tests/RunCMake/LinkLibrariesStrategy/Unknown.cmake | 5 ++ Tests/RunCMake/LinkLibrariesStrategy/main.c | 4 ++ 38 files changed, 323 insertions(+), 5 deletions(-) create mode 100644 Help/prop_tgt/LINK_LIBRARIES_STRATEGY.rst create mode 100644 Help/release/dev/link-strategy.rst create mode 100644 Help/variable/CMAKE_LINK_LIBRARIES_STRATEGY.rst create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER-run-stdout-reverse.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER-run-stdout.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER-stderr.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER.cmake create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER-run-stdout.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER-stderr.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER.cmake create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-common.cmake create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic.c create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/BasicA.c create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/BasicB.c create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/BasicC.c create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/BasicX.c create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/CMakeLists.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Duplicate-PRESERVE_ORDER-stderr.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Duplicate-PRESERVE_ORDER.cmake create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER-stderr.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER.cmake create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Duplicate-common.cmake create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Duplicate.c create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Inspect.cmake create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/RunCMakeTest.cmake create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Unknown-result.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Unknown-stderr.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Unknown.cmake create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/main.c diff --git a/Help/command/target_link_libraries.rst b/Help/command/target_link_libraries.rst index a82adda..94a2429 100644 --- a/Help/command/target_link_libraries.rst +++ b/Help/command/target_link_libraries.rst @@ -140,6 +140,11 @@ Items containing ``::``, such as ``Foo::Bar``, are assumed to be target names and will cause an error if no such target exists. See policy :policy:`CMP0028`. +See the :variable:`CMAKE_LINK_LIBRARIES_STRATEGY` variable and +corresponding :prop_tgt:`LINK_LIBRARIES_STRATEGY` target property +for details on how CMake orders direct link dependencies on linker +command lines. + See the :manual:`cmake-buildsystem(7)` manual for more on defining buildsystem properties. diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 9ad856d..10dbe10 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -336,6 +336,7 @@ Properties on Targets /prop_tgt/LINK_INTERFACE_MULTIPLICITY_CONFIG /prop_tgt/LINK_LIBRARIES /prop_tgt/LINK_LIBRARIES_ONLY_TARGETS + /prop_tgt/LINK_LIBRARIES_STRATEGY /prop_tgt/LINK_LIBRARY_OVERRIDE /prop_tgt/LINK_LIBRARY_OVERRIDE_LIBRARY /prop_tgt/LINK_OPTIONS diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index a79747f..cc10950 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -491,6 +491,7 @@ Variables that Control the Build /variable/CMAKE_LINK_GROUP_USING_FEATURE /variable/CMAKE_LINK_GROUP_USING_FEATURE_SUPPORTED /variable/CMAKE_LINK_INTERFACE_LIBRARIES + /variable/CMAKE_LINK_LIBRARIES_STRATEGY /variable/CMAKE_LINK_LIBRARY_FEATURE_ATTRIBUTES /variable/CMAKE_LINK_LIBRARY_FILE_FLAG /variable/CMAKE_LINK_LIBRARY_FLAG diff --git a/Help/prop_tgt/LINK_LIBRARIES.rst b/Help/prop_tgt/LINK_LIBRARIES.rst index b5c1d89..b449aa1 100644 --- a/Help/prop_tgt/LINK_LIBRARIES.rst +++ b/Help/prop_tgt/LINK_LIBRARIES.rst @@ -28,3 +28,8 @@ In advanced use cases, the list of direct link dependencies specified by this property may be updated by usage requirements from dependencies. See the :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` and :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE` target properties. + +See the :variable:`CMAKE_LINK_LIBRARIES_STRATEGY` variable and +corresponding :prop_tgt:`LINK_LIBRARIES_STRATEGY` target property +for details on how CMake orders direct link dependencies on linker +command lines. diff --git a/Help/prop_tgt/LINK_LIBRARIES_STRATEGY.rst b/Help/prop_tgt/LINK_LIBRARIES_STRATEGY.rst new file mode 100644 index 0000000..bba707c --- /dev/null +++ b/Help/prop_tgt/LINK_LIBRARIES_STRATEGY.rst @@ -0,0 +1,11 @@ +LINK_LIBRARIES_STRATEGY +----------------------- + +.. versionadded:: 3.31 + +Specify a strategy for ordering a target's direct link dependencies +on linker command lines. + +See the :variable:`CMAKE_LINK_LIBRARIES_STRATEGY` variable for details +and supported values. This property is initialized by the value of that +variable when a target is created. diff --git a/Help/release/dev/link-strategy.rst b/Help/release/dev/link-strategy.rst new file mode 100644 index 0000000..ab626d1 --- /dev/null +++ b/Help/release/dev/link-strategy.rst @@ -0,0 +1,7 @@ +link-strategy +------------- + +* The :variable:`CMAKE_LINK_LIBRARIES_STRATEGY` variable and + corresponding :prop_tgt:`LINK_LIBRARIES_STRATEGY` target + property were added to optionally specify the strategy + CMake uses to generate link lines. diff --git a/Help/variable/CMAKE_LINK_LIBRARIES_STRATEGY.rst b/Help/variable/CMAKE_LINK_LIBRARIES_STRATEGY.rst new file mode 100644 index 0000000..1ba6b27 --- /dev/null +++ b/Help/variable/CMAKE_LINK_LIBRARIES_STRATEGY.rst @@ -0,0 +1,68 @@ +CMAKE_LINK_LIBRARIES_STRATEGY +----------------------------- + +.. versionadded:: 3.31 + +Specify a strategy for ordering targets' direct link dependencies +on linker command lines. + +The value of this variable initializes the :prop_tgt:`LINK_LIBRARIES_STRATEGY` +target property of targets as they are created. Set that property directly +to specify a strategy for a single target. + +CMake generates a target's link line using its :ref:`Target Link Properties`. +In particular, the :prop_tgt:`LINK_LIBRARIES` target property records the +target's direct link dependencies, typically populated by calls to +:command:`target_link_libraries`. Indirect link dependencies are +propagated from those entries of :prop_tgt:`LINK_LIBRARIES` that name +library targets by following the transitive closure of their +:prop_tgt:`INTERFACE_LINK_LIBRARIES` properties. CMake supports multiple +strategies for passing direct and indirect link dependencies to the linker. + +Consider this example for the strategies below: + +.. code-block:: cmake + + add_library(A STATIC ...) + add_library(B STATIC ...) + add_library(C STATIC ...) + add_executable(main ...) + target_link_libraries(B PRIVATE A) + target_link_libraries(C PRIVATE A) + target_link_libraries(main PRIVATE A B C) + +The supported strategies are: + +``PRESERVE_ORDER`` + Entries of :prop_tgt:`LINK_LIBRARIES` always appear first and in their + original order. Indirect link dependencies not satisfied by the + original entries may be reordered and de-duplicated with respect to + one another, but are always appended after the original entries. + This may result in less efficient link lines, but gives projects + control of ordering among independent entries. Such control may be + important when intermixing link flags with libraries, or when multiple + libraries provide a given symbol. + + This is the default. + + In the above example, this strategy computes a link line for ``main`` + by starting with its original entries ``A B C``, and then appends + another ``A`` to satisfy the dependencies of ``B`` and ``C`` on ``A``. + The final order is ``A B C A``. + +``REORDER`` + Entries of :prop_tgt:`LINK_LIBRARIES` may be reordered, de-duplicated, + and intermixed with indirect link dependencies. This may result in + more efficient link lines, but does not give projects any control of + ordering among independent entries. + + In the above example, this strategy computes a link line for ``main`` + by re-ordering its original entries ``A B C`` to satisfy the + dependencies of ``B`` and ``C`` on ``A``. + The final order is ``B C A``. + +.. note:: + + Regardless of the strategy used, the actual linker invocation for + some platforms may de-duplicate entries based on linker capabilities. + See policy :policy:`CMP0156`. diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx index ae33c12..70e992e 100644 --- a/Source/cmComputeLinkDepends.cxx +++ b/Source/cmComputeLinkDepends.cxx @@ -581,7 +581,8 @@ std::string const& cmComputeLinkDepends::LinkEntry::DEFAULT = cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target, const std::string& config, - const std::string& linkLanguage) + const std::string& linkLanguage, + LinkLibrariesStrategy strategy) : Target(target) , Makefile(this->Target->Target->GetMakefile()) , GlobalGenerator(this->Target->GetLocalGenerator()->GetGlobalGenerator()) @@ -592,6 +593,7 @@ cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target, , LinkLanguage(linkLanguage) , LinkType(CMP0003_ComputeLinkType( this->Config, this->Makefile->GetCMakeInstance()->GetDebugConfigs())) + , Strategy(strategy) { // target oriented feature override property takes precedence over @@ -1488,8 +1490,22 @@ void cmComputeLinkDepends::OrderLinkEntries() } // Start with the original link line. - for (size_t originalEntry : this->OriginalEntries) { - this->VisitEntry(originalEntry); + switch (this->Strategy) { + case LinkLibrariesStrategy::PRESERVE_ORDER: { + // Emit the direct dependencies in their original order. + // This gives projects control over ordering. + for (size_t originalEntry : this->OriginalEntries) { + this->VisitEntry(originalEntry); + } + } break; + case LinkLibrariesStrategy::REORDER: { + // Schedule the direct dependencies for emission in topo order. + // This may produce more efficient link lines. + for (size_t originalEntry : this->OriginalEntries) { + this->MakePendingComponent( + this->CCG->GetComponentMap()[originalEntry]); + } + } break; } // Now explore anything left pending. Since the component graph is diff --git a/Source/cmComputeLinkDepends.h b/Source/cmComputeLinkDepends.h index 55f0032..8b8aba4 100644 --- a/Source/cmComputeLinkDepends.h +++ b/Source/cmComputeLinkDepends.h @@ -27,6 +27,12 @@ class cmMakefile; class cmSourceFile; class cmake; +enum class LinkLibrariesStrategy +{ + PRESERVE_ORDER, + REORDER, +}; + /** \class cmComputeLinkDepends * \brief Compute link dependencies for targets. */ @@ -35,7 +41,8 @@ class cmComputeLinkDepends public: cmComputeLinkDepends(cmGeneratorTarget const* target, const std::string& config, - const std::string& linkLanguage); + const std::string& linkLanguage, + LinkLibrariesStrategy strategy); ~cmComputeLinkDepends(); cmComputeLinkDepends(const cmComputeLinkDepends&) = delete; @@ -94,6 +101,7 @@ private: bool DebugMode = false; std::string LinkLanguage; cmTargetLinkLibraryType LinkType; + LinkLibrariesStrategy Strategy; EntryVector FinalLinkEntries; std::map LinkLibraryOverride; diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx index dd194ed..26ff326 100644 --- a/Source/cmComputeLinkInformation.cxx +++ b/Source/cmComputeLinkInformation.cxx @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -567,8 +568,25 @@ bool cmComputeLinkInformation::Compute() return false; } + LinkLibrariesStrategy strategy = LinkLibrariesStrategy::PRESERVE_ORDER; + if (cmValue s = this->Target->GetProperty("LINK_LIBRARIES_STRATEGY")) { + if (*s == "PRESERVE_ORDER"_s) { + strategy = LinkLibrariesStrategy::PRESERVE_ORDER; + } else if (*s == "REORDER"_s) { + strategy = LinkLibrariesStrategy::REORDER; + } else { + this->CMakeInstance->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("LINK_LIBRARIES_STRATEGY value '", *s, + "' is not recognized."), + this->Target->GetBacktrace()); + return false; + } + } + // Compute the ordered link line items. - cmComputeLinkDepends cld(this->Target, this->Config, this->LinkLanguage); + cmComputeLinkDepends cld(this->Target, this->Config, this->LinkLanguage, + strategy); cld.SetOldLinkDirMode(this->OldLinkDirMode); cmComputeLinkDepends::EntryVector const& linkEntries = cld.Compute(); FeatureDescriptor const* currentFeature = nullptr; diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index da0091b..f220837 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -466,6 +466,7 @@ TargetProperty const StaticTargetProperties[] = { { "LINKER_TYPE"_s, IC::CanCompileSources }, { "ENABLE_EXPORTS"_s, IC::TargetWithSymbolExports }, { "LINK_LIBRARIES_ONLY_TARGETS"_s, IC::NormalNonImportedTarget }, + { "LINK_LIBRARIES_STRATEGY"_s, IC::NormalNonImportedTarget }, { "LINK_SEARCH_START_STATIC"_s, IC::CanCompileSources }, { "LINK_SEARCH_END_STATIC"_s, IC::CanCompileSources }, // Initialize per-configuration name postfix property from the variable only diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 40db51b..ba8776f 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -836,6 +836,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "(Linux|Darwin|Windows)" endif() add_RunCMake_test(LinkLibrariesProcessing) +add_RunCMake_test(LinkLibrariesStrategy) add_RunCMake_test(File_Archive) add_RunCMake_test(File_Configure) add_RunCMake_test(File_Generate) diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER-run-stdout-reverse.txt b/Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER-run-stdout-reverse.txt new file mode 100644 index 0000000..7e46d1ba --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER-run-stdout-reverse.txt @@ -0,0 +1 @@ +^Library 'B' was linked first\.$ diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER-run-stdout.txt b/Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER-run-stdout.txt new file mode 100644 index 0000000..6ef12eb --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER-run-stdout.txt @@ -0,0 +1 @@ +^Library 'A' was linked first\.$ diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER-stderr.txt b/Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER-stderr.txt new file mode 100644 index 0000000..1d8f4ba --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER-stderr.txt @@ -0,0 +1,7 @@ +target \[main\] link dependency ordering: + target \[A\] + target \[B\] + target \[C\] + target \[A\] + +target \[main\] link line: diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER.cmake b/Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER.cmake new file mode 100644 index 0000000..f3dc096 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER.cmake @@ -0,0 +1,2 @@ +set(CMAKE_LINK_LIBRARIES_STRATEGY PRESERVE_ORDER) +include(Basic-common.cmake) diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER-run-stdout.txt b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER-run-stdout.txt new file mode 100644 index 0000000..7e46d1ba --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER-run-stdout.txt @@ -0,0 +1 @@ +^Library 'B' was linked first\.$ diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER-stderr.txt b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER-stderr.txt new file mode 100644 index 0000000..54b02bd --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER-stderr.txt @@ -0,0 +1,6 @@ +target \[main\] link dependency ordering: + target \[B\] + target \[C\] + target \[A\] + +target \[main\] link line: diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER.cmake b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER.cmake new file mode 100644 index 0000000..8e62377 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER.cmake @@ -0,0 +1,2 @@ +set(CMAKE_LINK_LIBRARIES_STRATEGY REORDER) +include(Basic-common.cmake) diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-common.cmake b/Tests/RunCMake/LinkLibrariesStrategy/Basic-common.cmake new file mode 100644 index 0000000..8dbbd39 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-common.cmake @@ -0,0 +1,15 @@ +enable_language(C) + +add_library(A STATIC BasicA.c BasicX.c) +add_library(B STATIC BasicB.c BasicX.c) +add_library(C STATIC BasicC.c BasicX.c) +target_link_libraries(B PRIVATE A) +target_link_libraries(C PRIVATE A) +target_compile_definitions(A PRIVATE BASIC_ID="A") +target_compile_definitions(B PRIVATE BASIC_ID="B") +target_compile_definitions(C PRIVATE BASIC_ID="C") + +add_executable(main Basic.c) +target_link_libraries(main PRIVATE A B C) +set_property(TARGET main PROPERTY LINK_DEPENDS_DEBUG_MODE 1) # undocumented +set_property(TARGET main PROPERTY RUNTIME_OUTPUT_DIRECTORY "$<1:${CMAKE_BINARY_DIR}>") diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic.c b/Tests/RunCMake/LinkLibrariesStrategy/Basic.c new file mode 100644 index 0000000..124d489 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic.c @@ -0,0 +1,11 @@ +extern int BasicB(void); +extern int BasicC(void); + +/* Use a symbol provided by a dedicated object file in A, B, and C. + The first library linked will determine the return value. */ +extern int BasicX(void); + +int main(void) +{ + return BasicB() + BasicC() + BasicX(); +} diff --git a/Tests/RunCMake/LinkLibrariesStrategy/BasicA.c b/Tests/RunCMake/LinkLibrariesStrategy/BasicA.c new file mode 100644 index 0000000..d3fe95d --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/BasicA.c @@ -0,0 +1,4 @@ +int BasicA(void) +{ + return 0; +} diff --git a/Tests/RunCMake/LinkLibrariesStrategy/BasicB.c b/Tests/RunCMake/LinkLibrariesStrategy/BasicB.c new file mode 100644 index 0000000..fd7a120 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/BasicB.c @@ -0,0 +1,5 @@ +extern int BasicA(void); +int BasicB(void) +{ + return BasicA(); +} diff --git a/Tests/RunCMake/LinkLibrariesStrategy/BasicC.c b/Tests/RunCMake/LinkLibrariesStrategy/BasicC.c new file mode 100644 index 0000000..7171dd1 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/BasicC.c @@ -0,0 +1,5 @@ +extern int BasicA(void); +int BasicC(void) +{ + return BasicA(); +} diff --git a/Tests/RunCMake/LinkLibrariesStrategy/BasicX.c b/Tests/RunCMake/LinkLibrariesStrategy/BasicX.c new file mode 100644 index 0000000..39f7863 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/BasicX.c @@ -0,0 +1,7 @@ +#include + +int BasicX(void) +{ + printf("Library '%s' was linked first.\n", BASIC_ID); + return 0; +} diff --git a/Tests/RunCMake/LinkLibrariesStrategy/CMakeLists.txt b/Tests/RunCMake/LinkLibrariesStrategy/CMakeLists.txt new file mode 100644 index 0000000..dda37d8 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.30) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-PRESERVE_ORDER-stderr.txt b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-PRESERVE_ORDER-stderr.txt new file mode 100644 index 0000000..2637f93 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-PRESERVE_ORDER-stderr.txt @@ -0,0 +1,15 @@ +target \[main\] link dependency ordering: + item \[-Flag1\] + target \[A\] + item \[-Flag1\] + target \[B\] + item \[-Flag2\] + target \[C\] + item \[-Flag2\] + target \[A\] + item \[-Flag2\] + target \[B\] + item \[-Flag3\] + target \[C\] + +target \[main\] link line: diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-PRESERVE_ORDER.cmake b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-PRESERVE_ORDER.cmake new file mode 100644 index 0000000..9f694db --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-PRESERVE_ORDER.cmake @@ -0,0 +1,2 @@ +set(CMAKE_LINK_LIBRARIES_STRATEGY PRESERVE_ORDER) +include(Duplicate-common.cmake) diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER-stderr.txt b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER-stderr.txt new file mode 100644 index 0000000..2353288 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER-stderr.txt @@ -0,0 +1,9 @@ +target \[main\] link dependency ordering: + item \[-Flag1\] + target \[A\] + target \[B\] + item \[-Flag2\] + target \[C\] + item \[-Flag3\] + +target \[main\] link line: diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER.cmake b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER.cmake new file mode 100644 index 0000000..cc51e0a --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER.cmake @@ -0,0 +1,2 @@ +set(CMAKE_LINK_LIBRARIES_STRATEGY REORDER) +include(Duplicate-common.cmake) diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-common.cmake b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-common.cmake new file mode 100644 index 0000000..5050a0a --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-common.cmake @@ -0,0 +1,12 @@ +enable_language(C) + +add_library(A INTERFACE IMPORTED) +add_library(B INTERFACE IMPORTED) +add_library(C INTERFACE IMPORTED) +set_property(TARGET A PROPERTY IMPORTED_LIBNAME A) +set_property(TARGET B PROPERTY IMPORTED_LIBNAME B) +set_property(TARGET C PROPERTY IMPORTED_LIBNAME C) + +add_executable(main Duplicate.c) +target_link_libraries(main PRIVATE -Flag1 A -Flag1 B -Flag2 C -Flag2 A -Flag2 B -Flag3 C) +set_property(TARGET main PROPERTY LINK_DEPENDS_DEBUG_MODE 1) # undocumented diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Duplicate.c b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate.c new file mode 100644 index 0000000..8488f4e --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Inspect.cmake b/Tests/RunCMake/LinkLibrariesStrategy/Inspect.cmake new file mode 100644 index 0000000..fd5da91 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Inspect.cmake @@ -0,0 +1,12 @@ +enable_language(C) + +set(info "") +foreach(var + CMAKE_C_LINK_LIBRARIES_PROCESSING + ) + if(DEFINED ${var}) + string(APPEND info "set(${var} \"${${var}}\")\n") + endif() +endforeach() + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/info.cmake" "${info}") diff --git a/Tests/RunCMake/LinkLibrariesStrategy/RunCMakeTest.cmake b/Tests/RunCMake/LinkLibrariesStrategy/RunCMakeTest.cmake new file mode 100644 index 0000000..1b02355 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/RunCMakeTest.cmake @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.30) + +include(RunCMake) + +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(RunCMake_TEST_OPTIONS -DCMAKE_CONFIGURATION_TYPES=Debug) +else() + set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) +endif() + +# Detect information from the toolchain: +# - CMAKE_C_LINK_LIBRARIES_PROCESSING +run_cmake(Inspect) +include("${RunCMake_BINARY_DIR}/Inspect-build/info.cmake") + +run_cmake(Unknown) + +function(run_strategy case exe) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-build) + run_cmake(${case}) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(${case}-build ${CMAKE_COMMAND} --build . --config Debug) + if(exe) + if("ORDER=REVERSE" IN_LIST CMAKE_C_LINK_LIBRARIES_PROCESSING) + set(RunCMake-stdout-file ${case}-run-stdout-reverse.txt) + endif() + run_cmake_command(${case}-run ${RunCMake_TEST_BINARY_DIR}/${exe}) + unset(RunCMake-stdout-file) + endif() +endfunction() + +run_strategy(Basic-PRESERVE_ORDER "main") +run_strategy(Basic-REORDER "main") + +run_cmake(Duplicate-PRESERVE_ORDER) +run_cmake(Duplicate-REORDER) diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Unknown-result.txt b/Tests/RunCMake/LinkLibrariesStrategy/Unknown-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Unknown-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Unknown-stderr.txt b/Tests/RunCMake/LinkLibrariesStrategy/Unknown-stderr.txt new file mode 100644 index 0000000..3081f32 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Unknown-stderr.txt @@ -0,0 +1,4 @@ +^CMake Error at Unknown.cmake:5 \(add_executable\): + LINK_LIBRARIES_STRATEGY value 'unknown' is not recognized\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9] \(include\) diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Unknown.cmake b/Tests/RunCMake/LinkLibrariesStrategy/Unknown.cmake new file mode 100644 index 0000000..d3ad586 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Unknown.cmake @@ -0,0 +1,5 @@ +enable_language(C) + +set(CMAKE_LINK_LIBRARIES_STRATEGY unknown) + +add_executable(main main.c) diff --git a/Tests/RunCMake/LinkLibrariesStrategy/main.c b/Tests/RunCMake/LinkLibrariesStrategy/main.c new file mode 100644 index 0000000..8488f4e --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/main.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} -- cgit v0.12