xref: /aosp_15_r20/build/soong/android/makevars.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1// Copyright 2016 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package android
16
17import (
18	"bytes"
19	"cmp"
20	"fmt"
21	"path/filepath"
22	"runtime"
23	"slices"
24	"sort"
25	"strings"
26
27	"github.com/google/blueprint"
28	"github.com/google/blueprint/pathtools"
29	"github.com/google/blueprint/proptools"
30)
31
32func init() {
33	RegisterMakeVarsProvider(pctx, androidMakeVarsProvider)
34}
35
36func androidMakeVarsProvider(ctx MakeVarsContext) {
37	ctx.Strict("MIN_SUPPORTED_SDK_VERSION", ctx.Config().MinSupportedSdkVersion().String())
38}
39
40// /////////////////////////////////////////////////////////////////////////////
41
42// BaseMakeVarsContext contains the common functions for other packages to use
43// to declare make variables
44type BaseMakeVarsContext interface {
45	Config() Config
46	DeviceConfig() DeviceConfig
47	AddNinjaFileDeps(deps ...string)
48
49	Failed() bool
50
51	// These are equivalent to Strict and Check, but do not attempt to
52	// evaluate the values before writing them to the Makefile. They can
53	// be used when all ninja variables have already been evaluated through
54	// Eval().
55	StrictRaw(name, value string)
56	CheckRaw(name, value string)
57
58	// GlobWithDeps returns a list of files that match the specified pattern but do not match any
59	// of the patterns in excludes.  It also adds efficient dependencies to rerun the primary
60	// builder whenever a file matching the pattern as added or removed, without rerunning if a
61	// file that does not match the pattern is added to a searched directory.
62	GlobWithDeps(pattern string, excludes []string) ([]string, error)
63
64	// Phony creates a phony rule in Make, which will allow additional DistForGoal
65	// dependencies to be added to it.  Phony can be called on the same name multiple
66	// times to add additional dependencies.
67	Phony(names string, deps ...Path)
68
69	// DistForGoal creates a rule to copy one or more Paths to the artifacts
70	// directory on the build server when the specified goal is built.
71	DistForGoal(goal string, paths ...Path)
72
73	// DistForGoalWithFilename creates a rule to copy a Path to the artifacts
74	// directory on the build server with the given filename when the specified
75	// goal is built.
76	DistForGoalWithFilename(goal string, path Path, filename string)
77
78	// DistForGoals creates a rule to copy one or more Paths to the artifacts
79	// directory on the build server when any of the specified goals are built.
80	DistForGoals(goals []string, paths ...Path)
81
82	// DistForGoalsWithFilename creates a rule to copy a Path to the artifacts
83	// directory on the build server with the given filename when any of the
84	// specified goals are built.
85	DistForGoalsWithFilename(goals []string, path Path, filename string)
86}
87
88// MakeVarsContext contains the set of functions available for MakeVarsProvider
89// and SingletonMakeVarsProvider implementations.
90type MakeVarsContext interface {
91	BaseMakeVarsContext
92
93	ModuleName(module blueprint.Module) string
94	ModuleDir(module blueprint.Module) string
95	ModuleSubDir(module blueprint.Module) string
96	ModuleType(module blueprint.Module) string
97	otherModuleProvider(module blueprint.Module, key blueprint.AnyProviderKey) (any, bool)
98	BlueprintFile(module blueprint.Module) string
99
100	ModuleErrorf(module blueprint.Module, format string, args ...interface{})
101	OtherModulePropertyErrorf(module Module, property, format string, args ...interface{})
102	Errorf(format string, args ...interface{})
103
104	VisitAllModules(visit func(Module))
105	VisitAllModulesIf(pred func(Module) bool, visit func(Module))
106
107	// Verify the make variable matches the Soong version, fail the build
108	// if it does not. If the make variable is empty, just set it.
109	Strict(name, ninjaStr string)
110	// Check to see if the make variable matches the Soong version, warn if
111	// it does not. If the make variable is empty, just set it.
112	Check(name, ninjaStr string)
113
114	// These are equivalent to the above, but sort the make and soong
115	// variables before comparing them. They also show the unique entries
116	// in each list when displaying the difference, instead of the entire
117	// string.
118	StrictSorted(name, ninjaStr string)
119	CheckSorted(name, ninjaStr string)
120
121	// Evaluates a ninja string and returns the result. Used if more
122	// complicated modification needs to happen before giving it to Make.
123	Eval(ninjaStr string) (string, error)
124}
125
126// MakeVarsModuleContext contains the set of functions available for modules
127// implementing the ModuleMakeVarsProvider interface.
128type MakeVarsModuleContext interface {
129	BaseMakeVarsContext
130}
131
132var _ PathContext = MakeVarsContext(nil)
133
134type MakeVarsProvider func(ctx MakeVarsContext)
135
136func RegisterMakeVarsProvider(pctx PackageContext, provider MakeVarsProvider) {
137	makeVarsInitProviders = append(makeVarsInitProviders, makeVarsProvider{pctx, provider})
138}
139
140// SingletonMakeVarsProvider is a Singleton with an extra method to provide extra values to be exported to Make.
141type SingletonMakeVarsProvider interface {
142	// MakeVars uses a MakeVarsContext to provide extra values to be exported to Make.
143	MakeVars(ctx MakeVarsContext)
144}
145
146var singletonMakeVarsProvidersKey = NewOnceKey("singletonMakeVarsProvidersKey")
147
148func getSingletonMakevarsProviders(config Config) *[]makeVarsProvider {
149	return config.Once(singletonMakeVarsProvidersKey, func() interface{} {
150		return &[]makeVarsProvider{}
151	}).(*[]makeVarsProvider)
152}
153
154// registerSingletonMakeVarsProvider adds a singleton that implements SingletonMakeVarsProvider to
155// the list of MakeVarsProviders to run.
156func registerSingletonMakeVarsProvider(config Config, singleton SingletonMakeVarsProvider) {
157	// Singletons are registered on the Context and may be different between different Contexts,
158	// for example when running multiple tests.  Store the SingletonMakeVarsProviders in the
159	// Config so they are attached to the Context.
160	singletonMakeVarsProviders := getSingletonMakevarsProviders(config)
161
162	*singletonMakeVarsProviders = append(*singletonMakeVarsProviders,
163		makeVarsProvider{pctx, singletonMakeVarsProviderAdapter(singleton)})
164}
165
166// singletonMakeVarsProviderAdapter converts a SingletonMakeVarsProvider to a MakeVarsProvider.
167func singletonMakeVarsProviderAdapter(singleton SingletonMakeVarsProvider) MakeVarsProvider {
168	return func(ctx MakeVarsContext) { singleton.MakeVars(ctx) }
169}
170
171// ModuleMakeVarsProvider is a Module with an extra method to provide extra values to be exported to Make.
172type ModuleMakeVarsProvider interface {
173	Module
174
175	// MakeVars uses a MakeVarsModuleContext to provide extra values to be exported to Make.
176	MakeVars(ctx MakeVarsModuleContext)
177}
178
179// /////////////////////////////////////////////////////////////////////////////
180
181func makeVarsSingletonFunc() Singleton {
182	return &makeVarsSingleton{}
183}
184
185type makeVarsSingleton struct {
186	varsForTesting     []makeVarsVariable
187	installsForTesting []byte
188}
189
190type makeVarsProvider struct {
191	pctx PackageContext
192	call MakeVarsProvider
193}
194
195// Collection of makevars providers that are registered in init() methods.
196var makeVarsInitProviders []makeVarsProvider
197
198type makeVarsContext struct {
199	SingletonContext
200	config  Config
201	pctx    PackageContext
202	vars    []makeVarsVariable
203	phonies []phony
204	dists   []dist
205}
206
207var _ MakeVarsContext = &makeVarsContext{}
208
209type makeVarsVariable struct {
210	name   string
211	value  string
212	sort   bool
213	strict bool
214}
215
216type phony struct {
217	name string
218	deps []string
219}
220
221type dist struct {
222	goals []string
223	paths []string
224}
225
226func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) {
227	if !ctx.Config().KatiEnabled() {
228		return
229	}
230
231	outFile := absolutePath(PathForOutput(ctx,
232		"make_vars"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
233
234	lateOutFile := absolutePath(PathForOutput(ctx,
235		"late"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
236
237	installsFile := absolutePath(PathForOutput(ctx,
238		"installs"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
239
240	if ctx.Failed() {
241		return
242	}
243
244	var vars []makeVarsVariable
245	var dists []dist
246	var phonies []phony
247	var katiInstalls []katiInstall
248	var katiInitRcInstalls []katiInstall
249	var katiVintfManifestInstalls []katiInstall
250	var katiSymlinks []katiInstall
251
252	providers := append([]makeVarsProvider(nil), makeVarsInitProviders...)
253	providers = append(providers, *getSingletonMakevarsProviders(ctx.Config())...)
254
255	for _, provider := range providers {
256		mctx := &makeVarsContext{
257			SingletonContext: ctx,
258			pctx:             provider.pctx,
259		}
260
261		provider.call(mctx)
262
263		vars = append(vars, mctx.vars...)
264		phonies = append(phonies, mctx.phonies...)
265		dists = append(dists, mctx.dists...)
266	}
267
268	ctx.VisitAllModules(func(m Module) {
269		if provider, ok := m.(ModuleMakeVarsProvider); ok && m.Enabled(ctx) {
270			mctx := &makeVarsContext{
271				SingletonContext: ctx,
272			}
273
274			provider.MakeVars(mctx)
275
276			vars = append(vars, mctx.vars...)
277			phonies = append(phonies, mctx.phonies...)
278			dists = append(dists, mctx.dists...)
279		}
280
281		if m.ExportedToMake() {
282			info := OtherModuleProviderOrDefault(ctx, m, InstallFilesProvider)
283			katiInstalls = append(katiInstalls, info.KatiInstalls...)
284			katiInitRcInstalls = append(katiInitRcInstalls, info.KatiInitRcInstalls...)
285			katiVintfManifestInstalls = append(katiVintfManifestInstalls, info.KatiVintfInstalls...)
286			katiSymlinks = append(katiSymlinks, info.KatiSymlinks...)
287		}
288	})
289
290	compareKatiInstalls := func(a, b katiInstall) int {
291		aTo, bTo := a.to.String(), b.to.String()
292		if cmpTo := cmp.Compare(aTo, bTo); cmpTo != 0 {
293			return cmpTo
294		}
295
296		aFrom, bFrom := a.from.String(), b.from.String()
297		return cmp.Compare(aFrom, bFrom)
298	}
299
300	slices.SortFunc(katiInitRcInstalls, compareKatiInstalls)
301	katiInitRcInstalls = slices.CompactFunc(katiInitRcInstalls, func(a, b katiInstall) bool {
302		return compareKatiInstalls(a, b) == 0
303	})
304	katiInstalls = append(katiInstalls, katiInitRcInstalls...)
305
306	slices.SortFunc(katiVintfManifestInstalls, compareKatiInstalls)
307	katiVintfManifestInstalls = slices.CompactFunc(katiVintfManifestInstalls, func(a, b katiInstall) bool {
308		return compareKatiInstalls(a, b) == 0
309	})
310
311	if ctx.Failed() {
312		return
313	}
314
315	sort.Slice(vars, func(i, j int) bool {
316		return vars[i].name < vars[j].name
317	})
318	sort.Slice(phonies, func(i, j int) bool {
319		return phonies[i].name < phonies[j].name
320	})
321	lessArr := func(a, b []string) bool {
322		if len(a) == len(b) {
323			for i := range a {
324				if a[i] < b[i] {
325					return true
326				}
327			}
328			return false
329		}
330		return len(a) < len(b)
331	}
332	sort.Slice(dists, func(i, j int) bool {
333		return lessArr(dists[i].goals, dists[j].goals) || lessArr(dists[i].paths, dists[j].paths)
334	})
335
336	outBytes := s.writeVars(vars)
337
338	if err := pathtools.WriteFileIfChanged(outFile, outBytes, 0666); err != nil {
339		ctx.Errorf(err.Error())
340	}
341
342	lateOutBytes := s.writeLate(phonies, dists)
343
344	if err := pathtools.WriteFileIfChanged(lateOutFile, lateOutBytes, 0666); err != nil {
345		ctx.Errorf(err.Error())
346	}
347
348	installsBytes := s.writeInstalls(katiInstalls, katiSymlinks, katiVintfManifestInstalls)
349	if err := pathtools.WriteFileIfChanged(installsFile, installsBytes, 0666); err != nil {
350		ctx.Errorf(err.Error())
351	}
352
353	// Only save state for tests when testing.
354	if ctx.Config().RunningInsideUnitTest() {
355		s.varsForTesting = vars
356		s.installsForTesting = installsBytes
357	}
358}
359
360func (s *makeVarsSingleton) writeVars(vars []makeVarsVariable) []byte {
361	buf := &bytes.Buffer{}
362
363	fmt.Fprint(buf, `# Autogenerated file
364
365# Compares SOONG_$(1) against $(1), and warns if they are not equal.
366#
367# If the original variable is empty, then just set it to the SOONG_ version.
368#
369# $(1): Name of the variable to check
370# $(2): If not-empty, sort the values before comparing
371# $(3): Extra snippet to run if it does not match
372define soong-compare-var
373ifneq ($$($(1)),)
374  my_val_make := $$(strip $(if $(2),$$(sort $$($(1))),$$($(1))))
375  my_val_soong := $(if $(2),$$(sort $$(SOONG_$(1))),$$(SOONG_$(1)))
376  ifneq ($$(my_val_make),$$(my_val_soong))
377    $$(warning $(1) does not match between Make and Soong:)
378    $(if $(2),$$(warning Make  adds: $$(filter-out $$(my_val_soong),$$(my_val_make))),$$(warning Make : $$(my_val_make)))
379    $(if $(2),$$(warning Soong adds: $$(filter-out $$(my_val_make),$$(my_val_soong))),$$(warning Soong: $$(my_val_soong)))
380    $(3)
381  endif
382  my_val_make :=
383  my_val_soong :=
384else
385  $(1) := $$(SOONG_$(1))
386endif
387.KATI_READONLY := $(1) SOONG_$(1)
388endef
389
390my_check_failed := false
391
392`)
393
394	// Write all the strict checks out first so that if one of them errors,
395	// we get all of the strict errors printed, but not the non-strict
396	// warnings.
397	for _, v := range vars {
398		if !v.strict {
399			continue
400		}
401
402		sort := ""
403		if v.sort {
404			sort = "true"
405		}
406
407		fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value)
408		fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s,my_check_failed := true))\n\n", v.name, sort)
409	}
410
411	fmt.Fprint(buf, `
412ifneq ($(my_check_failed),false)
413  $(error Soong variable check failed)
414endif
415my_check_failed :=
416
417
418`)
419
420	for _, v := range vars {
421		if v.strict {
422			continue
423		}
424
425		sort := ""
426		if v.sort {
427			sort = "true"
428		}
429
430		fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value)
431		fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s))\n\n", v.name, sort)
432	}
433
434	fmt.Fprintln(buf, "\nsoong-compare-var :=")
435
436	fmt.Fprintln(buf)
437
438	return buf.Bytes()
439}
440
441func (s *makeVarsSingleton) writeLate(phonies []phony, dists []dist) []byte {
442	buf := &bytes.Buffer{}
443
444	fmt.Fprint(buf, `# Autogenerated file
445
446# Values written by Soong read after parsing all Android.mk files.
447
448
449`)
450
451	for _, phony := range phonies {
452		fmt.Fprintf(buf, ".PHONY: %s\n", phony.name)
453		fmt.Fprintf(buf, "%s: %s\n", phony.name, strings.Join(phony.deps, "\\\n  "))
454	}
455
456	fmt.Fprintln(buf)
457
458	for _, dist := range dists {
459		fmt.Fprintf(buf, ".PHONY: %s\n", strings.Join(dist.goals, " "))
460		fmt.Fprintf(buf, "$(call dist-for-goals,%s,%s)\n",
461			strings.Join(dist.goals, " "), strings.Join(dist.paths, " "))
462	}
463
464	return buf.Bytes()
465}
466
467// writeInstalls writes the list of install rules generated by Soong to a makefile.  The rules
468// are exported to Make instead of written directly to the ninja file so that main.mk can add
469// the dependencies from the `required` property that are hard to resolve in Soong.
470func (s *makeVarsSingleton) writeInstalls(installs, symlinks, katiVintfManifestInstalls []katiInstall) []byte {
471	buf := &bytes.Buffer{}
472
473	fmt.Fprint(buf, `# Autogenerated file
474
475# Values written by Soong to generate install rules that can be amended by Kati.
476
477EXTRA_INSTALL_ZIPS :=
478`)
479
480	preserveSymlinksFlag := "-d"
481	if runtime.GOOS == "darwin" {
482		preserveSymlinksFlag = "-R"
483	}
484
485	for _, install := range installs {
486		// Write a rule for each install request in the form:
487		//  to: from [ deps ] [ | order only deps ]
488		//       cp -f -d $< $@ [ && chmod +x $@ ]
489		fmt.Fprintf(buf, "%s: %s", install.to.String(), install.from.String())
490		for _, dep := range install.implicitDeps {
491			fmt.Fprintf(buf, " %s", dep.String())
492		}
493		if extraFiles := install.extraFiles; extraFiles != nil {
494			fmt.Fprintf(buf, " %s", extraFiles.zip.String())
495		}
496		if len(install.orderOnlyDeps) > 0 {
497			fmt.Fprintf(buf, " |")
498		}
499		for _, dep := range install.orderOnlyDeps {
500			fmt.Fprintf(buf, " %s", dep.String())
501		}
502		fmt.Fprintln(buf)
503		fmt.Fprintln(buf, "\t@echo \"Install: $@\"")
504		fmt.Fprintf(buf, "\trm -f $@ && cp -f %s $< $@\n", preserveSymlinksFlag)
505		if install.executable {
506			fmt.Fprintf(buf, "\tchmod +x $@\n")
507		}
508		if extraFiles := install.extraFiles; extraFiles != nil {
509			fmt.Fprintf(buf, "\t( unzip -qDD -d '%s' '%s' 2>&1 | grep -v \"zipfile is empty\"; exit $${PIPESTATUS[0]} ) || \\\n", extraFiles.dir.String(), extraFiles.zip.String())
510			fmt.Fprintf(buf, "\t  ( code=$$?; if [ $$code -ne 0 -a $$code -ne 1 ]; then exit $$code; fi )\n")
511			fmt.Fprintf(buf, "EXTRA_INSTALL_ZIPS += %s:%s:%s\n", install.to.String(), extraFiles.dir.String(), extraFiles.zip.String())
512		}
513
514		fmt.Fprintln(buf)
515	}
516	fmt.Fprintf(buf, ".KATI_READONLY := EXTRA_INSTALL_ZIPS\n")
517	fmt.Fprintf(buf, "$(KATI_visibility_prefix EXTRA_INSTALL_ZIPS,build/make/core/Makefile)\n")
518
519	for _, symlink := range symlinks {
520		fmt.Fprintf(buf, "%s:", symlink.to.String())
521		if symlink.from != nil {
522			// The katiVintfManifestInstall doesn't need updating when the target is modified, but we sometimes
523			// have a dependency on a katiVintfManifestInstall to a binary instead of to the binary directly, and
524			// the mtime of the katiVintfManifestInstall must be updated when the binary is modified, so use a
525			// normal dependency here instead of an order-only dependency.
526			fmt.Fprintf(buf, " %s", symlink.from.String())
527		}
528		for _, dep := range symlink.implicitDeps {
529			fmt.Fprintf(buf, " %s", dep.String())
530		}
531		if len(symlink.orderOnlyDeps) > 0 {
532			fmt.Fprintf(buf, " |")
533		}
534		for _, dep := range symlink.orderOnlyDeps {
535			fmt.Fprintf(buf, " %s", dep.String())
536		}
537		fmt.Fprintln(buf)
538
539		fromStr := ""
540		if symlink.from != nil {
541			rel, err := filepath.Rel(filepath.Dir(symlink.to.String()), symlink.from.String())
542			if err != nil {
543				panic(fmt.Errorf("failed to find relative path for katiVintfManifestInstall from %q to %q: %w",
544					symlink.from.String(), symlink.to.String(), err))
545			}
546			fromStr = rel
547		} else {
548			fromStr = symlink.absFrom
549		}
550
551		fmt.Fprintln(buf, "\t@echo \"Symlink: $@\"")
552		fmt.Fprintf(buf, "\trm -f $@ && ln -sfn %s $@", fromStr)
553		fmt.Fprintln(buf)
554		fmt.Fprintln(buf)
555	}
556
557	for _, install := range katiVintfManifestInstalls {
558		// Write a rule for each vintf install request that calls the copy-vintf-manifest-chedk make function.
559		fmt.Fprintf(buf, "$(eval $(call copy-vintf-manifest-checked, %s, %s))\n", install.from.String(), install.to.String())
560
561		if len(install.implicitDeps) > 0 {
562			panic(fmt.Errorf("unsupported implicitDeps %q in vintf install rule %q", install.implicitDeps, install.to))
563		}
564		if len(install.orderOnlyDeps) > 0 {
565			panic(fmt.Errorf("unsupported orderOnlyDeps %q in vintf install rule %q", install.orderOnlyDeps, install.to))
566		}
567
568		fmt.Fprintln(buf)
569	}
570	return buf.Bytes()
571}
572
573func (c *makeVarsContext) DeviceConfig() DeviceConfig {
574	return DeviceConfig{c.Config().deviceConfig}
575}
576
577var ninjaDescaper = strings.NewReplacer("$$", "$")
578
579func (c *makeVarsContext) Eval(ninjaStr string) (string, error) {
580	s, err := c.SingletonContext.Eval(c.pctx, ninjaStr)
581	if err != nil {
582		return "", err
583	}
584	// SingletonContext.Eval returns an exapnded string that is valid for a ninja file, de-escape $$ to $ for use
585	// in a Makefile
586	return ninjaDescaper.Replace(s), nil
587}
588
589func (c *makeVarsContext) addVariableRaw(name, value string, strict, sort bool) {
590	c.vars = append(c.vars, makeVarsVariable{
591		name:   name,
592		value:  value,
593		strict: strict,
594		sort:   sort,
595	})
596}
597
598func (c *makeVarsContext) addVariable(name, ninjaStr string, strict, sort bool) {
599	value, err := c.Eval(ninjaStr)
600	if err != nil {
601		c.SingletonContext.Errorf(err.Error())
602	}
603	c.addVariableRaw(name, value, strict, sort)
604}
605
606func (c *makeVarsContext) addPhony(name string, deps []string) {
607	c.phonies = append(c.phonies, phony{name, deps})
608}
609
610func (c *makeVarsContext) addDist(goals []string, paths []string) {
611	c.dists = append(c.dists, dist{
612		goals: goals,
613		paths: paths,
614	})
615}
616
617func (c *makeVarsContext) Strict(name, ninjaStr string) {
618	c.addVariable(name, ninjaStr, true, false)
619}
620func (c *makeVarsContext) StrictSorted(name, ninjaStr string) {
621	c.addVariable(name, ninjaStr, true, true)
622}
623func (c *makeVarsContext) StrictRaw(name, value string) {
624	c.addVariableRaw(name, value, true, false)
625}
626
627func (c *makeVarsContext) Check(name, ninjaStr string) {
628	c.addVariable(name, ninjaStr, false, false)
629}
630func (c *makeVarsContext) CheckSorted(name, ninjaStr string) {
631	c.addVariable(name, ninjaStr, false, true)
632}
633func (c *makeVarsContext) CheckRaw(name, value string) {
634	c.addVariableRaw(name, value, false, false)
635}
636
637func (c *makeVarsContext) Phony(name string, deps ...Path) {
638	c.addPhony(name, Paths(deps).Strings())
639}
640
641func (c *makeVarsContext) DistForGoal(goal string, paths ...Path) {
642	c.DistForGoals([]string{goal}, paths...)
643}
644
645func (c *makeVarsContext) DistForGoalWithFilename(goal string, path Path, filename string) {
646	c.DistForGoalsWithFilename([]string{goal}, path, filename)
647}
648
649func (c *makeVarsContext) DistForGoals(goals []string, paths ...Path) {
650	c.addDist(goals, Paths(paths).Strings())
651}
652
653func (c *makeVarsContext) DistForGoalsWithFilename(goals []string, path Path, filename string) {
654	c.addDist(goals, []string{path.String() + ":" + filename})
655}
656