Merge "rust: Add support to emit certain Cargo env vars."
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 341d500..b272daa 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -57,6 +57,17 @@
archType ArchType
}
+// bazelHandler is the interface for a helper object related to deferring to Bazel for
+// processing a module (during Bazel mixed builds). Individual module types should define
+// their own bazel handler if they support deferring to Bazel.
+type BazelHandler interface {
+ // Issue query to Bazel to retrieve information about Bazel's view of the current module.
+ // If Bazel returns this information, set module properties on the current module to reflect
+ // the returned information.
+ // Returns true if information was available from Bazel, false if bazel invocation still needs to occur.
+ GenerateBazelBuildActions(ctx ModuleContext, label string) bool
+}
+
type BazelContext interface {
// The below methods involve queuing cquery requests to be later invoked
// by bazel. If any of these methods return (_, false), then the request
diff --git a/android/config.go b/android/config.go
index b3b8f3c..e3d05a6 100644
--- a/android/config.go
+++ b/android/config.go
@@ -209,6 +209,20 @@
Bool(configurable.GcovCoverage) ||
Bool(configurable.ClangCoverage))
+ // when Platform_sdk_final is true (or PLATFORM_VERSION_CODENAME is REL), use Platform_sdk_version;
+ // if false (pre-released version, for example), use Platform_sdk_codename.
+ if Bool(configurable.Platform_sdk_final) {
+ if configurable.Platform_sdk_version != nil {
+ configurable.Platform_sdk_version_or_codename =
+ proptools.StringPtr(strconv.Itoa(*(configurable.Platform_sdk_version)))
+ } else {
+ return fmt.Errorf("Platform_sdk_version cannot be pointed by a NULL pointer")
+ }
+ } else {
+ configurable.Platform_sdk_version_or_codename =
+ proptools.StringPtr(String(configurable.Platform_sdk_codename))
+ }
+
return saveToBazelConfigFile(configurable, filepath.Dir(filename))
}
diff --git a/android/filegroup.go b/android/filegroup.go
index e207412..97dd136 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -15,8 +15,9 @@
package android
import (
- "android/soong/bazel"
"strings"
+
+ "android/soong/bazel"
)
func init() {
@@ -108,7 +109,7 @@
return module
}
-func (fg *fileGroup) generateBazelBuildActions(ctx ModuleContext) bool {
+func (fg *fileGroup) GenerateBazelBuildActions(ctx ModuleContext) bool {
if !fg.MixedBuildsEnabled(ctx) {
return false
}
@@ -131,7 +132,7 @@
}
func (fg *fileGroup) GenerateAndroidBuildActions(ctx ModuleContext) {
- if fg.generateBazelBuildActions(ctx) {
+ if fg.GenerateBazelBuildActions(ctx) {
return
}
diff --git a/android/variable.go b/android/variable.go
index 0fb9078..5cf9aa8 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -42,6 +42,10 @@
Cflags []string
}
+ Platform_sdk_version_or_codename struct {
+ Java_resource_dirs []string
+ }
+
// unbundled_build is a catch-all property to annotate modules that don't build in one or
// more unbundled branches, usually due to dependencies missing from the manifest.
Unbundled_build struct {
@@ -164,6 +168,7 @@
Platform_version_name *string `json:",omitempty"`
Platform_sdk_version *int `json:",omitempty"`
Platform_sdk_codename *string `json:",omitempty"`
+ Platform_sdk_version_or_codename *string `json:",omitempty"`
Platform_sdk_final *bool `json:",omitempty"`
Platform_version_active_codenames []string `json:",omitempty"`
Platform_vndk_version *string `json:",omitempty"`
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 306e1ea..e0421f6 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -163,8 +163,8 @@
android.AssertPathsRelativeToTopEquals(t, "metadata flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/metadata.csv"}, info.MetadataPaths)
android.AssertPathsRelativeToTopEquals(t, "index flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/index.csv"}, info.IndexPaths)
- android.AssertArrayString(t, "stub flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/stub-flags.csv:out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/signature-patterns.csv"}, info.StubFlagSubsets.RelativeToTop())
- android.AssertArrayString(t, "all flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/all-flags.csv:out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/signature-patterns.csv"}, info.FlagSubsets.RelativeToTop())
+ android.AssertArrayString(t, "stub flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/filtered-stub-flags.csv:out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/signature-patterns.csv"}, info.StubFlagSubsets.RelativeToTop())
+ android.AssertArrayString(t, "all flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/filtered-flags.csv:out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/signature-patterns.csv"}, info.FlagSubsets.RelativeToTop())
}
func TestPlatformBootclasspathDependencies(t *testing.T) {
diff --git a/cc/Android.bp b/cc/Android.bp
index 164d32b..bff2761 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -13,6 +13,7 @@
"soong-bazel",
"soong-cc-config",
"soong-etc",
+ "soong-fuzz",
"soong-genrule",
"soong-snapshot",
"soong-tradefed",
@@ -59,7 +60,6 @@
"binary.go",
"binary_sdk_member.go",
"fuzz.go",
- "fuzz_common.go",
"library.go",
"library_headers.go",
"library_sdk_member.go",
diff --git a/cc/cc.go b/cc/cc.go
index 39d89e5..f65af30 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -29,6 +29,7 @@
"android/soong/android"
"android/soong/cc/config"
+ "android/soong/fuzz"
"android/soong/genrule"
)
@@ -589,17 +590,6 @@
installInRoot() bool
}
-// bazelHandler is the interface for a helper object related to deferring to Bazel for
-// processing a module (during Bazel mixed builds). Individual module types should define
-// their own bazel handler if they support deferring to Bazel.
-type bazelHandler interface {
- // Issue query to Bazel to retrieve information about Bazel's view of the current module.
- // If Bazel returns this information, set module properties on the current module to reflect
- // the returned information.
- // Returns true if information was available from Bazel, false if bazel invocation still needs to occur.
- generateBazelBuildActions(ctx android.ModuleContext, label string) bool
-}
-
type xref interface {
XrefCcFiles() android.Paths
}
@@ -773,7 +763,7 @@
// members of the cc.Module to this decorator. Thus, a cc_binary module has custom linker and
// installer logic.
type Module struct {
- FuzzModule
+ fuzz.FuzzModule
android.SdkBase
android.BazelModuleBase
@@ -796,7 +786,7 @@
compiler compiler
linker linker
installer installer
- bazelHandler bazelHandler
+ bazelHandler android.BazelHandler
features []feature
stl *stl
@@ -1696,7 +1686,7 @@
bazelModuleLabel := c.GetBazelLabel(actx, c)
bazelActionsUsed := false
if c.MixedBuildsEnabled(actx) && c.bazelHandler != nil {
- bazelActionsUsed = c.bazelHandler.generateBazelBuildActions(actx, bazelModuleLabel)
+ bazelActionsUsed = c.bazelHandler.GenerateBazelBuildActions(actx, bazelModuleLabel)
}
return bazelActionsUsed
}
@@ -3426,7 +3416,7 @@
&TestProperties{},
&TestBinaryProperties{},
&BenchmarkProperties{},
- &FuzzProperties{},
+ &fuzz.FuzzProperties{},
&StlProperties{},
&SanitizeProperties{},
&StripProperties{},
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 8b0f93e..fbef12b 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -15,7 +15,6 @@
package cc
import (
- "encoding/json"
"path/filepath"
"sort"
"strings"
@@ -24,55 +23,9 @@
"android/soong/android"
"android/soong/cc/config"
+ "android/soong/fuzz"
)
-type FuzzConfig struct {
- // Email address of people to CC on bugs or contact about this fuzz target.
- Cc []string `json:"cc,omitempty"`
- // Specify whether to enable continuous fuzzing on devices. Defaults to true.
- Fuzz_on_haiku_device *bool `json:"fuzz_on_haiku_device,omitempty"`
- // Specify whether to enable continuous fuzzing on host. Defaults to true.
- Fuzz_on_haiku_host *bool `json:"fuzz_on_haiku_host,omitempty"`
- // Component in Google's bug tracking system that bugs should be filed to.
- Componentid *int64 `json:"componentid,omitempty"`
- // Hotlists in Google's bug tracking system that bugs should be marked with.
- Hotlists []string `json:"hotlists,omitempty"`
- // Specify whether this fuzz target was submitted by a researcher. Defaults
- // to false.
- Researcher_submitted *bool `json:"researcher_submitted,omitempty"`
- // Specify who should be acknowledged for CVEs in the Android Security
- // Bulletin.
- Acknowledgement []string `json:"acknowledgement,omitempty"`
- // Additional options to be passed to libfuzzer when run in Haiku.
- Libfuzzer_options []string `json:"libfuzzer_options,omitempty"`
- // Additional options to be passed to HWASAN when running on-device in Haiku.
- Hwasan_options []string `json:"hwasan_options,omitempty"`
- // Additional options to be passed to HWASAN when running on host in Haiku.
- Asan_options []string `json:"asan_options,omitempty"`
-}
-
-func (f *FuzzConfig) String() string {
- b, err := json.Marshal(f)
- if err != nil {
- panic(err)
- }
-
- return string(b)
-}
-
-type FuzzProperties struct {
- // Optional list of seed files to be installed to the fuzz target's output
- // directory.
- Corpus []string `android:"path"`
- // Optional list of data files to be installed to the fuzz target's output
- // directory. Directory structure relative to the module is preserved.
- Data []string `android:"path"`
- // Optional dictionary to be installed to the fuzz target's output directory.
- Dictionary *string `android:"path"`
- // Config for running the target on fuzzing infrastructure.
- Fuzz_config *FuzzConfig
-}
-
func init() {
android.RegisterModuleType("cc_fuzz", FuzzFactory)
android.RegisterSingletonType("cc_fuzz_packaging", fuzzPackagingFactory)
@@ -94,7 +47,7 @@
*binaryDecorator
*baseCompiler
- fuzzPackagedModule FuzzPackagedModule
+ fuzzPackagedModule fuzz.FuzzPackagedModule
installedSharedDeps []string
}
@@ -355,7 +308,7 @@
// Responsible for generating GNU Make rules that package fuzz targets into
// their architecture & target/host specific zip file.
type ccFuzzPackager struct {
- FuzzPackager
+ fuzz.FuzzPackager
sharedLibInstallStrings []string
}
@@ -367,7 +320,7 @@
// Map between each architecture + host/device combination, and the files that
// need to be packaged (in the tuple of {source file, destination folder in
// archive}).
- archDirs := make(map[ArchOs][]FileToZip)
+ archDirs := make(map[fuzz.ArchOs][]fuzz.FileToZip)
// Map tracking whether each shared library has an install rule to avoid duplicate install rules from
// multiple fuzzers that depend on the same shared library.
@@ -384,7 +337,7 @@
}
// Discard non-fuzz targets.
- if ok := IsValid(ccModule.FuzzModule); !ok {
+ if ok := fuzz.IsValid(ccModule.FuzzModule); !ok {
return
}
@@ -400,12 +353,12 @@
archString := ccModule.Arch().ArchType.String()
archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString)
- archOs := ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
+ archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
// Grab the list of required shared libraries.
sharedLibraries := collectAllSharedDependencies(ctx, module)
- var files []FileToZip
+ var files []fuzz.FileToZip
builder := android.NewRuleBuilder(pctx, ctx)
// Package the corpus, data, dict and config into a zipfile.
@@ -414,7 +367,7 @@
// Find and mark all the transiently-dependent shared libraries for
// packaging.
for _, library := range sharedLibraries {
- files = append(files, FileToZip{library, "lib"})
+ files = append(files, fuzz.FileToZip{library, "lib"})
// For each architecture-specific shared library dependency, we need to
// install it to the output directory. Setup the install destination here,
@@ -446,7 +399,7 @@
}
// The executable.
- files = append(files, FileToZip{ccModule.UnstrippedOutputFile(), ""})
+ files = append(files, fuzz.FileToZip{ccModule.UnstrippedOutputFile(), ""})
archDirs[archOs], ok = s.BuildZipFile(ctx, module, fuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
if !ok {
@@ -454,7 +407,7 @@
}
})
- s.CreateFuzzPackage(ctx, archDirs, Cc)
+ s.CreateFuzzPackage(ctx, archDirs, fuzz.Cc, pctx)
}
diff --git a/cc/library.go b/cc/library.go
index 51cba20..b2360e9 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -556,7 +556,7 @@
}
type ccLibraryBazelHandler struct {
- bazelHandler
+ android.BazelHandler
module *Module
}
@@ -642,7 +642,7 @@
return android.OptionalPathForPath(android.PathForBazelOut(ctx, tocFile))
}
-func (handler *ccLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+func (handler *ccLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
bazelCtx := ctx.Config().BazelContext
ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
if err != nil {
diff --git a/cc/library_headers.go b/cc/library_headers.go
index e596ff7..1a276f4 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -44,13 +44,13 @@
}
type libraryHeaderBazelHander struct {
- bazelHandler
+ android.BazelHandler
module *Module
library *libraryDecorator
}
-func (h *libraryHeaderBazelHander) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+func (h *libraryHeaderBazelHander) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
bazelCtx := ctx.Config().BazelContext
ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
if err != nil {
diff --git a/cc/object.go b/cc/object.go
index bd9f228..5952f1e 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -47,12 +47,12 @@
}
type objectBazelHandler struct {
- bazelHandler
+ android.BazelHandler
module *Module
}
-func (handler *objectBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+func (handler *objectBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
bazelCtx := ctx.Config().BazelContext
objPaths, ok := bazelCtx.GetOutputFiles(label, ctx.Arch().ArchType)
if ok {
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index f7154ec..d324241 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -15,9 +15,10 @@
package cc
import (
- "android/soong/android"
"path/filepath"
"strings"
+
+ "android/soong/android"
)
func init() {
@@ -321,13 +322,13 @@
}
type prebuiltStaticLibraryBazelHandler struct {
- bazelHandler
+ android.BazelHandler
module *Module
library *libraryDecorator
}
-func (h *prebuiltStaticLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+func (h *prebuiltStaticLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
bazelCtx := ctx.Config().BazelContext
ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
if err != nil {
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 0336fb6..0099e87 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -469,6 +469,12 @@
ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, bp2buildCtx.Context, configuration)
ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
+ // Generate out/soong/.bootstrap/build-globs.ninja with the actions to generate flattened globfiles
+ // containing the globs seen during bp2build conversion
+ if blueprintArgs.GlobFile != "" {
+ bootstrap.WriteBuildGlobsNinjaFile(bootstrap.StageMain, bp2buildCtx.Context, blueprintArgs, configuration)
+ }
+ // Add the depfile on the expanded globs in out/soong/.primary/globs
ninjaDeps = append(ninjaDeps, bootstrap.GlobFileListFiles(configuration)...)
// Run the code-generation phase to convert BazelTargetModules to BUILD files
diff --git a/fuzz/Android.bp b/fuzz/Android.bp
new file mode 100644
index 0000000..9d96e48
--- /dev/null
+++ b/fuzz/Android.bp
@@ -0,0 +1,15 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-fuzz",
+ pkgPath: "android/soong/fuzz",
+ deps: [
+ "soong-android",
+ ],
+ srcs: [
+ "fuzz_common.go",
+ ],
+ pluginFor: ["soong_build"],
+}
diff --git a/cc/fuzz_common.go b/fuzz/fuzz_common.go
similarity index 73%
rename from cc/fuzz_common.go
rename to fuzz/fuzz_common.go
index 98ed7f4..ccadc0f 100644
--- a/cc/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -12,14 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cc
+package fuzz
// This file contains the common code for compiling C/C++ and Rust fuzzers for Android.
import (
+ "encoding/json"
"sort"
"strings"
+ "github.com/google/blueprint/proptools"
+
"android/soong/android"
)
@@ -30,6 +33,8 @@
Rust Lang = "rust"
)
+var BoolDefault = proptools.BoolDefault
+
type FuzzModule struct {
android.ModuleBase
android.DefaultableModuleBase
@@ -52,6 +57,44 @@
Dir string
}
+type FuzzConfig struct {
+ // Email address of people to CC on bugs or contact about this fuzz target.
+ Cc []string `json:"cc,omitempty"`
+ // Specify whether to enable continuous fuzzing on devices. Defaults to true.
+ Fuzz_on_haiku_device *bool `json:"fuzz_on_haiku_device,omitempty"`
+ // Specify whether to enable continuous fuzzing on host. Defaults to true.
+ Fuzz_on_haiku_host *bool `json:"fuzz_on_haiku_host,omitempty"`
+ // Component in Google's bug tracking system that bugs should be filed to.
+ Componentid *int64 `json:"componentid,omitempty"`
+ // Hotlists in Google's bug tracking system that bugs should be marked with.
+ Hotlists []string `json:"hotlists,omitempty"`
+ // Specify whether this fuzz target was submitted by a researcher. Defaults
+ // to false.
+ Researcher_submitted *bool `json:"researcher_submitted,omitempty"`
+ // Specify who should be acknowledged for CVEs in the Android Security
+ // Bulletin.
+ Acknowledgement []string `json:"acknowledgement,omitempty"`
+ // Additional options to be passed to libfuzzer when run in Haiku.
+ Libfuzzer_options []string `json:"libfuzzer_options,omitempty"`
+ // Additional options to be passed to HWASAN when running on-device in Haiku.
+ Hwasan_options []string `json:"hwasan_options,omitempty"`
+ // Additional options to be passed to HWASAN when running on host in Haiku.
+ Asan_options []string `json:"asan_options,omitempty"`
+}
+
+type FuzzProperties struct {
+ // Optional list of seed files to be installed to the fuzz target's output
+ // directory.
+ Corpus []string `android:"path"`
+ // Optional list of data files to be installed to the fuzz target's output
+ // directory. Directory structure relative to the module is preserved.
+ Data []string `android:"path"`
+ // Optional dictionary to be installed to the fuzz target's output directory.
+ Dictionary *string `android:"path"`
+ // Config for running the target on fuzzing infrastructure.
+ Fuzz_config *FuzzConfig
+}
+
type FuzzPackagedModule struct {
FuzzProperties FuzzProperties
Dictionary android.Path
@@ -151,7 +194,16 @@
return archDirs[archOs], true
}
-func (s *FuzzPackager) CreateFuzzPackage(ctx android.SingletonContext, archDirs map[ArchOs][]FileToZip, lang Lang) {
+func (f *FuzzConfig) String() string {
+ b, err := json.Marshal(f)
+ if err != nil {
+ panic(err)
+ }
+
+ return string(b)
+}
+
+func (s *FuzzPackager) CreateFuzzPackage(ctx android.SingletonContext, archDirs map[ArchOs][]FileToZip, lang Lang, pctx android.PackageContext) {
var archOsList []ArchOs
for archOs := range archDirs {
archOsList = append(archOsList, archOs)
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 71a8780..fdb3618 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -240,7 +240,7 @@
}
// Returns true if information was available from Bazel, false if bazel invocation still needs to occur.
-func (c *Module) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+func (c *Module) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
bazelCtx := ctx.Config().BazelContext
filePaths, ok := bazelCtx.GetOutputFiles(label, ctx.Arch().ArchType)
if ok {
@@ -560,7 +560,7 @@
bazelModuleLabel := g.GetBazelLabel(ctx, g)
bazelActionsUsed := false
if g.MixedBuildsEnabled(ctx) {
- bazelActionsUsed = g.generateBazelBuildActions(ctx, bazelModuleLabel)
+ bazelActionsUsed = g.GenerateBazelBuildActions(ctx, bazelModuleLabel)
}
if !bazelActionsUsed {
// For <= 6 outputs, just embed those directly in the users. Right now, that covers >90% of
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 8a06a99..8e39f40 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -948,6 +948,22 @@
return patternsFile
}
+// buildRuleRemoveBlockedFlag creates a rule that will remove entries from the input path which
+// only have blocked flags. It will not remove entries that have blocked as well as other flags,
+// e.g. blocked,core-platform-api.
+func buildRuleRemoveBlockedFlag(ctx android.BuilderContext, name string, desc string, inputPath android.Path, filteredPath android.WritablePath) {
+ rule := android.NewRuleBuilder(pctx, ctx)
+ rule.Command().
+ Text(`grep -vE "^[^,]+,blocked$"`).Input(inputPath).Text(">").Output(filteredPath).
+ // Grep's exit code depends on whether it finds anything. It is 0 (build success) when it finds
+ // something and 1 (build failure) when it does not and 2 (when it encounters an error).
+ // However, while it is unlikely it is not an error if this does not find any matches. The
+ // following will only run if the grep does not find something and in that case it will treat
+ // an exit code of 1 as success and anything else as failure.
+ Text("|| test $? -eq 1")
+ rule.Build(name, desc)
+}
+
// buildRuleValidateOverlappingCsvFiles checks that the modular CSV files, i.e. the files generated
// by the individual bootclasspath_fragment modules are subsets of the monolithic CSV file.
func buildRuleValidateOverlappingCsvFiles(ctx android.BuilderContext, name string, desc string, monolithicFilePath android.WritablePath, csvSubsets SignatureCsvSubsets) android.WritablePath {
@@ -1036,14 +1052,24 @@
encodedBootDexJarsByModule[name] = encodedDex
}
+ // Generate the filtered-stub-flags.csv file which contains the filtered stub flags that will be
+ // compared against the monolithic stub flags.
+ filteredStubFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "filtered-stub-flags.csv")
+ buildRuleRemoveBlockedFlag(ctx, "modularHiddenApiFilteredStubFlags", "modular hiddenapi filtered stub flags", stubFlagsCSV, filteredStubFlagsCSV)
+
+ // Generate the filtered-flags.csv file which contains the filtered flags that will be compared
+ // against the monolithic flags.
+ filteredFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "filtered-flags.csv")
+ buildRuleRemoveBlockedFlag(ctx, "modularHiddenApiFilteredFlags", "modular hiddenapi filtered flags", allFlagsCSV, filteredFlagsCSV)
+
// Store the paths in the info for use by other modules and sdk snapshot generation.
output := HiddenAPIOutput{
HiddenAPIFlagOutput: HiddenAPIFlagOutput{
- StubFlagsPath: stubFlagsCSV,
+ StubFlagsPath: filteredStubFlagsCSV,
AnnotationFlagsPath: annotationFlagsCSV,
MetadataPath: metadataCSV,
IndexPath: indexCSV,
- AllFlagsPath: allFlagsCSV,
+ AllFlagsPath: filteredFlagsCSV,
},
EncodedBootDexFilesByModule: encodedBootDexJarsByModule,
}
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 3470e51..be9e71e 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -29,7 +29,7 @@
defaultBindgenFlags = []string{""}
// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
- bindgenClangVersion = "clang-r416183b"
+ bindgenClangVersion = "clang-r428724"
_ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
diff --git a/rust/fuzz.go b/rust/fuzz.go
index 18b2513..5fb56ff 100644
--- a/rust/fuzz.go
+++ b/rust/fuzz.go
@@ -21,6 +21,7 @@
"android/soong/android"
"android/soong/cc"
+ "android/soong/fuzz"
"android/soong/rust/config"
)
@@ -32,7 +33,7 @@
type fuzzDecorator struct {
*binaryDecorator
- fuzzPackagedModule cc.FuzzPackagedModule
+ fuzzPackagedModule fuzz.FuzzPackagedModule
}
var _ compiler = (*binaryDecorator)(nil)
@@ -96,7 +97,7 @@
// Responsible for generating GNU Make rules that package fuzz targets into
// their architecture & target/host specific zip file.
type rustFuzzPackager struct {
- cc.FuzzPackager
+ fuzz.FuzzPackager
}
func rustFuzzPackagingFactory() android.Singleton {
@@ -105,7 +106,7 @@
func (s *rustFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
// Map between each architecture + host/device combination.
- archDirs := make(map[cc.ArchOs][]cc.FileToZip)
+ archDirs := make(map[fuzz.ArchOs][]fuzz.FileToZip)
// List of individual fuzz targets.
s.FuzzTargets = make(map[string]bool)
@@ -117,7 +118,7 @@
return
}
- if ok := cc.IsValid(rustModule.FuzzModule); !ok || rustModule.Properties.PreventInstall {
+ if ok := fuzz.IsValid(rustModule.FuzzModule); !ok || rustModule.Properties.PreventInstall {
return
}
@@ -133,16 +134,16 @@
archString := rustModule.Arch().ArchType.String()
archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString)
- archOs := cc.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
+ archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
- var files []cc.FileToZip
+ var files []fuzz.FileToZip
builder := android.NewRuleBuilder(pctx, ctx)
// Package the artifacts (data, corpus, config and dictionary into a zipfile.
files = s.PackageArtifacts(ctx, module, fuzzModule.fuzzPackagedModule, archDir, builder)
// The executable.
- files = append(files, cc.FileToZip{rustModule.unstrippedOutputFile.Path(), ""})
+ files = append(files, fuzz.FileToZip{rustModule.unstrippedOutputFile.Path(), ""})
archDirs[archOs], ok = s.BuildZipFile(ctx, module, fuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
if !ok {
@@ -150,7 +151,7 @@
}
})
- s.CreateFuzzPackage(ctx, archDirs, cc.Rust)
+ s.CreateFuzzPackage(ctx, archDirs, fuzz.Rust, pctx)
}
func (s *rustFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
diff --git a/rust/rust.go b/rust/rust.go
index 3ec550b..4ceeef1 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -25,6 +25,7 @@
"android/soong/bloaty"
"android/soong/cc"
cc_config "android/soong/cc/config"
+ "android/soong/fuzz"
"android/soong/rust/config"
)
@@ -123,7 +124,7 @@
}
type Module struct {
- cc.FuzzModule
+ fuzz.FuzzModule
VendorProperties cc.VendorProperties
diff --git a/scripts/hiddenapi/verify_overlaps.py b/scripts/hiddenapi/verify_overlaps.py
index a4a423e..6432bf1 100755
--- a/scripts/hiddenapi/verify_overlaps.py
+++ b/scripts/hiddenapi/verify_overlaps.py
@@ -331,8 +331,11 @@
for signature in allSignatures:
monolithicRow = monolithicFlagsDict.get(signature, {})
monolithicFlags = monolithicRow.get(None, [])
- modularRow = modularFlagsDict.get(signature, {})
- modularFlags = modularRow.get(None, [])
+ if signature in modularFlagsDict:
+ modularRow = modularFlagsDict.get(signature, {})
+ modularFlags = modularRow.get(None, [])
+ else:
+ modularFlags = ["blocked"]
if monolithicFlags != modularFlags:
mismatchingSignatures.append((signature, modularFlags, monolithicFlags))
return mismatchingSignatures
diff --git a/scripts/hiddenapi/verify_overlaps_test.py b/scripts/hiddenapi/verify_overlaps_test.py
index 7477254..00c0611 100755
--- a/scripts/hiddenapi/verify_overlaps_test.py
+++ b/scripts/hiddenapi/verify_overlaps_test.py
@@ -298,7 +298,7 @@
]
self.assertEqual(expected, mismatches)
- def test_missing_from_monolithic(self):
+ def test_match_treat_missing_from_modular_as_blocked(self):
monolithic = self.read_signature_csv_from_string_as_dict('')
modular = self.read_signature_csv_from_string_as_dict('''
Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
@@ -313,7 +313,7 @@
]
self.assertEqual(expected, mismatches)
- def test_missing_from_modular(self):
+ def test_mismatch_treat_missing_from_modular_as_blocked(self):
monolithic = self.read_signature_csv_from_string_as_dict('''
Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
''')
@@ -322,7 +322,7 @@
expected = [
(
'Ljava/lang/Object;->hashCode()I',
- [],
+ ['blocked'],
['public-api', 'system-api', 'test-api'],
),
]
@@ -334,13 +334,7 @@
''')
modular = {}
mismatches = compare_signature_flags(monolithic, modular)
- expected = [
- (
- 'Ljava/lang/Object;->hashCode()I',
- [],
- ['blocked'],
- ),
- ]
+ expected = []
self.assertEqual(expected, mismatches)
if __name__ == '__main__':
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 60cc6b3..bed2ecf 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -138,8 +138,8 @@
metadata: "hiddenapi/metadata.csv",
index: "hiddenapi/index.csv",
signature_patterns: "hiddenapi/signature-patterns.csv",
- stub_flags: "hiddenapi/stub-flags.csv",
- all_flags: "hiddenapi/all-flags.csv",
+ stub_flags: "hiddenapi/filtered-stub-flags.csv",
+ all_flags: "hiddenapi/filtered-flags.csv",
},
}
@@ -166,8 +166,8 @@
metadata: "hiddenapi/metadata.csv",
index: "hiddenapi/index.csv",
signature_patterns: "hiddenapi/signature-patterns.csv",
- stub_flags: "hiddenapi/stub-flags.csv",
- all_flags: "hiddenapi/all-flags.csv",
+ stub_flags: "hiddenapi/filtered-stub-flags.csv",
+ all_flags: "hiddenapi/filtered-flags.csv",
},
}
@@ -191,8 +191,8 @@
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/stub-flags.csv -> hiddenapi/stub-flags.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/all-flags.csv -> hiddenapi/all-flags.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
`),
snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
@@ -339,8 +339,8 @@
metadata: "hiddenapi/metadata.csv",
index: "hiddenapi/index.csv",
signature_patterns: "hiddenapi/signature-patterns.csv",
- stub_flags: "hiddenapi/stub-flags.csv",
- all_flags: "hiddenapi/all-flags.csv",
+ stub_flags: "hiddenapi/filtered-stub-flags.csv",
+ all_flags: "hiddenapi/filtered-flags.csv",
},
}
@@ -424,8 +424,8 @@
metadata: "hiddenapi/metadata.csv",
index: "hiddenapi/index.csv",
signature_patterns: "hiddenapi/signature-patterns.csv",
- stub_flags: "hiddenapi/stub-flags.csv",
- all_flags: "hiddenapi/all-flags.csv",
+ stub_flags: "hiddenapi/filtered-stub-flags.csv",
+ all_flags: "hiddenapi/filtered-flags.csv",
},
}
@@ -503,8 +503,8 @@
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/stub-flags.csv -> hiddenapi/stub-flags.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/all-flags.csv -> hiddenapi/all-flags.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
.intermediates/myothersdklibrary.stubs/android_common/javac/myothersdklibrary.stubs.jar -> sdk_library/public/myothersdklibrary-stubs.jar
.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_api.txt -> sdk_library/public/myothersdklibrary.txt
@@ -546,13 +546,13 @@
android.AssertStringEquals(t, "updatable-bcp-packages.txt", expectedContents, rule.Args["content"])
rule = module.Output("out/soong/hiddenapi/hiddenapi-flags.csv.valid")
- android.AssertStringDoesContain(t, "verify-overlaps", rule.RuleParams.Command, " snapshot/hiddenapi/all-flags.csv:snapshot/hiddenapi/signature-patterns.csv ")
+ android.AssertStringDoesContain(t, "verify-overlaps", rule.RuleParams.Command, " snapshot/hiddenapi/filtered-flags.csv:snapshot/hiddenapi/signature-patterns.csv ")
}),
snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot),
snapshotTestChecker(checkSnapshotWithSourcePreferred, func(t *testing.T, result *android.TestResult) {
module := result.ModuleForTests("platform-bootclasspath", "android_common")
rule := module.Output("out/soong/hiddenapi/hiddenapi-flags.csv.valid")
- android.AssertStringDoesContain(t, "verify-overlaps", rule.RuleParams.Command, " out/soong/.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/all-flags.csv:out/soong/.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv ")
+ android.AssertStringDoesContain(t, "verify-overlaps", rule.RuleParams.Command, " out/soong/.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/filtered-flags.csv:out/soong/.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv ")
}),
snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot),
)
@@ -655,8 +655,8 @@
metadata: "hiddenapi/metadata.csv",
index: "hiddenapi/index.csv",
signature_patterns: "hiddenapi/signature-patterns.csv",
- stub_flags: "hiddenapi/stub-flags.csv",
- all_flags: "hiddenapi/all-flags.csv",
+ stub_flags: "hiddenapi/filtered-stub-flags.csv",
+ all_flags: "hiddenapi/filtered-flags.csv",
},
}
@@ -858,8 +858,8 @@
metadata: "hiddenapi/metadata.csv",
index: "hiddenapi/index.csv",
signature_patterns: "hiddenapi/signature-patterns.csv",
- stub_flags: "hiddenapi/stub-flags.csv",
- all_flags: "hiddenapi/all-flags.csv",
+ stub_flags: "hiddenapi/filtered-stub-flags.csv",
+ all_flags: "hiddenapi/filtered-flags.csv",
},
}
@@ -902,8 +902,8 @@
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/stub-flags.csv -> hiddenapi/stub-flags.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/all-flags.csv -> hiddenapi/all-flags.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
.intermediates/mysdklibrary.stubs/android_common/javac/mysdklibrary.stubs.jar -> sdk_library/public/mysdklibrary-stubs.jar
.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
index e357710..b5c6863 100755
--- a/tests/bp2build_bazel_test.sh
+++ b/tests/bp2build_bazel_test.sh
@@ -8,6 +8,100 @@
readonly GENERATED_BUILD_FILE_NAME="BUILD.bazel"
+function test_bp2build_null_build() {
+ setup
+ run_bp2build
+ local output_mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+
+ run_bp2build
+ local output_mtime2=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+
+ if [[ "$output_mtime1" != "$output_mtime2" ]]; then
+ fail "Output bp2build marker file changed on null build"
+ fi
+}
+
+test_bp2build_null_build
+
+function test_bp2build_null_build_with_globs() {
+ setup
+
+ mkdir -p foo/bar
+ cat > foo/bar/Android.bp <<'EOF'
+filegroup {
+ name: "globs",
+ srcs: ["*.txt"],
+ }
+EOF
+ touch foo/bar/a.txt foo/bar/b.txt
+
+ run_bp2build
+ local output_mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+
+ run_bp2build
+ local output_mtime2=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+
+ if [[ "$output_mtime1" != "$output_mtime2" ]]; then
+ fail "Output bp2build marker file changed on null build"
+ fi
+}
+
+test_bp2build_null_build_with_globs
+
+function test_soong_after_bp2build_regenerates_build_globs_ninja() {
+ setup
+
+ mkdir -p foo/bar
+ cat > foo/bar/Android.bp <<'EOF'
+filegroup {
+ name: "bp2build-files",
+ srcs: ["bp2build.*"],
+ bazel_module: { bp2build_available: true },
+}
+
+filegroup {
+ name: "soong-files",
+ srcs: ["soong.*"],
+}
+EOF
+ touch foo/bar/bp2build.txt foo/bar/soong.txt
+
+ build_globs_file="out/soong/.bootstrap/build-globs.ninja"
+
+ # Test: the build-globs file for bp2build should only contain the bp2build-files
+ # glob, whereas the build-globs file for soong should contain both bp2build-files
+ # and soong-files globs.
+
+ run_bp2build
+ local output_mtime1=$(stat -c "%y" "${build_globs_file}")
+
+ if ! grep "\"foo/bar/bp2build.*\"" "${build_globs_file}"; then
+ fail "bp2build filegroup globs not found in bp2build's globs file"
+ fi
+
+ if grep "\"foo/bar/soong.*\"" "${build_globs_file}"; then
+ fail "soong filegroup globs unexpectedly found in bp2build's globs file"
+ fi
+
+ run_soong
+ local output_mtime2=$(stat -c "%y" "${build_globs_file}")
+
+ if [[ "$output_mtime1" == "$output_mtime2" ]]; then
+ fail "Output build-globs.ninja file did not change"
+ fi
+
+ if ! grep "\"foo/bar/bp2build.*\"" "${build_globs_file}"; then
+ fail "bp2build filegroup globs not found in bp2build's globs file"
+ fi
+
+ if ! grep "\"foo/bar/soong.*\"" "${build_globs_file}"; then
+ fail "soong filegroup globs not found in bp2build's globs file"
+ fi
+
+}
+
+test_soong_after_bp2build_regenerates_build_globs_ninja
+
function test_bp2build_generates_all_buildfiles {
setup
create_mock_bazel
diff --git a/tests/run_integration_tests.sh b/tests/run_integration_tests.sh
index 8399573..6304a11 100755
--- a/tests/run_integration_tests.sh
+++ b/tests/run_integration_tests.sh
@@ -6,3 +6,4 @@
"$TOP/build/soong/tests/bootstrap_test.sh"
"$TOP/build/soong/tests/mixed_mode_test.sh"
"$TOP/build/soong/tests/bp2build_bazel_test.sh"
+"$TOP/build/soong/tests/soong_test.sh"
diff --git a/tests/soong_test.sh b/tests/soong_test.sh
new file mode 100755
index 0000000..905d708
--- /dev/null
+++ b/tests/soong_test.sh
@@ -0,0 +1,22 @@
+#!/bin/bash -eu
+
+set -o pipefail
+
+# Tests of Soong functionality
+
+source "$(dirname "$0")/lib.sh"
+
+function test_m_clean_works {
+ setup
+
+ # Create a directory with files that cannot be removed
+ mkdir -p out/bad_directory_permissions
+ touch out/bad_directory_permissions/unremovable_file
+ # File permissions are fine but directory permissions are bad
+ chmod a+rwx out/bad_directory_permissions/unremovable_file
+ chmod a-rwx out/bad_directory_permissions
+
+ run_soong clean
+}
+
+test_m_clean_works
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 19b5690..65b91bc 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -17,6 +17,7 @@
import (
"bytes"
"fmt"
+ "io/fs"
"io/ioutil"
"os"
"path/filepath"
@@ -46,9 +47,49 @@
}
}
+// Based on https://stackoverflow.com/questions/28969455/how-to-properly-instantiate-os-filemode
+// Because Go doesn't provide a nice way to set bits on a filemode
+const (
+ FILEMODE_READ = 04
+ FILEMODE_WRITE = 02
+ FILEMODE_EXECUTE = 01
+ FILEMODE_USER_SHIFT = 6
+ FILEMODE_USER_READ = FILEMODE_READ << FILEMODE_USER_SHIFT
+ FILEMODE_USER_WRITE = FILEMODE_WRITE << FILEMODE_USER_SHIFT
+ FILEMODE_USER_EXECUTE = FILEMODE_EXECUTE << FILEMODE_USER_SHIFT
+)
+
+// Ensures that files and directories in the out dir can be deleted.
+// For example, Bazen can generate output directories where the write bit isn't set, causing 'm' clean' to fail.
+func ensureOutDirRemovable(ctx Context, config Config) {
+ err := filepath.WalkDir(config.OutDir(), func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ if d.IsDir() {
+ info, err := d.Info()
+ if err != nil {
+ return err
+ }
+ // Equivalent to running chmod u+rwx on each directory
+ newMode := info.Mode() | FILEMODE_USER_READ | FILEMODE_USER_WRITE | FILEMODE_USER_EXECUTE
+ if err := os.Chmod(path, newMode); err != nil {
+ return err
+ }
+ }
+ // Continue walking the out dir...
+ return nil
+ })
+ if err != nil && !os.IsNotExist(err) {
+ // Display the error, but don't crash.
+ ctx.Println(err.Error())
+ }
+}
+
// Remove everything under the out directory. Don't remove the out directory
// itself in case it's a symlink.
func clean(ctx Context, config Config) {
+ ensureOutDirRemovable(ctx, config)
removeGlobs(ctx, filepath.Join(config.OutDir(), "*"))
ctx.Println("Entire build directory removed.")
}