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 13 kB image not shown  

Quelle  kati.go   Sprache: unbekannt

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

// 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 (
 "crypto/md5"
 "fmt"
 "io/ioutil"
 "os"
 "os/user"
 "path/filepath"
 "strings"

 "android/soong/ui/metrics"
 "android/soong/ui/status"
)

var spaceSlashReplacer = strings.NewReplacer("/", "_", " ", "_")

const katiBuildSuffix = ""
const katiCleanspecSuffix = "-cleanspec"
const katiPackageSuffix = "-package"
const katiSoongOnlyPackageSuffix = "-soong-only-package"

// genKatiSuffix creates a filename suffix for kati-generated files so that we
// can cache them based on their inputs. Such files include the generated Ninja
// files and env.sh environment variable setup files.
//
// The filename suffix should encode all common changes to Kati inputs.
// Currently that includes the TARGET_PRODUCT and kati-processed command line
// arguments.
func genKatiSuffix(ctx Context, config Config) {
 targetProduct := "unknown"
 if p, err := config.TargetProductOrErr(); err == nil {
  targetProduct = p
 }
 // Construct the base suffix.
 katiSuffix := "-" + targetProduct + config.CoverageSuffix()

 // Append kati arguments to the suffix.
 if args := config.KatiArgs(); len(args) > 0 {
  katiSuffix += "-" + spaceSlashReplacer.Replace(strings.Join(args, "_"))
 }

 // If the suffix is too long, replace it with a md5 hash and write a
 // file that contains the original suffix.
 if len(katiSuffix) > 64 {
  shortSuffix := "-" + fmt.Sprintf("%x", md5.Sum([]byte(katiSuffix)))
  config.SetKatiSuffix(shortSuffix)

  ctx.Verbosef("Kati ninja suffix too long: %q", katiSuffix)
  ctx.Verbosef("Replacing with: %q", shortSuffix)

  if err := ioutil.WriteFile(strings.TrimSuffix(config.KatiBuildNinjaFile(), "ninja")+"suf", []byte(katiSuffix), 0777); err != nil {
   ctx.Println("Error writing suffix file:", err)
  }
 } else {
  config.SetKatiSuffix(katiSuffix)
 }
}

func writeValueIfChanged(ctx Context, filePath string, value string) {
 previousValue := ""
 rawPreviousValue, err := os.ReadFile(filePath)
 if err == nil {
  previousValue = string(rawPreviousValue)
 }

 if previousValue != value {
  if err = os.WriteFile(filePath, []byte(value), 0666); err != nil {
   ctx.Fatalf("Failed to write: %v", err)
  }
 }
}

func copyFileIfChanged(ctx Context, from, to string) {
 fromContents, err := os.ReadFile(from)
 if err != nil {
  ctx.Fatalf("Failed to read %s: ", err)
 }
 writeValueIfChanged(ctx, to, string(fromContents))
}

// Base function to construct and run the Kati command line with additional
// arguments, and a custom function closure to mutate the environment Kati runs
// in.
func runKati(ctx Context, config Config, e *TraceEvent, extraSuffix string, args []string, envFunc func(*Environment)) {
 executable := config.KatiBin()
 // Kati arguments.
 args = append([]string{
  // Instead of executing commands directly, generate a Ninja file.
  "--ninja",
  // Generate Ninja files in the output directory.
  "--ninja_dir=" + config.OutDir(),
  // Filename suffix of the generated Ninja file.
  "--ninja_suffix=" + config.KatiSuffix() + extraSuffix,
  // Remove common parts at the beginning of a Ninja file, like build_dir,
  // local_pool and _kati_always_build_. Allows Kati to be run multiple
  // times, with generated Ninja files combined in a single invocation
  // using 'include'.
  "--no_ninja_prelude",
  // Support declaring phony outputs in AOSP Ninja.
  "--use_ninja_phony_output",
  // Regenerate the Ninja file if environment inputs have changed. e.g.
  // CLI flags, .mk file timestamps, env vars, $(wildcard ..) and some
  // $(shell ..) results.
  "--regen",
  // Skip '-include' directives starting with the specified path. Used to
  // ignore generated .mk files.
  "--ignore_optional_include=" + filepath.Join(config.OutDir(), "%.P"),
  // Detect the use of $(shell echo ...).
  "--detect_android_echo",
  // Colorful ANSI-based warning and error messages.
  "--color_warnings",
  // Generate all targets, not just the top level requested ones.
  "--gen_all_targets",
  // Use the built-in emulator of GNU find for better file finding
  // performance. Used with $(shell find ...).
  "--use_find_emulator",
  // Fail when the find emulator encounters problems.
  "--werror_find_emulator",
  // Do not provide any built-in rules.
  "--no_builtin_rules",
  // Fail when suffix rules are used.
  "--werror_suffix_rules",
  // Fail when a real target depends on a phony target.
  "--werror_real_to_phony",
  // Makes real_to_phony checks assume that any top-level or leaf
  // dependencies that does *not* have a '/' in it is a phony target.
  "--top_level_phony",
  // Fail when a phony target contains slashes.
  "--werror_phony_looks_real",
  // Fail when writing to a read-only directory.
  "--werror_writable",
  // Print Kati's internal statistics, such as the number of variables,
  // implicit/explicit/suffix rules, and so on.
  "--kati_stats",
 }, args...)

 // Generate a minimal Ninja file.
 //
 // Used for build_test and multiproduct_kati, which runs Kati several
 // hundred times for different configurations to test file generation logic.
 // These can result in generating Ninja files reaching ~1GB or more,
 // resulting in ~hundreds of GBs of writes.
 //
 // Since we don't care about executing the Ninja files in these test cases,
 // generating the Ninja file content wastes time, so skip writing any
 // information out with --empty_ninja_file.
 //
 // From https://github.com/google/kati/commit/87b8da7af2c8bea28b1d8ab17679453d859f96e5
 if config.EmptyNinjaFile() {
  args = append(args, "--empty_ninja_file")
 }

 // Apply 'local_pool' to to all rules that don't specify a pool.
 if config.UseRemoteBuild() {
  args = append(args, "--default_pool=local_pool")
 }

 cmd := Command(ctx, config, e, "kati", executable, args...)

 // Set up the nsjail sandbox.
 cmd.Sandbox = katiSandbox

 // Set up stdout and stderr.
 pipe, err := cmd.StdoutPipe()
 if err != nil {
  ctx.Fatalln("Error getting output pipe for kati:", err)
 }
 cmd.Stderr = cmd.Stdout

 var username string
 // Pass on various build environment metadata to Kati.
 if usernameFromEnv, ok := cmd.Environment.Get("BUILD_USERNAME"); !ok {
  username = "unknown"
  if u, err := user.Current(); err == nil {
   username = u.Username
  } else {
   ctx.Println("Failed to get current user:", err)
  }
  cmd.Environment.Set("BUILD_USERNAME", username)
 } else {
  username = usernameFromEnv
 }

 // SOONG_USE_PARTIAL_COMPILE may be used in makefiles, but both cases must be supported.
 //
 // In general, the partial compile features will be implemented in Soong-based rules. We
 // also allow them to be used in makefiles.  Clear the environment variable when calling
 // kati so that we avoid reanalysis when the user changes it.  We will pass it to Ninja
 // (via the file `${OUT_DIR}/soong/use_partial_compile-${TARGET_PRODUCT}.sh`).
 // As a result, rules where we want to allow the developer to toggle the feature ("use
 // the partial compile feature" vs "legacy, aka full compile behavior") need to use this
 // in the rule, since changing it will not cause reanalysis.
 //
 // Shell syntax in the rule might look something like this:
 //     if [[ -n ${SOONG_USE_PARTIAL_COMPILE} ]]; then
 //         # partial compile behavior
 //     else
 //         # legacy behavior
 //     fi
 cmd.Environment.Unset("SOONG_USE_PARTIAL_COMPILE")

 // Unset BUILD_HOSTNAME during kati run to avoid kati rerun, kati will use BUILD_HOSTNAME from a file.
 cmd.Environment.Unset("BUILD_HOSTNAME")

 _, ok := cmd.Environment.Get("BUILD_NUMBER")
 // Unset BUILD_NUMBER during kati run to avoid kati rerun, kati will use BUILD_NUMBER from a file.
 cmd.Environment.Unset("BUILD_NUMBER")
 if ok {
  cmd.Environment.Set("HAS_BUILD_NUMBER", "true")
 } else {
  cmd.Environment.Set("HAS_BUILD_NUMBER", "false")
 }

 // Apply the caller's function closure to mutate the environment variables.
 envFunc(cmd.Environment)

 cmd.StartOrFatal()
 // Set up the ToolStatus command line reader for Kati for a consistent UI
 // for the user.
 status.KatiReader(ctx.Status.StartTool(), pipe)
 cmd.WaitOrFatal()
}

func runKatiBuild(ctx Context, config Config) {
 e := ctx.BeginTrace(metrics.RunKati, "kati build")
 defer e.End()

 args := []string{
  // Mark the output directory as writable.
  "--writable", config.OutDir() + "/",
  // Fail when encountering implicit rules. e.g.
  // %.foo: %.bar
  //   cp $< $@
  "--werror_implicit_rules",
  // Entry point for the Kati Ninja file generation.
  "-f", "build/make/core/main.mk",
 }

 if !config.BuildBrokenDupRules() {
  // Fail when redefining / duplicating a target.
  args = append(args, "--werror_overriding_commands")
 }

 args = append(args, config.KatiArgs()...)

 args = append(args,
  // Location of the Make vars .mk file generated by Soong.
  "SOONG_MAKEVARS_MK="+config.SoongMakeVarsMk(),
  // Location of the Android.mk file generated by Soong. This
  // file contains Soong modules represented as Kati modules,
  // allowing Kati modules to depend on Soong modules.
  "SOONG_ANDROID_MK="+config.SoongAndroidMk(),
  // Directory containing outputs for the target device.
  "TARGET_DEVICE_DIR="+config.TargetDeviceDir(),
  // Directory containing .mk files for packaging purposes, such as
  // the dist.mk file, containing dist-for-goals data.
  "KATI_PACKAGE_MK_DIR="+config.KatiPackageMkDir())

 runKati(ctx, config, e, katiBuildSuffix, args, func(env *Environment) {})

 // compress and dist the main build ninja file.
 distGzipFile(ctx, config, config.KatiBuildNinjaFile(), "soong_ui")

 // Cleanup steps.
 cleanCopyHeaders(ctx, config)
 cleanOldInstalledFiles(ctx, config)
}

// Clean out obsolete header files on the disk that were *not copied* during the
// build with BUILD_COPY_HEADERS and LOCAL_COPY_HEADERS.
//
// These should be increasingly uncommon, as it's a deprecated feature and there
// isn't an equivalent feature in Soong.
func cleanCopyHeaders(ctx Context, config Config) {
 e := ctx.BeginTrace("clean", "clean copy headers")
 defer e.End()

 // Read and parse the list of copied headers from a file in the product
 // output directory.
 data, err := ioutil.ReadFile(filepath.Join(config.ProductOut(), ".copied_headers_list"))
 if err != nil {
  if os.IsNotExist(err) {
   return
  }
  ctx.Fatalf("Failed to read copied headers list: %v", err)
 }

 headers := strings.Fields(string(data))
 if len(headers) < 1 {
  ctx.Fatal("Failed to parse copied headers list: %q", string(data))
 }
 headerDir := headers[0]
 headers = headers[1:]

 // Walk the tree and remove any headers that are not in the list of copied
 // headers in the current build.
 filepath.Walk(headerDir,
  func(path string, info os.FileInfo, err error) error {
   if err != nil {
    return nil
   }
   if info.IsDir() {
    return nil
   }
   if !inList(path, headers) {
    ctx.Printf("Removing obsolete header %q", path)
    if err := os.Remove(path); err != nil {
     ctx.Fatalf("Failed to remove obsolete header %q: %v", path, err)
    }
   }
   return nil
  })
}

// Clean out any previously installed files from the disk that are not installed
// in the current build.
func cleanOldInstalledFiles(ctx Context, config Config) {
 e := ctx.BeginTrace("clean", "clean old installed files")
 defer e.End()

 // We shouldn't be removing files from one side of the two-step asan builds
 var suffix string
 if v, ok := config.Environment().Get("SANITIZE_TARGET"); ok {
  if sanitize := strings.Fields(v); inList("address", sanitize) {
   suffix = "_asan"
  }
 }

 cleanOldFiles(ctx, config.ProductOut(), ".installable_files"+suffix)

 cleanOldFiles(ctx, config.HostOut(), ".installable_test_files")
}

// Generate the Ninja file containing the packaging command lines for the dist
// dir.
func runKatiPackage(ctx Context, config Config) {
 e := ctx.BeginTrace(metrics.RunKati, "kati package")
 defer e.End()

 entryPoint := "build/make/packaging/main.mk"
 suffix := katiPackageSuffix
 ninjaFile := config.KatiPackageNinjaFile()

 args := []string{
  // Mark the dist dir as writable.
  "--writable", config.DistDir() + "/",
  // Fail when encountering implicit rules. e.g.
  "--werror_implicit_rules",
  // Fail when redefining / duplicating a target.
  "--werror_overriding_commands",
  // Entry point.
  "-f", entryPoint,
  // Directory containing .mk files for packaging purposes, such as
  // the dist.mk file, containing dist-for-goals data.
  "KATI_PACKAGE_MK_DIR=" + config.KatiPackageMkDir(),
  "KATI_SUFFIX=" + config.KatiSuffix(),
 }

 // Run Kati against a restricted set of environment variables.
 runKati(ctx, config, e, suffix, args, func(env *Environment) {
  env.Allow([]string{
   // Some generic basics
   "LANG",
   "LC_MESSAGES",
   "PATH",
   "PWD",
   "TMPDIR",

   // Tool configs
   "ASAN_SYMBOLIZER_PATH",
   "JAVA_HOME",
   "PYTHONDONTWRITEBYTECODE",

   // Build configuration
   "ANDROID_BUILD_SHELL",
   "DIST_DIR",
   "OUT_DIR",
   "FILE_NAME_TAG",
  }...)

  if config.Dist() {
   env.Set("DIST", "true")
   env.Set("DIST_DIR", config.DistDir())
  }
 })

 // Compress and dist the packaging Ninja file.
 distGzipFile(ctx, config, ninjaFile, "soong_ui")
}

// Run Kati on the cleanspec files to clean the build.
func runKatiCleanSpec(ctx Context, config Config) {
 e := ctx.BeginTrace(metrics.RunKati, "kati cleanspec")
 defer e.End()

 runKati(ctx, config, e, katiCleanspecSuffix, []string{
  // Fail when encountering implicit rules. e.g.
  "--werror_implicit_rules",
  // Fail when redefining / duplicating a target.
  "--werror_overriding_commands",
  // Entry point.
  "-f", "build/make/core/cleanbuild.mk",
  "SOONG_MAKEVARS_MK=" + config.SoongMakeVarsMk(),
  "TARGET_DEVICE_DIR=" + config.TargetDeviceDir(),
 }, func(env *Environment) {})
}

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