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_download_tmp_dir": config.rbeDownloadTmpDir(), 65 "RBE_platform": "container-image=" + remoteexec.DefaultImage, 66 } 67 if config.StartRBE() { 68 name, err := config.rbeSockAddr(absPath(ctx, config.rbeTmpDir())) 69 if err != nil { 70 ctx.Fatalf("Error retrieving socket address: %v", err) 71 return nil 72 } 73 vars["RBE_server_address"] = fmt.Sprintf("unix://%v", name) 74 } 75 76 rf := 1.0 77 if config.Parallel() < runtime.NumCPU() { 78 rf = float64(config.Parallel()) / float64(runtime.NumCPU()) 79 } 80 vars["RBE_local_resource_fraction"] = fmt.Sprintf("%.2f", rf) 81 82 k, v := config.rbeAuth() 83 vars[k] = v 84 return vars 85} 86 87func cleanupRBELogsDir(ctx Context, config Config) { 88 if !config.shouldCleanupRBELogsDir() { 89 return 90 } 91 92 rbeTmpDir := config.rbeProxyLogsDir() 93 if err := os.RemoveAll(rbeTmpDir); err != nil { 94 fmt.Fprintln(ctx.Writer, "\033[33mUnable to remove RBE log directory: ", err, "\033[0m") 95 } 96} 97 98func checkRBERequirements(ctx Context, config Config) { 99 if !config.GoogleProdCredsExist() && prodCredsAuthType(config) { 100 ctx.Fatalf("Unable to start RBE reproxy\nFAILED: Missing LOAS credentials.") 101 } 102 103 if u := ulimitOrFatal(ctx, config, "-u"); u < rbeLeastNProcs { 104 ctx.Fatalf("max user processes is insufficient: %d; want >= %d.\n", u, rbeLeastNProcs) 105 } 106 if n := ulimitOrFatal(ctx, config, "-n"); n < rbeLeastNFiles { 107 ctx.Fatalf("max open files is insufficient: %d; want >= %d.\n", n, rbeLeastNFiles) 108 } 109 if _, err := os.Stat(config.rbeProxyLogsDir()); os.IsNotExist(err) { 110 if err := os.MkdirAll(config.rbeProxyLogsDir(), 0744); err != nil { 111 ctx.Fatalf("Unable to create logs dir (%v) for RBE: %v", config.rbeProxyLogsDir, err) 112 } 113 } 114} 115 116func startRBE(ctx Context, config Config) { 117 ctx.BeginTrace(metrics.RunSetupTool, "rbe_bootstrap") 118 defer ctx.EndTrace() 119 120 ctx.Status.Status("Starting rbe...") 121 122 cmd := Command(ctx, config, "startRBE bootstrap", rbeCommand(ctx, config, bootstrapCmd)) 123 124 if output, err := cmd.CombinedOutput(); err != nil { 125 ctx.Fatalf("Unable to start RBE reproxy\nFAILED: RBE bootstrap failed with: %v\n%s\n", err, output) 126 } 127} 128 129func stopRBE(ctx Context, config Config) { 130 cmd := Command(ctx, config, "stopRBE bootstrap", rbeCommand(ctx, config, bootstrapCmd), "-shutdown") 131 output, err := cmd.CombinedOutput() 132 if err != nil { 133 ctx.Fatalf("rbe bootstrap with shutdown failed with: %v\n%s\n", err, output) 134 } 135 136 if !config.Environment().IsEnvTrue("ANDROID_QUIET_BUILD") && len(output) > 0 { 137 fmt.Fprintln(ctx.Writer, "") 138 fmt.Fprintln(ctx.Writer, fmt.Sprintf("%s", output)) 139 } 140} 141 142func prodCredsAuthType(config Config) bool { 143 authVar, val := config.rbeAuth() 144 if strings.Contains(authVar, "use_google_prod_creds") && val != "" && val != "false" { 145 return true 146 } 147 return false 148} 149 150// Check whether proper auth exists for RBE builds run within a 151// Google dev environment. 152func CheckProdCreds(ctx Context, config Config) { 153 if !config.IsGooglerEnvironment() { 154 return 155 } 156 if !config.StubbyExists() && prodCredsAuthType(config) { 157 fmt.Fprintln(ctx.Writer, "") 158 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")) 159 fmt.Fprintln(ctx.Writer, "") 160 return 161 } 162} 163 164// DumpRBEMetrics creates a metrics protobuf file containing RBE related metrics. 165// The protobuf file is created if RBE is enabled and the proxy service has 166// started. The proxy service is shutdown in order to dump the RBE metrics to the 167// protobuf file. 168func DumpRBEMetrics(ctx Context, config Config, filename string) { 169 ctx.BeginTrace(metrics.RunShutdownTool, "dump_rbe_metrics") 170 defer ctx.EndTrace() 171 172 // Remove the previous metrics file in case there is a failure or RBE has been 173 // disable for this run. 174 os.Remove(filename) 175 176 // If RBE is not enabled then there are no metrics to generate. 177 // If RBE does not require to start, the RBE proxy maybe started 178 // manually for debugging purpose and can generate the metrics 179 // afterwards. 180 if !config.StartRBE() { 181 return 182 } 183 184 outputDir := config.rbeProxyLogsDir() 185 if outputDir == "" { 186 ctx.Fatal("RBE output dir variable not defined. Aborting metrics dumping.") 187 } 188 metricsFile := filepath.Join(outputDir, rbeMetricsPBFilename) 189 190 // Stop the proxy first in order to generate the RBE metrics protobuf file. 191 stopRBE(ctx, config) 192 193 if metricsFile == filename { 194 return 195 } 196 if _, err := copyFile(metricsFile, filename); err != nil { 197 ctx.Fatalf("failed to copy %q to %q: %v\n", metricsFile, filename, err) 198 } 199} 200 201// PrintOutDirWarning prints a warning to indicate to the user that 202// setting output directory to a path other than "out" in an RBE enabled 203// build can cause slow builds. 204func PrintOutDirWarning(ctx Context, config Config) { 205 if config.UseRBE() && config.OutDir() != defaultOutDir { 206 fmt.Fprintln(ctx.Writer, "") 207 fmt.Fprintln(ctx.Writer, "\033[33mWARNING:\033[0m") 208 fmt.Fprintln(ctx.Writer, fmt.Sprintf("Setting OUT_DIR to a path other than %v may result in slow RBE builds.", defaultOutDir)) 209 fmt.Fprintln(ctx.Writer, "See http://go/android_rbe_out_dir for a workaround.") 210 fmt.Fprintln(ctx.Writer, "") 211 } 212} 213