blob: 86db8878ad1d3bb97aa475688d043d0224a6b717 [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
68 executable := "prebuilts/build-tools/" + config.HostPrebuiltTag() + "/bin/ckati"
69 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",
76 }
77
78 if !config.Environment().IsFalse("KATI_EMULATE_FIND") {
79 args = append(args, "--use_find_emulator")
80 }
81
82 // The argument order could be simplified, but currently this matches
83 // the ordering in Make
84 args = append(args, "-f", "build/core/main.mk")
85
86 args = append(args, config.KatiArgs()...)
87
88 args = append(args,
89 "--gen_all_targets",
90 "BUILDING_WITH_NINJA=true",
91 "SOONG_ANDROID_MK="+config.SoongAndroidMk(),
92 "SOONG_MAKEVARS_MK="+config.SoongMakeVarsMk())
93
94 if config.UseGoma() {
95 args = append(args, "-j"+strconv.Itoa(config.Parallel()))
96 }
97
98 cmd := exec.CommandContext(ctx.Context, executable, args...)
99 cmd.Env = config.Environment().Environ()
Dan Willemsen29f88272017-02-18 18:12:41 -0800100 pipe, err := cmd.StdoutPipe()
101 if err != nil {
102 ctx.Fatalln("Error getting output pipe for ckati:", err)
103 }
104 cmd.Stderr = cmd.Stdout
105
Dan Willemsen1e704462016-08-21 15:17:17 -0700106 ctx.Verboseln(cmd.Path, cmd.Args)
Dan Willemsen29f88272017-02-18 18:12:41 -0800107 if err := cmd.Start(); err != nil {
108 ctx.Fatalln("Failed to run ckati:", err)
109 }
110
111 katiRewriteOutput(ctx, pipe)
112
113 if err := cmd.Wait(); err != nil {
Dan Willemsen1e704462016-08-21 15:17:17 -0700114 if e, ok := err.(*exec.ExitError); ok {
115 ctx.Fatalln("ckati failed with:", e.ProcessState.String())
116 } else {
117 ctx.Fatalln("Failed to run ckati:", err)
118 }
119 }
120}
Dan Willemsen29f88272017-02-18 18:12:41 -0800121
122var katiIncludeRe = regexp.MustCompile(`^(\[\d+/\d+] )?including [^ ]+ ...$`)
123
124func katiRewriteOutput(ctx Context, pipe io.ReadCloser) {
125 haveBlankLine := true
126 smartTerminal := ctx.IsTerminal()
127
128 scanner := bufio.NewScanner(pipe)
129 for scanner.Scan() {
130 line := scanner.Text()
131 verbose := katiIncludeRe.MatchString(line)
132
133 // For verbose lines, write them on the current line without a newline,
134 // then overwrite them if the next thing we're printing is another
135 // verbose line.
136 if smartTerminal && verbose {
137 // Limit line width to the terminal width, otherwise we'll wrap onto
138 // another line and we won't delete the previous line.
139 //
140 // Run this on every line in case the window has been resized while
141 // we're printing. This could be optimized to only re-run when we
142 // get SIGWINCH if it ever becomes too time consuming.
143 if max, ok := termWidth(ctx.Stdout()); ok {
144 if len(line) > max {
145 // Just do a max. Ninja elides the middle, but that's
146 // more complicated and these lines aren't that important.
147 line = line[:max]
148 }
149 }
150
151 // Move to the beginning on the line, print the output, then clear
152 // the rest of the line.
153 fmt.Fprint(ctx.Stdout(), "\r", line, "\x1b[K")
154 haveBlankLine = false
155 continue
156 } else if smartTerminal && !haveBlankLine {
157 // If we've previously written a verbose message, send a newline to save
158 // that message instead of overwriting it.
159 fmt.Fprintln(ctx.Stdout())
160 haveBlankLine = true
161 } else if !smartTerminal {
162 // Most editors display these as garbage, so strip them out.
163 line = string(stripAnsiEscapes([]byte(line)))
164 }
165
166 // Assume that non-verbose lines are important enough for stderr
167 fmt.Fprintln(ctx.Stderr(), line)
168 }
169
170 // Save our last verbose line.
171 if !haveBlankLine {
172 fmt.Fprintln(ctx.Stdout())
173 }
174}