Commit e3398c19 authored by Nico Weber's avatar Nico Weber

gn: Add runtime_link_output to tool("solib")

gn's tool("solink") has two settings for the output of the rule:
link_output and depend_output. link_output is what targets depending
on a shared_library are linked against, and depend_output is what
is used for the ninja dependency.

On POSIX, this is used to implement a component build optimization:
If a shared library is linked but its public interface doesn't change, then
it's not necessary to relink its downstream dependencies. To implement
this, depend_output is set to a text file that contains a description of all
public symbols of the shared library. The link step first links and then
checks if the new public symbols are different from the old contents of
the file, and only then does it overwrite the TOC file. This allows ninja's
restat optimization to work.

However, downstream dependencies need to link against the actual .so
file on the link line `ld -o foo libinput.so`, so link_output needs to be set
to the .so file.

On Windows, link.exe writes a .lib and a .dll file when it creates a .dll file.
The .lib is only written when needed with incremental links, so if
depend_output is set to the .lib then the restat optimization works there
too. And downstream dependencies also need to link to the .lib at
link time, so in Windows both depend_output and link_output must
be set the the .lib file.

So far, so good. However, `gn desc runtime_deps` is used to print what files
need to be copied to swarming bots to run an executable, and that currently
looks at link_output. On POSIX that's ok, but on Windows that ends up
copying the .lib instead of the .dll.

This patch adds a third setting "runtime_link_output" which can be set
to the output of the solink tool that must be present at runtime. It defaults
to link_output (which does the right thing on POSIX), but it can be set to the
.dll file to make `gn desc runtime_deps` do the right thing on Windows.

This is needed to make swarming work in component builds on Windows.

BUG=354261, 498033
R=dpranke@chromium.org, scottmg@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#374840}
parent 036f7e32
...@@ -2486,9 +2486,10 @@ ...@@ -2486,9 +2486,10 @@
that actually produces these files. that actually produces these files.
If you specify more than one output for shared library links, If you specify more than one output for shared library links,
you should consider setting link_output and depend_output. you should consider setting link_output, depend_output, and
Otherwise, the first entry in the outputs list should always be runtime_link_output. Otherwise, the first entry in the
the main output which will be linked to. outputs list should always be the main output which will be
linked to.
Example for a compiler tool that produces .obj files: Example for a compiler tool that produces .obj files:
outputs = [ outputs = [
...@@ -2507,20 +2508,22 @@ ...@@ -2507,20 +2508,22 @@
link_output [string with substitutions] link_output [string with substitutions]
depend_output [string with substitutions] depend_output [string with substitutions]
runtime_link_output [string with substitutions]
Valid for: "solink" only (optional) Valid for: "solink" only (optional)
These two files specify which of the outputs from the solink These three files specify which of the outputs from the solink
tool should be used for linking and dependency tracking. These tool should be used for linking and dependency tracking. These
should match entries in the "outputs". If unspecified, the should match entries in the "outputs". If unspecified, the
first item in the "outputs" array will be used for both. See first item in the "outputs" array will be used for all. See
"Separate linking and dependencies for shared libraries" "Separate linking and dependencies for shared libraries"
below for more. below for more. If link_output is set but runtime_link_output
is not set, runtime_link_output defaults to link_output.
On Windows, where the tools produce a .dll shared library and On Windows, where the tools produce a .dll shared library and
a .lib import library, you will want both of these to be the a .lib import library, you will want the first two to be the
import library. On Linux, if you're not doing the separate import library and the third one to be the .dll file.
linking/dependency optimization, both of these should be the On Linux, if you're not doing the separate linking/dependency
.so output. optimization, all of these should be the .so output.
output_prefix [string] output_prefix [string]
Valid for: Linker tools (optional) Valid for: Linker tools (optional)
......
...@@ -481,9 +481,10 @@ const char kTool_Help[] = ...@@ -481,9 +481,10 @@ const char kTool_Help[] =
" that actually produces these files.\n" " that actually produces these files.\n"
"\n" "\n"
" If you specify more than one output for shared library links,\n" " If you specify more than one output for shared library links,\n"
" you should consider setting link_output and depend_output.\n" " you should consider setting link_output, depend_output, and\n"
" Otherwise, the first entry in the outputs list should always be\n" " runtime_link_output. Otherwise, the first entry in the\n"
" the main output which will be linked to.\n" " outputs list should always be the main output which will be\n"
" linked to.\n"
"\n" "\n"
" Example for a compiler tool that produces .obj files:\n" " Example for a compiler tool that produces .obj files:\n"
" outputs = [\n" " outputs = [\n"
...@@ -503,20 +504,22 @@ const char kTool_Help[] = ...@@ -503,20 +504,22 @@ const char kTool_Help[] =
"\n" "\n"
" link_output [string with substitutions]\n" " link_output [string with substitutions]\n"
" depend_output [string with substitutions]\n" " depend_output [string with substitutions]\n"
" runtime_link_output [string with substitutions]\n"
" Valid for: \"solink\" only (optional)\n" " Valid for: \"solink\" only (optional)\n"
"\n" "\n"
" These two files specify which of the outputs from the solink\n" " These three files specify which of the outputs from the solink\n"
" tool should be used for linking and dependency tracking. These\n" " tool should be used for linking and dependency tracking. These\n"
" should match entries in the \"outputs\". If unspecified, the\n" " should match entries in the \"outputs\". If unspecified, the\n"
" first item in the \"outputs\" array will be used for both. See\n" " first item in the \"outputs\" array will be used for all. See\n"
" \"Separate linking and dependencies for shared libraries\"\n" " \"Separate linking and dependencies for shared libraries\"\n"
" below for more.\n" " below for more. If link_output is set but runtime_link_output\n"
" is not set, runtime_link_output defaults to link_output.\n"
"\n" "\n"
" On Windows, where the tools produce a .dll shared library and\n" " On Windows, where the tools produce a .dll shared library and\n"
" a .lib import library, you will want both of these to be the\n" " a .lib import library, you will want the first two to be the\n"
" import library. On Linux, if you're not doing the separate\n" " import library and the third one to be the .dll file.\n"
" linking/dependency optimization, both of these should be the\n" " On Linux, if you're not doing the separate linking/dependency\n"
" .so output.\n" " optimization, all of these should be the .so output.\n"
"\n" "\n"
" output_prefix [string]\n" " output_prefix [string]\n"
" Valid for: Linker tools (optional)\n" " Valid for: Linker tools (optional)\n"
...@@ -827,14 +830,16 @@ Value RunTool(Scope* scope, ...@@ -827,14 +830,16 @@ Value RunTool(Scope* scope,
!ReadDepsFormat(&block_scope, tool.get(), err) || !ReadDepsFormat(&block_scope, tool.get(), err) ||
!ReadPattern(&block_scope, "description", subst_validator, tool.get(), !ReadPattern(&block_scope, "description", subst_validator, tool.get(),
&Tool::set_description, err) || &Tool::set_description, err) ||
!ReadString(&block_scope, "lib_switch", tool.get(), !ReadString(&block_scope, "lib_switch", tool.get(), &Tool::set_lib_switch,
&Tool::set_lib_switch, err) || err) ||
!ReadString(&block_scope, "lib_dir_switch", tool.get(), !ReadString(&block_scope, "lib_dir_switch", tool.get(),
&Tool::set_lib_dir_switch, err) || &Tool::set_lib_dir_switch, err) ||
!ReadPattern(&block_scope, "link_output", subst_validator, tool.get(), !ReadPattern(&block_scope, "link_output", subst_validator, tool.get(),
&Tool::set_link_output, err) || &Tool::set_link_output, err) ||
!ReadPattern(&block_scope, "depend_output", subst_validator, tool.get(), !ReadPattern(&block_scope, "depend_output", subst_validator, tool.get(),
&Tool::set_depend_output, err) || &Tool::set_depend_output, err) ||
!ReadPattern(&block_scope, "runtime_link_output", subst_validator,
tool.get(), &Tool::set_runtime_link_output, err) ||
!ReadString(&block_scope, "output_prefix", tool.get(), !ReadString(&block_scope, "output_prefix", tool.get(),
&Tool::set_output_prefix, err) || &Tool::set_output_prefix, err) ||
!ReadPrecompiledHeaderType(&block_scope, tool.get(), err) || !ReadPrecompiledHeaderType(&block_scope, tool.get(), err) ||
...@@ -854,8 +859,9 @@ Value RunTool(Scope* scope, ...@@ -854,8 +859,9 @@ Value RunTool(Scope* scope,
return Value(); return Value();
} }
// Validate that the link_output and depend_output refer to items in the // Validate that the link_output, depend_output, and runtime_link_output
// outputs and aren't defined for irrelevant tool types. // refer to items in the outputs and aren't defined for irrelevant tool
// types.
if (!tool->link_output().empty()) { if (!tool->link_output().empty()) {
if (tool_type != Toolchain::TYPE_SOLINK && if (tool_type != Toolchain::TYPE_SOLINK &&
tool_type != Toolchain::TYPE_SOLINK_MODULE) { tool_type != Toolchain::TYPE_SOLINK_MODULE) {
...@@ -888,6 +894,19 @@ Value RunTool(Scope* scope, ...@@ -888,6 +894,19 @@ Value RunTool(Scope* scope,
"be specified or they should both be empty."); "be specified or they should both be empty.");
return Value(); return Value();
} }
if (!tool->runtime_link_output().empty()) {
if (tool_type != Toolchain::TYPE_SOLINK &&
tool_type != Toolchain::TYPE_SOLINK_MODULE) {
*err = Err(function, "This tool specifies a runtime_link_output.",
"This is only valid for solink and solink_module tools.");
return Value();
}
if (!IsPatternInOutputList(tool->outputs(), tool->runtime_link_output())) {
*err = Err(function, "This tool's runtime_link_output is bad.",
"It must match one of the outputs.");
return Value();
}
}
// Make sure there weren't any vars set in this tool that were unused. // Make sure there weren't any vars set in this tool that were unused.
if (!block_scope.CheckForUnusedVars(err)) if (!block_scope.CheckForUnusedVars(err))
......
...@@ -53,7 +53,7 @@ void AddIfNew(const std::string& str, ...@@ -53,7 +53,7 @@ void AddIfNew(const std::string& str,
// targets. This is weird only for shared libraries. // targets. This is weird only for shared libraries.
const OutputFile& GetMainOutput(const Target* target) { const OutputFile& GetMainOutput(const Target* target) {
if (target->output_type() == Target::SHARED_LIBRARY) if (target->output_type() == Target::SHARED_LIBRARY)
return target->link_output_file(); return target->runtime_link_output_file();
return target->dependency_output_file(); return target->dependency_output_file();
} }
......
...@@ -531,6 +531,13 @@ void Target::FillOutputFiles() { ...@@ -531,6 +531,13 @@ void Target::FillOutputFiles() {
this, tool, tool->depend_output()); this, tool, tool->depend_output());
} }
} }
if (tool->runtime_link_output().empty()) {
runtime_link_output_file_ = link_output_file_;
} else {
runtime_link_output_file_ =
SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
this, tool, tool->runtime_link_output());
}
break; break;
case UNKNOWN: case UNKNOWN:
default: default:
......
...@@ -257,6 +257,9 @@ class Target : public Item { ...@@ -257,6 +257,9 @@ class Target : public Item {
const OutputFile& dependency_output_file() const { const OutputFile& dependency_output_file() const {
return dependency_output_file_; return dependency_output_file_;
} }
const OutputFile& runtime_link_output_file() const {
return runtime_link_output_file_;
}
// Computes the set of output files resulting from compiling the given source // Computes the set of output files resulting from compiling the given source
// file. If the file can be compiled and the tool exists, fills the outputs // file. If the file can be compiled and the tool exists, fills the outputs
...@@ -350,6 +353,7 @@ class Target : public Item { ...@@ -350,6 +353,7 @@ class Target : public Item {
std::vector<OutputFile> computed_outputs_; std::vector<OutputFile> computed_outputs_;
OutputFile link_output_file_; OutputFile link_output_file_;
OutputFile dependency_output_file_; OutputFile dependency_output_file_;
OutputFile runtime_link_output_file_;
DISALLOW_COPY_AND_ASSIGN(Target); DISALLOW_COPY_AND_ASSIGN(Target);
}; };
......
...@@ -457,6 +457,46 @@ TEST(Target, LinkAndDepOutputs) { ...@@ -457,6 +457,46 @@ TEST(Target, LinkAndDepOutputs) {
EXPECT_EQ("./liba.so", target.link_output_file().value()); EXPECT_EQ("./liba.so", target.link_output_file().value());
EXPECT_EQ("./liba.so.TOC", target.dependency_output_file().value()); EXPECT_EQ("./liba.so.TOC", target.dependency_output_file().value());
EXPECT_EQ("./liba.so", target.runtime_link_output_file().value());
}
// Tests that runtime_link output works without an explicit link_output for
// solink tools.
TEST(Target, RuntimeLinkOuput) {
TestWithScope setup;
Err err;
Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc"));
scoped_ptr<Tool> solink_tool(new Tool());
solink_tool->set_output_prefix("");
solink_tool->set_default_output_extension(".dll");
const char kLibPattern[] =
"{{root_out_dir}}/{{target_output_name}}{{output_extension}}.lib";
SubstitutionPattern lib_output =
SubstitutionPattern::MakeForTest(kLibPattern);
const char kDllPattern[] =
"{{root_out_dir}}/{{target_output_name}}{{output_extension}}";
SubstitutionPattern dll_output =
SubstitutionPattern::MakeForTest(kDllPattern);
solink_tool->set_outputs(
SubstitutionList::MakeForTest(kLibPattern, kDllPattern));
solink_tool->set_runtime_link_output(dll_output);
toolchain.SetTool(Toolchain::TYPE_SOLINK, std::move(solink_tool));
Target target(setup.settings(), Label(SourceDir("//a/"), "a"));
target.set_output_type(Target::SHARED_LIBRARY);
target.SetToolchain(&toolchain);
ASSERT_TRUE(target.OnResolved(&err));
EXPECT_EQ("./a.dll.lib", target.link_output_file().value());
EXPECT_EQ("./a.dll.lib", target.dependency_output_file().value());
EXPECT_EQ("./a.dll", target.runtime_link_output_file().value());
} }
// Shared libraries should be inherited across public shared liobrary // Shared libraries should be inherited across public shared liobrary
......
...@@ -125,6 +125,14 @@ class Tool { ...@@ -125,6 +125,14 @@ class Tool {
depend_output_ = dep_out; depend_output_ = dep_out;
} }
const SubstitutionPattern& runtime_link_output() const {
return runtime_link_output_;
}
void set_runtime_link_output(const SubstitutionPattern& run_out) {
DCHECK(!complete_);
runtime_link_output_ = run_out;
}
const std::string& output_prefix() const { const std::string& output_prefix() const {
return output_prefix_; return output_prefix_;
} }
...@@ -187,6 +195,7 @@ class Tool { ...@@ -187,6 +195,7 @@ class Tool {
SubstitutionList outputs_; SubstitutionList outputs_;
SubstitutionPattern link_output_; SubstitutionPattern link_output_;
SubstitutionPattern depend_output_; SubstitutionPattern depend_output_;
SubstitutionPattern runtime_link_output_;
std::string output_prefix_; std::string output_prefix_;
bool restat_; bool restat_;
SubstitutionPattern rspfile_; SubstitutionPattern rspfile_;
......
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