Commit 65e89854 authored by brettw's avatar brettw Committed by Commit bot

Allow .o files for GN generated inputs.

GN checks inputs and sources for files that are in the build directory, and
requires those files be generated by a dependency of the target.

Previously, only the final outputs of a target were considered. But NaCl wants
to compile some CRT code and take one of the intermediate object files as
inputs to a subsequent action. The current code was not aware of the generated
object files.

This patch will check for generated object files when the normal checks for
generated inputs fails.

BUG=579629

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

Cr-Commit-Position: refs/heads/master@{#370539}
parent 01b20b2f
......@@ -92,49 +92,6 @@ struct IncludeWriter {
PathOutput& path_output_;
};
// 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 in
// and writes the tool type to computed_tool_type. If the file is not
// compilable, returns false.
//
// The target that the source belongs to is passed as an argument. In the case
// of linking to source sets, this can be different than the target this class
// is currently writing.
//
// The function can succeed with a "NONE" tool type for object files which are
// just passed to the output. The output will always be overwritten, not
// appended to.
bool GetOutputFilesForSource(const Target* target,
const SourceFile& source,
Toolchain::ToolType* computed_tool_type,
std::vector<OutputFile>* outputs) {
outputs->clear();
*computed_tool_type = Toolchain::TYPE_NONE;
SourceFileType file_type = GetSourceFileType(source);
if (file_type == SOURCE_UNKNOWN)
return false;
if (file_type == SOURCE_O) {
// Object files just get passed to the output and not compiled.
outputs->push_back(
OutputFile(target->settings()->build_settings(), source));
return true;
}
*computed_tool_type =
target->toolchain()->GetToolTypeForSourceType(file_type);
if (*computed_tool_type == Toolchain::TYPE_NONE)
return false; // No tool for this file (it's a header file or something).
const Tool* tool = target->toolchain()->GetTool(*computed_tool_type);
if (!tool)
return false; // Tool does not apply for this toolchain.file.
// Figure out what output(s) this compiler produces.
SubstitutionWriter::ApplyListToCompilerAsOutputFile(
target, source, tool->outputs(), outputs);
return !outputs->empty();
}
// Returns the language-specific suffix for precompiled header files.
const char* GetPCHLangSuffixForToolType(Toolchain::ToolType type) {
switch (type) {
......@@ -256,7 +213,7 @@ void AddSourceSetObjectFiles(const Target* source_set,
// the tool if there are more than one.
for (const auto& source : source_set->sources()) {
Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
if (GetOutputFilesForSource(source_set, source, &tool_type, &tool_outputs))
if (source_set->GetOutputFilesForSource(source, &tool_type, &tool_outputs))
obj_files->push_back(tool_outputs[0]);
used_types.Set(GetSourceFileType(source));
......@@ -662,7 +619,7 @@ void NinjaBinaryTargetWriter::WriteSources(
// Clear the vector but maintain the max capacity to prevent reallocations.
deps.resize(0);
Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
if (!GetOutputFilesForSource(target_, source, &tool_type, &tool_outputs)) {
if (!target_->GetOutputFilesForSource(source, &tool_type, &tool_outputs)) {
if (GetSourceFileType(source) == SOURCE_DEF)
other_files->push_back(source);
continue; // No output for this source.
......
......@@ -67,11 +67,7 @@ void NinjaTargetWriter::RunAndWriteFile(const Target* target) {
} else if (target->output_type() == Target::GROUP) {
NinjaGroupTargetWriter writer(target, file);
writer.Run();
} else if (target->output_type() == Target::EXECUTABLE ||
target->output_type() == Target::LOADABLE_MODULE ||
target->output_type() == Target::STATIC_LIBRARY ||
target->output_type() == Target::SHARED_LIBRARY ||
target->output_type() == Target::SOURCE_SET) {
} else if (target->IsBinary()) {
NinjaBinaryTargetWriter writer(target, file);
writer.Run();
} else {
......
......@@ -6,6 +6,8 @@
#include <stddef.h>
#include <algorithm>
#include "base/bind.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
......@@ -13,7 +15,10 @@
#include "tools/gn/deps_iterator.h"
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/scheduler.h"
#include "tools/gn/source_file_type.h"
#include "tools/gn/substitution_writer.h"
#include "tools/gn/tool.h"
#include "tools/gn/toolchain.h"
#include "tools/gn/trace.h"
namespace {
......@@ -69,9 +74,15 @@ Err MakeStaticLibDepsError(const Target* from, const Target* to) {
//
// Pass a pointer to an empty set for the first invocation. This will be used
// to avoid duplicate checking.
//
// Checking of object files is optional because it is much slower. This allows
// us to check targets for normal outputs, and then as a second pass check
// object files (since we know it will be an error otherwise). This allows
// us to avoid computing all object file names in the common case.
bool EnsureFileIsGeneratedByDependency(const Target* target,
const OutputFile& file,
bool check_private_deps,
bool consider_object_files,
std::set<const Target*>* seen_targets) {
if (seen_targets->find(target) != seen_targets->end())
return false; // Already checked this one and it's not found.
......@@ -85,11 +96,24 @@ bool EnsureFileIsGeneratedByDependency(const Target* target,
return true;
}
// Check binary target intermediate files if requested.
if (consider_object_files && target->IsBinary()) {
std::vector<OutputFile> source_outputs;
for (const SourceFile& source : target->sources()) {
Toolchain::ToolType tool_type;
if (!target->GetOutputFilesForSource(source, &tool_type, &source_outputs))
continue;
if (std::find(source_outputs.begin(), source_outputs.end(), file) !=
source_outputs.end())
return true;
}
}
// Check all public dependencies (don't do data ones since those are
// runtime-only).
for (const auto& pair : target->public_deps()) {
if (EnsureFileIsGeneratedByDependency(pair.ptr, file, false,
seen_targets))
consider_object_files, seen_targets))
return true; // Found a path.
}
......@@ -97,6 +121,7 @@ bool EnsureFileIsGeneratedByDependency(const Target* target,
if (check_private_deps) {
for (const auto& pair : target->private_deps()) {
if (EnsureFileIsGeneratedByDependency(pair.ptr, file, false,
consider_object_files,
seen_targets))
return true; // Found a path.
}
......@@ -215,6 +240,14 @@ bool Target::OnResolved(Err* err) {
return true;
}
bool Target::IsBinary() const {
return output_type_ == EXECUTABLE ||
output_type_ == SHARED_LIBRARY ||
output_type_ == LOADABLE_MODULE ||
output_type_ == STATIC_LIBRARY ||
output_type_ == SOURCE_SET;
}
bool Target::IsLinkable() const {
return output_type_ == STATIC_LIBRARY || output_type_ == SHARED_LIBRARY;
}
......@@ -287,6 +320,34 @@ bool Target::SetToolchain(const Toolchain* toolchain, Err* err) {
return false;
}
bool Target::GetOutputFilesForSource(const SourceFile& source,
Toolchain::ToolType* computed_tool_type,
std::vector<OutputFile>* outputs) const {
outputs->clear();
*computed_tool_type = Toolchain::TYPE_NONE;
SourceFileType file_type = GetSourceFileType(source);
if (file_type == SOURCE_UNKNOWN)
return false;
if (file_type == SOURCE_O) {
// Object files just get passed to the output and not compiled.
outputs->push_back(OutputFile(settings()->build_settings(), source));
return true;
}
*computed_tool_type = toolchain_->GetToolTypeForSourceType(file_type);
if (*computed_tool_type == Toolchain::TYPE_NONE)
return false; // No tool for this file (it's a header file or something).
const Tool* tool = toolchain_->GetTool(*computed_tool_type);
if (!tool)
return false; // Tool does not apply for this toolchain.file.
// Figure out what output(s) this compiler produces.
SubstitutionWriter::ApplyListToCompilerAsOutputFile(
this, source, tool->outputs(), outputs);
return !outputs->empty();
}
void Target::PullDependentTargetConfigsFrom(const Target* dep) {
MergeAllDependentConfigsFrom(dep, &configs_, &all_dependent_configs_);
MergePublicConfigsFrom(dep, &configs_);
......@@ -568,6 +629,13 @@ void Target::CheckSourceGenerated(const SourceFile& source) const {
// can be filtered out of this list.
OutputFile out_file(settings()->build_settings(), source);
std::set<const Target*> seen_targets;
if (!EnsureFileIsGeneratedByDependency(this, out_file, true, &seen_targets))
g_scheduler->AddUnknownGeneratedInput(this, source);
if (!EnsureFileIsGeneratedByDependency(this, out_file, true, false,
&seen_targets)) {
// Check object files (much slower and very rare) only if the "normal"
// output check failed.
seen_targets.clear();
if (!EnsureFileIsGeneratedByDependency(this, out_file, true, true,
&seen_targets))
g_scheduler->AddUnknownGeneratedInput(this, source);
}
}
......@@ -21,6 +21,7 @@
#include "tools/gn/ordered_set.h"
#include "tools/gn/output_file.h"
#include "tools/gn/source_file.h"
#include "tools/gn/toolchain.h"
#include "tools/gn/unique_vector.h"
class DepsIteratorRange;
......@@ -66,6 +67,10 @@ class Target : public Item {
OutputType output_type() const { return output_type_; }
void set_output_type(OutputType t) { output_type_ = t; }
// True for targets that compile source code (all types of libaries and
// executables).
bool IsBinary() const;
// Can be linked into other targets.
bool IsLinkable() const;
......@@ -245,6 +250,18 @@ class Target : public Item {
return dependency_output_file_;
}
// 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
// in and writes the tool type to computed_tool_type. If the file is not
// compilable, returns false.
//
// The function can succeed with a "NONE" tool type for object files which
// are just passed to the output. The output will always be overwritten, not
// appended to.
bool GetOutputFilesForSource(const SourceFile& source,
Toolchain::ToolType* computed_tool_type,
std::vector<OutputFile>* outputs) const;
private:
FRIEND_TEST_ALL_PREFIXES(Target, ResolvePrecompiledHeaders);
......
......@@ -587,6 +587,31 @@ TEST(Target, WriteFileGeneratedInputs) {
EXPECT_TRUE(scheduler.GetUnknownGeneratedInputs().empty());
}
// Tests that intermediate object files generated by binary targets are also
// considered generated for the purposes of input checking. Above, we tested
// the failure cases for generated inputs, so here only test .o files that are
// present.
TEST(Target, ObjectGeneratedInputs) {
Scheduler scheduler;
TestWithScope setup;
Err err;
// This target compiles the source.
SourceFile source_file("//source.cc");
TestTarget source_generator(setup, "//:source_target", Target::SOURCE_SET);
source_generator.sources().push_back(source_file);
EXPECT_TRUE(source_generator.OnResolved(&err));
// This is the object file that the test toolchain generates for the source.
SourceFile object_file("//out/Debug/obj/source_target.source.o");
TestTarget final_target(setup, "//:final", Target::ACTION);
final_target.inputs().push_back(object_file);
EXPECT_TRUE(final_target.OnResolved(&err));
AssertSchedulerHasOneUnknownFileMatching(&final_target, object_file);
}
TEST(Target, ResolvePrecompiledHeaders) {
TestWithScope setup;
Err err;
......
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