blob: 218ca698627b8f655137ba3bbf746156b53b9a5e [file] [log] [blame]
Dan Willemsen1e704462016-08-21 15:17:17 -07001// Copyright 2017 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 (
Dan Willemsen29f88272017-02-18 18:12:41 -080018 "bufio"
Dan Willemsen1e704462016-08-21 15:17:17 -070019 "crypto/md5"
20 "fmt"
Dan Willemsen29f88272017-02-18 18:12:41 -080021 "io"
Dan Willemsen1e704462016-08-21 15:17:17 -070022 "io/ioutil"
23 "os/exec"
24 "path/filepath"
Dan Willemsen29f88272017-02-18 18:12:41 -080025 "regexp"
Dan Willemsen1e704462016-08-21 15:17:17 -070026 "strconv"
27 "strings"
28)
29
30var spaceSlashReplacer = strings.NewReplacer("/", "_", " ", "_")
31
32// genKatiSuffix creates a suffix for kati-generated files so that we can cache
33// them based on their inputs. So this should encode all common changes to Kati
34// inputs. Currently that includes the TARGET_PRODUCT, kati-processed command
35// line arguments, and the directories specified by mm/mmm.
36func genKatiSuffix(ctx Context, config Config) {
37 katiSuffix := "-" + config.TargetProduct()
38 if args := config.KatiArgs(); len(args) > 0 {
39 katiSuffix += "-" + spaceSlashReplacer.Replace(strings.Join(args, "_"))
40 }
41 if oneShot, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
42 katiSuffix += "-" + spaceSlashReplacer.Replace(oneShot)
43 }
44
45 // If the suffix is too long, replace it with a md5 hash and write a
46 // file that contains the original suffix.
47 if len(katiSuffix) > 64 {
48 shortSuffix := "-" + fmt.Sprintf("%x", md5.Sum([]byte(katiSuffix)))
49 config.SetKatiSuffix(shortSuffix)
50
51 ctx.Verbosef("Kati ninja suffix too long: %q", katiSuffix)
52 ctx.Verbosef("Replacing with: %q", shortSuffix)
53
54 if err := ioutil.WriteFile(strings.TrimSuffix(config.KatiNinjaFile(), "ninja")+"suf", []byte(katiSuffix), 0777); err != nil {
55 ctx.Println("Error writing suffix file:", err)
56 }
57 } else {
58 config.SetKatiSuffix(katiSuffix)
59 }
60}
61
62func runKati(ctx Context, config Config) {
Dan Willemsend9f6fa22016-08-21 15:17:17 -070063 ctx.BeginTrace("kati")
64 defer ctx.EndTrace()
65
Dan Willemsen1e704462016-08-21 15:17:17 -070066 genKatiSuffix(ctx, config)
67
Dan Willemsenf173d592017-04-27 14:28:00 -070068 executable := config.PrebuiltBuildTool("ckati")
Dan Willemsen1e704462016-08-21 15:17:17 -070069 args := []string{
70 "--ninja",
71 "--ninja_dir=" + config.OutDir(),
72 "--ninja_suffix=" + config.KatiSuffix(),
73 "--regen",
74 "--ignore_optional_include=" + filepath.Join(config.OutDir(), "%.P"),
75 "--detect_android_echo",
Dan Willemsenc38d3662017-02-24 10:53:23 -080076 "--color_warnings",
77 "--gen_all_targets",
78 "-f", "build/core/main.mk",
Dan Willemsen1e704462016-08-21 15:17:17 -070079 }
80
81 if !config.Environment().IsFalse("KATI_EMULATE_FIND") {
82 args = append(args, "--use_find_emulator")
83 }
84
Dan Willemsen1e704462016-08-21 15:17:17 -070085 args = append(args, config.KatiArgs()...)
86
87 args = append(args,
Dan Willemsen1e704462016-08-21 15:17:17 -070088 "BUILDING_WITH_NINJA=true",
89 "SOONG_ANDROID_MK="+config.SoongAndroidMk(),
90 "SOONG_MAKEVARS_MK="+config.SoongMakeVarsMk())
91
92 if config.UseGoma() {
93 args = append(args, "-j"+strconv.Itoa(config.Parallel()))
94 }
95
96 cmd := exec.CommandContext(ctx.Context, executable, args...)
97 cmd.Env = config.Environment().Environ()
Dan Willemsen29f88272017-02-18 18:12:41 -080098 pipe, err := cmd.StdoutPipe()
99 if err != nil {
100 ctx.Fatalln("Error getting output pipe for ckati:", err)
101 }
102 cmd.Stderr = cmd.Stdout
103
Dan Willemsen1e704462016-08-21 15:17:17 -0700104 ctx.Verboseln(cmd.Path, cmd.Args)
Dan Willemsen29f88272017-02-18 18:12:41 -0800105 if err := cmd.Start(); err != nil {
106 ctx.Fatalln("Failed to run ckati:", err)
107 }
108
109 katiRewriteOutput(ctx, pipe)
110
111 if err := cmd.Wait(); err != nil {
Dan Willemsen1e704462016-08-21 15:17:17 -0700112 if e, ok := err.(*exec.ExitError); ok {
113 ctx.Fatalln("ckati failed with:", e.ProcessState.String())
114 } else {
115 ctx.Fatalln("Failed to run ckati:", err)
116 }
117 }
118}
Dan Willemsen29f88272017-02-18 18:12:41 -0800119
120var katiIncludeRe = regexp.MustCompile(`^(\[\d+/\d+] )?including [^ ]+ ...$`)
121
122func katiRewriteOutput(ctx Context, pipe io.ReadCloser) {
123 haveBlankLine := true
124 smartTerminal := ctx.IsTerminal()
125
126 scanner := bufio.NewScanner(pipe)
127 for scanner.Scan() {
128 line := scanner.Text()
129 verbose := katiIncludeRe.MatchString(line)
130
131 // For verbose lines, write them on the current line without a newline,
132 // then overwrite them if the next thing we're printing is another
133 // verbose line.
134 if smartTerminal && verbose {
135 // Limit line width to the terminal width, otherwise we'll wrap onto
136 // another line and we won't delete the previous line.
137 //
138 // Run this on every line in case the window has been resized while
139 // we're printing. This could be optimized to only re-run when we
140 // get SIGWINCH if it ever becomes too time consuming.
141 if max, ok := termWidth(ctx.Stdout()); ok {
142 if len(line) > max {
143 // Just do a max. Ninja elides the middle, but that's
144 // more complicated and these lines aren't that important.
145 line = line[:max]
146 }
147 }
148
149 // Move to the beginning on the line, print the output, then clear
150 // the rest of the line.
151 fmt.Fprint(ctx.Stdout(), "\r", line, "\x1b[K")
152 haveBlankLine = false
153 continue
154 } else if smartTerminal && !haveBlankLine {
155 // If we've previously written a verbose message, send a newline to save
156 // that message instead of overwriting it.
157 fmt.Fprintln(ctx.Stdout())
158 haveBlankLine = true
159 } else if !smartTerminal {
160 // Most editors display these as garbage, so strip them out.
161 line = string(stripAnsiEscapes([]byte(line)))
162 }
163
164 // Assume that non-verbose lines are important enough for stderr
165 fmt.Fprintln(ctx.Stderr(), line)
166 }
167
168 // Save our last verbose line.
169 if !haveBlankLine {
170 fmt.Fprintln(ctx.Stdout())
171 }
172}