Merge "Make USE_NINJA=true the default"
diff --git a/core/Makefile b/core/Makefile
index 9ded53b..dfacf28 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -1383,7 +1383,7 @@
     $(call intermediates-dir-for,EXECUTABLES,sqlite3,,,$(TARGET_PREFER_32_BIT))/sqlite3
 
 # We can't build static executables when SANITIZE_TARGET=address
-ifneq (address,$(SANITIZE_TARGET))
+ifeq ($(strip $(SANITIZE_TARGET)),)
 built_ota_tools += \
     $(call intermediates-dir-for,EXECUTABLES,check_prereq,,,$(TARGET_PREFER_32_BIT))/check_prereq \
     $(call intermediates-dir-for,EXECUTABLES,applypatch_static,,,$(TARGET_PREFER_32_BIT))/applypatch_static \
@@ -1526,7 +1526,7 @@
 	# OTA scripts are only interested in fingerprint related properties
 	$(hide) echo "oem_fingerprint_properties=$(OEM_THUMBPRINT_PROPERTIES)" >> $(zip_root)/META/misc_info.txt
 endif
-ifeq ($(SANITIZE_TARGET),address)
+ifneq ($(strip $(SANITIZE_TARGET)),)
 	# We need to create userdata.img with real data because the instrumented libraries are in userdata.img.
 	$(hide) echo "userdata_img_with_data=true" >> $(zip_root)/META/misc_info.txt
 endif
@@ -1565,7 +1565,7 @@
 ifeq ($(BUILD_OS),darwin)
 build_ota_package := false
 endif
-ifeq ($(SANITIZE_TARGET),address)
+ifneq ($(strip $(SANITIZE_TARGET)),)
 build_ota_package := false
 endif
 ifeq ($(TARGET_PRODUCT),sdk)
diff --git a/core/binary.mk b/core/binary.mk
index 7aa9937..093cc38 100644
--- a/core/binary.mk
+++ b/core/binary.mk
@@ -927,6 +927,20 @@
 endif
 
 ###########################################################
+## ObjC++: Compile .mm files to .o
+###########################################################
+
+objcpp_sources := $(filter %.mm,$(my_src_files))
+objcpp_objects := $(addprefix $(intermediates)/,$(objcpp_sources:.mm=.o))
+
+ifneq ($(strip $(objcpp_objects)),)
+$(objcpp_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.mm $(yacc_cpps) $(proto_generated_headers) \
+    $(my_additional_dependencies)
+	$(transform-$(PRIVATE_HOST)mm-to-o)
+-include $(objcpp_objects:%.o=%.P)
+endif
+
+###########################################################
 ## AS: Compile .S files to .o.
 ###########################################################
 
@@ -1039,6 +1053,7 @@
     $(c_objects) \
     $(gen_c_objects) \
     $(objc_objects) \
+    $(objcpp_objects) \
     $(yacc_objects) \
     $(lex_objects) \
     $(proto_generated_objects) \
diff --git a/core/combo/TARGET_linux-arm.mk b/core/combo/TARGET_linux-arm.mk
index f4f3061..5f0f1d2 100644
--- a/core/combo/TARGET_linux-arm.mk
+++ b/core/combo/TARGET_linux-arm.mk
@@ -166,6 +166,7 @@
 endif
 
 KERNEL_HEADERS_COMMON := $(libc_root)/kernel/uapi
+KERNEL_HEADERS_COMMON += $(libc_root)/kernel/common
 KERNEL_HEADERS_ARCH   := $(libc_root)/kernel/uapi/asm-$(TARGET_$(combo_2nd_arch_prefix)ARCH)
 KERNEL_HEADERS := $(KERNEL_HEADERS_COMMON) $(KERNEL_HEADERS_ARCH)
 
diff --git a/core/combo/TARGET_linux-arm64.mk b/core/combo/TARGET_linux-arm64.mk
index 88f4d6f..b213ea7 100644
--- a/core/combo/TARGET_linux-arm64.mk
+++ b/core/combo/TARGET_linux-arm64.mk
@@ -137,6 +137,7 @@
 	-print-file-name=libgcov.a)
 
 KERNEL_HEADERS_COMMON := $(libc_root)/kernel/uapi
+KERNEL_HEADERS_COMMON += $(libc_root)/kernel/common
 KERNEL_HEADERS_ARCH   := $(libc_root)/kernel/uapi/asm-$(TARGET_ARCH)
 KERNEL_HEADERS := $(KERNEL_HEADERS_COMMON) $(KERNEL_HEADERS_ARCH)
 
diff --git a/core/combo/TARGET_linux-mips.mk b/core/combo/TARGET_linux-mips.mk
index 3d215ba..8e117eb 100644
--- a/core/combo/TARGET_linux-mips.mk
+++ b/core/combo/TARGET_linux-mips.mk
@@ -140,6 +140,7 @@
 endif
 
 KERNEL_HEADERS_COMMON := $(libc_root)/kernel/uapi
+KERNEL_HEADERS_COMMON += $(libc_root)/kernel/common
 KERNEL_HEADERS_ARCH   := $(libc_root)/kernel/uapi/asm-mips # mips covers both mips and mips64.
 KERNEL_HEADERS := $(KERNEL_HEADERS_COMMON) $(KERNEL_HEADERS_ARCH)
 
diff --git a/core/combo/TARGET_linux-mips64.mk b/core/combo/TARGET_linux-mips64.mk
index 25b9c78..565083a 100644
--- a/core/combo/TARGET_linux-mips64.mk
+++ b/core/combo/TARGET_linux-mips64.mk
@@ -146,6 +146,7 @@
 endif
 
 KERNEL_HEADERS_COMMON := $(libc_root)/kernel/uapi
+KERNEL_HEADERS_COMMON += $(libc_root)/kernel/common
 KERNEL_HEADERS_ARCH   := $(libc_root)/kernel/uapi/asm-mips
 # TODO: perhaps use $(libc_root)/kernel/uapi/asm-$(TARGET_ARCH) instead of asm-mips ?
 KERNEL_HEADERS := $(KERNEL_HEADERS_COMMON) $(KERNEL_HEADERS_ARCH)
diff --git a/core/combo/TARGET_linux-x86.mk b/core/combo/TARGET_linux-x86.mk
index 2375d1a..5fff641 100644
--- a/core/combo/TARGET_linux-x86.mk
+++ b/core/combo/TARGET_linux-x86.mk
@@ -72,6 +72,7 @@
 libm_root := bionic/libm
 
 KERNEL_HEADERS_COMMON := $(libc_root)/kernel/uapi
+KERNEL_HEADERS_COMMON += $(libc_root)/kernel/common
 KERNEL_HEADERS_ARCH   := $(libc_root)/kernel/uapi/asm-x86 # x86 covers both x86 and x86_64.
 KERNEL_HEADERS := $(KERNEL_HEADERS_COMMON) $(KERNEL_HEADERS_ARCH)
 
diff --git a/core/combo/TARGET_linux-x86_64.mk b/core/combo/TARGET_linux-x86_64.mk
index 0bdfb46..cacfae1 100644
--- a/core/combo/TARGET_linux-x86_64.mk
+++ b/core/combo/TARGET_linux-x86_64.mk
@@ -72,6 +72,7 @@
 libm_root := bionic/libm
 
 KERNEL_HEADERS_COMMON := $(libc_root)/kernel/uapi
+KERNEL_HEADERS_COMMON += $(libc_root)/kernel/common
 KERNEL_HEADERS_ARCH   := $(libc_root)/kernel/uapi/asm-x86 # x86 covers both x86 and x86_64.
 KERNEL_HEADERS := $(KERNEL_HEADERS_COMMON) $(KERNEL_HEADERS_ARCH)
 
diff --git a/core/config_sanitizers.mk b/core/config_sanitizers.mk
index 1efc932..7189338 100644
--- a/core/config_sanitizers.mk
+++ b/core/config_sanitizers.mk
@@ -75,6 +75,14 @@
   my_sanitize := $(CLANG_DEFAULT_UB_CHECKS)
 endif
 
+ifneq ($(filter coverage,$(my_sanitize)),)
+  ifeq ($(filter address,$(my_sanitize)),)
+    $(error $(LOCAL_PATH): $(LOCAL_MODULE): Use of 'coverage' also requires 'address')
+  endif
+  my_cflags += -fsanitize-coverage=edge,indirect-calls,8bit-counters,trace-cmp
+  my_sanitize := $(filter-out coverage,$(my_sanitize))
+endif
+
 ifneq ($(my_sanitize),)
   fsanitize_arg := $(subst $(space),$(comma),$(my_sanitize)),
   my_cflags += -fsanitize=$(fsanitize_arg)
diff --git a/core/definitions.mk b/core/definitions.mk
index f56a49b..57b0af0 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -1198,6 +1198,14 @@
 $(transform-d-to-p)
 endef
 
+###########################################################
+## Commands for running gcc to compile a host Objective-C++ file
+###########################################################
+
+define transform-host-mm-to-o
+$(transform-host-cpp-to-o)
+endef
+
 
 ###########################################################
 ## Rules to compile a single C/C++ source with ../ in the path
@@ -1632,6 +1640,13 @@
 ## Commands for running javac to make .class files
 ###########################################################
 
+# Add BUILD_NUMBER to apps default version name if it's unbundled build.
+ifdef TARGET_BUILD_APPS
+APPS_DEFAULT_VERSION_NAME := $(PLATFORM_VERSION)-$(BUILD_NUMBER_FROM_FILE)
+else
+APPS_DEFAULT_VERSION_NAME := $(PLATFORM_VERSION)
+endif
+
 # TODO: Right now we generate the asset resources twice, first as part
 # of generating the Java classes, then at the end when packaging the final
 # assets.  This should be changed to do one of two things: (1) Don't generate
@@ -1658,7 +1673,7 @@
     $(addprefix --min-sdk-version , $(PRIVATE_DEFAULT_APP_TARGET_SDK)) \
     $(addprefix --target-sdk-version , $(PRIVATE_DEFAULT_APP_TARGET_SDK)) \
     $(if $(filter --version-code,$(PRIVATE_AAPT_FLAGS)),,--version-code $(PLATFORM_SDK_VERSION)) \
-    $(if $(filter --version-name,$(PRIVATE_AAPT_FLAGS)),,--version-name $(PLATFORM_VERSION)-$(BUILD_NUMBER_FROM_FILE)) \
+    $(if $(filter --version-name,$(PRIVATE_AAPT_FLAGS)),,--version-name $(APPS_DEFAULT_VERSION_NAME)) \
     $(addprefix --rename-manifest-package , $(PRIVATE_MANIFEST_PACKAGE_NAME)) \
     $(addprefix --rename-instrumentation-target-package , $(PRIVATE_MANIFEST_INSTRUMENTATION_FOR))
 endef
@@ -2009,7 +2024,7 @@
     $(addprefix --target-sdk-version , $(PRIVATE_DEFAULT_APP_TARGET_SDK)) \
     $(if $(filter --product,$(PRIVATE_AAPT_FLAGS)),,$(addprefix --product , $(TARGET_AAPT_CHARACTERISTICS))) \
     $(if $(filter --version-code,$(PRIVATE_AAPT_FLAGS)),,--version-code $(PLATFORM_SDK_VERSION)) \
-    $(if $(filter --version-name,$(PRIVATE_AAPT_FLAGS)),,--version-name $(PLATFORM_VERSION)-$(BUILD_NUMBER_FROM_FILE)) \
+    $(if $(filter --version-name,$(PRIVATE_AAPT_FLAGS)),,--version-name $(APPS_DEFAULT_VERSION_NAME)) \
     $(addprefix --rename-manifest-package , $(PRIVATE_MANIFEST_PACKAGE_NAME)) \
     $(addprefix --rename-instrumentation-target-package , $(PRIVATE_MANIFEST_INSTRUMENTATION_FOR)) \
     -F $@
diff --git a/core/envsetup.mk b/core/envsetup.mk
index ecfca9f..51a2fb3 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -276,7 +276,7 @@
 TARGET_OUT_COMMON_GEN := $(TARGET_COMMON_OUT_ROOT)/gen
 
 TARGET_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM)
-ifeq ($(SANITIZE_TARGET),address)
+ifneq ($(filter address,$(SANITIZE_TARGET)),)
 target_out_shared_libraries_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_DATA)
 else
 target_out_shared_libraries_base := $(TARGET_OUT)
@@ -333,7 +333,7 @@
 TARGET_OUT_CACHE := $(PRODUCT_OUT)/cache
 
 TARGET_OUT_VENDOR := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR)
-ifeq ($(SANITIZE_TARGET),address)
+ifneq ($(filter address,$(SANITIZE_TARGET)),)
 target_out_vendor_shared_libraries_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_DATA)/vendor
 else
 target_out_vendor_shared_libraries_base := $(TARGET_OUT_VENDOR)
diff --git a/core/executable.mk b/core/executable.mk
index 0ce400c..70ef0d9 100644
--- a/core/executable.mk
+++ b/core/executable.mk
@@ -7,7 +7,7 @@
 # LOCAL_MODULE_STEM_64
 
 my_skip_this_target :=
-ifeq (address,$(strip $(SANITIZE_TARGET)))
+ifneq ($(filter address,$(SANITIZE_TARGET)),)
   ifeq (true,$(LOCAL_FORCE_STATIC_EXECUTABLE))
     my_skip_this_target := true
   else ifeq (false, $(LOCAL_CLANG))
diff --git a/core/fuzz_test.mk b/core/fuzz_test.mk
index 065cc03..fc582b3 100644
--- a/core/fuzz_test.mk
+++ b/core/fuzz_test.mk
@@ -7,7 +7,7 @@
     $(error $(LOCAL_PATH): $(LOCAL_MODULE): NDK fuzz tests are not supported.)
 endif
 
-LOCAL_CFLAGS += -fsanitize-coverage=edge,indirect-calls,8bit-counters
+LOCAL_CFLAGS += -fsanitize-coverage=edge,indirect-calls,8bit-counters,trace-cmp
 LOCAL_STATIC_LIBRARIES += libLLVMFuzzer
 
 ifdef LOCAL_MODULE_PATH
diff --git a/core/host_fuzz_test.mk b/core/host_fuzz_test.mk
index e917959..cc7baad 100644
--- a/core/host_fuzz_test.mk
+++ b/core/host_fuzz_test.mk
@@ -3,7 +3,7 @@
 ## Common flags for host fuzz tests are added.
 ################################################
 
-LOCAL_CFLAGS += -fsanitize-coverage=edge,indirect-calls,8bit-counters
+LOCAL_CFLAGS += -fsanitize-coverage=edge,indirect-calls,8bit-counters,trace-cmp
 LOCAL_STATIC_LIBRARIES += libLLVMFuzzer
 
 include $(BUILD_HOST_EXECUTABLE)
diff --git a/core/ninja.mk b/core/ninja.mk
index d603996..56b4ac5 100644
--- a/core/ninja.mk
+++ b/core/ninja.mk
@@ -90,7 +90,7 @@
 ifdef KATI_REMOTE_NUM_JOBS_FLAG
 KATI_MAKEPARALLEL := $(MAKEPARALLEL)
 else
-NINJA_MAKEPARALLEL := $(MAKEPARALLEL)
+NINJA_MAKEPARALLEL := $(MAKEPARALLEL) --ninja
 endif
 
 ifeq (,$(filter generateonly,$(ORIGINAL_MAKECMDGOALS)))
diff --git a/envsetup.sh b/envsetup.sh
index fa4e2d6..fcf3b47 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -183,7 +183,7 @@
     fi
 
     export PATH=$ANDROID_BUILD_PATHS$PATH
-    export PYTHONPATH=$T/system/core:$PYTHONPATH
+    export PYTHONPATH=$T/development/python-packages:$PYTHONPATH
 
     unset ANDROID_JAVA_TOOLCHAIN
     unset ANDROID_PRE_BUILD_PATHS
diff --git a/tools/makeparallel/Makefile b/tools/makeparallel/Makefile
index ed8fdfc..4e79708 100644
--- a/tools/makeparallel/Makefile
+++ b/tools/makeparallel/Makefile
@@ -59,6 +59,34 @@
 
 -include $(MAKEPARALLEL_INTERMEDIATES_PATH)/*.d
 
-.PHONY: test
-test: $(MAKEPARALLEL)
-	MAKEFLAGS= $(MAKE) -j1234 -C $(MAKEPARALLEL_SRC_PATH) -f Makefile.test MAKEPARALLEL=$(MAKEPARALLEL) test
+.PHONY: makeparallel_test
+MAKEPARALLEL_TEST := MAKEFLAGS= MAKELEVEL= MAKEPARALLEL=$(MAKEPARALLEL) $(MAKE) -f Makefile.test test
+MAKEPARALLEL_NINJA_TEST := MAKEFLAGS= MAKELEVEL= MAKEPARALLEL="$(MAKEPARALLEL) --ninja" $(MAKE) -f Makefile.test test
+makeparallel_test: $(MAKEPARALLEL)
+	@EXPECTED="-j1234" $(MAKEPARALLEL_TEST) -j1234
+	@EXPECTED="-j123"  $(MAKEPARALLEL_TEST) -j123
+	@EXPECTED="-j1"    $(MAKEPARALLEL_TEST) -j1
+	@EXPECTED="-j1"    $(MAKEPARALLEL_TEST)
+
+	@EXPECTED="-j1234" $(MAKEPARALLEL_NINJA_TEST) -j1234
+	@EXPECTED="-j123"  $(MAKEPARALLEL_NINJA_TEST) -j123
+	@EXPECTED="-j1"    $(MAKEPARALLEL_NINJA_TEST) -j1
+	@EXPECTED="-j1"    $(MAKEPARALLEL_NINJA_TEST)
+	@EXPECTED=""       $(MAKEPARALLEL_NINJA_TEST) -j
+	@EXPECTED=""       $(MAKEPARALLEL_NINJA_TEST) -j -l
+
+	@EXPECTED="-j1234" $(MAKEPARALLEL_TEST) --no-print-directory -j1234
+	@EXPECTED="-j1234" $(MAKEPARALLEL_TEST) --no-print-directory -k -j1234
+	@EXPECTED="-j1234" $(MAKEPARALLEL_TEST) -k -j1234
+	@EXPECTED="-j1234" $(MAKEPARALLEL_TEST) -j1234 -k
+	@EXPECTED="-j1234" $(MAKEPARALLEL_TEST) -kt -j1234
+
+	@EXPECTED="-j1234"     $(MAKEPARALLEL_NINJA_TEST) --no-print-directory -j1234
+	@EXPECTED="-j1234 -k0" $(MAKEPARALLEL_NINJA_TEST) --no-print-directory -k -j1234
+	@EXPECTED="-j1234 -k0" $(MAKEPARALLEL_NINJA_TEST) -k -j1234
+	@EXPECTED="-j1234 -k0" $(MAKEPARALLEL_NINJA_TEST) -j1234 -k
+	@EXPECTED="-j1234 -k0" $(MAKEPARALLEL_NINJA_TEST) -kt -j1234
+
+	@EXPECTED="-j1"    $(MAKEPARALLEL_TEST) A=-j1234
+	@EXPECTED="-j1"    $(MAKEPARALLEL_TEST) A\ -j1234=-j1234
+	@EXPECTED="-j1234" $(MAKEPARALLEL_TEST) A\ -j1234=-j1234 -j1234
diff --git a/tools/makeparallel/Makefile.test b/tools/makeparallel/Makefile.test
index bd682f7..91aacf7 100644
--- a/tools/makeparallel/Makefile.test
+++ b/tools/makeparallel/Makefile.test
@@ -2,4 +2,11 @@
 
 .PHONY: test
 test:
-	+if [ "$$($(MAKEPARALLEL) echo)" = "-j1234" ]; then echo SUCCESS; else echo FAILED; fi
+	@+echo MAKEFLAGS=$${MAKEFLAGS};              \
+	result=$$($(MAKEPARALLEL) echo);             \
+	echo result: $${result};                     \
+	if [ "$${result}" = "$(EXPECTED)" ]; then    \
+	  echo SUCCESS && echo;                      \
+	else                                         \
+	  echo FAILED expected $(EXPECTED) && false; \
+	fi
diff --git a/tools/makeparallel/makeparallel.cpp b/tools/makeparallel/makeparallel.cpp
index 5eb1dd6..7dd0ceb 100644
--- a/tools/makeparallel/makeparallel.cpp
+++ b/tools/makeparallel/makeparallel.cpp
@@ -19,6 +19,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <getopt.h>
 #include <poll.h>
 #include <signal.h>
 #include <stdio.h>
@@ -55,41 +56,108 @@
   }
 }
 
-// Extract --jobserver-fds= argument from MAKEFLAGS environment variable.
-static int GetJobserver(int* in_fd, int* out_fd) {
+// Extract flags from MAKEFLAGS that need to be propagated to subproccess
+static std::vector<std::string> ReadMakeflags() {
+  std::vector<std::string> args;
+
   const char* makeflags_env = getenv("MAKEFLAGS");
   if (makeflags_env == nullptr) {
-    return false;
+    return args;
   }
 
+  // The MAKEFLAGS format is pretty useless.  The first argument might be empty
+  // (starts with a leading space), or it might be a set of one-character flags
+  // merged together with no leading space, or it might be a variable
+  // definition.
+
   std::string makeflags = makeflags_env;
 
-  const std::string jobserver_fds_arg = "--jobserver-fds=";
-  size_t start = makeflags.find(jobserver_fds_arg);
+  // Split makeflags into individual args on spaces.  Multiple spaces are
+  // elided, but an initial space will result in a blank arg.
+  size_t base = 0;
+  size_t found;
+  do {
+    found = makeflags.find_first_of(" ", base);
+    args.push_back(makeflags.substr(base, found - base));
+    base = found + 1;
+  } while (found != makeflags.npos);
 
-  if (start == std::string::npos) {
-    return false;
+  // Drop the first argument if it is empty
+  while (args.size() > 0 && args[0].size() == 0) {
+	  args.erase(args.begin());
   }
 
-  start += jobserver_fds_arg.size();
-
-  std::string::size_type end = makeflags.find(' ', start);
-
-  std::string::size_type len;
-  if (end == std::string::npos) {
-    len = std::string::npos;
-  } else {
-    len = end - start;
+  // Prepend a - to the first argument if it does not have one and is not a
+  // variable definition
+  if (args.size() > 0 && args[0][0] != '-') {
+    if (args[0].find('=') == makeflags.npos) {
+      args[0] = '-' + args[0];
+    }
   }
 
-  std::string jobserver_fds = makeflags.substr(start, len);
+  return args;
+}
 
-  if (sscanf(jobserver_fds.c_str(), "%d,%d", in_fd, out_fd) != 2) {
-    return false;
+static bool ParseMakeflags(std::vector<std::string>& args,
+    int* in_fd, int* out_fd, bool* parallel, bool* keep_going) {
+
+  std::vector<char*> getopt_argv;
+  // getopt starts reading at argv[1]
+  getopt_argv.reserve(args.size() + 1);
+  getopt_argv.push_back(strdup(""));
+  for (std::string& v : args) {
+    getopt_argv.push_back(strdup(v.c_str()));
   }
 
-  CheckFd(*in_fd);
-  CheckFd(*out_fd);
+  opterr = 0;
+  optind = 1;
+  while (1) {
+    const static option longopts[] = {
+        {"jobserver-fds", required_argument, 0, 0},
+        {0, 0, 0, 0},
+    };
+    int longopt_index = 0;
+
+    int c = getopt_long(getopt_argv.size(), getopt_argv.data(), "kj",
+        longopts, &longopt_index);
+
+    if (c == -1) {
+      break;
+    }
+
+    switch (c) {
+    case 0:
+      switch (longopt_index) {
+      case 0:
+      {
+        // jobserver-fds
+        if (sscanf(optarg, "%d,%d", in_fd, out_fd) != 2) {
+          error(EXIT_FAILURE, 0, "incorrect format for --jobserver-fds: %s", optarg);
+        }
+        // TODO: propagate in_fd, out_fd
+        break;
+      }
+      default:
+        abort();
+      }
+      break;
+    case 'j':
+      *parallel = true;
+      break;
+    case 'k':
+      *keep_going = true;
+      break;
+    case '?':
+      // ignore unknown arguments
+      break;
+    default:
+      abort();
+    }
+  }
+
+  for (char *v : getopt_argv) {
+    free(v);
+  }
 
   return true;
 }
@@ -219,20 +287,47 @@
 int main(int argc, char* argv[]) {
   int in_fd = -1;
   int out_fd = -1;
+  bool parallel = false;
+  bool keep_going = false;
+  bool ninja = false;
   int tokens = 0;
 
+  if (argc > 1 && strcmp(argv[1], "--ninja") == 0) {
+    ninja = true;
+    argv++;
+    argc--;
+  }
+
   const char* path = argv[1];
   std::vector<char*> args(&argv[1], &argv[argc]);
 
-  if (GetJobserver(&in_fd, &out_fd)) {
-    fcntl(in_fd, F_SETFD, FD_CLOEXEC);
-    fcntl(out_fd, F_SETFD, FD_CLOEXEC);
-
-    tokens = GetJobserverTokens(in_fd);
+  std::vector<std::string> makeflags = ReadMakeflags();
+  if (ParseMakeflags(makeflags, &in_fd, &out_fd, &parallel, &keep_going)) {
+    if (in_fd >= 0 && out_fd >= 0) {
+      CheckFd(in_fd);
+      CheckFd(out_fd);
+      fcntl(in_fd, F_SETFD, FD_CLOEXEC);
+      fcntl(out_fd, F_SETFD, FD_CLOEXEC);
+      tokens = GetJobserverTokens(in_fd);
+    }
   }
 
   std::string jarg = "-j" + std::to_string(tokens + 1);
-  args.push_back(strdup(jarg.c_str()));
+
+  if (ninja) {
+    if (!parallel) {
+      // ninja is parallel by default, pass -j1 to disable parallelism if make wasn't parallel
+      args.push_back(strdup("-j1"));
+    } else if (tokens > 0) {
+      args.push_back(strdup(jarg.c_str()));
+    }
+    if (keep_going) {
+      args.push_back(strdup("-k0"));
+    }
+  } else {
+    args.push_back(strdup(jarg.c_str()));
+  }
+
   args.push_back(nullptr);
 
   pid_t pid = fork();