1// Copyright 2019 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 build 16 17import ( 18 "fmt" 19 "os" 20 "path/filepath" 21 "runtime" 22 "strings" 23 24 "android/soong/remoteexec" 25 "android/soong/ui/metrics" 26) 27 28const ( 29 rbeLeastNProcs = 2500 30 rbeLeastNFiles = 16000 31 32 // prebuilt RBE binaries 33 bootstrapCmd = "bootstrap" 34 35 // RBE metrics proto buffer file 36 rbeMetricsPBFilename = "rbe_metrics.pb" 37 38 defaultOutDir = "out" 39) 40 41func rbeCommand(ctx Context, config Config, rbeCmd string) string { 42 var cmdPath string 43 if rbeDir := config.rbeDir(); rbeDir != "" { 44 cmdPath = filepath.Join(rbeDir, rbeCmd) 45 } else { 46 ctx.Fatalf("rbe command path not found") 47 } 48 49 if _, err := os.Stat(cmdPath); err != nil && os.IsNotExist(err) { 50 ctx.Fatalf("rbe command %q not found", rbeCmd) 51 } 52 53 return cmdPath 54} 55 56func getRBEVars(ctx Context, config Config) map[string]string { 57 vars := map[string]string{ 58 "RBE_log_dir": config.rbeProxyLogsDir(), 59 "RBE_re_proxy": config.rbeReproxy(), 60 "RBE_exec_root": config.rbeExecRoot(), 61 "RBE_output_dir": config.rbeProxyLogsDir(), 62 "RBE_proxy_log_dir": config.rbeProxyLogsDir(), 63 "RBE_cache_dir": config.rbeCacheDir(), 64 "RBE_platform": "container-image=" + remoteexec.DefaultImage, 65 } 66 if config.StartRBE() { 67 name, err := config.rbeSockAddr(absPath(ctx, config.TempDir())) 68 if err != nil { 69 ctx.Fatalf("Error retrieving socket address: %v", err) 70 return nil 71 } 72 vars["RBE_server_address"] = fmt.Sprintf("unix://%v", name) 73 } 74 75 rf := 1.0 76 if config.Parallel() < runtime.NumCPU() { 77 rf = float64(config.Parallel()) / float64(runtime.NumCPU()) 78 } 79 vars["RBE_local_resource_fraction"] = fmt.Sprintf("%.2f", rf) 80 81 k, v := config.rbeAuth() 82 vars[k] = v 83 return vars 84} 85 86func cleanupRBELogsDir(ctx Context, config Config) { 87 if !config.shouldCleanupRBELogsDir() { 88 return 89 } 90 91 rbeTmpDir := config.rbeProxyLogsDir() 92 if err := os.RemoveAll(rbeTmpDir); err != nil { 93 fmt.Fprintln(ctx.Writer, "\033[33mUnable to remove RBE log directory: ", err, "\033[0m") 94 } 95} 96 97func startRBE(ctx Context, config Config) { 98 if !config.GoogleProdCredsExist() && prodCredsAuthType(config) { 99 ctx.Fatalf("Unable to start RBE reproxy\nFAILED: Missing LOAS credentials.") 100 } 101 ctx.BeginTrace(metrics.RunSetupTool, "rbe_bootstrap") 102 defer ctx.EndTrace() 103 104 ctx.Status.Status("Starting rbe...") 105 106 if u := ulimitOrFatal(ctx, config, "-u"); u < rbeLeastNProcs { 107 ctx.Fatalf("max user processes is insufficient: %d; want >= %d.\n", u, rbeLeastNProcs) 108 } 109 if n := ulimitOrFatal(ctx, config, "-n"); n < rbeLeastNFiles { 110 ctx.Fatalf("max open files is insufficient: %d; want >= %d.\n", n, rbeLeastNFiles) 111 } 112 if _, err := os.Stat(config.rbeProxyLogsDir()); os.IsNotExist(err) { 113 if err := os.MkdirAll(config.rbeProxyLogsDir(), 0744); err != nil { 114 ctx.Fatalf("Unable to create logs dir (%v) for RBE: %v", config.rbeProxyLogsDir, err) 115 } 116 } 117 118 cmd := Command(ctx, config, "startRBE bootstrap", rbeCommand(ctx, config, bootstrapCmd)) 119 120 if output, err := cmd.CombinedOutput(); err != nil { 121 ctx.Fatalf("Unable to start RBE reproxy\nFAILED: RBE bootstrap failed with: %v\n%s\n", err, output) 122 } 123} 124 125func stopRBE(ctx Context, config Config) { 126 cmd := Command(ctx, config, "stopRBE bootstrap", rbeCommand(ctx, config, bootstrapCmd), "-shutdown") 127 output, err := cmd.CombinedOutput() 128 if err != nil { 129 ctx.Fatalf("rbe bootstrap with shutdown failed with: %v\n%s\n", err, output) 130 } 131 132 if !config.Environment().IsEnvTrue("ANDROID_QUIET_BUILD") && len(output) > 0 { 133 fmt.Fprintln(ctx.Writer, "") 134 fmt.Fprintln(ctx.Writer, fmt.Sprintf("%s", output)) 135 } 136} 137 138func prodCredsAuthType(config Config) bool { 139 authVar, val := config.rbeAuth() 140 if strings.Contains(authVar, "use_google_prod_creds") && val != "" && val != "false" { 141 return true 142 } 143 return false 144} 145 146// Check whether proper auth exists for RBE builds run within a 147// Google dev environment. 148func CheckProdCreds(ctx Context, config Config) { 149 if !config.IsGooglerEnvironment() { 150 return 151 } 152 if !config.StubbyExists() && prodCredsAuthType(config) { 153 fmt.Fprintln(ctx.Writer, "") 154 fmt.Fprintln(ctx.Writer, fmt.Sprintf("\033[33mWARNING: %q binary not found in $PATH, follow go/build-fast-without-stubby instead for authenticating with RBE.\033[0m", "stubby")) 155 fmt.Fprintln(ctx.Writer, "") 156 return 157 } 158 if config.GoogleProdCredsExist() { 159 return 160 } 161 fmt.Fprintln(ctx.Writer, "") 162 fmt.Fprintln(ctx.Writer, "\033[33mWARNING: Missing LOAS credentials, please run `gcert`. This will result in failing builds in the future, see go/rbe-android-default-announcement.\033[0m") 163 fmt.Fprintln(ctx.Writer, "") 164} 165 166// DumpRBEMetrics creates a metrics protobuf file containing RBE related metrics. 167// The protobuf file is created if RBE is enabled and the proxy service has 168// started. The proxy service is shutdown in order to dump the RBE metrics to the 169// protobuf file. 170func DumpRBEMetrics(ctx Context, config Config, filename string) { 171 ctx.BeginTrace(metrics.RunShutdownTool, "dump_rbe_metrics") 172 defer ctx.EndTrace() 173 174 // Remove the previous metrics file in case there is a failure or RBE has been 175 // disable for this run. 176 os.Remove(filename) 177 178 // If RBE is not enabled then there are no metrics to generate. 179 // If RBE does not require to start, the RBE proxy maybe started 180 // manually for debugging purpose and can generate the metrics 181 // afterwards. 182 if !config.StartRBE() { 183 return 184 } 185 186 ctx.Status.Status("Dumping rbe metrics...") 187 188 outputDir := config.rbeProxyLogsDir() 189 if outputDir == "" { 190 ctx.Fatal("RBE output dir variable not defined. Aborting metrics dumping.") 191 } 192 metricsFile := filepath.Join(outputDir, rbeMetricsPBFilename) 193 194 // Stop the proxy first in order to generate the RBE metrics protobuf file. 195 stopRBE(ctx, config) 196 197 if metricsFile == filename { 198 return 199 } 200 if _, err := copyFile(metricsFile, filename); err != nil { 201 ctx.Fatalf("failed to copy %q to %q: %v\n", metricsFile, filename, err) 202 } 203} 204 205// PrintOutDirWarning prints a warning to indicate to the user that 206// setting output directory to a path other than "out" in an RBE enabled 207// build can cause slow builds. 208func PrintOutDirWarning(ctx Context, config Config) { 209 if config.UseRBE() && config.OutDir() != defaultOutDir { 210 fmt.Fprintln(ctx.Writer, "") 211 fmt.Fprintln(ctx.Writer, "\033[33mWARNING:\033[0m") 212 fmt.Fprintln(ctx.Writer, fmt.Sprintf("Setting OUT_DIR to a path other than %v may result in slow RBE builds.", defaultOutDir)) 213 fmt.Fprintln(ctx.Writer, "See http://go/android_rbe_out_dir for a workaround.") 214 fmt.Fprintln(ctx.Writer, "") 215 } 216} 217