Merge "Add preprocessed_ndk_headers."
diff --git a/android/variable.go b/android/variable.go
index 29f7c31..68fa7cd 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -54,14 +54,6 @@
 			Cflags []string `android:"arch_variant"`
 		} `android:"arch_variant"`
 
-		Cpusets struct {
-			Cflags []string
-		}
-
-		Schedboost struct {
-			Cflags []string
-		}
-
 		Binder32bit struct {
 			Cflags []string
 		}
@@ -118,8 +110,6 @@
 	Malloc_not_svelte          *bool `json:",omitempty"`
 	Safestack                  *bool `json:",omitempty"`
 	HostStaticBinaries         *bool `json:",omitempty"`
-	Cpusets                    *bool `json:",omitempty"`
-	Schedboost                 *bool `json:",omitempty"`
 	Binder32bit                *bool `json:",omitempty"`
 	UseGoma                    *bool `json:",omitempty"`
 	Debuggable                 *bool `json:",omitempty"`
diff --git a/cmd/microfactory/microfactory.go b/cmd/microfactory/microfactory.go
index 3ed5a2c..d0febe7 100644
--- a/cmd/microfactory/microfactory.go
+++ b/cmd/microfactory/microfactory.go
@@ -67,8 +67,22 @@
 	verbose = false
 
 	goToolDir = filepath.Join(runtime.GOROOT(), "pkg", "tool", runtime.GOOS+"_"+runtime.GOARCH)
+	goVersion = findGoVersion()
 )
 
+func findGoVersion() string {
+	if version, err := ioutil.ReadFile(filepath.Join(runtime.GOROOT(), "VERSION")); err == nil {
+		return string(version)
+	}
+
+	cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), "version")
+	if version, err := cmd.Output(); err == nil {
+		return string(version)
+	} else {
+		panic(fmt.Sprintf("Unable to discover go version: %v", err))
+	}
+}
+
 type GoPackage struct {
 	Name string
 
@@ -218,7 +232,7 @@
 	shaFile := p.output + ".hash"
 
 	hash := sha1.New()
-	fmt.Fprintln(hash, runtime.GOOS, runtime.GOARCH, runtime.Version())
+	fmt.Fprintln(hash, runtime.GOOS, runtime.GOARCH, goVersion)
 
 	cmd := exec.Command(filepath.Join(goToolDir, "compile"),
 		"-o", p.output,
diff --git a/soong_ui.bash b/soong_ui.bash
index 724d9c5..e1af359 100755
--- a/soong_ui.bash
+++ b/soong_ui.bash
@@ -53,7 +53,7 @@
 {
     # Increment when microfactory changes enough that it cannot rebuild itself.
     # For example, if we use a new command line argument that doesn't work on older versions.
-    local mf_version=1
+    local mf_version=2
 
     local mf_src="${TOP}/build/soong/cmd/microfactory"
 
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index e4044e1..51aed2c 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -33,5 +33,12 @@
     ],
     testSrcs: [
         "environment_test.go",
+        "util_test.go",
     ],
+    darwin: {
+        srcs: ["util_darwin.go"],
+    },
+    linux: {
+        srcs: ["util_linux.go"],
+    },
 }
diff --git a/ui/build/context.go b/ui/build/context.go
index 8144e58..f85bb6c 100644
--- a/ui/build/context.go
+++ b/ui/build/context.go
@@ -95,3 +95,10 @@
 		c.Tracer.ImportNinjaLog(c.Thread, filename, startOffset)
 	}
 }
+
+func (c ContextImpl) IsTerminal() bool {
+	if term, ok := os.LookupEnv("TERM"); ok {
+		return term != "dumb" && isTerminal(c.Stdout()) && isTerminal(c.Stderr())
+	}
+	return false
+}
diff --git a/ui/build/kati.go b/ui/build/kati.go
index 423bcbc..86db887 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -15,11 +15,14 @@
 package build
 
 import (
+	"bufio"
 	"crypto/md5"
 	"fmt"
+	"io"
 	"io/ioutil"
 	"os/exec"
 	"path/filepath"
+	"regexp"
 	"strconv"
 	"strings"
 )
@@ -94,10 +97,20 @@
 
 	cmd := exec.CommandContext(ctx.Context, executable, args...)
 	cmd.Env = config.Environment().Environ()
-	cmd.Stdout = ctx.Stdout()
-	cmd.Stderr = ctx.Stderr()
+	pipe, err := cmd.StdoutPipe()
+	if err != nil {
+		ctx.Fatalln("Error getting output pipe for ckati:", err)
+	}
+	cmd.Stderr = cmd.Stdout
+
 	ctx.Verboseln(cmd.Path, cmd.Args)
-	if err := cmd.Run(); err != nil {
+	if err := cmd.Start(); err != nil {
+		ctx.Fatalln("Failed to run ckati:", err)
+	}
+
+	katiRewriteOutput(ctx, pipe)
+
+	if err := cmd.Wait(); err != nil {
 		if e, ok := err.(*exec.ExitError); ok {
 			ctx.Fatalln("ckati failed with:", e.ProcessState.String())
 		} else {
@@ -105,3 +118,57 @@
 		}
 	}
 }
+
+var katiIncludeRe = regexp.MustCompile(`^(\[\d+/\d+] )?including [^ ]+ ...$`)
+
+func katiRewriteOutput(ctx Context, pipe io.ReadCloser) {
+	haveBlankLine := true
+	smartTerminal := ctx.IsTerminal()
+
+	scanner := bufio.NewScanner(pipe)
+	for scanner.Scan() {
+		line := scanner.Text()
+		verbose := katiIncludeRe.MatchString(line)
+
+		// 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 !smartTerminal {
+			// 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())
+	}
+}
diff --git a/ui/build/signal.go b/ui/build/signal.go
index 3c8c8e1..6643e2f 100644
--- a/ui/build/signal.go
+++ b/ui/build/signal.go
@@ -21,40 +21,74 @@
 	"syscall"
 
 	"android/soong/ui/logger"
+	"time"
 )
 
-// SetupSignals sets up signal handling to kill our children and allow us to cleanly finish
-// writing our log/trace files.
+// SetupSignals sets up signal handling to ensure all of our subprocesses are killed and that
+// our log/trace buffers are flushed to disk.
 //
-// Currently, on the first SIGINT|SIGALARM we call the cancel() function, which is usually
-// the CancelFunc returned by context.WithCancel, which will kill all the commands running
-// within that Context. Usually that's enough, and you'll run through your normal error paths.
+// All of our subprocesses are in the same process group, so they'll receive a SIGINT at the
+// same time we do. Most of the time this means we just need to ignore the signal and we'll
+// just see errors from all of our subprocesses. But in case that fails, when we get a signal:
 //
-// If another signal comes in after the first one, we'll trigger a panic with full stacktraces
-// from every goroutine so that it's possible to debug what is stuck. Just before the process
-// exits, we'll call the cleanup() function so that you can flush your log files.
+//   1. Wait two seconds to exit normally.
+//   2. Call cancel() which is normally the cancellation of a Context. This will send a SIGKILL
+//      to any subprocesses attached to that context.
+//   3. Wait two seconds to exit normally.
+//   4. Call cleanup() to close the log/trace buffers, then panic.
+//   5. If another two seconds passes (if cleanup got stuck, etc), then panic.
+//
 func SetupSignals(log logger.Logger, cancel, cleanup func()) {
 	signals := make(chan os.Signal, 5)
-	// TODO: Handle other signals
-	signal.Notify(signals, os.Interrupt, syscall.SIGALRM)
+	signal.Notify(signals, os.Interrupt, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM)
 	go handleSignals(signals, log, cancel, cleanup)
 }
 
 func handleSignals(signals chan os.Signal, log logger.Logger, cancel, cleanup func()) {
-	defer cleanup()
+	var timeouts int
+	var timeout <-chan time.Time
 
-	var force bool
+	handleTimeout := func() {
+		timeouts += 1
+		switch timeouts {
+		case 1:
+			// Things didn't exit cleanly, cancel our ctx (SIGKILL to subprocesses)
+			// Do this asynchronously to ensure it won't block and prevent us from
+			// taking more drastic measures.
+			log.Println("Still alive, killing subprocesses...")
+			go cancel()
+		case 2:
+			// Cancel didn't work. Try to run cleanup manually, then we'll panic
+			// at the next timer whether it finished or not.
+			log.Println("Still alive, cleaning up...")
+
+			// Get all stacktraces to see what was stuck
+			debug.SetTraceback("all")
+
+			go func() {
+				defer log.Panicln("Timed out exiting...")
+				cleanup()
+			}()
+		default:
+			// In case cleanup() deadlocks, the next tick will panic.
+			log.Panicln("Got signal, but timed out exiting...")
+		}
+	}
 
 	for {
-		s := <-signals
-		if force {
-			// So that we can better see what was stuck
-			debug.SetTraceback("all")
-			log.Panicln("Second signal received:", s)
-		} else {
+		select {
+		case s := <-signals:
 			log.Println("Got signal:", s)
-			cancel()
-			force = true
+
+			// Another signal triggers our next timeout handler early
+			if timeout != nil {
+				handleTimeout()
+			}
+
+			// Wait 2 seconds for everything to exit cleanly.
+			timeout = time.Tick(time.Second * 2)
+		case <-timeout:
+			handleTimeout()
 		}
 	}
 }
diff --git a/ui/build/util.go b/ui/build/util.go
index ad084da..37ac6b9 100644
--- a/ui/build/util.go
+++ b/ui/build/util.go
@@ -15,9 +15,13 @@
 package build
 
 import (
+	"bytes"
+	"io"
 	"os"
 	"path/filepath"
 	"strings"
+	"syscall"
+	"unsafe"
 )
 
 // indexList finds the index of a string in a []string
@@ -77,3 +81,81 @@
 	}
 	return str[:idx], str[idx+1:], true
 }
+
+func isTerminal(w io.Writer) bool {
+	if f, ok := w.(*os.File); ok {
+		var termios syscall.Termios
+		_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
+			ioctlGetTermios, uintptr(unsafe.Pointer(&termios)),
+			0, 0, 0)
+		return err == 0
+	}
+	return false
+}
+
+func termWidth(w io.Writer) (int, bool) {
+	if f, ok := w.(*os.File); ok {
+		var winsize struct {
+			ws_row, ws_column    uint16
+			ws_xpixel, ws_ypixel uint16
+		}
+		_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
+			syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&winsize)),
+			0, 0, 0)
+		return int(winsize.ws_column), err == 0
+	}
+	return 0, false
+}
+
+// stripAnsiEscapes strips ANSI control codes from a byte array in place.
+func stripAnsiEscapes(input []byte) []byte {
+	// read represents the remaining part of input that needs to be processed.
+	read := input
+	// write represents where we should be writing in input.
+	// It will share the same backing store as input so that we make our modifications
+	// in place.
+	write := input
+
+	// advance will copy count bytes from read to write and advance those slices
+	advance := func(write, read []byte, count int) ([]byte, []byte) {
+		copy(write, read[:count])
+		return write[count:], read[count:]
+	}
+
+	for {
+		// Find the next escape sequence
+		i := bytes.IndexByte(read, 0x1b)
+		// If it isn't found, or if there isn't room for <ESC>[, finish
+		if i == -1 || i+1 >= len(read) {
+			copy(write, read)
+			break
+		}
+
+		// Not a CSI code, continue searching
+		if read[i+1] != '[' {
+			write, read = advance(write, read, i+1)
+			continue
+		}
+
+		// Found a CSI code, advance up to the <ESC>
+		write, read = advance(write, read, i)
+
+		// Find the end of the CSI code
+		i = bytes.IndexFunc(read, func(r rune) bool {
+			return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z')
+		})
+		if i == -1 {
+			// We didn't find the end of the code, just remove the rest
+			i = len(read) - 1
+		}
+
+		// Strip off the end marker too
+		i = i + 1
+
+		// Skip the reader forward and reduce final length by that amount
+		read = read[i:]
+		input = input[:len(input)-i]
+	}
+
+	return input
+}
diff --git a/ui/build/util_darwin.go b/ui/build/util_darwin.go
new file mode 100644
index 0000000..254a9b8
--- /dev/null
+++ b/ui/build/util_darwin.go
@@ -0,0 +1,21 @@
+// 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 (
+	"syscall"
+)
+
+const ioctlGetTermios = syscall.TIOCGETA
diff --git a/ui/build/util_linux.go b/ui/build/util_linux.go
new file mode 100644
index 0000000..0a4e1d2
--- /dev/null
+++ b/ui/build/util_linux.go
@@ -0,0 +1,21 @@
+// 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 (
+	"syscall"
+)
+
+const ioctlGetTermios = syscall.TCGETS
diff --git a/ui/build/util_test.go b/ui/build/util_test.go
new file mode 100644
index 0000000..e85eada
--- /dev/null
+++ b/ui/build/util_test.go
@@ -0,0 +1,62 @@
+// 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 "testing"
+
+func TestStripAnsiEscapes(t *testing.T) {
+	testcases := []struct {
+		input  string
+		output string
+	}{
+		{
+			"",
+			"",
+		},
+		{
+			"This is a test",
+			"This is a test",
+		},
+		{
+			"interrupted: \x1b[12",
+			"interrupted: ",
+		},
+		{
+			"other \x1bescape \x1b",
+			"other \x1bescape \x1b",
+		},
+		{ // from pretty-error macro
+			"\x1b[1mart/Android.mk: \x1b[31merror:\x1b[0m\x1b[1m art: test error \x1b[0m",
+			"art/Android.mk: error: art: test error ",
+		},
+		{ // from envsetup.sh make wrapper
+			"\x1b[0;31m#### make failed to build some targets (2 seconds) ####\x1b[00m",
+			"#### make failed to build some targets (2 seconds) ####",
+		},
+		{ // from clang (via ninja testcase)
+			"\x1b[1maffixmgr.cxx:286:15: \x1b[0m\x1b[0;1;35mwarning: \x1b[0m\x1b[1musing the result... [-Wparentheses]\x1b[0m",
+			"affixmgr.cxx:286:15: warning: using the result... [-Wparentheses]",
+		},
+	}
+	for _, tc := range testcases {
+		got := string(stripAnsiEscapes([]byte(tc.input)))
+		if got != tc.output {
+			t.Errorf("output strings didn't match\n"+
+				"input: %#v\n"+
+				" want: %#v\n"+
+				"  got: %#v", tc.input, tc.output, got)
+		}
+	}
+}