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.
This commit is contained in:
Kyle Edwards
2020-07-29 14:19:56 -04:00
committed by Brad King
parent 0cd3b5d0ca
commit 2e42651dff
47 changed files with 560 additions and 9 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -0,0 +1,4 @@
CMAKE_OPTIMIZE_DEPENDENCIES
---------------------------
Initializes the :prop_tgt:`OPTIMIZE_DEPENDENCIES` target property.

View File

@@ -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::DisplayComponents(
cmComputeComponentGraph const& ccg)
void cmComputeTargetDepends::DisplaySideEffects()
{
fprintf(stderr, "The strongly connected components are:\n");
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, const std::string& name)
{
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) {

View File

@@ -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);

View File

@@ -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()) {

View File

@@ -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 \
{ \

View File

@@ -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");

View File

@@ -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.)

View File

@@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.18)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)

View File

@@ -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()

View File

@@ -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}
)

View File

@@ -0,0 +1 @@
include(OptimizeFortranCommon.cmake)

View File

@@ -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}
)

View File

@@ -0,0 +1 @@
include(OptimizeFortranCommon.cmake)

View File

@@ -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}
)

View File

@@ -0,0 +1 @@
include(OptimizeFortranCommon.cmake)

View File

@@ -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}
)

View File

@@ -0,0 +1 @@
include(OptimizeFortranCommon.cmake)

View File

@@ -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()

View File

@@ -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}
)

View File

@@ -0,0 +1 @@
include(OptimizeCommon.cmake)

View File

@@ -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}
)

View File

@@ -0,0 +1 @@
include(OptimizeCommon.cmake)

View File

@@ -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}
)

View File

@@ -0,0 +1 @@
include(OptimizeCommon.cmake)

View File

@@ -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}
)

View File

@@ -0,0 +1 @@
include(OptimizeCommon.cmake)

View File

@@ -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}
)

View File

@@ -0,0 +1 @@
include(OptimizeCommon.cmake)

View File

@@ -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}
)

View File

@@ -0,0 +1 @@
include(OptimizeCommon.cmake)

View File

@@ -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}
)

View File

@@ -0,0 +1 @@
include(OptimizeCommon.cmake)

View File

@@ -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}
)

View File

@@ -0,0 +1 @@
include(OptimizeCommon.cmake)

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -0,0 +1,6 @@
#ifdef _WIN32
__declspec(dllexport)
#endif
void mylib(void)
{
}

View File

@@ -0,0 +1,3 @@
function mylib_fortran()
mylib_fortran = 42
end function mylib_fortran