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() {
if (err_->has_error())
return;
FillCompleteStaticLib();
if (err_->has_error())
return;
// Config values (compiler flags, etc.) set directly on this target.
ConfigValuesGenerator gen(&target_->config_values(), scope_,
scope_->GetSourceDir(), err_);
......@@ -76,6 +80,17 @@ void BinaryTargetGenerator::FillCheckIncludes() {
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() {
const Value* value = scope_->GetValue(variables::kOutputName, true);
if (!value)
......
......@@ -24,6 +24,7 @@ class BinaryTargetGenerator : public TargetGenerator {
private:
void FillCheckIncludes();
void FillCompleteStaticLib();
void FillOutputName();
void FillOutputExtension();
void FillAllowCircularIncludesFrom();
......@@ -34,4 +35,3 @@ class BinaryTargetGenerator : public TargetGenerator {
};
#endif // TOOLS_GN_BINARY_TARGET_GENERATOR_H_
......@@ -393,27 +393,26 @@ void NinjaBinaryTargetWriter::ClassifyDependency(
UniqueVector<OutputFile>* extra_object_files,
UniqueVector<const Target*>* linkable_deps,
UniqueVector<const Target*>* non_linkable_deps) const {
// Only these types of outputs have libraries linked into them. Child deps of
// 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_->output_type() == Target::EXECUTABLE ||
target_->output_type() == Target::SHARED_LIBRARY);
// Only the following types of outputs have libraries linked into them:
// EXECUTABLE
// SHARED_LIBRARY
// _complete_ STATIC_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) {
// Source sets have their object files linked into final targets (shared
// libraries and executables). Intermediate static libraries and other
// source sets just forward the dependency, otherwise the files in the
// source set can easily get linked more than once which will cause
// Source sets have their object files linked into final targets
// (shared libraries, executables, and complete static
// libraries). Intermediate static libraries and other source sets
// just forward the dependency, otherwise the files in the source
// set can easily get linked more than once which will cause
// multiple definition errors.
//
// In the future, we may need a way to specify a "complete" static library
// for cases where you want a static library that includes all source sets
// (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.
if (can_link_libs) {
// Linking in a source set to an executable, shared library, or
// complete static library, so copy its object files.
std::vector<OutputFile> tool_outputs; // Prevent allocation in loop.
for (size_t i = 0; i < dep->sources().size(); i++) {
Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
......
......@@ -125,6 +125,38 @@ TEST(NinjaBinaryTargetWriter, SourceSet) {
std::string out_str = 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
......
......@@ -73,6 +73,7 @@ Target::Target(const Settings* settings, const Label& label)
output_type_(UNKNOWN),
all_headers_public_(true),
check_includes_(true),
complete_static_lib_(false),
testonly_(false),
hard_dep_(false),
toolchain_(NULL) {
......@@ -160,6 +161,11 @@ bool Target::IsLinkable() const {
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 {
DCHECK(toolchain_)
<< "Toolchain must be specified before getting the computed output name.";
......@@ -235,8 +241,7 @@ void Target::PullDependentTargetInfo() {
// Inherited libraries and flags are inherited across static library
// boundaries.
if (dep->output_type() != SHARED_LIBRARY &&
dep->output_type() != EXECUTABLE) {
if (!dep->IsFinal()) {
inherited_libraries_.Append(dep->inherited_libraries().begin(),
dep->inherited_libraries().end());
......
......@@ -58,8 +58,12 @@ class Target : public Item {
OutputType output_type() const { return output_type_; }
void set_output_type(OutputType t) { output_type_ = t; }
// Can be linked into other targets.
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.
// See GetComputedOutputName().
const std::string& output_name() const { return output_name_; }
......@@ -95,6 +99,13 @@ class Target : public Item {
bool check_includes() const { return check_includes_; }
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_; }
void set_testonly(bool value) { testonly_ = value; }
......@@ -239,6 +250,7 @@ class Target : public Item {
bool all_headers_public_;
FileList public_headers_;
bool check_includes_;
bool complete_static_lib_;
bool testonly_;
FileList inputs_;
FileList data_;
......
......@@ -274,6 +274,41 @@ TEST(Target, InheritLibs) {
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) {
TestWithScope setup;
Err err;
......
......@@ -368,6 +368,31 @@ const char kCheckIncludes_Help[] =
" ...\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_HelpShort[] =
"configs: [label list] Configs applying to this target.";
......@@ -918,6 +943,7 @@ const VariableInfoMap& GetTargetVariables() {
INSERT_VARIABLE(CflagsObjC)
INSERT_VARIABLE(CflagsObjCC)
INSERT_VARIABLE(CheckIncludes)
INSERT_VARIABLE(CompleteStaticLib)
INSERT_VARIABLE(Configs)
INSERT_VARIABLE(Data)
INSERT_VARIABLE(Datadeps)
......
......@@ -99,6 +99,10 @@ extern const char kCheckIncludes[];
extern const char kCheckIncludes_HelpShort[];
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_HelpShort[];
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