Commit a426cf93 authored by cmasone's avatar cmasone Committed by Commit bot

GN: Add notion of 'complete' static libraries, akin to GYP.

In general, GN assumes that static libraries are intermediate targets and
will forward their child dependencies up the tree until a "final" target
is reached. Until now, this meant only shared_library and executable targets.
This change introduces the notion of a "complete" static_library target
which is linked and, as such, suitable for distribution.

BUG=413776
TEST=gn_unittests
R=brettw@chromium.org

Review URL: https://codereview.chromium.org/565283002

Cr-Commit-Position: refs/heads/master@{#294709}
parent 35099b04
...@@ -59,6 +59,10 @@ void BinaryTargetGenerator::DoRun() { ...@@ -59,6 +59,10 @@ void BinaryTargetGenerator::DoRun() {
if (err_->has_error()) if (err_->has_error())
return; return;
FillCompleteStaticLib();
if (err_->has_error())
return;
// Config values (compiler flags, etc.) set directly on this target. // Config values (compiler flags, etc.) set directly on this target.
ConfigValuesGenerator gen(&target_->config_values(), scope_, ConfigValuesGenerator gen(&target_->config_values(), scope_,
scope_->GetSourceDir(), err_); scope_->GetSourceDir(), err_);
...@@ -76,6 +80,17 @@ void BinaryTargetGenerator::FillCheckIncludes() { ...@@ -76,6 +80,17 @@ void BinaryTargetGenerator::FillCheckIncludes() {
target_->set_check_includes(value->boolean_value()); target_->set_check_includes(value->boolean_value());
} }
void BinaryTargetGenerator::FillCompleteStaticLib() {
if (target_->output_type() == Target::STATIC_LIBRARY) {
const Value* value = scope_->GetValue(variables::kCompleteStaticLib, true);
if (!value)
return;
if (!value->VerifyTypeIs(Value::BOOLEAN, err_))
return;
target_->set_complete_static_lib(value->boolean_value());
}
}
void BinaryTargetGenerator::FillOutputName() { void BinaryTargetGenerator::FillOutputName() {
const Value* value = scope_->GetValue(variables::kOutputName, true); const Value* value = scope_->GetValue(variables::kOutputName, true);
if (!value) if (!value)
......
...@@ -24,6 +24,7 @@ class BinaryTargetGenerator : public TargetGenerator { ...@@ -24,6 +24,7 @@ class BinaryTargetGenerator : public TargetGenerator {
private: private:
void FillCheckIncludes(); void FillCheckIncludes();
void FillCompleteStaticLib();
void FillOutputName(); void FillOutputName();
void FillOutputExtension(); void FillOutputExtension();
void FillAllowCircularIncludesFrom(); void FillAllowCircularIncludesFrom();
...@@ -34,4 +35,3 @@ class BinaryTargetGenerator : public TargetGenerator { ...@@ -34,4 +35,3 @@ class BinaryTargetGenerator : public TargetGenerator {
}; };
#endif // TOOLS_GN_BINARY_TARGET_GENERATOR_H_ #endif // TOOLS_GN_BINARY_TARGET_GENERATOR_H_
...@@ -393,27 +393,26 @@ void NinjaBinaryTargetWriter::ClassifyDependency( ...@@ -393,27 +393,26 @@ void NinjaBinaryTargetWriter::ClassifyDependency(
UniqueVector<OutputFile>* extra_object_files, UniqueVector<OutputFile>* extra_object_files,
UniqueVector<const Target*>* linkable_deps, UniqueVector<const Target*>* linkable_deps,
UniqueVector<const Target*>* non_linkable_deps) const { UniqueVector<const Target*>* non_linkable_deps) const {
// Only these types of outputs have libraries linked into them. Child deps of // Only the following types of outputs have libraries linked into them:
// static libraries get pushed up the dependency tree until one of these is // EXECUTABLE
// reached, and source sets don't link at all. // SHARED_LIBRARY
bool can_link_libs = // _complete_ STATIC_LIBRARY
(target_->output_type() == Target::EXECUTABLE || //
target_->output_type() == Target::SHARED_LIBRARY); // Child deps of intermediate static libraries get pushed up the
// dependency tree until one of these is reached, and source sets
// don't link at all.
bool can_link_libs = target_->IsFinal();
if (dep->output_type() == Target::SOURCE_SET) { if (dep->output_type() == Target::SOURCE_SET) {
// Source sets have their object files linked into final targets (shared // Source sets have their object files linked into final targets
// libraries and executables). Intermediate static libraries and other // (shared libraries, executables, and complete static
// source sets just forward the dependency, otherwise the files in the // libraries). Intermediate static libraries and other source sets
// source set can easily get linked more than once which will cause // just forward the dependency, otherwise the files in the source
// set can easily get linked more than once which will cause
// multiple definition errors. // multiple definition errors.
// if (can_link_libs) {
// In the future, we may need a way to specify a "complete" static library // Linking in a source set to an executable, shared library, or
// for cases where you want a static library that includes all source sets // complete static library, so copy its object files.
// (like if you're shipping that to customers to link against).
if (target_->output_type() != Target::SOURCE_SET &&
target_->output_type() != Target::STATIC_LIBRARY) {
// Linking in a source set to an executable or shared library, copy its
// object files.
std::vector<OutputFile> tool_outputs; // Prevent allocation in loop. std::vector<OutputFile> tool_outputs; // Prevent allocation in loop.
for (size_t i = 0; i < dep->sources().size(); i++) { for (size_t i = 0; i < dep->sources().size(); i++) {
Toolchain::ToolType tool_type = Toolchain::TYPE_NONE; Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
......
...@@ -125,6 +125,38 @@ TEST(NinjaBinaryTargetWriter, SourceSet) { ...@@ -125,6 +125,38 @@ TEST(NinjaBinaryTargetWriter, SourceSet) {
std::string out_str = out.str(); std::string out_str = out.str();
EXPECT_EQ(expected, out_str); EXPECT_EQ(expected, out_str);
} }
// Make the static library 'complete', which means it should be linked.
stlib_target.set_complete_static_lib(true);
{
std::ostringstream out;
NinjaBinaryTargetWriter writer(&stlib_target, out);
writer.Run();
const char expected[] =
"defines =\n"
"include_dirs =\n"
"cflags =\n"
"cflags_c =\n"
"cflags_cc =\n"
"cflags_objc =\n"
"cflags_objcc =\n"
"root_out_dir = .\n"
"target_out_dir = obj/foo\n"
"target_output_name = libstlib\n"
"\n"
"\n"
// Ordering of the obj files here should come out in the order
// specified, with the target's first, followed by the source set's, in
// order.
"build obj/foo/libstlib.a: alink obj/foo/bar.input1.o "
"obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj\n"
" ldflags =\n"
" libs =\n"
" output_extension = \n";
std::string out_str = out.str();
EXPECT_EQ(expected, out_str);
}
} }
// This tests that output extension overrides apply, and input dependencies // This tests that output extension overrides apply, and input dependencies
......
...@@ -73,6 +73,7 @@ Target::Target(const Settings* settings, const Label& label) ...@@ -73,6 +73,7 @@ Target::Target(const Settings* settings, const Label& label)
output_type_(UNKNOWN), output_type_(UNKNOWN),
all_headers_public_(true), all_headers_public_(true),
check_includes_(true), check_includes_(true),
complete_static_lib_(false),
testonly_(false), testonly_(false),
hard_dep_(false), hard_dep_(false),
toolchain_(NULL) { toolchain_(NULL) {
...@@ -160,6 +161,11 @@ bool Target::IsLinkable() const { ...@@ -160,6 +161,11 @@ bool Target::IsLinkable() const {
return output_type_ == STATIC_LIBRARY || output_type_ == SHARED_LIBRARY; return output_type_ == STATIC_LIBRARY || output_type_ == SHARED_LIBRARY;
} }
bool Target::IsFinal() const {
return output_type_ == EXECUTABLE || output_type_ == SHARED_LIBRARY ||
(output_type_ == STATIC_LIBRARY && complete_static_lib_);
}
std::string Target::GetComputedOutputName(bool include_prefix) const { std::string Target::GetComputedOutputName(bool include_prefix) const {
DCHECK(toolchain_) DCHECK(toolchain_)
<< "Toolchain must be specified before getting the computed output name."; << "Toolchain must be specified before getting the computed output name.";
...@@ -235,8 +241,7 @@ void Target::PullDependentTargetInfo() { ...@@ -235,8 +241,7 @@ void Target::PullDependentTargetInfo() {
// Inherited libraries and flags are inherited across static library // Inherited libraries and flags are inherited across static library
// boundaries. // boundaries.
if (dep->output_type() != SHARED_LIBRARY && if (!dep->IsFinal()) {
dep->output_type() != EXECUTABLE) {
inherited_libraries_.Append(dep->inherited_libraries().begin(), inherited_libraries_.Append(dep->inherited_libraries().begin(),
dep->inherited_libraries().end()); dep->inherited_libraries().end());
......
...@@ -58,8 +58,12 @@ class Target : public Item { ...@@ -58,8 +58,12 @@ class Target : public Item {
OutputType output_type() const { return output_type_; } OutputType output_type() const { return output_type_; }
void set_output_type(OutputType t) { output_type_ = t; } void set_output_type(OutputType t) { output_type_ = t; }
// Can be linked into other targets.
bool IsLinkable() const; bool IsLinkable() const;
// Can have dependencies linked in.
bool IsFinal() const;
// Will be the empty string to use the target label as the output name. // Will be the empty string to use the target label as the output name.
// See GetComputedOutputName(). // See GetComputedOutputName().
const std::string& output_name() const { return output_name_; } const std::string& output_name() const { return output_name_; }
...@@ -95,6 +99,13 @@ class Target : public Item { ...@@ -95,6 +99,13 @@ class Target : public Item {
bool check_includes() const { return check_includes_; } bool check_includes() const { return check_includes_; }
void set_check_includes(bool ci) { check_includes_ = ci; } void set_check_includes(bool ci) { check_includes_ = ci; }
// Whether this static_library target should have code linked in.
bool complete_static_lib() const { return complete_static_lib_; }
void set_complete_static_lib(bool complete) {
DCHECK_EQ(STATIC_LIBRARY, output_type_);
complete_static_lib_ = complete;
}
bool testonly() const { return testonly_; } bool testonly() const { return testonly_; }
void set_testonly(bool value) { testonly_ = value; } void set_testonly(bool value) { testonly_ = value; }
...@@ -239,6 +250,7 @@ class Target : public Item { ...@@ -239,6 +250,7 @@ class Target : public Item {
bool all_headers_public_; bool all_headers_public_;
FileList public_headers_; FileList public_headers_;
bool check_includes_; bool check_includes_;
bool complete_static_lib_;
bool testonly_; bool testonly_;
FileList inputs_; FileList inputs_;
FileList data_; FileList data_;
......
...@@ -274,6 +274,41 @@ TEST(Target, InheritLibs) { ...@@ -274,6 +274,41 @@ TEST(Target, InheritLibs) {
EXPECT_TRUE(a_inherited.IndexOf(&b) != static_cast<size_t>(-1)); EXPECT_TRUE(a_inherited.IndexOf(&b) != static_cast<size_t>(-1));
} }
TEST(Target, InheritCompleteStaticLib) {
TestWithScope setup;
Err err;
// Create a dependency chain:
// A (executable) -> B (complete static lib) -> C (source set)
Target a(setup.settings(), Label(SourceDir("//foo/"), "a"));
a.set_output_type(Target::EXECUTABLE);
a.SetToolchain(setup.toolchain());
Target b(setup.settings(), Label(SourceDir("//foo/"), "b"));
b.set_output_type(Target::STATIC_LIBRARY);
b.set_complete_static_lib(true);
b.SetToolchain(setup.toolchain());
Target c(setup.settings(), Label(SourceDir("//foo/"), "c"));
c.set_output_type(Target::SOURCE_SET);
c.SetToolchain(setup.toolchain());
a.deps().push_back(LabelTargetPair(&b));
b.deps().push_back(LabelTargetPair(&c));
ASSERT_TRUE(c.OnResolved(&err));
ASSERT_TRUE(b.OnResolved(&err));
ASSERT_TRUE(a.OnResolved(&err));
// B should have C in its inherited libs.
const UniqueVector<const Target*>& b_inherited = b.inherited_libraries();
EXPECT_EQ(1u, b_inherited.size());
EXPECT_TRUE(b_inherited.IndexOf(&c) != static_cast<size_t>(-1));
// A should have B in its inherited libs, but not any others (the complete
// static library will include the source set).
const UniqueVector<const Target*>& a_inherited = a.inherited_libraries();
EXPECT_EQ(1u, a_inherited.size());
EXPECT_TRUE(a_inherited.IndexOf(&b) != static_cast<size_t>(-1));
}
TEST(Target, GetComputedOutputName) { TEST(Target, GetComputedOutputName) {
TestWithScope setup; TestWithScope setup;
Err err; Err err;
......
...@@ -368,6 +368,31 @@ const char kCheckIncludes_Help[] = ...@@ -368,6 +368,31 @@ const char kCheckIncludes_Help[] =
" ...\n" " ...\n"
" }\n"; " }\n";
const char kCompleteStaticLib[] = "complete_static_lib";
const char kCompleteStaticLib_HelpShort[] =
"complete_static_lib: [boolean] Links all deps into a static library.\n";
const char kCompleteStaticLib_Help[] =
"complete_static_lib: [boolean] Links all deps into a static library.\n"
"\n"
" A static library normally doesn't include code from dependencies, but\n"
" instead forward the static libraries and source sets in its deps up\n"
" the dependency chain until a linkable target (an executable or shared\n"
" library) is reached. The final linkable target only links each static\n"
" library once, even if it appears more than once in its dependency\n"
" graph.\n"
"\n"
" In some cases the static library might be the final desired output.\n"
" For example, you may be producing a static library for distribution to\n"
" third parties. In this case, the static library should include code\n"
" for all dependencies in one complete package.\n"
"\n"
"Example\n"
"\n"
" static_library(\"foo\") {\n"
" complete_static_lib = true\n"
" deps = [ \"bar\" ]\n"
" }\n";
const char kConfigs[] = "configs"; const char kConfigs[] = "configs";
const char kConfigs_HelpShort[] = const char kConfigs_HelpShort[] =
"configs: [label list] Configs applying to this target."; "configs: [label list] Configs applying to this target.";
...@@ -918,6 +943,7 @@ const VariableInfoMap& GetTargetVariables() { ...@@ -918,6 +943,7 @@ const VariableInfoMap& GetTargetVariables() {
INSERT_VARIABLE(CflagsObjC) INSERT_VARIABLE(CflagsObjC)
INSERT_VARIABLE(CflagsObjCC) INSERT_VARIABLE(CflagsObjCC)
INSERT_VARIABLE(CheckIncludes) INSERT_VARIABLE(CheckIncludes)
INSERT_VARIABLE(CompleteStaticLib)
INSERT_VARIABLE(Configs) INSERT_VARIABLE(Configs)
INSERT_VARIABLE(Data) INSERT_VARIABLE(Data)
INSERT_VARIABLE(Datadeps) INSERT_VARIABLE(Datadeps)
......
...@@ -99,6 +99,10 @@ extern const char kCheckIncludes[]; ...@@ -99,6 +99,10 @@ extern const char kCheckIncludes[];
extern const char kCheckIncludes_HelpShort[]; extern const char kCheckIncludes_HelpShort[];
extern const char kCheckIncludes_Help[]; extern const char kCheckIncludes_Help[];
extern const char kCompleteStaticLib[];
extern const char kCompleteStaticLib_HelpShort[];
extern const char kCompleteStaticLib_Help[];
extern const char kConfigs[]; extern const char kConfigs[];
extern const char kConfigs_HelpShort[]; extern const char kConfigs_HelpShort[];
extern const char kConfigs_Help[]; extern const char kConfigs_Help[];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment