Commit 85423a03 authored by Brett Wilson's avatar Brett Wilson

Add testonly flag to GN

This also reworks visibility so both it and the testonly flag are checked when
a target is marked resolved, rather than when it is written out. The previous
code would not check visibility when doing non-"gen" commands like "check",
which is counterintuitive.

This change required OnResolved to be able to report a failure, which required
updating many callers. But it makes visibility mush easier to test, so I
added some additional visibility tests.

BUG=357779
R=hclam@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#292976}
parent d2787a39
...@@ -418,7 +418,9 @@ bool Builder::ResolveItem(BuilderRecord* record, Err* err) { ...@@ -418,7 +418,9 @@ bool Builder::ResolveItem(BuilderRecord* record, Err* err) {
} }
record->set_resolved(true); record->set_resolved(true);
record->item()->OnResolved();
if (!record->item()->OnResolved(err))
return false;
if (!resolved_callback_.is_null()) if (!resolved_callback_.is_null())
resolved_callback_.Run(record); resolved_callback_.Run(record);
......
...@@ -25,20 +25,9 @@ const char kSwitchQuiet[] = "q"; ...@@ -25,20 +25,9 @@ const char kSwitchQuiet[] = "q";
const char kSwitchCheck[] = "check"; const char kSwitchCheck[] = "check";
void BackgroundDoWrite(const Target* target, // Called on worker thread to write the ninja file.
const std::vector<const Item*>& deps_for_visibility) { void BackgroundDoWrite(const Target* target) {
// Validate visibility. NinjaTargetWriter::RunAndWriteFile(target);
Err err;
for (size_t i = 0; i < deps_for_visibility.size(); i++) {
if (!Visibility::CheckItemVisibility(target, deps_for_visibility[i],
&err)) {
g_scheduler->FailWithError(err);
break; // Don't return early since we need DecrementWorkCount below.
}
}
if (!err.has_error())
NinjaTargetWriter::RunAndWriteFile(target);
g_scheduler->DecrementWorkCount(); g_scheduler->DecrementWorkCount();
} }
...@@ -51,16 +40,8 @@ void ItemResolvedCallback(base::subtle::Atomic32* write_counter, ...@@ -51,16 +40,8 @@ void ItemResolvedCallback(base::subtle::Atomic32* write_counter,
const Item* item = record->item(); const Item* item = record->item();
const Target* target = item->AsTarget(); const Target* target = item->AsTarget();
if (target) { if (target) {
// Collect all dependencies.
std::vector<const Item*> deps;
for (BuilderRecord::BuilderRecordSet::const_iterator iter =
record->all_deps().begin();
iter != record->all_deps().end();
++iter)
deps.push_back((*iter)->item());
g_scheduler->IncrementWorkCount(); g_scheduler->IncrementWorkCount();
g_scheduler->ScheduleWork(base::Bind(&BackgroundDoWrite, target, deps)); g_scheduler->ScheduleWork(base::Bind(&BackgroundDoWrite, target));
} }
} }
......
...@@ -28,6 +28,7 @@ struct IncludeWriter { ...@@ -28,6 +28,7 @@ struct IncludeWriter {
TEST(ConfigValuesExtractors, IncludeOrdering) { TEST(ConfigValuesExtractors, IncludeOrdering) {
TestWithScope setup; TestWithScope setup;
Err err;
// Construct a chain of dependencies: target -> dep1 -> dep2 // Construct a chain of dependencies: target -> dep1 -> dep2
// Add representative values: cflags (opaque, always copied) and include_dirs // Add representative values: cflags (opaque, always copied) and include_dirs
...@@ -100,9 +101,9 @@ TEST(ConfigValuesExtractors, IncludeOrdering) { ...@@ -100,9 +101,9 @@ TEST(ConfigValuesExtractors, IncludeOrdering) {
SourceDir("//target/")); SourceDir("//target/"));
// Mark targets resolved. This should push dependent configs. // Mark targets resolved. This should push dependent configs.
dep2.OnResolved(); ASSERT_TRUE(dep2.OnResolved(&err));
dep1.OnResolved(); ASSERT_TRUE(dep1.OnResolved(&err));
target.OnResolved(); ASSERT_TRUE(target.OnResolved(&err));
// Verify cflags by serializing. // Verify cflags by serializing.
std::ostringstream flag_out; std::ostringstream flag_out;
......
...@@ -32,3 +32,7 @@ std::string Item::GetItemTypeName() const { ...@@ -32,3 +32,7 @@ std::string Item::GetItemTypeName() const {
NOTREACHED(); NOTREACHED();
return "this thing that I have no idea what it is"; return "this thing that I have no idea what it is";
} }
bool Item::OnResolved(Err* err) {
return true;
}
...@@ -48,8 +48,9 @@ class Item { ...@@ -48,8 +48,9 @@ class Item {
std::string GetItemTypeName() const; std::string GetItemTypeName() const;
// Called when this item is resolved, meaning it and all of its dependents // Called when this item is resolved, meaning it and all of its dependents
// have no unresolved deps. // have no unresolved deps. Returns true on success. Sets the error and
virtual void OnResolved() {} // returns false on failure.
virtual bool OnResolved(Err* err);
private: private:
const Settings* settings_; const Settings* settings_;
......
...@@ -36,7 +36,11 @@ struct LabelPtrPair { ...@@ -36,7 +36,11 @@ struct LabelPtrPair {
Label label; Label label;
const T* ptr; // May be NULL. const T* ptr; // May be NULL.
const ParseNode* origin; // May be NULL.
// The origin of this dependency. This will be null for internally generated
// dependencies. This happens when a group is automatically expanded and that
// group's members are added to the target that depends on that group.
const ParseNode* origin;
}; };
typedef LabelPtrPair<Config> LabelConfigPair; typedef LabelPtrPair<Config> LabelConfigPair;
......
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
TEST(NinjaActionTargetWriter, WriteOutputFilesForBuildLine) { TEST(NinjaActionTargetWriter, WriteOutputFilesForBuildLine) {
TestWithScope setup; TestWithScope setup;
Err err;
setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
...@@ -22,7 +24,7 @@ TEST(NinjaActionTargetWriter, WriteOutputFilesForBuildLine) { ...@@ -22,7 +24,7 @@ TEST(NinjaActionTargetWriter, WriteOutputFilesForBuildLine) {
"//out/Debug/gen/{{source_name_part}}.cc"); "//out/Debug/gen/{{source_name_part}}.cc");
target.SetToolchain(setup.toolchain()); target.SetToolchain(setup.toolchain());
target.OnResolved(); ASSERT_TRUE(target.OnResolved(&err));
std::ostringstream out; std::ostringstream out;
NinjaActionTargetWriter writer(&target, out); NinjaActionTargetWriter writer(&target, out);
...@@ -37,6 +39,8 @@ TEST(NinjaActionTargetWriter, WriteOutputFilesForBuildLine) { ...@@ -37,6 +39,8 @@ TEST(NinjaActionTargetWriter, WriteOutputFilesForBuildLine) {
// Tests an action with no sources. // Tests an action with no sources.
TEST(NinjaActionTargetWriter, ActionNoSources) { TEST(NinjaActionTargetWriter, ActionNoSources) {
TestWithScope setup; TestWithScope setup;
Err err;
setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
target.set_output_type(Target::ACTION); target.set_output_type(Target::ACTION);
...@@ -48,7 +52,7 @@ TEST(NinjaActionTargetWriter, ActionNoSources) { ...@@ -48,7 +52,7 @@ TEST(NinjaActionTargetWriter, ActionNoSources) {
SubstitutionList::MakeForTest("//out/Debug/foo.out"); SubstitutionList::MakeForTest("//out/Debug/foo.out");
target.SetToolchain(setup.toolchain()); target.SetToolchain(setup.toolchain());
target.OnResolved(); ASSERT_TRUE(target.OnResolved(&err));
setup.settings()->set_target_os(Settings::LINUX); setup.settings()->set_target_os(Settings::LINUX);
setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL( setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL(
...@@ -76,6 +80,8 @@ TEST(NinjaActionTargetWriter, ActionNoSources) { ...@@ -76,6 +80,8 @@ TEST(NinjaActionTargetWriter, ActionNoSources) {
// both sources and inputs (ACTION_FOREACH treats the sources differently). // both sources and inputs (ACTION_FOREACH treats the sources differently).
TEST(NinjaActionTargetWriter, ActionWithSources) { TEST(NinjaActionTargetWriter, ActionWithSources) {
TestWithScope setup; TestWithScope setup;
Err err;
setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
target.set_output_type(Target::ACTION); target.set_output_type(Target::ACTION);
...@@ -89,7 +95,7 @@ TEST(NinjaActionTargetWriter, ActionWithSources) { ...@@ -89,7 +95,7 @@ TEST(NinjaActionTargetWriter, ActionWithSources) {
SubstitutionList::MakeForTest("//out/Debug/foo.out"); SubstitutionList::MakeForTest("//out/Debug/foo.out");
target.SetToolchain(setup.toolchain()); target.SetToolchain(setup.toolchain());
target.OnResolved(); ASSERT_TRUE(target.OnResolved(&err));
// Posix. // Posix.
{ {
...@@ -146,6 +152,8 @@ TEST(NinjaActionTargetWriter, ActionWithSources) { ...@@ -146,6 +152,8 @@ TEST(NinjaActionTargetWriter, ActionWithSources) {
TEST(NinjaActionTargetWriter, ForEach) { TEST(NinjaActionTargetWriter, ForEach) {
TestWithScope setup; TestWithScope setup;
Err err;
setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
// Some dependencies that the action can depend on. Use actions for these // Some dependencies that the action can depend on. Use actions for these
...@@ -155,12 +163,12 @@ TEST(NinjaActionTargetWriter, ForEach) { ...@@ -155,12 +163,12 @@ TEST(NinjaActionTargetWriter, ForEach) {
Target dep(setup.settings(), Label(SourceDir("//foo/"), "dep")); Target dep(setup.settings(), Label(SourceDir("//foo/"), "dep"));
dep.set_output_type(Target::ACTION); dep.set_output_type(Target::ACTION);
dep.SetToolchain(setup.toolchain()); dep.SetToolchain(setup.toolchain());
dep.OnResolved(); ASSERT_TRUE(dep.OnResolved(&err));
Target datadep(setup.settings(), Label(SourceDir("//foo/"), "datadep")); Target datadep(setup.settings(), Label(SourceDir("//foo/"), "datadep"));
datadep.set_output_type(Target::ACTION); datadep.set_output_type(Target::ACTION);
datadep.SetToolchain(setup.toolchain()); datadep.SetToolchain(setup.toolchain());
datadep.OnResolved(); ASSERT_TRUE(datadep.OnResolved(&err));
Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
target.set_output_type(Target::ACTION_FOREACH); target.set_output_type(Target::ACTION_FOREACH);
...@@ -182,7 +190,7 @@ TEST(NinjaActionTargetWriter, ForEach) { ...@@ -182,7 +190,7 @@ TEST(NinjaActionTargetWriter, ForEach) {
target.inputs().push_back(SourceFile("//foo/included.txt")); target.inputs().push_back(SourceFile("//foo/included.txt"));
target.SetToolchain(setup.toolchain()); target.SetToolchain(setup.toolchain());
target.OnResolved(); ASSERT_TRUE(target.OnResolved(&err));
// Posix. // Posix.
{ {
...@@ -268,6 +276,8 @@ TEST(NinjaActionTargetWriter, ForEach) { ...@@ -268,6 +276,8 @@ TEST(NinjaActionTargetWriter, ForEach) {
TEST(NinjaActionTargetWriter, ForEachWithDepfile) { TEST(NinjaActionTargetWriter, ForEachWithDepfile) {
TestWithScope setup; TestWithScope setup;
Err err;
setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
target.set_output_type(Target::ACTION_FOREACH); target.set_output_type(Target::ACTION_FOREACH);
...@@ -278,10 +288,9 @@ TEST(NinjaActionTargetWriter, ForEachWithDepfile) { ...@@ -278,10 +288,9 @@ TEST(NinjaActionTargetWriter, ForEachWithDepfile) {
target.action_values().set_script(SourceFile("//foo/script.py")); target.action_values().set_script(SourceFile("//foo/script.py"));
target.SetToolchain(setup.toolchain()); target.SetToolchain(setup.toolchain());
target.OnResolved(); ASSERT_TRUE(target.OnResolved(&err));
SubstitutionPattern depfile; SubstitutionPattern depfile;
Err err;
ASSERT_TRUE( ASSERT_TRUE(
depfile.Parse("//out/Debug/gen/{{source_name_part}}.d", NULL, &err)); depfile.Parse("//out/Debug/gen/{{source_name_part}}.d", NULL, &err));
target.action_values().set_depfile(depfile); target.action_values().set_depfile(depfile);
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
TEST(NinjaBinaryTargetWriter, SourceSet) { TEST(NinjaBinaryTargetWriter, SourceSet) {
TestWithScope setup; TestWithScope setup;
Err err;
setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
setup.settings()->set_target_os(Settings::WIN); setup.settings()->set_target_os(Settings::WIN);
...@@ -23,7 +25,7 @@ TEST(NinjaBinaryTargetWriter, SourceSet) { ...@@ -23,7 +25,7 @@ TEST(NinjaBinaryTargetWriter, SourceSet) {
target.sources().push_back(SourceFile("//foo/input3.o")); target.sources().push_back(SourceFile("//foo/input3.o"));
target.sources().push_back(SourceFile("//foo/input4.obj")); target.sources().push_back(SourceFile("//foo/input4.obj"));
target.SetToolchain(setup.toolchain()); target.SetToolchain(setup.toolchain());
target.OnResolved(); ASSERT_TRUE(target.OnResolved(&err));
// Source set itself. // Source set itself.
{ {
...@@ -57,7 +59,7 @@ TEST(NinjaBinaryTargetWriter, SourceSet) { ...@@ -57,7 +59,7 @@ TEST(NinjaBinaryTargetWriter, SourceSet) {
shlib_target.set_output_type(Target::SHARED_LIBRARY); shlib_target.set_output_type(Target::SHARED_LIBRARY);
shlib_target.deps().push_back(LabelTargetPair(&target)); shlib_target.deps().push_back(LabelTargetPair(&target));
shlib_target.SetToolchain(setup.toolchain()); shlib_target.SetToolchain(setup.toolchain());
shlib_target.OnResolved(); ASSERT_TRUE(shlib_target.OnResolved(&err));
{ {
std::ostringstream out; std::ostringstream out;
...@@ -94,7 +96,7 @@ TEST(NinjaBinaryTargetWriter, SourceSet) { ...@@ -94,7 +96,7 @@ TEST(NinjaBinaryTargetWriter, SourceSet) {
stlib_target.set_output_type(Target::STATIC_LIBRARY); stlib_target.set_output_type(Target::STATIC_LIBRARY);
stlib_target.deps().push_back(LabelTargetPair(&target)); stlib_target.deps().push_back(LabelTargetPair(&target));
stlib_target.SetToolchain(setup.toolchain()); stlib_target.SetToolchain(setup.toolchain());
stlib_target.OnResolved(); ASSERT_TRUE(stlib_target.OnResolved(&err));
{ {
std::ostringstream out; std::ostringstream out;
...@@ -129,6 +131,8 @@ TEST(NinjaBinaryTargetWriter, SourceSet) { ...@@ -129,6 +131,8 @@ TEST(NinjaBinaryTargetWriter, SourceSet) {
// are applied. // are applied.
TEST(NinjaBinaryTargetWriter, ProductExtensionAndInputDeps) { TEST(NinjaBinaryTargetWriter, ProductExtensionAndInputDeps) {
TestWithScope setup; TestWithScope setup;
Err err;
setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
setup.settings()->set_target_os(Settings::LINUX); setup.settings()->set_target_os(Settings::LINUX);
...@@ -136,7 +140,7 @@ TEST(NinjaBinaryTargetWriter, ProductExtensionAndInputDeps) { ...@@ -136,7 +140,7 @@ TEST(NinjaBinaryTargetWriter, ProductExtensionAndInputDeps) {
Target action(setup.settings(), Label(SourceDir("//foo/"), "action")); Target action(setup.settings(), Label(SourceDir("//foo/"), "action"));
action.set_output_type(Target::ACTION_FOREACH); action.set_output_type(Target::ACTION_FOREACH);
action.SetToolchain(setup.toolchain()); action.SetToolchain(setup.toolchain());
action.OnResolved(); ASSERT_TRUE(action.OnResolved(&err));
// A shared library w/ the product_extension set to a custom value. // A shared library w/ the product_extension set to a custom value.
Target target(setup.settings(), Label(SourceDir("//foo/"), "shlib")); Target target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
...@@ -146,7 +150,7 @@ TEST(NinjaBinaryTargetWriter, ProductExtensionAndInputDeps) { ...@@ -146,7 +150,7 @@ TEST(NinjaBinaryTargetWriter, ProductExtensionAndInputDeps) {
target.sources().push_back(SourceFile("//foo/input2.cc")); target.sources().push_back(SourceFile("//foo/input2.cc"));
target.deps().push_back(LabelTargetPair(&action)); target.deps().push_back(LabelTargetPair(&action));
target.SetToolchain(setup.toolchain()); target.SetToolchain(setup.toolchain());
target.OnResolved(); ASSERT_TRUE(target.OnResolved(&err));
std::ostringstream out; std::ostringstream out;
NinjaBinaryTargetWriter writer(&target, out); NinjaBinaryTargetWriter writer(&target, out);
...@@ -185,6 +189,8 @@ TEST(NinjaBinaryTargetWriter, ProductExtensionAndInputDeps) { ...@@ -185,6 +189,8 @@ TEST(NinjaBinaryTargetWriter, ProductExtensionAndInputDeps) {
TEST(NinjaBinaryTargetWriter, EmptyProductExtension) { TEST(NinjaBinaryTargetWriter, EmptyProductExtension) {
TestWithScope setup; TestWithScope setup;
Err err;
setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
setup.settings()->set_target_os(Settings::LINUX); setup.settings()->set_target_os(Settings::LINUX);
...@@ -197,7 +203,7 @@ TEST(NinjaBinaryTargetWriter, EmptyProductExtension) { ...@@ -197,7 +203,7 @@ TEST(NinjaBinaryTargetWriter, EmptyProductExtension) {
target.sources().push_back(SourceFile("//foo/input2.cc")); target.sources().push_back(SourceFile("//foo/input2.cc"));
target.SetToolchain(setup.toolchain()); target.SetToolchain(setup.toolchain());
target.OnResolved(); ASSERT_TRUE(target.OnResolved(&err));
std::ostringstream out; std::ostringstream out;
NinjaBinaryTargetWriter writer(&target, out); NinjaBinaryTargetWriter writer(&target, out);
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
// Tests mutliple files with an output pattern and no toolchain dependency. // Tests mutliple files with an output pattern and no toolchain dependency.
TEST(NinjaCopyTargetWriter, Run) { TEST(NinjaCopyTargetWriter, Run) {
TestWithScope setup; TestWithScope setup;
Err err;
setup.settings()->set_target_os(Settings::LINUX); setup.settings()->set_target_os(Settings::LINUX);
setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
...@@ -26,7 +27,7 @@ TEST(NinjaCopyTargetWriter, Run) { ...@@ -26,7 +27,7 @@ TEST(NinjaCopyTargetWriter, Run) {
SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out"); SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
target.SetToolchain(setup.toolchain()); target.SetToolchain(setup.toolchain());
target.OnResolved(); ASSERT_TRUE(target.OnResolved(&err));
std::ostringstream out; std::ostringstream out;
NinjaCopyTargetWriter writer(&target, out); NinjaCopyTargetWriter writer(&target, out);
...@@ -44,6 +45,7 @@ TEST(NinjaCopyTargetWriter, Run) { ...@@ -44,6 +45,7 @@ TEST(NinjaCopyTargetWriter, Run) {
// Tests a single file with no output pattern. // Tests a single file with no output pattern.
TEST(NinjaCopyTargetWriter, ToolchainDeps) { TEST(NinjaCopyTargetWriter, ToolchainDeps) {
TestWithScope setup; TestWithScope setup;
Err err;
setup.settings()->set_target_os(Settings::LINUX); setup.settings()->set_target_os(Settings::LINUX);
setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
...@@ -56,7 +58,7 @@ TEST(NinjaCopyTargetWriter, ToolchainDeps) { ...@@ -56,7 +58,7 @@ TEST(NinjaCopyTargetWriter, ToolchainDeps) {
SubstitutionList::MakeForTest("//out/Debug/output.out"); SubstitutionList::MakeForTest("//out/Debug/output.out");
target.SetToolchain(setup.toolchain()); target.SetToolchain(setup.toolchain());
target.OnResolved(); ASSERT_TRUE(target.OnResolved(&err));
std::ostringstream out; std::ostringstream out;
NinjaCopyTargetWriter writer(&target, out); NinjaCopyTargetWriter writer(&target, out);
......
...@@ -32,6 +32,7 @@ class TestingNinjaTargetWriter : public NinjaTargetWriter { ...@@ -32,6 +32,7 @@ class TestingNinjaTargetWriter : public NinjaTargetWriter {
TEST(NinjaTargetWriter, WriteInputDepsStampAndGetDep) { TEST(NinjaTargetWriter, WriteInputDepsStampAndGetDep) {
TestWithScope setup; TestWithScope setup;
Err err;
// Make a base target that's a hard dep (action). // Make a base target that's a hard dep (action).
Target base_target(setup.settings(), Label(SourceDir("//foo/"), "base")); Target base_target(setup.settings(), Label(SourceDir("//foo/"), "base"));
...@@ -57,9 +58,9 @@ TEST(NinjaTargetWriter, WriteInputDepsStampAndGetDep) { ...@@ -57,9 +58,9 @@ TEST(NinjaTargetWriter, WriteInputDepsStampAndGetDep) {
action.sources().push_back(SourceFile("//foo/action_source.txt")); action.sources().push_back(SourceFile("//foo/action_source.txt"));
action.deps().push_back(LabelTargetPair(&target)); action.deps().push_back(LabelTargetPair(&target));
base_target.OnResolved(); ASSERT_TRUE(base_target.OnResolved(&err));
target.OnResolved(); ASSERT_TRUE(target.OnResolved(&err));
action.OnResolved(); ASSERT_TRUE(action.OnResolved(&err));
// Input deps for the base (should be only the script itself). // Input deps for the base (should be only the script itself).
{ {
...@@ -105,6 +106,7 @@ TEST(NinjaTargetWriter, WriteInputDepsStampAndGetDep) { ...@@ -105,6 +106,7 @@ TEST(NinjaTargetWriter, WriteInputDepsStampAndGetDep) {
// Tests WriteInputDepsStampAndGetDep when toolchain deps are present. // Tests WriteInputDepsStampAndGetDep when toolchain deps are present.
TEST(NinjaTargetWriter, WriteInputDepsStampAndGetDepWithToolchainDeps) { TEST(NinjaTargetWriter, WriteInputDepsStampAndGetDepWithToolchainDeps) {
TestWithScope setup; TestWithScope setup;
Err err;
// Toolchain dependency. Here we make a target in the same toolchain for // Toolchain dependency. Here we make a target in the same toolchain for
// simplicity, but in real life (using the Builder) this would be rejected // simplicity, but in real life (using the Builder) this would be rejected
...@@ -114,14 +116,14 @@ TEST(NinjaTargetWriter, WriteInputDepsStampAndGetDepWithToolchainDeps) { ...@@ -114,14 +116,14 @@ TEST(NinjaTargetWriter, WriteInputDepsStampAndGetDepWithToolchainDeps) {
Label(SourceDir("//foo/"), "setup")); Label(SourceDir("//foo/"), "setup"));
toolchain_dep_target.set_output_type(Target::ACTION); toolchain_dep_target.set_output_type(Target::ACTION);
toolchain_dep_target.SetToolchain(setup.toolchain()); toolchain_dep_target.SetToolchain(setup.toolchain());
toolchain_dep_target.OnResolved(); ASSERT_TRUE(toolchain_dep_target.OnResolved(&err));
setup.toolchain()->deps().push_back(LabelTargetPair(&toolchain_dep_target)); setup.toolchain()->deps().push_back(LabelTargetPair(&toolchain_dep_target));
// Make a binary target // Make a binary target
Target target(setup.settings(), Label(SourceDir("//foo/"), "target")); Target target(setup.settings(), Label(SourceDir("//foo/"), "target"));
target.set_output_type(Target::EXECUTABLE); target.set_output_type(Target::EXECUTABLE);
target.SetToolchain(setup.toolchain()); target.SetToolchain(setup.toolchain());
target.OnResolved(); ASSERT_TRUE(target.OnResolved(&err));
std::ostringstream stream; std::ostringstream stream;
TestingNinjaTargetWriter writer(&target, setup.toolchain(), stream); TestingNinjaTargetWriter writer(&target, setup.toolchain(), stream);
......
...@@ -173,11 +173,12 @@ TEST(SubstutitionWriter, SourceSubstitutions) { ...@@ -173,11 +173,12 @@ TEST(SubstutitionWriter, SourceSubstitutions) {
TEST(SubstitutionWriter, TargetSubstitutions) { TEST(SubstitutionWriter, TargetSubstitutions) {
TestWithScope setup; TestWithScope setup;
Err err;
Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz")); Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz"));
target.set_output_type(Target::STATIC_LIBRARY); target.set_output_type(Target::STATIC_LIBRARY);
target.SetToolchain(setup.toolchain()); target.SetToolchain(setup.toolchain());
target.OnResolved(); ASSERT_TRUE(target.OnResolved(&err));
std::string result; std::string result;
EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution( EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
...@@ -207,11 +208,12 @@ TEST(SubstitutionWriter, TargetSubstitutions) { ...@@ -207,11 +208,12 @@ TEST(SubstitutionWriter, TargetSubstitutions) {
TEST(SubstitutionWriter, CompilerSubstitutions) { TEST(SubstitutionWriter, CompilerSubstitutions) {
TestWithScope setup; TestWithScope setup;
Err err;
Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz")); Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz"));
target.set_output_type(Target::STATIC_LIBRARY); target.set_output_type(Target::STATIC_LIBRARY);
target.SetToolchain(setup.toolchain()); target.SetToolchain(setup.toolchain());
target.OnResolved(); ASSERT_TRUE(target.OnResolved(&err));
// The compiler substitution is just source + target combined. So test one // The compiler substitution is just source + target combined. So test one
// of each of those classes of things to make sure this is hooked up. // of each of those classes of things to make sure this is hooked up.
...@@ -227,11 +229,12 @@ TEST(SubstitutionWriter, CompilerSubstitutions) { ...@@ -227,11 +229,12 @@ TEST(SubstitutionWriter, CompilerSubstitutions) {
TEST(SubstitutionWriter, LinkerSubstitutions) { TEST(SubstitutionWriter, LinkerSubstitutions) {
TestWithScope setup; TestWithScope setup;
Err err;
Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz")); Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz"));
target.set_output_type(Target::SHARED_LIBRARY); target.set_output_type(Target::SHARED_LIBRARY);
target.SetToolchain(setup.toolchain()); target.SetToolchain(setup.toolchain());
target.OnResolved(); ASSERT_TRUE(target.OnResolved(&err));
const Tool* tool = setup.toolchain()->GetToolForTargetFinalOutput(&target); const Tool* tool = setup.toolchain()->GetToolForTargetFinalOutput(&target);
...@@ -246,7 +249,6 @@ TEST(SubstitutionWriter, LinkerSubstitutions) { ...@@ -246,7 +249,6 @@ TEST(SubstitutionWriter, LinkerSubstitutions) {
// Test that we handle paths that end up in the root build dir properly // Test that we handle paths that end up in the root build dir properly
// (no leading "./" or "/"). // (no leading "./" or "/").
Err err;
SubstitutionPattern pattern; SubstitutionPattern pattern;
ASSERT_TRUE( ASSERT_TRUE(
pattern.Parse("{{root_out_dir}}/{{target_output_name}}.so", NULL, &err)); pattern.Parse("{{root_out_dir}}/{{target_output_name}}.so", NULL, &err));
......
...@@ -39,6 +39,33 @@ void MergeAllDependentConfigsFrom(const Target* from_target, ...@@ -39,6 +39,33 @@ void MergeAllDependentConfigsFrom(const Target* from_target,
} }
} }
Err MakeTestOnlyError(const Target* from, const Target* to) {
return Err(from->defined_from(), "Test-only dependency not allowed.",
from->label().GetUserVisibleName(false) + "\n"
"which is NOT marked testonly can't depend on\n" +
to->label().GetUserVisibleName(false) + "\n"
"which is marked testonly. Only targets with \"testonly = true\"\n"
"can depend on other test-only targets.\n"
"\n"
"Either mark it test-only or don't do this dependency.");
}
// Inserts the given groups dependencies, starting at the given index of the
// given vector. Returns the number of items inserted.
size_t InsertGroupDeps(LabelTargetVector* vector,
size_t insert_at,
const Target* group) {
const LabelTargetVector& deps = group->deps();
vector->insert(vector->begin() + insert_at, deps.begin(), deps.end());
// Clear the origin of each of the insertions. This marks these dependencies
// as internally generated.
for (size_t i = insert_at; i < deps.size() + insert_at; i++)
(*vector)[i].origin = NULL;
return deps.size();
}
} // namespace } // namespace
Target::Target(const Settings* settings, const Label& label) Target::Target(const Settings* settings, const Label& label)
...@@ -46,6 +73,7 @@ Target::Target(const Settings* settings, const Label& label) ...@@ -46,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),
testonly_(false),
hard_dep_(false), hard_dep_(false),
toolchain_(NULL) { toolchain_(NULL) {
} }
...@@ -87,22 +115,11 @@ const Target* Target::AsTarget() const { ...@@ -87,22 +115,11 @@ const Target* Target::AsTarget() const {
return this; return this;
} }
void Target::OnResolved() { bool Target::OnResolved(Err* err) {
DCHECK(output_type_ != UNKNOWN); DCHECK(output_type_ != UNKNOWN);
DCHECK(toolchain_) << "Toolchain should have been set before resolving."; DCHECK(toolchain_) << "Toolchain should have been set before resolving.";
// Convert any groups we depend on to just direct dependencies on that ExpandGroups();
// group's deps. We insert the new deps immediately after the group so that
// the ordering is preserved. We need to keep the original group so that any
// flags, etc. that it specifies itself are applied to us.
for (size_t i = 0; i < deps_.size(); i++) {
const Target* dep = deps_[i].ptr;
if (dep->output_type_ == GROUP) {
// TODO(brettw) bug 403488 this should also handle datadeps.
deps_.insert(deps_.begin() + i + 1, dep->deps_.begin(), dep->deps_.end());
i += dep->deps_.size();
}
}
// Copy our own dependent configs to the list of configs applying to us. // Copy our own dependent configs to the list of configs applying to us.
configs_.Append(all_dependent_configs_.begin(), all_dependent_configs_.end()); configs_.Append(all_dependent_configs_.begin(), all_dependent_configs_.end());
...@@ -130,6 +147,13 @@ void Target::OnResolved() { ...@@ -130,6 +147,13 @@ void Target::OnResolved() {
PullRecursiveHardDeps(); PullRecursiveHardDeps();
FillOutputFiles(); FillOutputFiles();
if (!CheckVisibility(err))
return false;
if (!CheckTestonly(err))
return false;
return true;
} }
bool Target::IsLinkable() const { bool Target::IsLinkable() const {
...@@ -183,6 +207,19 @@ bool Target::SetToolchain(const Toolchain* toolchain, Err* err) { ...@@ -183,6 +207,19 @@ bool Target::SetToolchain(const Toolchain* toolchain, Err* err) {
return false; return false;
} }
void Target::ExpandGroups() {
// Convert any groups we depend on to just direct dependencies on that
// group's deps. We insert the new deps immediately after the group so that
// the ordering is preserved. We need to keep the original group so that any
// flags, etc. that it specifies itself are applied to us.
// TODO(brettw) bug 403488 this should also handle datadeps.
for (size_t i = 0; i < deps_.size(); i++) {
const Target* dep = deps_[i].ptr;
if (dep->output_type_ == GROUP)
i += InsertGroupDeps(&deps_, i + 1, dep);
}
}
void Target::PullDependentTargetInfo() { void Target::PullDependentTargetInfo() {
// Gather info from our dependents we need. // Gather info from our dependents we need.
for (size_t dep_i = 0; dep_i < deps_.size(); dep_i++) { for (size_t dep_i = 0; dep_i < deps_.size(); dep_i++) {
...@@ -306,3 +343,48 @@ void Target::FillOutputFiles() { ...@@ -306,3 +343,48 @@ void Target::FillOutputFiles() {
NOTREACHED(); NOTREACHED();
} }
} }
bool Target::CheckVisibility(Err* err) const {
// Only check visibility when the origin of the dependency is non-null. These
// are dependencies added by the GN files. Internally added dependencies
// (expanded groups) will have a null origin. We don't want to check
// visibility for these, since the point of a group would often be to
// forward visibility.
for (size_t i = 0; i < deps_.size(); i++) {
if (deps_[i].origin &&
!Visibility::CheckItemVisibility(this, deps_[i].ptr, err))
return false;
}
for (size_t i = 0; i < datadeps_.size(); i++) {
if (deps_[i].origin &&
!Visibility::CheckItemVisibility(this, datadeps_[i].ptr, err))
return false;
}
return true;
}
bool Target::CheckTestonly(Err* err) const {
// If there current target is marked testonly, it can include both testonly
// and non-testonly targets, so there's nothing to check.
if (testonly())
return true;
// Verify no deps have "testonly" set.
for (size_t i = 0; i < deps_.size(); i++) {
if (deps_[i].ptr->testonly()) {
*err = MakeTestOnlyError(this, deps_[i].ptr);
return false;
}
}
for (size_t i = 0; i < datadeps_.size(); i++) {
if (datadeps_[i].ptr->testonly()) {
*err = MakeTestOnlyError(this, datadeps_[i].ptr);
return false;
}
}
return true;
}
...@@ -53,7 +53,7 @@ class Target : public Item { ...@@ -53,7 +53,7 @@ class Target : public Item {
// Item overrides. // Item overrides.
virtual Target* AsTarget() OVERRIDE; virtual Target* AsTarget() OVERRIDE;
virtual const Target* AsTarget() const OVERRIDE; virtual const Target* AsTarget() const OVERRIDE;
virtual void OnResolved() OVERRIDE; virtual bool OnResolved(Err* err) OVERRIDE;
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; }
...@@ -95,6 +95,9 @@ class Target : public Item { ...@@ -95,6 +95,9 @@ 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; }
bool testonly() const { return testonly_; }
void set_testonly(bool value) { testonly_ = value; }
// Compile-time extra dependencies. // Compile-time extra dependencies.
const FileList& inputs() const { return inputs_; } const FileList& inputs() const { return inputs_; }
FileList& inputs() { return inputs_; } FileList& inputs() { return inputs_; }
...@@ -210,6 +213,8 @@ class Target : public Item { ...@@ -210,6 +213,8 @@ class Target : public Item {
} }
private: private:
void ExpandGroups();
// Pulls necessary information from dependencies to this one when all // Pulls necessary information from dependencies to this one when all
// dependencies have been resolved. // dependencies have been resolved.
void PullDependentTargetInfo(); void PullDependentTargetInfo();
...@@ -222,6 +227,10 @@ class Target : public Item { ...@@ -222,6 +227,10 @@ class Target : public Item {
// Fills the link and dependency output files when a target is resolved. // Fills the link and dependency output files when a target is resolved.
void FillOutputFiles(); void FillOutputFiles();
// Validates the given thing when a target is resolved.
bool CheckVisibility(Err* err) const;
bool CheckTestonly(Err* err) const;
OutputType output_type_; OutputType output_type_;
std::string output_name_; std::string output_name_;
std::string output_extension_; std::string output_extension_;
...@@ -230,6 +239,7 @@ class Target : public Item { ...@@ -230,6 +239,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 testonly_;
FileList inputs_; FileList inputs_;
FileList data_; FileList data_;
......
...@@ -48,6 +48,10 @@ void TargetGenerator::Run() { ...@@ -48,6 +48,10 @@ void TargetGenerator::Run() {
if (err_->has_error()) if (err_->has_error())
return; return;
FillTestonly();
if (err_->has_error())
return;
if (!Visibility::FillItemVisibility(target_, scope_, err_)) if (!Visibility::FillItemVisibility(target_, scope_, err_))
return; return;
...@@ -217,6 +221,15 @@ void TargetGenerator::FillDependencies() { ...@@ -217,6 +221,15 @@ void TargetGenerator::FillDependencies() {
return; return;
} }
void TargetGenerator::FillTestonly() {
const Value* value = scope_->GetValue(variables::kTestonly, true);
if (value) {
if (!value->VerifyTypeIs(Value::BOOLEAN, err_))
return;
target_->set_testonly(value->boolean_value());
}
}
void TargetGenerator::FillOutputs(bool allow_substitutions) { void TargetGenerator::FillOutputs(bool allow_substitutions) {
const Value* value = scope_->GetValue(variables::kOutputs, true); const Value* value = scope_->GetValue(variables::kOutputs, true);
if (!value) if (!value)
......
...@@ -70,6 +70,7 @@ class TargetGenerator { ...@@ -70,6 +70,7 @@ class TargetGenerator {
void FillDependentConfigs(); // Includes all types of dependent configs. void FillDependentConfigs(); // Includes all types of dependent configs.
void FillData(); void FillData();
void FillDependencies(); // Includes data dependencies. void FillDependencies(); // Includes data dependencies.
void FillTestonly();
// Reads configs/deps from the given var name, and uses the given setting on // Reads configs/deps from the given var name, and uses the given setting on
// the target to save them. // the target to save them.
......
...@@ -14,37 +14,47 @@ ...@@ -14,37 +14,47 @@
// deps. // deps.
TEST(Target, GroupDeps) { TEST(Target, GroupDeps) {
TestWithScope setup; TestWithScope setup;
Err err;
// Used as the origin for the not-automatically-set deps.
IdentifierNode origin;
// Two low-level targets. // Two low-level targets.
Target x(setup.settings(), Label(SourceDir("//component/"), "x")); Target x(setup.settings(), Label(SourceDir("//component/"), "x"));
x.set_output_type(Target::STATIC_LIBRARY); x.set_output_type(Target::STATIC_LIBRARY);
x.SetToolchain(setup.toolchain()); x.SetToolchain(setup.toolchain());
x.OnResolved(); ASSERT_TRUE(x.OnResolved(&err));
Target y(setup.settings(), Label(SourceDir("//component/"), "y")); Target y(setup.settings(), Label(SourceDir("//component/"), "y"));
y.set_output_type(Target::STATIC_LIBRARY); y.set_output_type(Target::STATIC_LIBRARY);
y.SetToolchain(setup.toolchain()); y.SetToolchain(setup.toolchain());
y.OnResolved(); ASSERT_TRUE(y.OnResolved(&err));
// Make a group for both x and y. // Make a group for both x and y.
Target g(setup.settings(), Label(SourceDir("//group/"), "g")); Target g(setup.settings(), Label(SourceDir("//group/"), "g"));
g.set_output_type(Target::GROUP); g.set_output_type(Target::GROUP);
g.visibility().SetPublic();
g.deps().push_back(LabelTargetPair(&x)); g.deps().push_back(LabelTargetPair(&x));
g.deps()[0].origin = &origin;
g.deps().push_back(LabelTargetPair(&y)); g.deps().push_back(LabelTargetPair(&y));
g.deps()[1].origin = &origin;
// Random placeholder target so we can see the group's deps get inserted at // Random placeholder target so we can see the group's deps get inserted at
// the right place. // the right place.
Target b(setup.settings(), Label(SourceDir("//app/"), "b")); Target b(setup.settings(), Label(SourceDir("//app/"), "b"));
b.set_output_type(Target::STATIC_LIBRARY); b.set_output_type(Target::STATIC_LIBRARY);
b.SetToolchain(setup.toolchain()); b.SetToolchain(setup.toolchain());
b.OnResolved(); b.visibility().SetPublic();
ASSERT_TRUE(b.OnResolved(&err));
// Make a target depending on the group and "b". OnResolved will expand. // Make a target depending on the group and "b". OnResolved will expand.
Target a(setup.settings(), Label(SourceDir("//app/"), "a")); Target a(setup.settings(), Label(SourceDir("//app/"), "a"));
a.set_output_type(Target::EXECUTABLE); a.set_output_type(Target::EXECUTABLE);
a.deps().push_back(LabelTargetPair(&g)); a.deps().push_back(LabelTargetPair(&g));
a.deps()[0].origin = &origin;
a.deps().push_back(LabelTargetPair(&b)); a.deps().push_back(LabelTargetPair(&b));
a.deps()[1].origin = &origin;
a.SetToolchain(setup.toolchain()); a.SetToolchain(setup.toolchain());
a.OnResolved(); ASSERT_TRUE(a.OnResolved(&err));
// The group's deps should be inserted after the group itself in the deps // The group's deps should be inserted after the group itself in the deps
// list, so we should get "g, x, y, b" // list, so we should get "g, x, y, b"
...@@ -53,12 +63,20 @@ TEST(Target, GroupDeps) { ...@@ -53,12 +63,20 @@ TEST(Target, GroupDeps) {
EXPECT_EQ(&x, a.deps()[1].ptr); EXPECT_EQ(&x, a.deps()[1].ptr);
EXPECT_EQ(&y, a.deps()[2].ptr); EXPECT_EQ(&y, a.deps()[2].ptr);
EXPECT_EQ(&b, a.deps()[3].ptr); EXPECT_EQ(&b, a.deps()[3].ptr);
// The "regular" deps on a should have the origin set. The automatically
// expanded ones will have a null origin so we know they are generated.
EXPECT_EQ(&origin, a.deps()[0].origin);
EXPECT_EQ(NULL, a.deps()[1].origin);
EXPECT_EQ(NULL, a.deps()[2].origin);
EXPECT_EQ(&origin, a.deps()[3].origin);
} }
// Tests that lib[_dir]s are inherited across deps boundaries for static // Tests that lib[_dir]s are inherited across deps boundaries for static
// libraries but not executables. // libraries but not executables.
TEST(Target, LibInheritance) { TEST(Target, LibInheritance) {
TestWithScope setup; TestWithScope setup;
Err err;
const std::string lib("foo"); const std::string lib("foo");
const SourceDir libdir("/foo_dir/"); const SourceDir libdir("/foo_dir/");
...@@ -69,7 +87,7 @@ TEST(Target, LibInheritance) { ...@@ -69,7 +87,7 @@ TEST(Target, LibInheritance) {
z.config_values().libs().push_back(lib); z.config_values().libs().push_back(lib);
z.config_values().lib_dirs().push_back(libdir); z.config_values().lib_dirs().push_back(libdir);
z.SetToolchain(setup.toolchain()); z.SetToolchain(setup.toolchain());
z.OnResolved(); ASSERT_TRUE(z.OnResolved(&err));
// All lib[_dir]s should be set when target is resolved. // All lib[_dir]s should be set when target is resolved.
ASSERT_EQ(1u, z.all_libs().size()); ASSERT_EQ(1u, z.all_libs().size());
...@@ -87,7 +105,7 @@ TEST(Target, LibInheritance) { ...@@ -87,7 +105,7 @@ TEST(Target, LibInheritance) {
shared.config_values().lib_dirs().push_back(second_libdir); shared.config_values().lib_dirs().push_back(second_libdir);
shared.deps().push_back(LabelTargetPair(&z)); shared.deps().push_back(LabelTargetPair(&z));
shared.SetToolchain(setup.toolchain()); shared.SetToolchain(setup.toolchain());
shared.OnResolved(); ASSERT_TRUE(shared.OnResolved(&err));
ASSERT_EQ(2u, shared.all_libs().size()); ASSERT_EQ(2u, shared.all_libs().size());
EXPECT_EQ(second_lib, shared.all_libs()[0]); EXPECT_EQ(second_lib, shared.all_libs()[0]);
...@@ -101,7 +119,7 @@ TEST(Target, LibInheritance) { ...@@ -101,7 +119,7 @@ TEST(Target, LibInheritance) {
exec.set_output_type(Target::EXECUTABLE); exec.set_output_type(Target::EXECUTABLE);
exec.deps().push_back(LabelTargetPair(&shared)); exec.deps().push_back(LabelTargetPair(&shared));
exec.SetToolchain(setup.toolchain()); exec.SetToolchain(setup.toolchain());
exec.OnResolved(); ASSERT_TRUE(exec.OnResolved(&err));
EXPECT_EQ(0u, exec.all_libs().size()); EXPECT_EQ(0u, exec.all_libs().size());
EXPECT_EQ(0u, exec.all_lib_dirs().size()); EXPECT_EQ(0u, exec.all_lib_dirs().size());
} }
...@@ -110,6 +128,7 @@ TEST(Target, LibInheritance) { ...@@ -110,6 +128,7 @@ TEST(Target, LibInheritance) {
// forward_dependent_configs_from // forward_dependent_configs_from
TEST(Target, DependentConfigs) { TEST(Target, DependentConfigs) {
TestWithScope setup; TestWithScope setup;
Err err;
// Set up a dependency chain of a -> b -> c // Set up a dependency chain of a -> b -> c
Target a(setup.settings(), Label(SourceDir("//foo/"), "a")); Target a(setup.settings(), Label(SourceDir("//foo/"), "a"));
...@@ -136,9 +155,9 @@ TEST(Target, DependentConfigs) { ...@@ -136,9 +155,9 @@ TEST(Target, DependentConfigs) {
Config direct(setup.settings(), Label(SourceDir("//foo/"), "direct")); Config direct(setup.settings(), Label(SourceDir("//foo/"), "direct"));
c.direct_dependent_configs().push_back(LabelConfigPair(&direct)); c.direct_dependent_configs().push_back(LabelConfigPair(&direct));
c.OnResolved(); ASSERT_TRUE(c.OnResolved(&err));
b.OnResolved(); ASSERT_TRUE(b.OnResolved(&err));
a.OnResolved(); ASSERT_TRUE(a.OnResolved(&err));
// B should have gotten both dependent configs from C. // B should have gotten both dependent configs from C.
ASSERT_EQ(2u, b.configs().size()); ASSERT_EQ(2u, b.configs().size());
...@@ -163,8 +182,8 @@ TEST(Target, DependentConfigs) { ...@@ -163,8 +182,8 @@ TEST(Target, DependentConfigs) {
b_fwd.deps().push_back(LabelTargetPair(&c)); b_fwd.deps().push_back(LabelTargetPair(&c));
b_fwd.forward_dependent_configs().push_back(LabelTargetPair(&c)); b_fwd.forward_dependent_configs().push_back(LabelTargetPair(&c));
b_fwd.OnResolved(); ASSERT_TRUE(b_fwd.OnResolved(&err));
a_fwd.OnResolved(); ASSERT_TRUE(a_fwd.OnResolved(&err));
// A_fwd should now have both configs. // A_fwd should now have both configs.
ASSERT_EQ(2u, a_fwd.configs().size()); ASSERT_EQ(2u, a_fwd.configs().size());
...@@ -178,6 +197,7 @@ TEST(Target, DependentConfigs) { ...@@ -178,6 +197,7 @@ TEST(Target, DependentConfigs) {
// group's deps' dependent configs. // group's deps' dependent configs.
TEST(Target, ForwardDependentConfigsFromGroups) { TEST(Target, ForwardDependentConfigsFromGroups) {
TestWithScope setup; TestWithScope setup;
Err err;
Target a(setup.settings(), Label(SourceDir("//foo/"), "a")); Target a(setup.settings(), Label(SourceDir("//foo/"), "a"));
a.set_output_type(Target::EXECUTABLE); a.set_output_type(Target::EXECUTABLE);
...@@ -198,9 +218,9 @@ TEST(Target, ForwardDependentConfigsFromGroups) { ...@@ -198,9 +218,9 @@ TEST(Target, ForwardDependentConfigsFromGroups) {
// A forwards the dependent configs from B. // A forwards the dependent configs from B.
a.forward_dependent_configs().push_back(LabelTargetPair(&b)); a.forward_dependent_configs().push_back(LabelTargetPair(&b));
c.OnResolved(); ASSERT_TRUE(c.OnResolved(&err));
b.OnResolved(); ASSERT_TRUE(b.OnResolved(&err));
a.OnResolved(); ASSERT_TRUE(a.OnResolved(&err));
// The config should now be on A, and in A's direct dependent configs. // The config should now be on A, and in A's direct dependent configs.
ASSERT_EQ(1u, a.configs().size()); ASSERT_EQ(1u, a.configs().size());
...@@ -211,6 +231,7 @@ TEST(Target, ForwardDependentConfigsFromGroups) { ...@@ -211,6 +231,7 @@ TEST(Target, ForwardDependentConfigsFromGroups) {
TEST(Target, InheritLibs) { TEST(Target, InheritLibs) {
TestWithScope setup; TestWithScope setup;
Err err;
// Create a dependency chain: // Create a dependency chain:
// A (executable) -> B (shared lib) -> C (static lib) -> D (source set) // A (executable) -> B (shared lib) -> C (static lib) -> D (source set)
...@@ -230,10 +251,10 @@ TEST(Target, InheritLibs) { ...@@ -230,10 +251,10 @@ TEST(Target, InheritLibs) {
b.deps().push_back(LabelTargetPair(&c)); b.deps().push_back(LabelTargetPair(&c));
c.deps().push_back(LabelTargetPair(&d)); c.deps().push_back(LabelTargetPair(&d));
d.OnResolved(); ASSERT_TRUE(d.OnResolved(&err));
c.OnResolved(); ASSERT_TRUE(c.OnResolved(&err));
b.OnResolved(); ASSERT_TRUE(b.OnResolved(&err));
a.OnResolved(); ASSERT_TRUE(a.OnResolved(&err));
// C should have D in its inherited libs. // C should have D in its inherited libs.
const UniqueVector<const Target*>& c_inherited = c.inherited_libraries(); const UniqueVector<const Target*>& c_inherited = c.inherited_libraries();
...@@ -255,13 +276,14 @@ TEST(Target, InheritLibs) { ...@@ -255,13 +276,14 @@ TEST(Target, InheritLibs) {
TEST(Target, GetComputedOutputName) { TEST(Target, GetComputedOutputName) {
TestWithScope setup; TestWithScope setup;
Err err;
// Basic target with no prefix (executable type tool in the TestWithScope has // Basic target with no prefix (executable type tool in the TestWithScope has
// no prefix) or output name. // no prefix) or output name.
Target basic(setup.settings(), Label(SourceDir("//foo/"), "bar")); Target basic(setup.settings(), Label(SourceDir("//foo/"), "bar"));
basic.set_output_type(Target::EXECUTABLE); basic.set_output_type(Target::EXECUTABLE);
basic.SetToolchain(setup.toolchain()); basic.SetToolchain(setup.toolchain());
basic.OnResolved(); ASSERT_TRUE(basic.OnResolved(&err));
EXPECT_EQ("bar", basic.GetComputedOutputName(false)); EXPECT_EQ("bar", basic.GetComputedOutputName(false));
EXPECT_EQ("bar", basic.GetComputedOutputName(true)); EXPECT_EQ("bar", basic.GetComputedOutputName(true));
...@@ -270,7 +292,7 @@ TEST(Target, GetComputedOutputName) { ...@@ -270,7 +292,7 @@ TEST(Target, GetComputedOutputName) {
with_name.set_output_type(Target::EXECUTABLE); with_name.set_output_type(Target::EXECUTABLE);
with_name.set_output_name("myoutput"); with_name.set_output_name("myoutput");
with_name.SetToolchain(setup.toolchain()); with_name.SetToolchain(setup.toolchain());
with_name.OnResolved(); ASSERT_TRUE(with_name.OnResolved(&err));
EXPECT_EQ("myoutput", with_name.GetComputedOutputName(false)); EXPECT_EQ("myoutput", with_name.GetComputedOutputName(false));
EXPECT_EQ("myoutput", with_name.GetComputedOutputName(true)); EXPECT_EQ("myoutput", with_name.GetComputedOutputName(true));
...@@ -279,7 +301,7 @@ TEST(Target, GetComputedOutputName) { ...@@ -279,7 +301,7 @@ TEST(Target, GetComputedOutputName) {
Target with_prefix(setup.settings(), Label(SourceDir("//foo/"), "bar")); Target with_prefix(setup.settings(), Label(SourceDir("//foo/"), "bar"));
with_prefix.set_output_type(Target::STATIC_LIBRARY); with_prefix.set_output_type(Target::STATIC_LIBRARY);
with_prefix.SetToolchain(setup.toolchain()); with_prefix.SetToolchain(setup.toolchain());
with_prefix.OnResolved(); ASSERT_TRUE(with_prefix.OnResolved(&err));
EXPECT_EQ("bar", with_prefix.GetComputedOutputName(false)); EXPECT_EQ("bar", with_prefix.GetComputedOutputName(false));
EXPECT_EQ("libbar", with_prefix.GetComputedOutputName(true)); EXPECT_EQ("libbar", with_prefix.GetComputedOutputName(true));
...@@ -289,7 +311,94 @@ TEST(Target, GetComputedOutputName) { ...@@ -289,7 +311,94 @@ TEST(Target, GetComputedOutputName) {
dup_prefix.set_output_type(Target::STATIC_LIBRARY); dup_prefix.set_output_type(Target::STATIC_LIBRARY);
dup_prefix.set_output_name("libbar"); dup_prefix.set_output_name("libbar");
dup_prefix.SetToolchain(setup.toolchain()); dup_prefix.SetToolchain(setup.toolchain());
dup_prefix.OnResolved(); ASSERT_TRUE(dup_prefix.OnResolved(&err));
EXPECT_EQ("libbar", dup_prefix.GetComputedOutputName(false)); EXPECT_EQ("libbar", dup_prefix.GetComputedOutputName(false));
EXPECT_EQ("libbar", dup_prefix.GetComputedOutputName(true)); EXPECT_EQ("libbar", dup_prefix.GetComputedOutputName(true));
} }
// Test visibility failure case.
TEST(Target, VisibilityFails) {
TestWithScope setup;
Err err;
Target b(setup.settings(), Label(SourceDir("//private/"), "b"));
b.set_output_type(Target::STATIC_LIBRARY);
b.SetToolchain(setup.toolchain());
b.visibility().SetPrivate(b.label().dir());
ASSERT_TRUE(b.OnResolved(&err));
// Make a target depending on "b". The dependency must have an origin to mark
// it as user-set so we check visibility. This check should fail.
Target a(setup.settings(), Label(SourceDir("//app/"), "a"));
a.set_output_type(Target::EXECUTABLE);
a.deps().push_back(LabelTargetPair(&b));
IdentifierNode origin; // Dummy origin.
a.deps()[0].origin = &origin;
a.SetToolchain(setup.toolchain());
ASSERT_FALSE(a.OnResolved(&err));
}
// Tests that A -> Group -> B where the group is visible from A but B isn't,
// passes visibility even though the group's deps get expanded into A.
TEST(Target, VisibilityGroup) {
TestWithScope setup;
Err err;
IdentifierNode origin; // Dummy origin.
// B has private visibility. This lets the group see it since the group is in
// the same directory.
Target b(setup.settings(), Label(SourceDir("//private/"), "b"));
b.set_output_type(Target::STATIC_LIBRARY);
b.SetToolchain(setup.toolchain());
b.visibility().SetPrivate(b.label().dir());
ASSERT_TRUE(b.OnResolved(&err));
// The group has public visibility and depends on b.
Target g(setup.settings(), Label(SourceDir("//private/"), "g"));
g.set_output_type(Target::GROUP);
g.SetToolchain(setup.toolchain());
g.deps().push_back(LabelTargetPair(&b));
g.deps()[0].origin = &origin;
g.visibility().SetPublic();
ASSERT_TRUE(b.OnResolved(&err));
// Make a target depending on "g". This should succeed.
Target a(setup.settings(), Label(SourceDir("//app/"), "a"));
a.set_output_type(Target::EXECUTABLE);
a.deps().push_back(LabelTargetPair(&g));
a.deps()[0].origin = &origin;
a.SetToolchain(setup.toolchain());
ASSERT_TRUE(a.OnResolved(&err));
}
// Verifies that only testonly targets can depend on other testonly targets.
// Many of the above dependency checking cases covered the non-testonly
// case.
TEST(Target, Testonly) {
TestWithScope setup;
Err err;
// "testlib" is a test-only library.
Target testlib(setup.settings(), Label(SourceDir("//test/"), "testlib"));
testlib.set_testonly(true);
testlib.set_output_type(Target::STATIC_LIBRARY);
testlib.SetToolchain(setup.toolchain());
ASSERT_TRUE(testlib.OnResolved(&err));
// "test" is a test-only executable depending on testlib, this is OK.
Target test(setup.settings(), Label(SourceDir("//test/"), "test"));
test.set_testonly(true);
test.set_output_type(Target::EXECUTABLE);
test.deps().push_back(LabelTargetPair(&testlib));
test.SetToolchain(setup.toolchain());
ASSERT_TRUE(test.OnResolved(&err));
// "product" is a non-test depending on testlib. This should fail.
Target product(setup.settings(), Label(SourceDir("//app/"), "product"));
product.set_testonly(false);
product.set_output_type(Target::EXECUTABLE);
product.deps().push_back(LabelTargetPair(&testlib));
product.SetToolchain(setup.toolchain());
ASSERT_FALSE(product.OnResolved(&err));
}
...@@ -795,6 +795,28 @@ const char kSources_Help[] = ...@@ -795,6 +795,28 @@ const char kSources_Help[] =
"\n" "\n"
" A list of files relative to the current buildfile.\n"; " A list of files relative to the current buildfile.\n";
const char kTestonly[] = "testonly";
const char kTestonly_HelpShort[] =
"testonly: [boolean] Declares a target must only be used for testing.";
const char kTestonly_Help[] =
"testonly: Declares a target must only be used for testing.\n"
"\n"
" Boolean. Defaults to false.\n"
"\n"
" When a target is marked \"testonly = true\", it must only be depended\n"
" on by other test-only targets. Otherwise, GN will issue an error\n"
" that the depenedency is not allowed.\n"
"\n"
" This feature is intended to prevent accidentally shipping test code\n"
" in a final product.\n"
"\n"
"Example\n"
"\n"
" source_set(\"test_support\") {\n"
" testonly = true\n"
" ...\n"
" }\n";
const char kVisibility[] = "visibility"; const char kVisibility[] = "visibility";
const char kVisibility_HelpShort[] = const char kVisibility_HelpShort[] =
"visibility: [label list] A list of labels that can depend on a target."; "visibility: [label list] A list of labels that can depend on a target.";
...@@ -915,6 +937,7 @@ const VariableInfoMap& GetTargetVariables() { ...@@ -915,6 +937,7 @@ const VariableInfoMap& GetTargetVariables() {
INSERT_VARIABLE(Public) INSERT_VARIABLE(Public)
INSERT_VARIABLE(Script) INSERT_VARIABLE(Script)
INSERT_VARIABLE(Sources) INSERT_VARIABLE(Sources)
INSERT_VARIABLE(Testonly)
INSERT_VARIABLE(Visibility) INSERT_VARIABLE(Visibility)
} }
return info_map; return info_map;
......
...@@ -175,6 +175,10 @@ extern const char kSources[]; ...@@ -175,6 +175,10 @@ extern const char kSources[];
extern const char kSources_HelpShort[]; extern const char kSources_HelpShort[];
extern const char kSources_Help[]; extern const char kSources_Help[];
extern const char kTestonly[];
extern const char kTestonly_HelpShort[];
extern const char kTestonly_Help[];
extern const char kVisibility[]; extern const char kVisibility[];
extern const char kVisibility_HelpShort[]; extern const char kVisibility_HelpShort[];
extern const char kVisibility_Help[]; extern const char kVisibility_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