diff options
-rw-r--r-- | ui/build/Android.bp | 1 | ||||
-rw-r--r-- | ui/build/build.go | 91 | ||||
-rw-r--r-- | ui/build/cleanbuild.go | 169 | ||||
-rw-r--r-- | ui/build/config.go | 47 |
4 files changed, 220 insertions, 88 deletions
diff --git a/ui/build/Android.bp b/ui/build/Android.bp index 7a83684c..8f5250b3 100644 --- a/ui/build/Android.bp +++ b/ui/build/Android.bp @@ -21,6 +21,7 @@ bootstrap_go_package { ], srcs: [ "build.go", + "cleanbuild.go", "config.go", "context.go", "environment.go", diff --git a/ui/build/build.go b/ui/build/build.go index 598e342d..51dce05e 100644 --- a/ui/build/build.go +++ b/ui/build/build.go @@ -18,7 +18,6 @@ import ( "io/ioutil" "os" "path/filepath" - "strings" "text/template" ) @@ -92,60 +91,13 @@ func checkCaseSensitivity(ctx Context, config Config) { } } -// Since products and build variants (unfortunately) shared the same -// PRODUCT_OUT staging directory, things can get out of sync if different -// build configurations are built in the same tree. This function will -// notice when the configuration has changed and call installclean to -// remove the files necessary to keep things consistent. -func installcleanIfNecessary(ctx Context, config Config) { - if inList("installclean", config.Arguments()) { - return - } - - configFile := config.DevicePreviousProductConfig() - prefix := "PREVIOUS_BUILD_CONFIG := " - suffix := "\n" - currentProduct := prefix + config.TargetProduct() + "-" + config.TargetBuildVariant() + suffix - - writeConfig := func() { - err := ioutil.WriteFile(configFile, []byte(currentProduct), 0777) - if err != nil { - ctx.Fatalln("Failed to write product config:", err) - } - } - - prev, err := ioutil.ReadFile(configFile) - if err != nil { - if os.IsNotExist(err) { - writeConfig() - return - } else { - ctx.Fatalln("Failed to read previous product config:", err) - } - } else if string(prev) == currentProduct { - return - } - - if disable, _ := config.Environment().Get("DISABLE_AUTO_INSTALLCLEAN"); disable == "true" { - ctx.Println("DISABLE_AUTO_INSTALLCLEAN is set; skipping auto-clean. Your tree may be in an inconsistent state.") - return - } - - ctx.BeginTrace("installclean") - defer ctx.EndTrace() - - prevConfig := strings.TrimPrefix(strings.TrimSuffix(string(prev), suffix), prefix) - currentConfig := strings.TrimPrefix(strings.TrimSuffix(currentProduct, suffix), prefix) - - ctx.Printf("Build configuration changed: %q -> %q, forcing installclean\n", prevConfig, currentConfig) - - cleanConfig := CopyConfig(ctx, config, "installclean") - cleanConfig.SetKatiArgs([]string{"installclean"}) - cleanConfig.SetNinjaArgs([]string{"installclean"}) - - Build(ctx, cleanConfig, BuildKati|BuildNinja) - - writeConfig() +func help(ctx Context, config Config, what int) { + cmd := Command(ctx, config, "make", + "make", "-f", "build/core/help.mk") + cmd.Sandbox = makeSandbox + cmd.Stdout = ctx.Stdout() + cmd.Stderr = ctx.Stderr() + cmd.RunOrFatal() } // Build the tree. The 'what' argument can be used to chose which components of @@ -155,23 +107,10 @@ func Build(ctx Context, config Config, what int) { ctx.Verboseln("Environment:", config.Environment().Environ()) if inList("help", config.Arguments()) { - cmd := Command(ctx, config, "make", - "make", "-f", "build/core/help.mk") - cmd.Sandbox = makeSandbox - cmd.Stdout = ctx.Stdout() - cmd.Stderr = ctx.Stderr() - cmd.RunOrFatal() + help(ctx, config, what) return } else if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) { - // We can't use os.RemoveAll, since we don't want to remove the - // output directory itself, in case it's a symlink. So just do - // exactly what make used to do. - cmd := Command(ctx, config, "rm -rf $OUT_DIR/*", - "/bin/bash", "-c", "rm -rf "+config.OutDir()+"/*") - cmd.Stdout = ctx.Stdout() - cmd.Stderr = ctx.Stderr() - cmd.RunOrFatal() - ctx.Println("Entire build directory removed.") + clean(ctx, config, what) return } @@ -187,6 +126,16 @@ func Build(ctx Context, config Config, what int) { runMakeProductConfig(ctx, config) } + if inList("installclean", config.Arguments()) { + installClean(ctx, config, what) + ctx.Println("Deleted images and staging directories.") + return + } else if inList("dataclean", config.Arguments()) { + dataClean(ctx, config, what) + ctx.Println("Deleted data files.") + return + } + if what&BuildSoong != 0 { // Run Soong runSoongBootstrap(ctx, config) @@ -202,7 +151,7 @@ func Build(ctx Context, config Config, what int) { } if what&BuildNinja != 0 { - installcleanIfNecessary(ctx, config) + installCleanIfNecessary(ctx, config) // Write combined ninja file createCombinedBuildNinjaFile(ctx, config) diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go new file mode 100644 index 00000000..27b6d14c --- /dev/null +++ b/ui/build/cleanbuild.go @@ -0,0 +1,169 @@ +// Copyright 2017 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package build + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" +) + +func removeGlobs(ctx Context, globs ...string) { + for _, glob := range globs { + files, err := filepath.Glob(glob) + if err != nil { + // Only possible error is ErrBadPattern + panic(fmt.Errorf("%q: %s", glob, err)) + } + + for _, file := range files { + err = os.RemoveAll(file) + if err != nil { + ctx.Fatalf("Failed to remove file %q: %v", file, err) + } + } + } +} + +// 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, what int) { + removeGlobs(ctx, filepath.Join(config.OutDir(), "*")) + ctx.Println("Entire build directory removed.") +} + +func dataClean(ctx Context, config Config, what int) { + removeGlobs(ctx, filepath.Join(config.ProductOut(), "data", "*")) +} + +// installClean deletes all of the installed files -- the intent is to remove +// files that may no longer be installed, either because the user previously +// installed them, or they were previously installed by default but no longer +// are. +// +// This is faster than a full clean, since we're not deleting the +// intermediates. Instead of recompiling, we can just copy the results. +func installClean(ctx Context, config Config, what int) { + dataClean(ctx, config, what) + + if hostCrossOutPath := config.hostCrossOut(); hostCrossOutPath != "" { + hostCrossOut := func(path string) string { + return filepath.Join(hostCrossOutPath, path) + } + removeGlobs(ctx, + hostCrossOut("bin"), + hostCrossOut("coverage"), + hostCrossOut("lib*"), + hostCrossOut("nativetest*")) + } + + hostOutPath := config.HostOut() + hostOut := func(path string) string { + return filepath.Join(hostOutPath, path) + } + + productOutPath := config.ProductOut() + productOut := func(path string) string { + return filepath.Join(productOutPath, path) + } + + // Host bin, frameworks, and lib* are intentionally omitted, since + // otherwise we'd have to rebuild any generated files created with + // those tools. + removeGlobs(ctx, + hostOut("obj/NOTICE_FILES"), + hostOut("obj/PACKAGING"), + hostOut("coverage"), + hostOut("cts"), + hostOut("nativetest*"), + hostOut("sdk"), + hostOut("sdk_addon"), + hostOut("testcases"), + hostOut("vts"), + productOut("*.img"), + productOut("*.ini"), + productOut("*.txt"), + productOut("*.xlb"), + productOut("*.zip"), + productOut("kernel"), + productOut("data"), + productOut("skin"), + productOut("obj/NOTICE_FILES"), + productOut("obj/PACKAGING"), + productOut("recovery"), + productOut("root"), + productOut("system"), + productOut("system_other"), + productOut("vendor"), + productOut("oem"), + productOut("obj/FAKE"), + productOut("breakpad"), + productOut("cache"), + productOut("coverage"), + productOut("installer"), + productOut("odm"), + productOut("sysloader"), + productOut("testcases")) +} + +// Since products and build variants (unfortunately) shared the same +// PRODUCT_OUT staging directory, things can get out of sync if different +// build configurations are built in the same tree. This function will +// notice when the configuration has changed and call installclean to +// remove the files necessary to keep things consistent. +func installCleanIfNecessary(ctx Context, config Config) { + configFile := config.DevicePreviousProductConfig() + prefix := "PREVIOUS_BUILD_CONFIG := " + suffix := "\n" + currentProduct := prefix + config.TargetProduct() + "-" + config.TargetBuildVariant() + suffix + + writeConfig := func() { + err := ioutil.WriteFile(configFile, []byte(currentProduct), 0666) + if err != nil { + ctx.Fatalln("Failed to write product config:", err) + } + } + + prev, err := ioutil.ReadFile(configFile) + if err != nil { + if os.IsNotExist(err) { + writeConfig() + return + } else { + ctx.Fatalln("Failed to read previous product config:", err) + } + } else if string(prev) == currentProduct { + return + } + + if disable, _ := config.Environment().Get("DISABLE_AUTO_INSTALLCLEAN"); disable == "true" { + ctx.Println("DISABLE_AUTO_INSTALLCLEAN is set; skipping auto-clean. Your tree may be in an inconsistent state.") + return + } + + ctx.BeginTrace("installclean") + defer ctx.EndTrace() + + prevConfig := strings.TrimPrefix(strings.TrimSuffix(string(prev), suffix), prefix) + currentConfig := strings.TrimPrefix(strings.TrimSuffix(currentProduct, suffix), prefix) + + ctx.Printf("Build configuration changed: %q -> %q, forcing installclean\n", prevConfig, currentConfig) + + installClean(ctx, config, 0) + + writeConfig() +} diff --git a/ui/build/config.go b/ui/build/config.go index 51cff506..7e8091b9 100644 --- a/ui/build/config.go +++ b/ui/build/config.go @@ -167,22 +167,6 @@ func NewConfig(ctx Context, args ...string) Config { return Config{ret} } -// CopyConfig copies the configuration from an existing configuration, but replaces -// the Arguments() list with a new set. Useful if you need to run a different build -// with the same state as an existing build config. -func CopyConfig(ctx Context, config Config, args ...string) Config { - return Config{&configImpl{ - arguments: args, - goma: config.goma, - environ: config.environ.Copy(), - - parallel: config.parallel, - keepGoing: config.keepGoing, - verbose: config.verbose, - dist: config.dist, - }} -} - // Lunch configures the environment for a specific product similarly to the // `lunch` bash function. func (c *configImpl) Lunch(ctx Context, product, variant string) { @@ -369,8 +353,37 @@ func (c *configImpl) SoongMakeVarsMk() string { return filepath.Join(c.SoongOutDir(), "make_vars-"+c.TargetProduct()+".mk") } +func (c *configImpl) ProductOut() string { + if buildType, ok := c.environ.Get("TARGET_BUILD_TYPE"); ok && buildType == "debug" { + return filepath.Join(c.OutDir(), "debug", "target", "product", c.TargetDevice()) + } else { + return filepath.Join(c.OutDir(), "target", "product", c.TargetDevice()) + } +} + func (c *configImpl) DevicePreviousProductConfig() string { - return filepath.Join(c.OutDir(), "target", "product", c.TargetDevice(), "previous_build_config.mk") + return filepath.Join(c.ProductOut(), "previous_build_config.mk") +} + +func (c *configImpl) hostOutRoot() string { + if buildType, ok := c.environ.Get("HOST_BUILD_TYPE"); ok && buildType == "debug" { + return filepath.Join(c.OutDir(), "debug", "host") + } else { + return filepath.Join(c.OutDir(), "host") + } +} + +func (c *configImpl) HostOut() string { + return filepath.Join(c.hostOutRoot(), c.HostPrebuiltTag()) +} + +// This probably needs to be multi-valued, so not exporting it for now +func (c *configImpl) hostCrossOut() string { + if runtime.GOOS == "linux" { + return filepath.Join(c.hostOutRoot(), "windows-x86") + } else { + return "" + } } func (c *configImpl) HostPrebuiltTag() string { |