From 8e51f9a29aba91118c8657163850fef33c9d53e9 Mon Sep 17 00:00:00 2001 From: Chris Novakovic Date: Mon, 23 Feb 2026 17:41:07 +0000 Subject: [PATCH 1/3] Add `query runtimedeps` sub-command `plz query runtimedeps [TARGET]...` outputs the list of transitive run-time dependencies for the given (binary) `TARGET`s. --- src/please.go | 10 +++ src/query/runtime_deps.go | 28 ++++++++ src/query/runtime_deps_test.go | 118 +++++++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 src/query/runtime_deps.go create mode 100644 src/query/runtime_deps_test.go diff --git a/src/please.go b/src/please.go index e262318e1..26e16a02e 100644 --- a/src/please.go +++ b/src/please.go @@ -368,6 +368,11 @@ var opts struct { Targets []core.BuildLabel `positional-arg-name:"targets" description:"Targets to query" required:"true"` } `positional-args:"true" required:"true"` } `command:"revdeps" alias:"reverseDeps" description:"Queries all the reverse dependencies of a target."` + RuntimeDeps struct { + Args struct { + Targets []core.BuildLabel `positional-arg-name:"targets" description:"Targets to query" required:"true"` + } `positional-args:"true" required:"true"` + } `command:"runtimedeps" description:"Queries all the run-time dependencies of a target."` SomePath struct { Except []core.BuildLabel `long:"except" description:"Targets to exclude from path calculation"` Hidden bool `long:"hidden" description:"Show hidden targets as well"` @@ -792,6 +797,11 @@ var buildFunctions = map[string]func() int{ query.ReverseDeps(state, state.ExpandLabels(labels), opts.Query.ReverseDeps.Level, opts.Query.ReverseDeps.Hidden) }) }, + "query.runtimedeps": func() int { + return runQuery(true, opts.Query.RuntimeDeps.Args.Targets, func(state *core.BuildState) { + query.RuntimeDeps(os.Stdout, state, state.ExpandOriginalLabels()) + }) + }, "query.somepath": func() int { a := plz.ReadStdinLabels([]core.BuildLabel{opts.Query.SomePath.Args.Target1}) b := plz.ReadStdinLabels([]core.BuildLabel{opts.Query.SomePath.Args.Target2}) diff --git a/src/query/runtime_deps.go b/src/query/runtime_deps.go new file mode 100644 index 000000000..bc3a92d6d --- /dev/null +++ b/src/query/runtime_deps.go @@ -0,0 +1,28 @@ +package query + +import ( + "fmt" + "io" + + "github.com/thought-machine/please/src/core" +) + +// RuntimeDeps prints all transitive run-time dependencies of a set of targets. +func RuntimeDeps(out io.Writer, state *core.BuildState, labels []core.BuildLabel) { + done := map[core.BuildLabel]bool{} + for _, label := range labels { + runtimeDeps(out, state, state.Graph.TargetOrDie(label), done) + } +} + +// runtimeDeps prints all transitive run-time dependencies of a target, except for those that have +// already been printed (which are given by the keys of "done"). +func runtimeDeps(out io.Writer, state *core.BuildState, target *core.BuildTarget, done map[core.BuildLabel]bool) { + for l := range target.IterAllRuntimeDependencies(state.Graph) { + if done[l] { + continue + } + done[l] = true + fmt.Fprintf(out, "%s\n", l.String()) + } +} diff --git a/src/query/runtime_deps_test.go b/src/query/runtime_deps_test.go new file mode 100644 index 000000000..16920e451 --- /dev/null +++ b/src/query/runtime_deps_test.go @@ -0,0 +1,118 @@ +package query + +import ( + "bytes" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/thought-machine/please/src/core" +) + +func TestRuntimeDeps(t *testing.T) { + var addTarget func(string, bool, bool, []*core.BuildTarget, []*core.BuildTarget, []*core.BuildTarget) *core.BuildTarget + + for _, test := range []struct{ + Description string + GraphFunc func() *core.BuildTarget + Expected []string + }{ + { + Description: "Explicit run-time deps", + GraphFunc: func() *core.BuildTarget { + run1 := addTarget("runtime_dep_1", false, false, nil, nil, nil) + run2 := addTarget("runtime_dep_2", false, false, nil, nil, []*core.BuildTarget{run1}) + root := addTarget("root", false, false, nil, nil, []*core.BuildTarget{run2}) + return root + }, + Expected: []string{ + "//test:runtime_dep_1", + "//test:runtime_dep_2", + }, + }, + { + Description: "Run-time deps from srcs but not deps", + GraphFunc: func() *core.BuildTarget { + run1 := addTarget("runtime_dep_1", false, false, nil, nil, nil) + run2 := addTarget("runtime_dep_2", false, false, nil, nil, []*core.BuildTarget{run1}) + src := addTarget("src", false, false, nil, nil, []*core.BuildTarget{run2}) + run3 := addTarget("runtime_dep_3", false, false, nil, nil, nil) + run4 := addTarget("runtime_dep_4", false, false, nil, nil, []*core.BuildTarget{run3}) + dep := addTarget("dep", false, false, nil, nil, []*core.BuildTarget{run4}) + root := addTarget("root", true, false, []*core.BuildTarget{src}, []*core.BuildTarget{dep}, nil) + return root + }, + Expected: []string{ + "//test:runtime_dep_1", + "//test:runtime_dep_2", + }, + }, + { + Description: "Run-time deps from deps but not srcs", + GraphFunc: func() *core.BuildTarget { + run1 := addTarget("runtime_dep_1", false, false, nil, nil, nil) + run2 := addTarget("runtime_dep_2", false, false, nil, nil, []*core.BuildTarget{run1}) + src := addTarget("src", false, false, nil, nil, []*core.BuildTarget{run2}) + run3 := addTarget("runtime_dep_3", false, false, nil, nil, nil) + run4 := addTarget("runtime_dep_4", false, false, nil, nil, []*core.BuildTarget{run3}) + dep := addTarget("dep", false, false, nil, nil, []*core.BuildTarget{run4}) + root := addTarget("root", false, true, []*core.BuildTarget{src}, []*core.BuildTarget{dep}, nil) + return root + }, + Expected: []string{ + "//test:runtime_dep_3", + "//test:runtime_dep_4", + }, + }, + { + Description: "Run-time deps from both srcs and deps", + GraphFunc: func() *core.BuildTarget { + run1 := addTarget("runtime_dep_1", false, false, nil, nil, nil) + run2 := addTarget("runtime_dep_2", false, false, nil, nil, []*core.BuildTarget{run1}) + src := addTarget("src", false, false, nil, nil, []*core.BuildTarget{run2}) + run3 := addTarget("runtime_dep_3", false, false, nil, nil, nil) + run4 := addTarget("runtime_dep_4", false, false, nil, nil, []*core.BuildTarget{run3}) + dep := addTarget("dep", false, false, nil, nil, []*core.BuildTarget{run4}) + root := addTarget("root", true, true, []*core.BuildTarget{src}, []*core.BuildTarget{dep}, nil) + return root + }, + Expected: []string{ + "//test:runtime_dep_1", + "//test:runtime_dep_2", + "//test:runtime_dep_3", + "//test:runtime_dep_4", + }, + }, + } { + t.Run(test.Description, func(t *testing.T) { + state := core.NewDefaultBuildState() + testPkg := core.NewPackage("test") + + addTarget = func(name string, fromSrcs, fromDeps bool, srcs, deps, runtimeDeps []*core.BuildTarget) *core.BuildTarget { + t := addNewTarget(state.Graph, testPkg, name, nil) + t.RuntimeDependenciesFromSources = fromSrcs + t.RuntimeDependenciesFromDependencies = fromDeps + for _, src := range srcs { + t.AddSource(src.Label) + } + for _, dep := range deps { + t.AddDependency(dep.Label) + } + if runtimeDeps != nil { + t.IsBinary = true + for _, runtimeDep := range runtimeDeps { + t.AddMaybeExportedDependency(runtimeDep.Label, false, false, false, true) + } + } + return t + } + + root := test.GraphFunc() + + var buf bytes.Buffer + RuntimeDeps(&buf, state, []core.BuildLabel{root.Label}) + assert.ElementsMatch(t, test.Expected, strings.Split(strings.TrimSuffix(buf.String(), "\n"), "\n")) + }) + } +} From 3950d3ce489cf310bac03b782407bc93bc333780 Mon Sep 17 00:00:00 2001 From: Chris Novakovic Date: Mon, 23 Feb 2026 17:49:01 +0000 Subject: [PATCH 2/3] Linter --- src/query/runtime_deps_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/query/runtime_deps_test.go b/src/query/runtime_deps_test.go index 16920e451..d4b0303f8 100644 --- a/src/query/runtime_deps_test.go +++ b/src/query/runtime_deps_test.go @@ -13,7 +13,7 @@ import ( func TestRuntimeDeps(t *testing.T) { var addTarget func(string, bool, bool, []*core.BuildTarget, []*core.BuildTarget, []*core.BuildTarget) *core.BuildTarget - for _, test := range []struct{ + for _, test := range []struct { Description string GraphFunc func() *core.BuildTarget Expected []string From e2618f02ed9ad09ef2f836e9a533c11eca7b9618 Mon Sep 17 00:00:00 2001 From: Chris Novakovic Date: Mon, 23 Feb 2026 17:50:45 +0000 Subject: [PATCH 3/3] More linter --- src/query/runtime_deps_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/query/runtime_deps_test.go b/src/query/runtime_deps_test.go index d4b0303f8..9a0ca1154 100644 --- a/src/query/runtime_deps_test.go +++ b/src/query/runtime_deps_test.go @@ -20,7 +20,7 @@ func TestRuntimeDeps(t *testing.T) { }{ { Description: "Explicit run-time deps", - GraphFunc: func() *core.BuildTarget { + GraphFunc: func() *core.BuildTarget { run1 := addTarget("runtime_dep_1", false, false, nil, nil, nil) run2 := addTarget("runtime_dep_2", false, false, nil, nil, []*core.BuildTarget{run1}) root := addTarget("root", false, false, nil, nil, []*core.BuildTarget{run2}) @@ -33,7 +33,7 @@ func TestRuntimeDeps(t *testing.T) { }, { Description: "Run-time deps from srcs but not deps", - GraphFunc: func() *core.BuildTarget { + GraphFunc: func() *core.BuildTarget { run1 := addTarget("runtime_dep_1", false, false, nil, nil, nil) run2 := addTarget("runtime_dep_2", false, false, nil, nil, []*core.BuildTarget{run1}) src := addTarget("src", false, false, nil, nil, []*core.BuildTarget{run2}) @@ -50,7 +50,7 @@ func TestRuntimeDeps(t *testing.T) { }, { Description: "Run-time deps from deps but not srcs", - GraphFunc: func() *core.BuildTarget { + GraphFunc: func() *core.BuildTarget { run1 := addTarget("runtime_dep_1", false, false, nil, nil, nil) run2 := addTarget("runtime_dep_2", false, false, nil, nil, []*core.BuildTarget{run1}) src := addTarget("src", false, false, nil, nil, []*core.BuildTarget{run2}) @@ -67,7 +67,7 @@ func TestRuntimeDeps(t *testing.T) { }, { Description: "Run-time deps from both srcs and deps", - GraphFunc: func() *core.BuildTarget { + GraphFunc: func() *core.BuildTarget { run1 := addTarget("runtime_dep_1", false, false, nil, nil, nil) run2 := addTarget("runtime_dep_2", false, false, nil, nil, []*core.BuildTarget{run1}) src := addTarget("src", false, false, nil, nil, []*core.BuildTarget{run2})