diff options
author | Kyle Edwards <kyle.edwards@kitware.com> | 2020-07-29 18:19:56 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2020-08-12 15:31:07 (GMT) |
commit | 2e42651dff43c4e962f03fc24281cbf446880ded (patch) | |
tree | 5a1fa6f2098d9cb5bdc04c7f9285dc454c3a5ebf | |
parent | 0cd3b5d0ca8d541fc3769f467db71a07a95be7f6 (diff) | |
download | CMake-2e42651dff43c4e962f03fc24281cbf446880ded.zip CMake-2e42651dff43c4e962f03fc24281cbf446880ded.tar.gz CMake-2e42651dff43c4e962f03fc24281cbf446880ded.tar.bz2 |
Add option to optimize link dependencies for static libraries
Add an `OPTIMIZE_DEPENDENCIES` target property and supporting
`CMAKE_OPTIMIZE_DEPENDENCIES` variable to optionally enable pruning and
flattening of outgoing dependencies from static libraries. Since they
do not actually link, they only depend on side effects of their
dependencies. Therefore we can drop dependencies that contribute no
side effects.
47 files changed, 559 insertions, 8 deletions
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 30b2a05..afdf78c 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -307,6 +307,7 @@ Properties on Targets /prop_tgt/OBJCXX_EXTENSIONS /prop_tgt/OBJCXX_STANDARD /prop_tgt/OBJCXX_STANDARD_REQUIRED + /prop_tgt/OPTIMIZE_DEPENDENCIES /prop_tgt/OSX_ARCHITECTURES_CONFIG /prop_tgt/OSX_ARCHITECTURES /prop_tgt/OUTPUT_NAME_CONFIG diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index d780a65..4b40917 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -437,6 +437,7 @@ Variables that Control the Build /variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX /variable/CMAKE_NO_BUILTIN_CHRPATH /variable/CMAKE_NO_SYSTEM_FROM_IMPORTED + /variable/CMAKE_OPTIMIZE_DEPENDENCIES /variable/CMAKE_OSX_ARCHITECTURES /variable/CMAKE_OSX_DEPLOYMENT_TARGET /variable/CMAKE_OSX_SYSROOT diff --git a/Help/prop_tgt/OPTIMIZE_DEPENDENCIES.rst b/Help/prop_tgt/OPTIMIZE_DEPENDENCIES.rst new file mode 100644 index 0000000..533cf6a --- /dev/null +++ b/Help/prop_tgt/OPTIMIZE_DEPENDENCIES.rst @@ -0,0 +1,38 @@ +OPTIMIZE_DEPENDENCIES +--------------------- + +Activates dependency optimization of static and object libraries. + +When this property is set to true, some dependencies for a static or object +library may be removed at generation time if they are not necessary to build +the library, since static and object libraries don't actually link against +anything. + +If a static or object library has dependency optimization enabled, it first +discards all dependencies. Then, it looks through all of the direct and +indirect dependencies that it initially had, and adds them back if they meet +any of the following criteria: + +* The dependency was added to the library by :command:`add_dependencies`. +* The dependency was added to the library through a source file in the library + generated by a custom command that uses the dependency. +* The dependency has any ``PRE_BUILD``, ``PRE_LINK``, or ``POST_BUILD`` custom + commands associated with it. +* The dependency contains any source files that were generated by a custom + command. +* The dependency contains any languages which produce side effects that are + relevant to the library. Currently, all languages except C, C++, Objective-C, + Objective-C++, assembly, and CUDA are assumed to produce side effects. + However, side effects from one language are assumed not to be relevant to + another (for example, a Fortran library is assumed to not have any side + effects that are relevant for a Swift library.) + +As an example, assume you have a static Fortran library which depends on a +static C library, which in turn depends on a static Fortran library. The +top-level Fortran library has optimization enabled, but the middle C library +does not. If you build the top Fortran library, the bottom Fortran library will +also build, but not the middle C library, since the C library does not have any +side effects that are relevant for the Fortran library. However, if you build +the middle C library, the bottom Fortran library will also build, even though +it does not have any side effects that are relevant to the C library, since the +C library does not have optimization enabled. diff --git a/Help/release/dev/optimize-link-dependencies.rst b/Help/release/dev/optimize-link-dependencies.rst new file mode 100644 index 0000000..cfda826 --- /dev/null +++ b/Help/release/dev/optimize-link-dependencies.rst @@ -0,0 +1,7 @@ +optimize-link-dependencies +-------------------------- + +* A new target property, :prop_tgt:`OPTIMIZE_DEPENDENCIES`, was added to + avoid unnecessarily building dependencies for a static library. +* A new variable, :variable:`CMAKE_OPTIMIZE_DEPENDENCIES`, was added to + initialize the :prop_tgt:`OPTIMIZE_DEPENDENCIES` target property. diff --git a/Help/variable/CMAKE_OPTIMIZE_DEPENDENCIES.rst b/Help/variable/CMAKE_OPTIMIZE_DEPENDENCIES.rst new file mode 100644 index 0000000..eed352a --- /dev/null +++ b/Help/variable/CMAKE_OPTIMIZE_DEPENDENCIES.rst @@ -0,0 +1,4 @@ +CMAKE_OPTIMIZE_DEPENDENCIES +--------------------------- + +Initializes the :prop_tgt:`OPTIMIZE_DEPENDENCIES` target property. diff --git a/Source/cmComputeTargetDepends.cxx b/Source/cmComputeTargetDepends.cxx index e717f71..1f22ce6 100644 --- a/Source/cmComputeTargetDepends.cxx +++ b/Source/cmComputeTargetDepends.cxx @@ -17,10 +17,12 @@ #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" +#include "cmProperty.h" #include "cmRange.h" #include "cmSourceFile.h" #include "cmState.h" #include "cmStateTypes.h" +#include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" #include "cmTargetDepend.h" @@ -115,19 +117,32 @@ bool cmComputeTargetDepends::Compute() if (this->DebugMode) { this->DisplayGraph(this->InitialGraph, "initial"); } + cmComputeComponentGraph ccg1(this->InitialGraph); + ccg1.Compute(); + if (!this->CheckComponents(ccg1)) { + return false; + } + + // Compute the intermediate graph. + this->CollectSideEffects(); + this->ComputeIntermediateGraph(); + if (this->DebugMode) { + this->DisplaySideEffects(); + this->DisplayGraph(this->IntermediateGraph, "intermediate"); + } // Identify components. - cmComputeComponentGraph ccg(this->InitialGraph); - ccg.Compute(); + cmComputeComponentGraph ccg2(this->IntermediateGraph); + ccg2.Compute(); if (this->DebugMode) { - this->DisplayComponents(ccg); + this->DisplayComponents(ccg2, "intermediate"); } - if (!this->CheckComponents(ccg)) { + if (!this->CheckComponents(ccg2)) { return false; } // Compute the final dependency graph. - if (!this->ComputeFinalDepends(ccg)) { + if (!this->ComputeFinalDepends(ccg2)) { return false; } if (this->DebugMode) { @@ -382,6 +397,111 @@ void cmComputeTargetDepends::AddTargetDepend( } } +void cmComputeTargetDepends::CollectSideEffects() +{ + this->SideEffects.resize(0); + this->SideEffects.resize(this->InitialGraph.size()); + + int n = static_cast<int>(this->InitialGraph.size()); + std::set<int> visited; + for (int i = 0; i < n; ++i) { + this->CollectSideEffectsForTarget(visited, i); + } +} + +void cmComputeTargetDepends::CollectSideEffectsForTarget( + std::set<int>& visited, int depender_index) +{ + if (!visited.count(depender_index)) { + auto& se = this->SideEffects[depender_index]; + visited.insert(depender_index); + this->Targets[depender_index]->AppendCustomCommandSideEffects( + se.CustomCommandSideEffects); + this->Targets[depender_index]->AppendLanguageSideEffects( + se.LanguageSideEffects); + + for (auto const& edge : this->InitialGraph[depender_index]) { + this->CollectSideEffectsForTarget(visited, edge); + auto const& dse = this->SideEffects[edge]; + se.CustomCommandSideEffects.insert(dse.CustomCommandSideEffects.cbegin(), + dse.CustomCommandSideEffects.cend()); + for (auto const& it : dse.LanguageSideEffects) { + se.LanguageSideEffects[it.first].insert(it.second.cbegin(), + it.second.cend()); + } + } + } +} + +void cmComputeTargetDepends::ComputeIntermediateGraph() +{ + this->IntermediateGraph.resize(0); + this->IntermediateGraph.resize(this->InitialGraph.size()); + + int n = static_cast<int>(this->InitialGraph.size()); + for (int i = 0; i < n; ++i) { + auto const& initialEdges = this->InitialGraph[i]; + auto& intermediateEdges = this->IntermediateGraph[i]; + cmGeneratorTarget const* gt = this->Targets[i]; + if (gt->GetType() != cmStateEnums::STATIC_LIBRARY && + gt->GetType() != cmStateEnums::OBJECT_LIBRARY) { + intermediateEdges = initialEdges; + } else { + if (cmProp optimizeDependencies = + gt->GetProperty("OPTIMIZE_DEPENDENCIES")) { + if (cmIsOn(optimizeDependencies)) { + this->OptimizeLinkDependencies(gt, intermediateEdges, initialEdges); + } else { + intermediateEdges = initialEdges; + } + } else { + intermediateEdges = initialEdges; + } + } + } +} + +void cmComputeTargetDepends::OptimizeLinkDependencies( + cmGeneratorTarget const* gt, cmGraphEdgeList& outputEdges, + cmGraphEdgeList const& inputEdges) +{ + std::set<int> emitted; + for (auto const& edge : inputEdges) { + if (edge.IsStrong()) { + // Preserve strong edges + outputEdges.push_back(edge); + } else { + auto const& dse = this->SideEffects[edge]; + + // Add edges that have custom command side effects + for (cmGeneratorTarget const* dep : dse.CustomCommandSideEffects) { + auto index = this->TargetIndex[dep]; + if (!emitted.count(index)) { + emitted.insert(index); + outputEdges.push_back( + cmGraphEdge(index, false, edge.IsCross(), edge.GetBacktrace())); + } + } + + // Add edges that have language side effects for languages we + // care about + for (auto const& lang : gt->GetAllConfigCompileLanguages()) { + auto it = dse.LanguageSideEffects.find(lang); + if (it != dse.LanguageSideEffects.end()) { + for (cmGeneratorTarget const* dep : it->second) { + auto index = this->TargetIndex[dep]; + if (!emitted.count(index)) { + emitted.insert(index); + outputEdges.push_back(cmGraphEdge(index, false, edge.IsCross(), + edge.GetBacktrace())); + } + } + } + } + } + } +} + void cmComputeTargetDepends::DisplayGraph(Graph const& graph, const std::string& name) { @@ -402,10 +522,39 @@ void cmComputeTargetDepends::DisplayGraph(Graph const& graph, fprintf(stderr, "\n"); } +void cmComputeTargetDepends::DisplaySideEffects() +{ + fprintf(stderr, "The side effects are:\n"); + int n = static_cast<int>(SideEffects.size()); + for (int depender_index = 0; depender_index < n; ++depender_index) { + cmGeneratorTarget const* depender = this->Targets[depender_index]; + fprintf(stderr, "target %d is [%s]\n", depender_index, + depender->GetName().c_str()); + if (!this->SideEffects[depender_index].CustomCommandSideEffects.empty()) { + fprintf(stderr, " custom commands\n"); + for (auto const* gt : + this->SideEffects[depender_index].CustomCommandSideEffects) { + fprintf(stderr, " from target %d [%s]\n", this->TargetIndex[gt], + gt->GetName().c_str()); + } + } + for (auto const& it : + this->SideEffects[depender_index].LanguageSideEffects) { + fprintf(stderr, " language %s\n", it.first.c_str()); + for (auto const* gt : it.second) { + fprintf(stderr, " from target %d [%s]\n", this->TargetIndex[gt], + gt->GetName().c_str()); + } + } + } + fprintf(stderr, "\n"); +} + void cmComputeTargetDepends::DisplayComponents( - cmComputeComponentGraph const& ccg) + cmComputeComponentGraph const& ccg, const std::string& name) { - fprintf(stderr, "The strongly connected components are:\n"); + fprintf(stderr, "The strongly connected components for the %s graph are:\n", + name.c_str()); std::vector<NodeList> const& components = ccg.GetComponents(); int n = static_cast<int>(components.size()); for (int c = 0; c < n; ++c) { diff --git a/Source/cmComputeTargetDepends.h b/Source/cmComputeTargetDepends.h index e0d625f..277521d 100644 --- a/Source/cmComputeTargetDepends.h +++ b/Source/cmComputeTargetDepends.h @@ -42,6 +42,13 @@ public: cmTargetDependSet& deps); private: + struct TargetSideEffects + { + std::set<cmGeneratorTarget const*> CustomCommandSideEffects; + std::map<std::string, std::set<cmGeneratorTarget const*>> + LanguageSideEffects; + }; + void CollectTargets(); void CollectDepends(); void CollectTargetDepends(int depender_index); @@ -50,6 +57,12 @@ private: void AddTargetDepend(int depender_index, cmGeneratorTarget const* dependee, cmListFileBacktrace const& dependee_backtrace, bool linking, bool cross); + void CollectSideEffects(); + void CollectSideEffectsForTarget(std::set<int>& visited, int depender_index); + void ComputeIntermediateGraph(); + void OptimizeLinkDependencies(cmGeneratorTarget const* gt, + cmGraphEdgeList& outputEdges, + cmGraphEdgeList const& inputEdges); bool ComputeFinalDepends(cmComputeComponentGraph const& ccg); void AddInterfaceDepends(int depender_index, cmLinkItem const& dependee_name, const std::string& config, @@ -74,11 +87,15 @@ private: using EdgeList = cmGraphEdgeList; using Graph = cmGraphAdjacencyList; Graph InitialGraph; + Graph IntermediateGraph; Graph FinalGraph; + std::vector<TargetSideEffects> SideEffects; void DisplayGraph(Graph const& graph, const std::string& name); + void DisplaySideEffects(); // Deal with connected components. - void DisplayComponents(cmComputeComponentGraph const& ccg); + void DisplayComponents(cmComputeComponentGraph const& ccg, + const std::string& name); bool CheckComponents(cmComputeComponentGraph const& ccg); void ComplainAboutBadComponent(cmComputeComponentGraph const& ccg, int c, bool strong = false); diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 889ad7c..9611e14 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -1084,6 +1084,37 @@ std::vector<cmCustomCommand> const& cmGeneratorTarget::GetPostBuildCommands() return this->Target->GetPostBuildCommands(); } +void cmGeneratorTarget::AppendCustomCommandSideEffects( + std::set<cmGeneratorTarget const*>& sideEffects) const +{ + if (!this->GetPreBuildCommands().empty() || + !this->GetPreLinkCommands().empty() || + !this->GetPostBuildCommands().empty()) { + sideEffects.insert(this); + } else { + for (auto const& source : this->GetAllConfigSources()) { + if (source.Source->GetCustomCommand() != nullptr) { + sideEffects.insert(this); + break; + } + } + } +} + +void cmGeneratorTarget::AppendLanguageSideEffects( + std::map<std::string, std::set<cmGeneratorTarget const*>>& sideEffects) const +{ + static const std::set<cm::string_view> LANGS_WITH_NO_SIDE_EFFECTS = { + "C"_s, "CXX"_s, "OBJC"_s, "OBJCXX"_s, "ASM"_s, "CUDA"_s, + }; + + for (auto const& lang : this->GetAllConfigCompileLanguages()) { + if (!LANGS_WITH_NO_SIDE_EFFECTS.count(lang)) { + sideEffects[lang].insert(this); + } + } +} + bool cmGeneratorTarget::IsInBuildSystem() const { if (this->IsImported()) { diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 4a03f65..69c5faf 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -55,6 +55,12 @@ public: std::vector<cmCustomCommand> const& GetPreLinkCommands() const; std::vector<cmCustomCommand> const& GetPostBuildCommands() const; + void AppendCustomCommandSideEffects( + std::set<cmGeneratorTarget const*>& sideEffects) const; + void AppendLanguageSideEffects( + std::map<std::string, std::set<cmGeneratorTarget const*>>& sideEffects) + const; + #define DECLARE_TARGET_POLICY(POLICY) \ cmPolicies::PolicyStatus GetPolicyStatus##POLICY() const \ { \ diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 51b4e9e..60416a3 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -373,6 +373,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, initProp("VS_JUST_MY_CODE_DEBUGGING"); initProp("DISABLE_PRECOMPILE_HEADERS"); initProp("UNITY_BUILD"); + initProp("OPTIMIZE_DEPENDENCIES"); initPropValue("UNITY_BUILD_BATCH_SIZE", "8"); initPropValue("UNITY_BUILD_MODE", "BATCH"); initPropValue("PCH_WARN_INVALID", "ON"); diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index e08b30a..98a1f87 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -401,6 +401,7 @@ add_RunCMake_test(no_install_prefix) add_RunCMake_test(configure_file) add_RunCMake_test(CTestTimeout -DTIMEOUT=${CTestTestTimeout_TIME}) add_RunCMake_test(CTestTimeoutAfterMatch) +add_RunCMake_test(DependencyGraph -DCMAKE_Fortran_COMPILER=${CMAKE_Fortran_COMPILER}) # ctresalloc links against CMakeLib and CTestLib, which means it can't be built # if CMake_TEST_EXTERNAL_CMAKE is activated (the compiler might be different.) diff --git a/Tests/RunCMake/DependencyGraph/CMakeLists.txt b/Tests/RunCMake/DependencyGraph/CMakeLists.txt new file mode 100644 index 0000000..b646c4a --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.18) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeCommon.cmake b/Tests/RunCMake/DependencyGraph/OptimizeCommon.cmake new file mode 100644 index 0000000..4954bc4 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeCommon.cmake @@ -0,0 +1,40 @@ +enable_language(C) + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY out) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY out) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY out) + +add_library(SharedTop SHARED mylib.c) +add_library(StaticTop STATIC mylib.c) +add_library(StaticMiddle STATIC mylib.c) + +add_library(StaticNone STATIC mylib.c) +add_library(StaticPreBuild STATIC mylib.c) +add_library(StaticPreLink STATIC mylib.c) +add_library(StaticPostBuild STATIC mylib.c) +add_library(StaticCc STATIC mylibcc.c) + +add_custom_command(TARGET StaticPreBuild PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E true) +add_custom_command(TARGET StaticPreLink PRE_LINK + COMMAND ${CMAKE_COMMAND} -E true) +add_custom_command(TARGET StaticPostBuild POST_BUILD + COMMAND ${CMAKE_COMMAND} -E true) +add_custom_command(OUTPUT mylibcc.c + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/mylib.c ${CMAKE_BINARY_DIR}/mylibcc.c) + +target_link_libraries(SharedTop PRIVATE StaticMiddle) +target_link_libraries(StaticTop PRIVATE StaticMiddle) +target_link_libraries(StaticMiddle PRIVATE StaticNone StaticPreBuild StaticPreLink StaticPostBuild StaticCc) + +if(OPTIMIZE_TOP) + set_target_properties(SharedTop StaticTop PROPERTIES + OPTIMIZE_DEPENDENCIES TRUE) +endif() +if(OPTIMIZE_MIDDLE) + set_target_properties(StaticMiddle PROPERTIES + OPTIMIZE_DEPENDENCIES TRUE) +endif() + +include(WriteTargets.cmake) +write_targets() diff --git a/Tests/RunCMake/DependencyGraph/OptimizeFortran-both-build-check.cmake b/Tests/RunCMake/DependencyGraph/OptimizeFortran-both-build-check.cmake new file mode 100644 index 0000000..1020cb3 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeFortran-both-build-check.cmake @@ -0,0 +1,5 @@ +include(${RunCMake_TEST_BINARY_DIR}/target_files.cmake) +check_files(${RunCMake_TEST_BINARY_DIR}/out + ${FortranTop_TARGET_FILE} + ${FortranBottom_TARGET_FILE} + ) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeFortran-both-build-stderr.txt b/Tests/RunCMake/DependencyGraph/OptimizeFortran-both-build-stderr.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeFortran-both-build-stderr.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/DependencyGraph/OptimizeFortran-both.cmake b/Tests/RunCMake/DependencyGraph/OptimizeFortran-both.cmake new file mode 100644 index 0000000..581fd46 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeFortran-both.cmake @@ -0,0 +1 @@ +include(OptimizeFortranCommon.cmake) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeFortran-middle-build-check.cmake b/Tests/RunCMake/DependencyGraph/OptimizeFortran-middle-build-check.cmake new file mode 100644 index 0000000..5c7e8cd --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeFortran-middle-build-check.cmake @@ -0,0 +1,6 @@ +include(${RunCMake_TEST_BINARY_DIR}/target_files.cmake) +check_files(${RunCMake_TEST_BINARY_DIR}/out + ${FortranTop_TARGET_FILE} + ${CMiddle_TARGET_FILE} + ${FortranBottom_TARGET_FILE} + ) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeFortran-middle-build-stderr.txt b/Tests/RunCMake/DependencyGraph/OptimizeFortran-middle-build-stderr.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeFortran-middle-build-stderr.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/DependencyGraph/OptimizeFortran-middle.cmake b/Tests/RunCMake/DependencyGraph/OptimizeFortran-middle.cmake new file mode 100644 index 0000000..581fd46 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeFortran-middle.cmake @@ -0,0 +1 @@ +include(OptimizeFortranCommon.cmake) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeFortran-none-build-check.cmake b/Tests/RunCMake/DependencyGraph/OptimizeFortran-none-build-check.cmake new file mode 100644 index 0000000..5c7e8cd --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeFortran-none-build-check.cmake @@ -0,0 +1,6 @@ +include(${RunCMake_TEST_BINARY_DIR}/target_files.cmake) +check_files(${RunCMake_TEST_BINARY_DIR}/out + ${FortranTop_TARGET_FILE} + ${CMiddle_TARGET_FILE} + ${FortranBottom_TARGET_FILE} + ) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeFortran-none-build-stderr.txt b/Tests/RunCMake/DependencyGraph/OptimizeFortran-none-build-stderr.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeFortran-none-build-stderr.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/DependencyGraph/OptimizeFortran-none.cmake b/Tests/RunCMake/DependencyGraph/OptimizeFortran-none.cmake new file mode 100644 index 0000000..581fd46 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeFortran-none.cmake @@ -0,0 +1 @@ +include(OptimizeFortranCommon.cmake) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeFortran-top-build-check.cmake b/Tests/RunCMake/DependencyGraph/OptimizeFortran-top-build-check.cmake new file mode 100644 index 0000000..1020cb3 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeFortran-top-build-check.cmake @@ -0,0 +1,5 @@ +include(${RunCMake_TEST_BINARY_DIR}/target_files.cmake) +check_files(${RunCMake_TEST_BINARY_DIR}/out + ${FortranTop_TARGET_FILE} + ${FortranBottom_TARGET_FILE} + ) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeFortran-top-build-stderr.txt b/Tests/RunCMake/DependencyGraph/OptimizeFortran-top-build-stderr.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeFortran-top-build-stderr.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/DependencyGraph/OptimizeFortran-top.cmake b/Tests/RunCMake/DependencyGraph/OptimizeFortran-top.cmake new file mode 100644 index 0000000..581fd46 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeFortran-top.cmake @@ -0,0 +1 @@ +include(OptimizeFortranCommon.cmake) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeFortranCommon.cmake b/Tests/RunCMake/DependencyGraph/OptimizeFortranCommon.cmake new file mode 100644 index 0000000..354d3fc --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeFortranCommon.cmake @@ -0,0 +1,25 @@ +enable_language(C) +enable_language(Fortran) + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY out) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY out) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY out) + +add_library(FortranTop STATIC mylib.f90) +add_library(CMiddle STATIC mylib.c) +add_library(FortranBottom STATIC mylib.f90) + +target_link_libraries(FortranTop PRIVATE CMiddle) +target_link_libraries(CMiddle PRIVATE FortranBottom) + +if(OPTIMIZE_TOP) + set_target_properties(FortranTop PROPERTIES + OPTIMIZE_DEPENDENCIES TRUE) +endif() +if(OPTIMIZE_MIDDLE) + set_target_properties(CMiddle PROPERTIES + OPTIMIZE_DEPENDENCIES TRUE) +endif() + +include(WriteTargets.cmake) +write_targets() diff --git a/Tests/RunCMake/DependencyGraph/OptimizeShared-both-build-check.cmake b/Tests/RunCMake/DependencyGraph/OptimizeShared-both-build-check.cmake new file mode 100644 index 0000000..312de04 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeShared-both-build-check.cmake @@ -0,0 +1,11 @@ +include(${RunCMake_TEST_BINARY_DIR}/target_files.cmake) +check_files(${RunCMake_TEST_BINARY_DIR}/out + ${SharedTop_TARGET_FILE} + ${SharedTop_TARGET_LINKER_FILE} + ${StaticMiddle_TARGET_FILE} + ${StaticNone_TARGET_FILE} + ${StaticPreBuild_TARGET_FILE} + ${StaticPreLink_TARGET_FILE} + ${StaticPostBuild_TARGET_FILE} + ${StaticCc_TARGET_FILE} + ) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeShared-both.cmake b/Tests/RunCMake/DependencyGraph/OptimizeShared-both.cmake new file mode 100644 index 0000000..c150e62 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeShared-both.cmake @@ -0,0 +1 @@ +include(OptimizeCommon.cmake) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeShared-middle-build-check.cmake b/Tests/RunCMake/DependencyGraph/OptimizeShared-middle-build-check.cmake new file mode 100644 index 0000000..312de04 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeShared-middle-build-check.cmake @@ -0,0 +1,11 @@ +include(${RunCMake_TEST_BINARY_DIR}/target_files.cmake) +check_files(${RunCMake_TEST_BINARY_DIR}/out + ${SharedTop_TARGET_FILE} + ${SharedTop_TARGET_LINKER_FILE} + ${StaticMiddle_TARGET_FILE} + ${StaticNone_TARGET_FILE} + ${StaticPreBuild_TARGET_FILE} + ${StaticPreLink_TARGET_FILE} + ${StaticPostBuild_TARGET_FILE} + ${StaticCc_TARGET_FILE} + ) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeShared-middle.cmake b/Tests/RunCMake/DependencyGraph/OptimizeShared-middle.cmake new file mode 100644 index 0000000..c150e62 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeShared-middle.cmake @@ -0,0 +1 @@ +include(OptimizeCommon.cmake) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeShared-none-build-check.cmake b/Tests/RunCMake/DependencyGraph/OptimizeShared-none-build-check.cmake new file mode 100644 index 0000000..312de04 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeShared-none-build-check.cmake @@ -0,0 +1,11 @@ +include(${RunCMake_TEST_BINARY_DIR}/target_files.cmake) +check_files(${RunCMake_TEST_BINARY_DIR}/out + ${SharedTop_TARGET_FILE} + ${SharedTop_TARGET_LINKER_FILE} + ${StaticMiddle_TARGET_FILE} + ${StaticNone_TARGET_FILE} + ${StaticPreBuild_TARGET_FILE} + ${StaticPreLink_TARGET_FILE} + ${StaticPostBuild_TARGET_FILE} + ${StaticCc_TARGET_FILE} + ) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeShared-none.cmake b/Tests/RunCMake/DependencyGraph/OptimizeShared-none.cmake new file mode 100644 index 0000000..c150e62 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeShared-none.cmake @@ -0,0 +1 @@ +include(OptimizeCommon.cmake) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeShared-top-build-check.cmake b/Tests/RunCMake/DependencyGraph/OptimizeShared-top-build-check.cmake new file mode 100644 index 0000000..312de04 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeShared-top-build-check.cmake @@ -0,0 +1,11 @@ +include(${RunCMake_TEST_BINARY_DIR}/target_files.cmake) +check_files(${RunCMake_TEST_BINARY_DIR}/out + ${SharedTop_TARGET_FILE} + ${SharedTop_TARGET_LINKER_FILE} + ${StaticMiddle_TARGET_FILE} + ${StaticNone_TARGET_FILE} + ${StaticPreBuild_TARGET_FILE} + ${StaticPreLink_TARGET_FILE} + ${StaticPostBuild_TARGET_FILE} + ${StaticCc_TARGET_FILE} + ) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeShared-top.cmake b/Tests/RunCMake/DependencyGraph/OptimizeShared-top.cmake new file mode 100644 index 0000000..c150e62 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeShared-top.cmake @@ -0,0 +1 @@ +include(OptimizeCommon.cmake) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeStatic-both-build-check.cmake b/Tests/RunCMake/DependencyGraph/OptimizeStatic-both-build-check.cmake new file mode 100644 index 0000000..5222ed7 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeStatic-both-build-check.cmake @@ -0,0 +1,8 @@ +include(${RunCMake_TEST_BINARY_DIR}/target_files.cmake) +check_files(${RunCMake_TEST_BINARY_DIR}/out + ${StaticTop_TARGET_FILE} + ${StaticPreBuild_TARGET_FILE} + ${StaticPreLink_TARGET_FILE} + ${StaticPostBuild_TARGET_FILE} + ${StaticCc_TARGET_FILE} + ) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeStatic-both.cmake b/Tests/RunCMake/DependencyGraph/OptimizeStatic-both.cmake new file mode 100644 index 0000000..c150e62 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeStatic-both.cmake @@ -0,0 +1 @@ +include(OptimizeCommon.cmake) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeStatic-middle-build-check.cmake b/Tests/RunCMake/DependencyGraph/OptimizeStatic-middle-build-check.cmake new file mode 100644 index 0000000..5cba223 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeStatic-middle-build-check.cmake @@ -0,0 +1,10 @@ +include(${RunCMake_TEST_BINARY_DIR}/target_files.cmake) +check_files(${RunCMake_TEST_BINARY_DIR}/out + ${StaticTop_TARGET_FILE} + ${StaticMiddle_TARGET_FILE} + ${StaticNone_TARGET_FILE} + ${StaticPreBuild_TARGET_FILE} + ${StaticPreLink_TARGET_FILE} + ${StaticPostBuild_TARGET_FILE} + ${StaticCc_TARGET_FILE} + ) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeStatic-middle.cmake b/Tests/RunCMake/DependencyGraph/OptimizeStatic-middle.cmake new file mode 100644 index 0000000..c150e62 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeStatic-middle.cmake @@ -0,0 +1 @@ +include(OptimizeCommon.cmake) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeStatic-none-build-check.cmake b/Tests/RunCMake/DependencyGraph/OptimizeStatic-none-build-check.cmake new file mode 100644 index 0000000..5cba223 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeStatic-none-build-check.cmake @@ -0,0 +1,10 @@ +include(${RunCMake_TEST_BINARY_DIR}/target_files.cmake) +check_files(${RunCMake_TEST_BINARY_DIR}/out + ${StaticTop_TARGET_FILE} + ${StaticMiddle_TARGET_FILE} + ${StaticNone_TARGET_FILE} + ${StaticPreBuild_TARGET_FILE} + ${StaticPreLink_TARGET_FILE} + ${StaticPostBuild_TARGET_FILE} + ${StaticCc_TARGET_FILE} + ) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeStatic-none.cmake b/Tests/RunCMake/DependencyGraph/OptimizeStatic-none.cmake new file mode 100644 index 0000000..c150e62 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeStatic-none.cmake @@ -0,0 +1 @@ +include(OptimizeCommon.cmake) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeStatic-top-build-check.cmake b/Tests/RunCMake/DependencyGraph/OptimizeStatic-top-build-check.cmake new file mode 100644 index 0000000..5222ed7 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeStatic-top-build-check.cmake @@ -0,0 +1,8 @@ +include(${RunCMake_TEST_BINARY_DIR}/target_files.cmake) +check_files(${RunCMake_TEST_BINARY_DIR}/out + ${StaticTop_TARGET_FILE} + ${StaticPreBuild_TARGET_FILE} + ${StaticPreLink_TARGET_FILE} + ${StaticPostBuild_TARGET_FILE} + ${StaticCc_TARGET_FILE} + ) diff --git a/Tests/RunCMake/DependencyGraph/OptimizeStatic-top.cmake b/Tests/RunCMake/DependencyGraph/OptimizeStatic-top.cmake new file mode 100644 index 0000000..c150e62 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/OptimizeStatic-top.cmake @@ -0,0 +1 @@ +include(OptimizeCommon.cmake) diff --git a/Tests/RunCMake/DependencyGraph/Property.cmake b/Tests/RunCMake/DependencyGraph/Property.cmake new file mode 100644 index 0000000..08fdd2b --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/Property.cmake @@ -0,0 +1,24 @@ +enable_language(C) + +add_library(Unset STATIC mylib.c) + +set(CMAKE_OPTIMIZE_DEPENDENCIES TRUE) +add_library(SetTrue STATIC mylib.c) + +set(CMAKE_OPTIMIZE_DEPENDENCIES FALSE) +add_library(SetFalse STATIC mylib.c) + +get_property(_set TARGET Unset PROPERTY OPTIMIZE_DEPENDENCIES SET) +if(_set) + message(SEND_ERROR "OPTIMIZE_DEPENDENCIES property should not be set on Unset target") +endif() + +get_property(_true TARGET SetTrue PROPERTY OPTIMIZE_DEPENDENCIES) +if(NOT _true STREQUAL "TRUE") + message(SEND_ERROR "OPTIMIZE_DEPENDENCIES property should be TRUE on SetTrue target") +endif() + +get_property(_false TARGET SetFalse PROPERTY OPTIMIZE_DEPENDENCIES) +if(NOT _false STREQUAL "FALSE") + message(SEND_ERROR "OPTIMIZE_DEPENDENCIES property should be FALSE on SetFalse target") +endif() diff --git a/Tests/RunCMake/DependencyGraph/RunCMakeTest.cmake b/Tests/RunCMake/DependencyGraph/RunCMakeTest.cmake new file mode 100644 index 0000000..cb0d541 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/RunCMakeTest.cmake @@ -0,0 +1,60 @@ +include(RunCMake) + +function(check_files dir) + set(expected ${ARGN}) + list(FILTER expected EXCLUDE REGEX "^$") + list(REMOVE_DUPLICATES expected) + list(SORT expected) + + file(GLOB_RECURSE glob "${dir}/*") + set(actual) + foreach(i IN LISTS glob) + if(NOT i MATCHES "(\\.manifest$)|(\\.exp$)|(\\.tds$)") + list(APPEND actual ${i}) + endif() + endforeach() + list(REMOVE_DUPLICATES actual) + list(SORT actual) + + if(NOT "${expected}" STREQUAL "${actual}") + string(REPLACE ";" "\n " expected_formatted "${expected}") + string(REPLACE ";" "\n " actual_formatted "${actual}") + string(APPEND RunCMake_TEST_FAILED "Actual files did not match expected\nExpected:\n ${expected_formatted}\nActual:\n ${actual_formatted}\n") + endif() + + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) +endfunction() + +function(run_cmake_build name) + set(RunCMake_TEST_NO_CLEAN TRUE) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${name}-build) + file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}) + if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release) + endif() + run_cmake(${name}) + set(RunCMake_TEST_OPTIONS) + run_cmake_command(${name}-build ${CMAKE_COMMAND} + --build ${RunCMake_TEST_BINARY_DIR} + --config Release + --target ${ARGN}) +endfunction() + +function(run_optimize_test name) + set(RunCMake_TEST_OPTIONS) + run_cmake_build(${name}-none ${ARGN}) + set(RunCMake_TEST_OPTIONS -DOPTIMIZE_TOP=TRUE) + run_cmake_build(${name}-top ${ARGN}) + set(RunCMake_TEST_OPTIONS -DOPTIMIZE_MIDDLE=TRUE) + run_cmake_build(${name}-middle ${ARGN}) + set(RunCMake_TEST_OPTIONS -DOPTIMIZE_TOP=TRUE -DOPTIMIZE_MIDDLE=TRUE) + run_cmake_build(${name}-both ${ARGN}) +endfunction() + +run_cmake(Property) + +run_optimize_test(OptimizeShared SharedTop) +run_optimize_test(OptimizeStatic StaticTop) +if(CMAKE_Fortran_COMPILER) + run_optimize_test(OptimizeFortran FortranTop) +endif() diff --git a/Tests/RunCMake/DependencyGraph/WriteTargets.cmake b/Tests/RunCMake/DependencyGraph/WriteTargets.cmake new file mode 100644 index 0000000..e1012c1 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/WriteTargets.cmake @@ -0,0 +1,16 @@ +function(write_targets) + set(_input "") + + get_property(_targets DIRECTORY . PROPERTY BUILDSYSTEM_TARGETS) + foreach(_t IN LISTS _targets) + get_property(_type TARGET "${_t}" PROPERTY TYPE) + if(_type STREQUAL "SHARED_LIBRARY") + string(APPEND _input "set(${_t}_TARGET_FILE [==[$<TARGET_FILE:${_t}>]==])\n") + string(APPEND _input "set(${_t}_TARGET_LINKER_FILE [==[$<TARGET_LINKER_FILE:${_t}>]==])\n") + elseif(_type STREQUAL "STATIC_LIBRARY") + string(APPEND _input "set(${_t}_TARGET_FILE [==[$<TARGET_FILE:${_t}>]==])\n") + endif() + endforeach() + + file(GENERATE OUTPUT target_files.cmake CONTENT "${_input}" CONDITION $<CONFIG:Release>) +endfunction() diff --git a/Tests/RunCMake/DependencyGraph/mylib.c b/Tests/RunCMake/DependencyGraph/mylib.c new file mode 100644 index 0000000..5422fe3 --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/mylib.c @@ -0,0 +1,6 @@ +#ifdef _WIN32 +__declspec(dllexport) +#endif + void mylib(void) +{ +} diff --git a/Tests/RunCMake/DependencyGraph/mylib.f90 b/Tests/RunCMake/DependencyGraph/mylib.f90 new file mode 100644 index 0000000..104768f --- /dev/null +++ b/Tests/RunCMake/DependencyGraph/mylib.f90 @@ -0,0 +1,3 @@ +function mylib_fortran() + mylib_fortran = 42 +end function mylib_fortran |