Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/build/build/soong/ui/build/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 14 kB image not shown  

Quelle  rbe.go   Sprache: unbekannt

 
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

// Copyright 2019 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 (
 "bytes"
 "fmt"
 "math"
 "os"
 "path/filepath"
 "runtime"
 "strconv"
 "strings"
 "text/template"

 "android/soong/remoteexec"
 "android/soong/ui/metrics"
)

const (
 rbeLeastNProcs = 2500
 rbeLeastNFiles = 16000

 // prebuilt RBE binaries
 bootstrapCmd = "bootstrap"

 // RBE metrics proto buffer file
 rbeMetricsPBFilename = "rbe_metrics.pb"

 defaultOutDir = "out"
)

func rbeCommand(ctx Context, config Config, rbeCmd string) string {
 var cmdPath string
 if rbeDir := config.rbeDir(); rbeDir != "" {
  cmdPath = filepath.Join(rbeDir, rbeCmd)
 } else {
  ctx.Fatalf("rbe command path not found")
 }

 if _, err := os.Stat(cmdPath); err != nil && os.IsNotExist(err) {
  ctx.Fatalf("rbe command %q not found", rbeCmd)
 }

 return cmdPath
}

var defaultRBEPlatform = "container-image=" + remoteexec.DefaultImage

func getRBEVars(ctx Context, config Config) map[string]string {
 vars := map[string]string{
  "RBE_log_dir":          config.rbeProxyLogsDir(),
  "RBE_re_proxy":         config.rbeReproxy(),
  "RBE_exec_root":        config.rbeExecRoot(),
  "RBE_output_dir":       config.rbeProxyLogsDir(),
  "RBE_proxy_log_dir":    config.rbeProxyLogsDir(),
  "RBE_cache_dir":        config.rbeCacheDir(),
  "RBE_download_tmp_dir": config.rbeDownloadTmpDir(),
  // bump the maximum size a command's stdout can be before being truncated
  "RBE_max_listen_size_kb": strconv.Itoa(16 * 1024),
  "RBE_platform":           config.rbePlatform(),
 }
 if config.StartReproxy() {
  name, err := config.rbeSockAddr(absPath(ctx, config.rbeTmpDir()))
  if err != nil {
   ctx.Fatalf("Error retrieving socket address: %v", err)
   return nil
  }
  vars["RBE_server_address"] = fmt.Sprintf("unix://%v", name)
 }

 rf := 1.0
 if config.Parallel() < runtime.NumCPU() {
  rf = float64(config.Parallel()) / float64(runtime.NumCPU())
 }
 vars["RBE_local_resource_fraction"] = fmt.Sprintf("%.2f", rf)

 k, v := config.rbeAuth()
 vars[k] = v
 return vars
}

func cleanupRBELogsDir(ctx Context, config Config) {
 if !config.shouldCleanupRBELogsDir() {
  return
 }

 rbeTmpDir := config.rbeProxyLogsDir()
 if err := os.RemoveAll(rbeTmpDir); err != nil {
  fmt.Fprintln(ctx.Writer, "\033[33mUnable to remove RBE log directory: ", err, "\033[0m")
 }
}

func checkRBERequirements(ctx Context, config Config) {
 if !config.GoogleProdCredsExist() && prodCredsAuthType(config) {
  ctx.Fatalf("Unable to start RBE reproxy\nFAILED: Missing LOAS credentials.")
 }

 if u := ulimitOrFatal(ctx, config, "-u"); u < rbeLeastNProcs {
  ctx.Fatalf("max user processes is insufficient: %d; want >= %d.\n", u, rbeLeastNProcs)
 }
 if n := ulimitOrFatal(ctx, config, "-n"); n < rbeLeastNFiles {
  ctx.Fatalf("max open files is insufficient: %d; want >= %d.\n", n, rbeLeastNFiles)
 }
 if _, err := os.Stat(config.rbeProxyLogsDir()); os.IsNotExist(err) {
  if err := os.MkdirAll(config.rbeProxyLogsDir(), 0744); err != nil {
   ctx.Fatalf("Unable to create logs dir (%v) for RBE: %v", config.rbeProxyLogsDir, err)
  }
 }
}

var DEFAULT_SISO_CONFIG_DIR = "build/soong/siso_config"

func ensureSymlink(ctx Context, dir, name, target string) {
 if err := os.MkdirAll(dir, 0777); err != nil {
  ctx.Fatalf("Could not ensure that directory %q exists: %w", dir, err)
 }

 absTarget := target
 if !filepath.IsAbs(target) {
  var err error
  absTarget, err = filepath.Abs(target)
  if err != nil {
   ctx.Fatalf("Could not create absolute path for %s in %s: %w", dir, target, err)
  }
 }

 // Remove any xisting symlink.
 linkPath := filepath.Join(dir, name)
 currentTarget, err := os.Readlink(linkPath)
 if err == nil {
  if absTarget == currentTarget {
   return
  }
  if err := os.Remove(linkPath); err != nil {
   ctx.Fatalf("Failed to remove existing symlink %q: %w", linkPath, err)
  }
 } else if !os.IsNotExist(err) {
  if err := os.Remove(linkPath); err != nil {
   ctx.Fatalf("Failed to remove existing symlink %q: %w", linkPath, err)
  }
 }

 // Create the new one.
 if err := os.Symlink(absTarget, linkPath); err != nil {
  ctx.Fatalf("Failed to create symlink %q => %q: %w", linkPath, absTarget, err)
 }
}

var sisoConfigTemplate = template.Must(template.New("siso-config").Parse(`
load("@builtin//struct.star", "module")
load("main/main.star", "main")
{{ if .UseExtension -}}
load("extension/main.star", "extension")

imports = [main, extension]
{{ else -}}
imports = [main]
{{ end -}}

def init(ctx):
    vars = module(
        "config",
        {{- range $key, $value := .Config.SisoStringVars }}
        {{ $key }} = "{{ $value }}",{{ end }}
        {{- range $key, $value := .Config.SisoBoolVars }}
        {{ $key }} = {{ if $value }}True{{ else }}False{{ end }},{{ end }}
    )

    return main.generate(ctx, vars, imports)
`))

func createSisoConfigDir(ctx Context, config Config, value string) string {
 // We need to fabricate a working directory.
 confDir := filepath.Join(config.OutDir(), "siso_config")
 if value == confDir {
  _, err := os.Stat(filepath.Join(confDir, "main.star"))
  if err != nil {
   ctx.Fatalf("${SISO_CONFIG_DIR}/main.star is missing: %w", err)
  }
  return value
 }
 templateConfig := struct {
  Config       *Config
  UseExtension bool
 }{Config: &config}
 ensureSymlink(ctx, confDir, "main", DEFAULT_SISO_CONFIG_DIR)
 if value != DEFAULT_SISO_CONFIG_DIR && value != "" {
  ensureSymlink(ctx, confDir, "extension", value)
  templateConfig.UseExtension = true
 }

 var sb bytes.Buffer
 if err := sisoConfigTemplate.Execute(&sb, templateConfig); err != nil {
  ctx.Fatalf("Failed to generate siso config:", err)
 }
 confFile := filepath.Join(confDir, "main.star")
 if err := os.WriteFile(confFile, sb.Bytes(), 0666); err != nil {
  ctx.Fatalf("Failed to write siso config to %q: %w", confFile, err)
 }
 return confDir
}

// Create a script for siso to get credentials.
// Siso will invoke ${SISO_CREDENTIAL_HELPER} with "get", so put the actual credhelper command
// invocation in `soong-convert-command`.
func createSisoCredsHelper(ctx Context, config Config) (string, error) {
 var helperPath string
 var helperArgs string

 // RBE_credentials_helper_args contains space-separated arguments for the helper
 if envArgs, ok := config.environ.Get("RBE_credentials_helper_args"); ok && envArgs != "" {
  helperArgs = envArgs
 }
 var ok bool
 if helperPath, ok = config.Environment().Get("RBE_credentials_helper"); !ok {
  helperPath = "execrel://"
 }
 if strings.HasPrefix(helperPath, "execrel://") {
  relpath, _ := strings.CutPrefix(helperPath, "execrel://")
  dir, ok := config.Environment().Get("RBE_DIR")
  if !ok {
   dir = "prebuilts/remoteexecution-client/live"
  }
  // Use the one from RBE_DIR.
  helperPath = filepath.Join(dir, relpath, "credshelper")
 }
 if helperArgs == "" {
  helperArgs = "--auth_source=automaticAuth --gcert_refresh_timeout=20"
 }
 if helperPath == "" {
  return "", fmt.Errorf("missing RBE_credentials_helper")
 }
 ctx.Verbosef("Using '%s %s' for RBE credentials helper\n", helperPath, helperArgs)
 cacheDir := config.rbeCacheDir()
 helperArgs = strings.TrimSpace(helperArgs)
 args := []string{helperPath, helperArgs, "--cache_dir", cacheDir, "-bazel_compat"}
 cmdFile := filepath.Join(cacheDir, "soong-convert-command")
 err := os.WriteFile(cmdFile, []byte(strings.Join(args, " ")), 0666)
 return "build/soong/scripts/siso-creds-helper.py", err
}

func startRBEproxy(ctx Context, config Config) {
 e := ctx.BeginTrace(metrics.RunSetupTool, "rbe_bootstrap")
 defer e.End()

 ctx.Status.Status("Starting rbe...")
 executable := config.SisoBin()
 args := []string{
  "proxy",
  "--addr", getRBEproxySocket(ctx, config),
 }
 authType, _ := config.rbeAuth()
 switch authType {
 case "RBE_credentials_helper":
  helper, err := createSisoCredsHelper(ctx, config)
  if err != nil {
   ctx.Fatalf("Failed to create credential helper script: %v\n", err)
  }
  config.environ.Set("SISO_CREDENTIAL_HELPER", helper)
 case "RBE_use_google_prod_creds":
  ctx.Printf("Using google prod credentials\n")
 default:
  config.environ.Set("SISO_CREDENTIAL_HELPER", "google-application-default")
  ctx.Printf("Using google application default credentials\n")
 }
 if instance, ok := config.environ.Get("RBE_instance"); ok {
  args = append(args, "--reapi_instance", instance)
 }
 if service, ok := config.environ.Get("RBE_service"); ok {
  // Pass the actual service to siso proxy.
  args = append(args, "--reapi_address", service)
 }
 if project := getRBEProject(ctx, config); project != "" {
  args = append(args, "--project", project)
 }

 cmd := Command(ctx, config, e, "startRbeproxy bootstrap", executable, args...)
 ctx.Printf("Starting RBE proxy\n")
 ctx.Verbosef("RBE proxy command: %s\n", cmd)
 cmd.Stdin = strings.NewReader("")
 cmd.Stdout = os.Stdout
 cmd.Stderr = os.Stderr

 if err := cmd.Start(); err != nil {
  ctx.Fatalf("Unable to start siso proxy\nFAILED: siso proxy failed with: %v\n%s\n", err)
 }
 ctx.RBEProxyCmd = cmd
}

func getRBEproxySocket(ctx Context, config Config) string {
 service, ok := config.environ.Get("RBE_service")
 if !ok {
  ctx.Fatalf("RBE_service is not set")
 }
 config.environ.Set("RBE_actual_service", service)
 rel_socket := filepath.Join(config.OutDir(), "rbeproxy.socket")
 abs_socket, err := filepath.Abs(rel_socket)
 if err != nil {
  ctx.Fatalf("Could not resolve %s to an absolute path: %v\n", rel_socket, err)
 }
 return fmt.Sprintf("unix://%s", abs_socket)
}

func getRBEProject(ctx Context, config Config) string {
 project, found := config.environ.Get("RBE_project")
 if !found {
  // If RBE_project is not set, try RBE_metrics_project.
  project, _ = config.environ.Get("RBE_metrics_project")
 }
 if project == "" {
  ctx.Printf("No RBE project found\n")
 }
 return project
}

func startReproxy(ctx Context, config Config) {
 e := ctx.BeginTrace(metrics.RunSetupTool, "rbe_bootstrap")
 defer e.End()

 ctx.Status.Status("Starting rbe...")
 cmd := Command(ctx, config, e, "startReproxy bootstrap", rbeCommand(ctx, config, bootstrapCmd))

 if output, err := cmd.CombinedOutput(); err != nil {
  ctx.Fatalf("Unable to start RBE reproxy\nFAILED: RBE bootstrap failed with: %v\n%s\n", err, output)
 }
}

func stopRBE(ctx Context, config Config) {
 var output []byte
 var err error
 if ctx.RBEProxyCmd != nil {
  ctx.RBEProxyCmd.Kill()
 } else {
  cmd := Command(ctx, config, nil, "stopRBE bootstrap", rbeCommand(ctx, config, bootstrapCmd), "-shutdown")
  output, err = cmd.CombinedOutput()
  if err != nil {
   ctx.Fatalf("rbe bootstrap with shutdown failed with: %v\n%s\n", err, output)
  }
 }

 if !config.Environment().IsEnvTrue("ANDROID_QUIET_BUILD") && len(output) > 0 {
  ctx.PrintFinal("\n")
  ctx.PrintFinal(string(output))
 }
}

func prodCredsAuthType(config Config) bool {
 authVar, val := config.rbeAuth()
 if strings.Contains(authVar, "use_google_prod_creds") && val != "" && val != "false" {
  return true
 }
 return false
}

// Check whether proper auth exists for RBE builds run within a
// Google dev environment.
func CheckProdCreds(ctx Context, config Config) {
 if !config.IsGooglerEnvironment() {
  return
 }
 if !config.StubbyExists() && prodCredsAuthType(config) {
  fmt.Fprintln(ctx.Writer, "")
  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"))
  fmt.Fprintln(ctx.Writer, "")
  return
 }
}

// DumpRBEMetrics creates a metrics protobuf file containing RBE related metrics.
// The protobuf file is created if RBE is enabled and the proxy service has
// started. The proxy service is shutdown in order to dump the RBE metrics to the
// protobuf file.
func DumpRBEMetrics(ctx Context, config Config, filename string) {
 e := ctx.BeginTrace(metrics.RunShutdownTool, "dump_rbe_metrics")
 defer e.End()

 // Remove the previous metrics file in case there is a failure or RBE has been
 // disable for this run.
 os.Remove(filename)

 // If RBE is not enabled then there are no metrics to generate.
 // If RBE does not require to start, the RBE proxy maybe started
 // manually for debugging purpose and can generate the metrics
 // afterwards.
 if !config.StartReproxy() {
  return
 }

 outputDir := config.rbeProxyLogsDir()
 if outputDir == "" {
  ctx.Fatal("RBE output dir variable not defined. Aborting metrics dumping.")
 }
 metricsFile := filepath.Join(outputDir, rbeMetricsPBFilename)

 // Stop the proxy first in order to generate the RBE metrics protobuf file.
 stopRBE(ctx, config)

 if metricsFile == filename {
  return
 }
 if _, err := copyFile(metricsFile, filename); err != nil {
  ctx.Fatalf("failed to copy %q to %q: %v\n", metricsFile, filename, err)
 }
}

// PrintOutDirWarning prints a warning to indicate to the user that
// setting output directory to a path other than "out" in an RBE enabled
// build can cause slow builds.
func PrintOutDirWarning(ctx Context, config Config) {
 if config.UseRBE() && config.OutDir() != defaultOutDir {
  fmt.Fprintln(ctx.Writer, "")
  fmt.Fprintln(ctx.Writer, "\033[33mWARNING:\033[0m")
  fmt.Fprintln(ctx.Writer, fmt.Sprintf("Setting OUT_DIR to a path other than %v may result in slow RBE builds.", defaultOutDir))
  fmt.Fprintln(ctx.Writer, "See http://go/android_rbe_out_dir for a workaround.")
  fmt.Fprintln(ctx.Writer, "")
 }
}

// ulimit returns ulimit result for |opt|.
// if the resource is unlimited, it returns math.MaxInt32 so that a caller do
// not need special handling of the returned value.
//
// Note that since go syscall package do not have RLIMIT_NPROC constant,
// we use bash ulimit instead.
func ulimitOrFatal(ctx Context, config Config, opt string) int {
 commandText := fmt.Sprintf("ulimit %s", opt)
 cmd := Command(ctx, config, nil, commandText, "bash", "-c", commandText)
 output := strings.TrimRight(string(cmd.CombinedOutputOrFatal()), "\n")
 ctx.Verbose(output + "\n")
 ctx.Verbose("done\n")

 if output == "unlimited" {
  return math.MaxInt32
 }
 num, err := strconv.Atoi(output)
 if err != nil {
  ctx.Fatalf("ulimit returned unexpected value: %s: %v\n", opt, err)
 }
 return num
}

[Dauer der Verarbeitung: 0.21 Sekunden, vorverarbeitet 2026-06-28]