1 // Copyright 2017 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 15 package build 16 17 import ( 18 "fmt" 19 "io/ioutil" 20 "os" 21 "path/filepath" 22 "strings" 23 ) 24 25 func removeGlobs(ctx Context, globs ...string) { 26 for _, glob := range globs { 27 files, err := filepath.Glob(glob) 28 if err != nil { 29 // Only possible error is ErrBadPattern 30 panic(fmt.Errorf("%q: %s", glob, err)) 31 } 32 33 for _, file := range files { 34 err = os.RemoveAll(file) 35 if err != nil { 36 ctx.Fatalf("Failed to remove file %q: %v", file, err) 37 } 38 } 39 } 40 } 41 42 // Remove everything under the out directory. Don't remove the out directory 43 // itself in case it's a symlink. 44 func clean(ctx Context, config Config, what int) { 45 removeGlobs(ctx, filepath.Join(config.OutDir(), "*")) 46 ctx.Println("Entire build directory removed.") 47 } 48 49 func dataClean(ctx Context, config Config, what int) { 50 removeGlobs(ctx, filepath.Join(config.ProductOut(), "data", "*")) 51 } 52 53 // installClean deletes all of the installed files -- the intent is to remove 54 // files that may no longer be installed, either because the user previously 55 // installed them, or they were previously installed by default but no longer 56 // are. 57 // 58 // This is faster than a full clean, since we're not deleting the 59 // intermediates. Instead of recompiling, we can just copy the results. 60 func installClean(ctx Context, config Config, what int) { 61 dataClean(ctx, config, what) 62 63 if hostCrossOutPath := config.hostCrossOut(); hostCrossOutPath != "" { 64 hostCrossOut := func(path string) string { 65 return filepath.Join(hostCrossOutPath, path) 66 } 67 removeGlobs(ctx, 68 hostCrossOut("bin"), 69 hostCrossOut("coverage"), 70 hostCrossOut("lib*"), 71 hostCrossOut("nativetest*")) 72 } 73 74 hostOutPath := config.HostOut() 75 hostOut := func(path string) string { 76 return filepath.Join(hostOutPath, path) 77 } 78 79 productOutPath := config.ProductOut() 80 productOut := func(path string) string { 81 return filepath.Join(productOutPath, path) 82 } 83 84 // Host bin, frameworks, and lib* are intentionally omitted, since 85 // otherwise we'd have to rebuild any generated files created with 86 // those tools. 87 removeGlobs(ctx, 88 hostOut("obj/NOTICE_FILES"), 89 hostOut("obj/PACKAGING"), 90 hostOut("coverage"), 91 hostOut("cts"), 92 hostOut("nativetest*"), 93 hostOut("sdk"), 94 hostOut("sdk_addon"), 95 hostOut("testcases"), 96 hostOut("vts"), 97 productOut("*.img"), 98 productOut("*.zip"), 99 productOut("android-info.txt"), 100 productOut("kernel"), 101 productOut("data"), 102 productOut("skin"), 103 productOut("obj/NOTICE_FILES"), 104 productOut("obj/PACKAGING"), 105 productOut("recovery"), 106 productOut("root"), 107 productOut("system"), 108 productOut("system_other"), 109 productOut("vendor"), 110 productOut("product"), 111 productOut("oem"), 112 productOut("obj/FAKE"), 113 productOut("breakpad"), 114 productOut("cache"), 115 productOut("coverage"), 116 productOut("installer"), 117 productOut("odm"), 118 productOut("sysloader"), 119 productOut("testcases")) 120 } 121 122 // Since products and build variants (unfortunately) shared the same 123 // PRODUCT_OUT staging directory, things can get out of sync if different 124 // build configurations are built in the same tree. This function will 125 // notice when the configuration has changed and call installclean to 126 // remove the files necessary to keep things consistent. 127 func installCleanIfNecessary(ctx Context, config Config) { 128 configFile := config.DevicePreviousProductConfig() 129 prefix := "PREVIOUS_BUILD_CONFIG := " 130 suffix := "\n" 131 currentProduct := prefix + config.TargetProduct() + "-" + config.TargetBuildVariant() + suffix 132 133 ensureDirectoriesExist(ctx, filepath.Dir(configFile)) 134 135 writeConfig := func() { 136 err := ioutil.WriteFile(configFile, []byte(currentProduct), 0666) 137 if err != nil { 138 ctx.Fatalln("Failed to write product config:", err) 139 } 140 } 141 142 prev, err := ioutil.ReadFile(configFile) 143 if err != nil { 144 if os.IsNotExist(err) { 145 writeConfig() 146 return 147 } else { 148 ctx.Fatalln("Failed to read previous product config:", err) 149 } 150 } else if string(prev) == currentProduct { 151 return 152 } 153 154 if disable, _ := config.Environment().Get("DISABLE_AUTO_INSTALLCLEAN"); disable == "true" { 155 ctx.Println("DISABLE_AUTO_INSTALLCLEAN is set; skipping auto-clean. Your tree may be in an inconsistent state.") 156 return 157 } 158 159 ctx.BeginTrace("installclean") 160 defer ctx.EndTrace() 161 162 prevConfig := strings.TrimPrefix(strings.TrimSuffix(string(prev), suffix), prefix) 163 currentConfig := strings.TrimPrefix(strings.TrimSuffix(currentProduct, suffix), prefix) 164 165 ctx.Printf("Build configuration changed: %q -> %q, forcing installclean\n", prevConfig, currentConfig) 166 167 installClean(ctx, config, 0) 168 169 writeConfig() 170 } 171