269 lines
7.3 KiB
Go
269 lines
7.3 KiB
Go
// 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"
|
|
|
|
// genKatiSuffix creates a suffix for kati-generated files so that we can cache
|
|
// them based on their inputs. So this should encode all common changes to Kati
|
|
// inputs. Currently that includes the TARGET_PRODUCT, kati-processed command
|
|
// line arguments, and the directories specified by mm/mmm.
|
|
func genKatiSuffix(ctx Context, config Config) {
|
|
katiSuffix := "-" + config.TargetProduct()
|
|
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 runKati(ctx Context, config Config, extraSuffix string, args []string, envFunc func(*Environment)) {
|
|
executable := config.PrebuiltBuildTool("ckati")
|
|
args = append([]string{
|
|
"--ninja",
|
|
"--ninja_dir=" + config.OutDir(),
|
|
"--ninja_suffix=" + config.KatiSuffix() + extraSuffix,
|
|
"--no_ninja_prelude",
|
|
"--use_ninja_phony_output",
|
|
"--use_ninja_symlink_outputs",
|
|
"--regen",
|
|
"--ignore_optional_include=" + filepath.Join(config.OutDir(), "%.P"),
|
|
"--detect_android_echo",
|
|
"--color_warnings",
|
|
"--gen_all_targets",
|
|
"--use_find_emulator",
|
|
"--werror_find_emulator",
|
|
"--no_builtin_rules",
|
|
"--werror_suffix_rules",
|
|
"--warn_real_to_phony",
|
|
"--warn_phony_looks_real",
|
|
"--werror_real_to_phony",
|
|
"--werror_phony_looks_real",
|
|
"--werror_writable",
|
|
"--top_level_phony",
|
|
"--kati_stats",
|
|
}, args...)
|
|
|
|
if config.Environment().IsEnvTrue("EMPTY_NINJA_FILE") {
|
|
args = append(args, "--empty_ninja_file")
|
|
}
|
|
|
|
if config.UseRemoteBuild() {
|
|
args = append(args, "--default_pool=local_pool")
|
|
}
|
|
|
|
cmd := Command(ctx, config, "ckati", executable, args...)
|
|
cmd.Sandbox = katiSandbox
|
|
pipe, err := cmd.StdoutPipe()
|
|
if err != nil {
|
|
ctx.Fatalln("Error getting output pipe for ckati:", err)
|
|
}
|
|
cmd.Stderr = cmd.Stdout
|
|
|
|
envFunc(cmd.Environment)
|
|
|
|
if _, 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)
|
|
}
|
|
|
|
if _, ok := cmd.Environment.Get("BUILD_HOSTNAME"); !ok {
|
|
hostname, err := os.Hostname()
|
|
if err != nil {
|
|
ctx.Println("Failed to read hostname:", err)
|
|
hostname = "unknown"
|
|
}
|
|
cmd.Environment.Set("BUILD_HOSTNAME", hostname)
|
|
}
|
|
|
|
cmd.StartOrFatal()
|
|
status.KatiReader(ctx.Status.StartTool(), pipe)
|
|
cmd.WaitOrFatal()
|
|
}
|
|
|
|
func runKatiBuild(ctx Context, config Config) {
|
|
ctx.BeginTrace(metrics.RunKati, "kati build")
|
|
defer ctx.EndTrace()
|
|
|
|
args := []string{
|
|
"--writable", config.OutDir() + "/",
|
|
"--werror_implicit_rules",
|
|
"-f", "build/make/core/main.mk",
|
|
}
|
|
|
|
if !config.BuildBrokenDupRules() {
|
|
args = append(args, "--werror_overriding_commands")
|
|
}
|
|
|
|
args = append(args, config.KatiArgs()...)
|
|
|
|
args = append(args,
|
|
"SOONG_MAKEVARS_MK="+config.SoongMakeVarsMk(),
|
|
"SOONG_ANDROID_MK="+config.SoongAndroidMk(),
|
|
"TARGET_DEVICE_DIR="+config.TargetDeviceDir(),
|
|
"KATI_PACKAGE_MK_DIR="+config.KatiPackageMkDir())
|
|
|
|
runKati(ctx, config, katiBuildSuffix, args, func(env *Environment) {})
|
|
|
|
distGzipFile(ctx, config, config.KatiBuildNinjaFile())
|
|
|
|
cleanCopyHeaders(ctx, config)
|
|
cleanOldInstalledFiles(ctx, config)
|
|
}
|
|
|
|
func cleanCopyHeaders(ctx Context, config Config) {
|
|
ctx.BeginTrace("clean", "clean copy headers")
|
|
defer ctx.EndTrace()
|
|
|
|
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:]
|
|
|
|
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
|
|
})
|
|
}
|
|
|
|
func cleanOldInstalledFiles(ctx Context, config Config) {
|
|
ctx.BeginTrace("clean", "clean old installed files")
|
|
defer ctx.EndTrace()
|
|
|
|
// 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")
|
|
}
|
|
|
|
func runKatiPackage(ctx Context, config Config) {
|
|
ctx.BeginTrace(metrics.RunKati, "kati package")
|
|
defer ctx.EndTrace()
|
|
|
|
args := []string{
|
|
"--writable", config.DistDir() + "/",
|
|
"--werror_implicit_rules",
|
|
"--werror_overriding_commands",
|
|
"-f", "build/make/packaging/main.mk",
|
|
"KATI_PACKAGE_MK_DIR=" + config.KatiPackageMkDir(),
|
|
}
|
|
|
|
runKati(ctx, config, katiPackageSuffix, 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",
|
|
}...)
|
|
|
|
if config.Dist() {
|
|
env.Set("DIST", "true")
|
|
env.Set("DIST_DIR", config.DistDir())
|
|
}
|
|
})
|
|
|
|
distGzipFile(ctx, config, config.KatiPackageNinjaFile())
|
|
}
|
|
|
|
func runKatiCleanSpec(ctx Context, config Config) {
|
|
ctx.BeginTrace(metrics.RunKati, "kati cleanspec")
|
|
defer ctx.EndTrace()
|
|
|
|
runKati(ctx, config, katiCleanspecSuffix, []string{
|
|
"--werror_implicit_rules",
|
|
"--werror_overriding_commands",
|
|
"-f", "build/make/core/cleanbuild.mk",
|
|
"SOONG_MAKEVARS_MK=" + config.SoongMakeVarsMk(),
|
|
"TARGET_DEVICE_DIR=" + config.TargetDeviceDir(),
|
|
}, func(env *Environment) {})
|
|
}
|