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) {
}
record->set_resolved(true);
record->item()->OnResolved();
if (!record->item()->OnResolved(err))
return false;
if (!resolved_callback_.is_null())
resolved_callback_.Run(record);
......
......@@ -25,20 +25,9 @@ const char kSwitchQuiet[] = "q";
const char kSwitchCheck[] = "check";
void BackgroundDoWrite(const Target* target,
const std::vector<const Item*>& deps_for_visibility) {
// Validate visibility.
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);
// Called on worker thread to write the ninja file.
void BackgroundDoWrite(const Target* target) {
NinjaTargetWriter::RunAndWriteFile(target);
g_scheduler->DecrementWorkCount();
}
......@@ -51,16 +40,8 @@ void ItemResolvedCallback(base::subtle::Atomic32* write_counter,
const Item* item = record->item();
const Target* target = item->AsTarget();
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->ScheduleWork(base::Bind(&BackgroundDoWrite, target, deps));
g_scheduler->ScheduleWork(base::Bind(&BackgroundDoWrite, target));
}
}
......
......@@ -28,6 +28,7 @@ struct IncludeWriter {
TEST(ConfigValuesExtractors, IncludeOrdering) {
TestWithScope setup;
Err err;
// Construct a chain of dependencies: target -> dep1 -> dep2
// Add representative values: cflags (opaque, always copied) and include_dirs
......@@ -100,9 +101,9 @@ TEST(ConfigValuesExtractors, IncludeOrdering) {
SourceDir("//target/"));
// Mark targets resolved. This should push dependent configs.
dep2.OnResolved();
dep1.OnResolved();
target.OnResolved();
ASSERT_TRUE(dep2.OnResolved(&err));
ASSERT_TRUE(dep1.OnResolved(&err));
ASSERT_TRUE(target.OnResolved(&err));
// Verify cflags by serializing.
std::ostringstream flag_out;
......
......@@ -32,3 +32,7 @@ std::string Item::GetItemTypeName() const {
NOTREACHED();
return "this thing that I have no idea what it is";
}
bool Item::OnResolved(Err* err) {
return true;
}
......@@ -48,8 +48,9 @@ class Item {
std::string GetItemTypeName() const;
// Called when this item is resolved, meaning it and all of its dependents
// have no unresolved deps.
virtual void OnResolved() {}
// have no unresolved deps. Returns true on success. Sets the error and
// returns false on failure.
virtual bool OnResolved(Err* err);
private:
const Settings* settings_;
......
......@@ -36,7 +36,11 @@ struct LabelPtrPair {
Label label;
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;
......
......@@ -13,6 +13,8 @@
TEST(NinjaActionTargetWriter, WriteOutputFilesForBuildLine) {
TestWithScope setup;
Err err;
setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
......@@ -22,7 +24,7 @@ TEST(NinjaActionTargetWriter, WriteOutputFilesForBuildLine) {
"//out/Debug/gen/{{source_name_part}}.cc");
target.SetToolchain(setup.toolchain());
target.OnResolved();
ASSERT_TRUE(target.OnResolved(&err));
std::ostringstream out;
NinjaActionTargetWriter writer(&target, out);
......@@ -37,6 +39,8 @@ TEST(NinjaActionTargetWriter, WriteOutputFilesForBuildLine) {
// Tests an action with no sources.
TEST(NinjaActionTargetWriter, ActionNoSources) {
TestWithScope setup;
Err err;
setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
target.set_output_type(Target::ACTION);
......@@ -48,7 +52,7 @@ TEST(NinjaActionTargetWriter, ActionNoSources) {
SubstitutionList::MakeForTest("//out/Debug/foo.out");
target.SetToolchain(setup.toolchain());
target.OnResolved();
ASSERT_TRUE(target.OnResolved(&err));
setup.settings()->set_target_os(Settings::LINUX);
setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL(
......@@ -76,6 +80,8 @@ TEST(NinjaActionTargetWriter, ActionNoSources) {
// both sources and inputs (ACTION_FOREACH treats the sources differently).
TEST(NinjaActionTargetWriter, ActionWithSources) {
TestWithScope setup;
Err err;
setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
target.set_output_type(Target::ACTION);
......@@ -89,7 +95,7 @@ TEST(NinjaActionTargetWriter, ActionWithSources) {
SubstitutionList::MakeForTest("//out/Debug/foo.out");
target.SetToolchain(setup.toolchain());
target.OnResolved();
ASSERT_TRUE(target.OnResolved(&err));
// Posix.
{
......@@ -146,6 +152,8 @@ TEST(NinjaActionTargetWriter, ActionWithSources) {
TEST(NinjaActionTargetWriter, ForEach) {
TestWithScope setup;
Err err;
setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
// Some dependencies that the action can depend on. Use actions for these
......@@ -155,12 +163,12 @@ TEST(NinjaActionTargetWriter, ForEach) {
Target dep(setup.settings(), Label(SourceDir("//foo/"), "dep"));
dep.set_output_type(Target::ACTION);
dep.SetToolchain(setup.toolchain());
dep.OnResolved();
ASSERT_TRUE(dep.OnResolved(&err));
Target datadep(setup.settings(), Label(SourceDir("//foo/"), "datadep"));
datadep.set_output_type(Target::ACTION);
datadep.SetToolchain(setup.toolchain());
datadep.OnResolved();
ASSERT_TRUE(datadep.OnResolved(&err));
Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
target.set_output_type(Target::ACTION_FOREACH);
......@@ -182,7 +190,7 @@ TEST(NinjaActionTargetWriter, ForEach) {
target.inputs().push_back(SourceFile("//foo/included.txt"));
target.SetToolchain(setup.toolchain());
target.OnResolved();
ASSERT_TRUE(target.OnResolved(&err));
// Posix.
{
......@@ -268,6 +276,8 @@ TEST(NinjaActionTargetWriter, ForEach) {
TEST(NinjaActionTargetWriter, ForEachWithDepfile) {
TestWithScope setup;
Err err;
setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
target.set_output_type(Target::ACTION_FOREACH);
......@@ -278,10 +288,9 @@ TEST(NinjaActionTargetWriter, ForEachWithDepfile) {
target.action_values().set_script(SourceFile("//foo/script.py"));
target.SetToolchain(setup.toolchain());
target.OnResolved();
ASSERT_TRUE(target.OnResolved(&err));
SubstitutionPattern depfile;
Err err;
ASSERT_TRUE(
depfile.Parse("//out/Debug/gen/{{source_name_part}}.d", NULL, &err));
target.action_values().set_depfile(depfile);
......
......@@ -11,6 +11,8 @@
TEST(NinjaBinaryTargetWriter, SourceSet) {
TestWithScope setup;
Err err;
setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
setup.settings()->set_target_os(Settings::WIN);
......@@ -23,7 +25,7 @@ TEST(NinjaBinaryTargetWriter, SourceSet) {
target.sources().push_back(SourceFile("//foo/input3.o"));
target.sources().push_back(SourceFile("//foo/input4.obj"));
target.SetToolchain(setup.toolchain());
target.OnResolved();
ASSERT_TRUE(target.OnResolved(&err));
// Source set itself.
{
......@@ -57,7 +59,7 @@ TEST(NinjaBinaryTargetWriter, SourceSet) {
shlib_target.set_output_type(Target::SHARED_LIBRARY);
shlib_target.deps().push_back(LabelTargetPair(&target));
shlib_target.SetToolchain(setup.toolchain());
shlib_target.OnResolved();
ASSERT_TRUE(shlib_target.OnResolved(&err));
{
std::ostringstream out;
......@@ -94,7 +96,7 @@ TEST(NinjaBinaryTargetWriter, SourceSet) {
stlib_target.set_output_type(Target::STATIC_LIBRARY);
stlib_target.deps().push_back(LabelTargetPair(&target));
stlib_target.SetToolchain(setup.toolchain());
stlib_target.OnResolved();
ASSERT_TRUE(stlib_target.OnResolved(&err));
{
std::ostringstream out;
......@@ -129,6 +131,8 @@ TEST(NinjaBinaryTargetWriter, SourceSet) {
// are applied.
TEST(NinjaBinaryTargetWriter, ProductExtensionAndInputDeps) {
TestWithScope setup;
Err err;
setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
setup.settings()->set_target_os(Settings::LINUX);
......@@ -136,7 +140,7 @@ TEST(NinjaBinaryTargetWriter, ProductExtensionAndInputDeps) {
Target action(setup.settings(), Label(SourceDir("//foo/"), "action"));
action.set_output_type(Target::ACTION_FOREACH);
action.SetToolchain(setup.toolchain());
action.OnResolved();
ASSERT_TRUE(action.OnResolved(&err));
// A shared library w/ the product_extension set to a custom value.
Target target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
......@@ -146,7 +150,7 @@ TEST(NinjaBinaryTargetWriter, ProductExtensionAndInputDeps) {
target.sources().push_back(SourceFile("//foo/input2.cc"));
target.deps().push_back(LabelTargetPair(&action));
target.SetToolchain(setup.toolchain());
target.OnResolved();
ASSERT_TRUE(target.OnResolved(&err));
std::ostringstream out;
NinjaBinaryTargetWriter writer(&target, out);
......@@ -185,6 +189,8 @@ TEST(NinjaBinaryTargetWriter, ProductExtensionAndInputDeps) {
TEST(NinjaBinaryTargetWriter, EmptyProductExtension) {
TestWithScope setup;
Err err;
setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
setup.settings()->set_target_os(Settings::LINUX);
......@@ -197,7 +203,7 @@ TEST(NinjaBinaryTargetWriter, EmptyProductExtension) {
target.sources().push_back(SourceFile("//foo/input2.cc"));
target.SetToolchain(setup.toolchain());
target.OnResolved();
ASSERT_TRUE(target.OnResolved(&err));
std::ostringstream out;
NinjaBinaryTargetWriter writer(&target, out);
......
......@@ -13,6 +13,7 @@
// Tests mutliple files with an output pattern and no toolchain dependency.
TEST(NinjaCopyTargetWriter, Run) {
TestWithScope setup;
Err err;
setup.settings()->set_target_os(Settings::LINUX);
setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
......@@ -26,7 +27,7 @@ TEST(NinjaCopyTargetWriter, Run) {
SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
target.SetToolchain(setup.toolchain());
target.OnResolved();
ASSERT_TRUE(target.OnResolved(&err));
std::ostringstream out;
NinjaCopyTargetWriter writer(&target, out);
......@@ -44,6 +45,7 @@ TEST(NinjaCopyTargetWriter, Run) {
// Tests a single file with no output pattern.
TEST(NinjaCopyTargetWriter, ToolchainDeps) {
TestWithScope setup;
Err err;
setup.settings()->set_target_os(Settings::LINUX);
setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
......@@ -56,7 +58,7 @@ TEST(NinjaCopyTargetWriter, ToolchainDeps) {
SubstitutionList::MakeForTest("//out/Debug/output.out");
target.SetToolchain(setup.toolchain());
target.OnResolved();
ASSERT_TRUE(target.OnResolved(&err));
std::ostringstream out;
NinjaCopyTargetWriter writer(&target, out);
......
......@@ -32,6 +32,7 @@ class TestingNinjaTargetWriter : public NinjaTargetWriter {
TEST(NinjaTargetWriter, WriteInputDepsStampAndGetDep) {
TestWithScope setup;
Err err;
// Make a base target that's a hard dep (action).
Target base_target(setup.settings(), Label(SourceDir("//foo/"), "base"));
......@@ -57,9 +58,9 @@ TEST(NinjaTargetWriter, WriteInputDepsStampAndGetDep) {
action.sources().push_back(SourceFile("//foo/action_source.txt"));
action.deps().push_back(LabelTargetPair(&target));
base_target.OnResolved();
target.OnResolved();
action.OnResolved();
ASSERT_TRUE(base_target.OnResolved(&err));
ASSERT_TRUE(target.OnResolved(&err));
ASSERT_TRUE(action.OnResolved(&err));
// Input deps for the base (should be only the script itself).
{
......@@ -105,6 +106,7 @@ TEST(NinjaTargetWriter, WriteInputDepsStampAndGetDep) {
// Tests WriteInputDepsStampAndGetDep when toolchain deps are present.
TEST(NinjaTargetWriter, WriteInputDepsStampAndGetDepWithToolchainDeps) {
TestWithScope setup;
Err err;
// Toolchain dependency. Here we make a target in the same toolchain for
// simplicity, but in real life (using the Builder) this would be rejected
......@@ -114,14 +116,14 @@ TEST(NinjaTargetWriter, WriteInputDepsStampAndGetDepWithToolchainDeps) {
Label(SourceDir("//foo/"), "setup"));
toolchain_dep_target.set_output_type(Target::ACTION);
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));
// Make a binary target
Target target(setup.settings(), Label(SourceDir("//foo/"), "target"));
target.set_output_type(Target::EXECUTABLE);
target.SetToolchain(setup.toolchain());
target.OnResolved();
ASSERT_TRUE(target.OnResolved(&err));
std::ostringstream stream;
TestingNinjaTargetWriter writer(&target, setup.toolchain(), stream);
......
......@@ -173,11 +173,12 @@ TEST(SubstutitionWriter, SourceSubstitutions) {
TEST(SubstitutionWriter, TargetSubstitutions) {
TestWithScope setup;
Err err;
Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz"));
target.set_output_type(Target::STATIC_LIBRARY);
target.SetToolchain(setup.toolchain());
target.OnResolved();
ASSERT_TRUE(target.OnResolved(&err));
std::string result;
EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
......@@ -207,11 +208,12 @@ TEST(SubstitutionWriter, TargetSubstitutions) {
TEST(SubstitutionWriter, CompilerSubstitutions) {
TestWithScope setup;
Err err;
Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz"));
target.set_output_type(Target::STATIC_LIBRARY);
target.SetToolchain(setup.toolchain());
target.OnResolved();
ASSERT_TRUE(target.OnResolved(&err));
// 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.
......@@ -227,11 +229,12 @@ TEST(SubstitutionWriter, CompilerSubstitutions) {
TEST(SubstitutionWriter, LinkerSubstitutions) {
TestWithScope setup;
Err err;
Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz"));
target.set_output_type(Target::SHARED_LIBRARY);
target.SetToolchain(setup.toolchain());
target.OnResolved();
ASSERT_TRUE(target.OnResolved(&err));
const Tool* tool = setup.toolchain()->GetToolForTargetFinalOutput(&target);
......@@ -246,7 +249,6 @@ TEST(SubstitutionWriter, LinkerSubstitutions) {
// Test that we handle paths that end up in the root build dir properly
// (no leading "./" or "/").
Err err;
SubstitutionPattern pattern;
ASSERT_TRUE(
pattern.Parse("{{root_out_dir}}/{{target_output_name}}.so", NULL, &err));
......
......@@ -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
Target::Target(const Settings* settings, const Label& label)
......@@ -46,6 +73,7 @@ Target::Target(const Settings* settings, const Label& label)
output_type_(UNKNOWN),
all_headers_public_(true),
check_includes_(true),
testonly_(false),
hard_dep_(false),
toolchain_(NULL) {
}
......@@ -87,22 +115,11 @@ const Target* Target::AsTarget() const {
return this;
}
void Target::OnResolved() {
bool Target::OnResolved(Err* err) {
DCHECK(output_type_ != UNKNOWN);
DCHECK(toolchain_) << "Toolchain should have been set before resolving.";
// 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.
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();
}
}
ExpandGroups();
// Copy our own dependent configs to the list of configs applying to us.
configs_.Append(all_dependent_configs_.begin(), all_dependent_configs_.end());
......@@ -130,6 +147,13 @@ void Target::OnResolved() {
PullRecursiveHardDeps();
FillOutputFiles();
if (!CheckVisibility(err))
return false;
if (!CheckTestonly(err))
return false;
return true;
}
bool Target::IsLinkable() const {
......@@ -183,6 +207,19 @@ bool Target::SetToolchain(const Toolchain* toolchain, Err* err) {
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() {
// Gather info from our dependents we need.
for (size_t dep_i = 0; dep_i < deps_.size(); dep_i++) {
......@@ -306,3 +343,48 @@ void Target::FillOutputFiles() {
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 {
// Item overrides.
virtual Target* AsTarget() OVERRIDE;
virtual const Target* AsTarget() const OVERRIDE;
virtual void OnResolved() OVERRIDE;
virtual bool OnResolved(Err* err) OVERRIDE;
OutputType output_type() const { return output_type_; }
void set_output_type(OutputType t) { output_type_ = t; }
......@@ -95,6 +95,9 @@ class Target : public Item {
bool check_includes() const { return check_includes_; }
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.
const FileList& inputs() const { return inputs_; }
FileList& inputs() { return inputs_; }
......@@ -210,6 +213,8 @@ class Target : public Item {
}
private:
void ExpandGroups();
// Pulls necessary information from dependencies to this one when all
// dependencies have been resolved.
void PullDependentTargetInfo();
......@@ -222,6 +227,10 @@ class Target : public Item {
// Fills the link and dependency output files when a target is resolved.
void FillOutputFiles();
// Validates the given thing when a target is resolved.
bool CheckVisibility(Err* err) const;
bool CheckTestonly(Err* err) const;
OutputType output_type_;
std::string output_name_;
std::string output_extension_;
......@@ -230,6 +239,7 @@ class Target : public Item {
bool all_headers_public_;
FileList public_headers_;
bool check_includes_;
bool testonly_;
FileList inputs_;
FileList data_;
......
......@@ -48,6 +48,10 @@ void TargetGenerator::Run() {
if (err_->has_error())
return;
FillTestonly();
if (err_->has_error())
return;
if (!Visibility::FillItemVisibility(target_, scope_, err_))
return;
......@@ -217,6 +221,15 @@ void TargetGenerator::FillDependencies() {
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) {
const Value* value = scope_->GetValue(variables::kOutputs, true);
if (!value)
......
......@@ -70,6 +70,7 @@ class TargetGenerator {
void FillDependentConfigs(); // Includes all types of dependent configs.
void FillData();
void FillDependencies(); // Includes data dependencies.
void FillTestonly();
// Reads configs/deps from the given var name, and uses the given setting on
// the target to save them.
......
This diff is collapsed.
......@@ -795,6 +795,28 @@ const char kSources_Help[] =
"\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_HelpShort[] =
"visibility: [label list] A list of labels that can depend on a target.";
......@@ -915,6 +937,7 @@ const VariableInfoMap& GetTargetVariables() {
INSERT_VARIABLE(Public)
INSERT_VARIABLE(Script)
INSERT_VARIABLE(Sources)
INSERT_VARIABLE(Testonly)
INSERT_VARIABLE(Visibility)
}
return info_map;
......
......@@ -175,6 +175,10 @@ extern const char kSources[];
extern const char kSources_HelpShort[];
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_HelpShort[];
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