Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

keep going flag #2932

Merged
merged 3 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/build/build_step.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ func Build(state *core.BuildState, target *core.BuildTarget, remote bool) {
log.Errorf("Failed to remove outputs for %s: %s", target.Label, err)
}
target.SetState(core.Failed)
target.FinishBuild()
return
}
if remote {
Expand Down
29 changes: 16 additions & 13 deletions src/core/build_target.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,19 +309,20 @@ type BuildTargetState uint8

// The available states for a target.
const (
Inactive BuildTargetState = iota // Target isn't used in current build
Semiactive // Target would be active if we needed a build
Active // Target is going to be used in current build
Pending // Target is ready to be built but not yet started.
Building // Target is currently being built
Stopped // We stopped building the target because we'd gone as far as needed.
Built // Target has been successfully built
Cached // Target has been retrieved from the cache
Unchanged // Target has been built but hasn't changed since last build
Reused // Outputs of previous build have been reused.
BuiltRemotely // Target has been built but outputs are not necessarily local.
ReusedRemotely // Outputs of previous remote action have been reused.
Failed // Target failed for some reason
Inactive BuildTargetState = iota // Target isn't used in current build
Semiactive // Target would be active if we needed a build
Active // Target is going to be used in current build
Pending // Target is ready to be built but not yet started.
Building // Target is currently being built
Stopped // We stopped building the target because we'd gone as far as needed.
Built // Target has been successfully built
Cached // Target has been retrieved from the cache
Unchanged // Target has been built but hasn't changed since last build
Reused // Outputs of previous build have been reused.
BuiltRemotely // Target has been built but outputs are not necessarily local.
ReusedRemotely // Outputs of previous remote action have been reused.
DependencyFailed // At least one dependency of this target has failed.
Failed // Target failed for some reason
)

// String implements the fmt.Stringer interface.
Expand All @@ -346,6 +347,8 @@ func (s BuildTargetState) String() string {
return "Unchanged"
} else if s == Reused {
return "Reused"
} else if s == DependencyFailed {
return "Dependency Failed"
} else if s == Failed {
return "Failed"
} else if s == BuiltRemotely {
Expand Down
9 changes: 9 additions & 0 deletions src/core/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ type BuildState struct {
Arch cli.Arch
// Aggregated coverage for this run
Coverage TestCoverage
// True if we want to keep going on build failures and not exit early on the first error encountered
KeepGoing bool
// True if we require rule hashes to be correctly verified (usually the case).
VerifyHashes bool
// True if tests should calculate coverage metrics
Expand Down Expand Up @@ -1141,6 +1143,13 @@ func (state *BuildState) queueTargetAsync(target *BuildTarget, forceBuild, build
if building {
for _, t := range target.Dependencies() {
t.WaitForBuild()
if t.State() >= DependencyFailed { // Either the target failed or its dependencies failed
// Give up and set the original target as dependency failed
target.SetState(DependencyFailed)
state.LogBuildResult(target, TargetBuilt, "Dependency failed")
target.FinishBuild()
return
}
}
}
if !called.Value() {
Expand Down
4 changes: 3 additions & 1 deletion src/output/targets.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@ func (bt *buildingTargets) handleOutput(result *core.BuildResult) {
if result.Status != core.TargetTestFailed {
// Reset colour so the entire compiler error output doesn't appear red.
log.Errorf("%s failed:\x1b[0m\n%s", label, shortError(result.Err))
bt.state.Stop()
if !bt.state.KeepGoing {
bt.state.Stop()
}
} else if msg := shortError(result.Err); msg != "" {
log.Errorf("%s failed: %s", result.Label, msg)
} else {
Expand Down
2 changes: 2 additions & 0 deletions src/please.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ var opts struct {
KeepWorkdirs bool `long:"keep_workdirs" description:"Don't clean directories in plz-out/tmp after successfully building targets."`
HTTPProxy cli.URL `long:"http_proxy" env:"HTTP_PROXY" description:"HTTP proxy to use for downloads"`
Debug bool `long:"debug" description:"When enabled, Please will enter into an interactive debugger when breakpoint() is called during parsing."`
KeepGoing bool `long:"keep_going" description:"Continue as much as possible after an error. While the target that failed and those that depend on it cannot be build, other prerequisites of these targets can be."`
} `group:"Options that enable / disable certain behaviors"`

HelpFlags struct {
Expand Down Expand Up @@ -1084,6 +1085,7 @@ func Please(targets []core.BuildLabel, config *core.Configuration, shouldBuild,
config.Build.Config = "dbg"
}
state := core.NewBuildState(config)
state.KeepGoing = opts.BehaviorFlags.KeepGoing
state.VerifyHashes = !opts.BehaviorFlags.NoHashVerification
// Only one of these two can be passed
state.NumTestRuns = uint16(opts.Test.NumRuns)
Expand Down
2 changes: 1 addition & 1 deletion test/build_defs/test.build_defs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def please_repo_e2e_test(
test_cmd += [f'_STR="$(cat {o})" _SUBSTR="{c}" && if [ "${_STR##*$_SUBSTR*}" ]; then echo "$_STR"; exit 1; fi' for o, c in expect_output_contains.items()]

if expect_output_doesnt_contain:
test_cmd += [f'_STR="$(cat {o})" _SUBSTR="{c}" && if [ -z "${_STR##*$_SUBSTR*}" ]; then echo "$_STR"; exit 1; fi' for o, c in expect_output_contains.items()]
test_cmd += [f'_STR="$(cat {o})" _SUBSTR="{c}" && if [ -z "${_STR##*$_SUBSTR*}" ]; then echo "$_STR"; exit 1; fi' for o, c in expect_output_doesnt_contain.items()]

test_cmd = ' && '.join(test_cmd)

Expand Down
27 changes: 27 additions & 0 deletions test/keep_going/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
subinclude("//test/build_defs")

please_repo_e2e_test(
name = "plz_build_all",
expected_failure = True,
goddenrich marked this conversation as resolved.
Show resolved Hide resolved
expected_output = {
"plz-out/gen/package/pass": "",
"plz-out/gen/package/dep_pass": "",
"plz-out/gen/package/fail_test_pass_dep": "",
},
plz_command = "plz build --keep_going //package:all",
repo = "test_repo",
)

please_repo_e2e_test(
name = "plz_test_all",
expect_output_contains = {
"plz-out/log/test_results.xml": "fail_test_pass_dep",
},
expect_output_doesnt_contain = {
"plz-out/log/test_results.xml": "fail_test_fail_dep",
},
expected_failure = True,
plz_command = "plz test --keep_going //package:all",
repo = "test_repo",
)

Empty file.
56 changes: 56 additions & 0 deletions test/keep_going/test_repo/package/BUILD_FILE
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
build_rule(
name = "fail",
cmd = "exit 1",
outs = ["fail"],
)

build_rule(
name = "fail2",
cmd = "exit 1",
outs = ["fail2"],
)

build_rule(
name = "pass",
cmd = "touch $OUTS",
outs = ["pass"],
)

build_rule(
name = "dep_pass",
deps = [":pass"],
cmd = "touch $OUTS",
outs = ["dep_pass"],
)

build_rule(
name = "dep_fail",
deps = [":fail"],
cmd = "touch $OUTS",
outs = ["dep_fail"],
)

build_rule(
name = "dep_dep_fail",
deps = [":dep_fail"],
cmd = "touch $OUTS",
outs = ["dep_dep_fail"],
)

build_rule(
test = True,
name = "fail_test_pass_dep",
cmd = "touch $OUTS",
test_cmd = "exit 1",
outs = ["fail_test_pass_dep"],
deps = [":pass"],
)

build_rule(
test = True,
name = "pass_test_fail_dep",
cmd = "touch $OUTS",
test_cmd = "exit 0",
outs = ["pass_test_fail_deps"],
deps = [":fail"],
)
Loading