208 lines
6.1 KiB
Go
208 lines
6.1 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 (
|
|
"bufio"
|
|
"crypto/md5"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
var spaceSlashReplacer = strings.NewReplacer("/", "_", " ", "_")
|
|
|
|
// 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 oneShot, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
|
|
katiSuffix += "-" + spaceSlashReplacer.Replace(oneShot)
|
|
}
|
|
|
|
// 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.KatiNinjaFile(), "ninja")+"suf", []byte(katiSuffix), 0777); err != nil {
|
|
ctx.Println("Error writing suffix file:", err)
|
|
}
|
|
} else {
|
|
config.SetKatiSuffix(katiSuffix)
|
|
}
|
|
}
|
|
|
|
func runKati(ctx Context, config Config) {
|
|
genKatiSuffix(ctx, config)
|
|
|
|
runKatiCleanSpec(ctx, config)
|
|
|
|
ctx.BeginTrace("kati")
|
|
defer ctx.EndTrace()
|
|
|
|
executable := config.PrebuiltBuildTool("ckati")
|
|
args := []string{
|
|
"--ninja",
|
|
"--ninja_dir=" + config.OutDir(),
|
|
"--ninja_suffix=" + config.KatiSuffix(),
|
|
"--regen",
|
|
"--ignore_optional_include=" + filepath.Join(config.OutDir(), "%.P"),
|
|
"--detect_android_echo",
|
|
"--color_warnings",
|
|
"--gen_all_targets",
|
|
"--werror_find_emulator",
|
|
"--kati_stats",
|
|
"-f", "build/make/core/main.mk",
|
|
}
|
|
|
|
if !config.Environment().IsFalse("KATI_EMULATE_FIND") {
|
|
args = append(args, "--use_find_emulator")
|
|
}
|
|
|
|
args = append(args, config.KatiArgs()...)
|
|
|
|
args = append(args,
|
|
"BUILDING_WITH_NINJA=true",
|
|
"SOONG_ANDROID_MK="+config.SoongAndroidMk(),
|
|
"SOONG_MAKEVARS_MK="+config.SoongMakeVarsMk())
|
|
|
|
if config.UseGoma() {
|
|
args = append(args, "-j"+strconv.Itoa(config.Parallel()))
|
|
}
|
|
|
|
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
|
|
|
|
cmd.StartOrFatal()
|
|
katiRewriteOutput(ctx, pipe)
|
|
cmd.WaitOrFatal()
|
|
}
|
|
|
|
var katiIncludeRe = regexp.MustCompile(`^(\[\d+/\d+] )?including [^ ]+ ...$`)
|
|
var katiLogRe = regexp.MustCompile(`^\*kati\*: `)
|
|
|
|
func katiRewriteOutput(ctx Context, pipe io.ReadCloser) {
|
|
haveBlankLine := true
|
|
smartTerminal := ctx.IsTerminal()
|
|
errSmartTerminal := ctx.IsErrTerminal()
|
|
|
|
scanner := bufio.NewScanner(pipe)
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
verbose := katiIncludeRe.MatchString(line)
|
|
|
|
// Only put kati debug/stat lines in our verbose log
|
|
if katiLogRe.MatchString(line) {
|
|
ctx.Verbose(line)
|
|
continue
|
|
}
|
|
|
|
// For verbose lines, write them on the current line without a newline,
|
|
// then overwrite them if the next thing we're printing is another
|
|
// verbose line.
|
|
if smartTerminal && verbose {
|
|
// Limit line width to the terminal width, otherwise we'll wrap onto
|
|
// another line and we won't delete the previous line.
|
|
//
|
|
// Run this on every line in case the window has been resized while
|
|
// we're printing. This could be optimized to only re-run when we
|
|
// get SIGWINCH if it ever becomes too time consuming.
|
|
if max, ok := termWidth(ctx.Stdout()); ok {
|
|
if len(line) > max {
|
|
// Just do a max. Ninja elides the middle, but that's
|
|
// more complicated and these lines aren't that important.
|
|
line = line[:max]
|
|
}
|
|
}
|
|
|
|
// Move to the beginning on the line, print the output, then clear
|
|
// the rest of the line.
|
|
fmt.Fprint(ctx.Stdout(), "\r", line, "\x1b[K")
|
|
haveBlankLine = false
|
|
continue
|
|
} else if smartTerminal && !haveBlankLine {
|
|
// If we've previously written a verbose message, send a newline to save
|
|
// that message instead of overwriting it.
|
|
fmt.Fprintln(ctx.Stdout())
|
|
haveBlankLine = true
|
|
} else if !errSmartTerminal {
|
|
// Most editors display these as garbage, so strip them out.
|
|
line = string(stripAnsiEscapes([]byte(line)))
|
|
}
|
|
|
|
// Assume that non-verbose lines are important enough for stderr
|
|
fmt.Fprintln(ctx.Stderr(), line)
|
|
}
|
|
|
|
// Save our last verbose line.
|
|
if !haveBlankLine {
|
|
fmt.Fprintln(ctx.Stdout())
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
ctx.Println("Error from kati parser:", err)
|
|
io.Copy(ctx.Stderr(), pipe)
|
|
}
|
|
}
|
|
|
|
func runKatiCleanSpec(ctx Context, config Config) {
|
|
ctx.BeginTrace("kati cleanspec")
|
|
defer ctx.EndTrace()
|
|
|
|
executable := config.PrebuiltBuildTool("ckati")
|
|
args := []string{
|
|
"--ninja",
|
|
"--ninja_dir=" + config.OutDir(),
|
|
"--ninja_suffix=" + config.KatiSuffix() + "-cleanspec",
|
|
"--regen",
|
|
"--detect_android_echo",
|
|
"--color_warnings",
|
|
"--gen_all_targets",
|
|
"--werror_find_emulator",
|
|
"--use_find_emulator",
|
|
"-f", "build/make/core/cleanbuild.mk",
|
|
"BUILDING_WITH_NINJA=true",
|
|
"SOONG_MAKEVARS_MK=" + config.SoongMakeVarsMk(),
|
|
}
|
|
|
|
cmd := Command(ctx, config, "ckati", executable, args...)
|
|
cmd.Sandbox = katiCleanSpecSandbox
|
|
cmd.Stdout = ctx.Stdout()
|
|
cmd.Stderr = ctx.Stderr()
|
|
|
|
// Kati leaks memory, so ensure leak detection is turned off
|
|
cmd.Environment.Set("ASAN_OPTIONS", "detect_leaks=0")
|
|
cmd.RunOrFatal()
|
|
}
|