Support for Fuchsia operating system
diff --git a/configure.py b/configure.py
index a443748..ca29f00 100755
--- a/configure.py
+++ b/configure.py
@@ -62,12 +62,14 @@
             self._platform = 'aix'
         elif self._platform.startswith('dragonfly'):
             self._platform = 'dragonfly'
+        elif self._platform.startswith('fuchsia'):
+            self._platform = 'fuchsia'
 
     @staticmethod
     def known_platforms():
       return ['linux', 'darwin', 'freebsd', 'openbsd', 'solaris', 'sunos5',
               'mingw', 'msvc', 'gnukfreebsd', 'bitrig', 'netbsd', 'aix',
-              'dragonfly']
+              'dragonfly', 'fuchsia']
 
     def platform(self):
         return self._platform
@@ -97,6 +99,9 @@
     def is_aix(self):
         return self._platform == 'aix'
 
+    def is_fuchsia(self):
+        return self._platform == 'fuchsia'
+
     def uses_usr_local(self):
         return self._platform in ('freebsd', 'openbsd', 'bitrig', 'dragonfly')
 
@@ -107,7 +112,8 @@
     def supports_ninja_browse(self):
         return (not self.is_windows()
                 and not self.is_solaris()
-                and not self.is_aix())
+                and not self.is_aix()
+                and not self.is_fuchsia())
 
     def can_rebuild_in_place(self):
         return not (self.is_windows() or self.is_aix())
@@ -506,6 +512,8 @@
     if platform.is_msvc():
         objs += cxx('minidump-win32')
     objs += cc('getopt')
+elif platform.is_fuchsia():
+    objs += cxx('subprocess-fuchsia')
 else:
     objs += cxx('subprocess-posix')
 if platform.is_aix():
diff --git a/src/subprocess-fuchsia.cc b/src/subprocess-fuchsia.cc
new file mode 100644
index 0000000..b59cca1
--- /dev/null
+++ b/src/subprocess-fuchsia.cc
@@ -0,0 +1,194 @@
+// Copyright 2018 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.
+
+#include "subprocess.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <launchpad/launchpad.h>
+#include <poll.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <zircon/errors.h>
+#include <zircon/process.h>
+#include <zircon/syscalls.h>
+#include <zircon/types.h>
+
+extern char** environ;
+
+#include "util.h"
+
+Subprocess::Subprocess(bool use_console) : fd_(-1), handle_(ZX_HANDLE_INVALID),
+                                           use_console_(use_console) {
+}
+
+Subprocess::~Subprocess() {
+  if (fd_ >= 0)
+    close(fd_);
+  // Reap child if forgotten.
+  if (handle_ != ZX_HANDLE_INVALID)
+    Finish();
+}
+
+bool Subprocess::Start(SubprocessSet* set, const string& command) {
+  printf("executing %s\n", command.c_str());
+
+  int output_pipe[2];
+  if (pipe(output_pipe) < 0)
+    Fatal("pipe: %s", strerror(errno));
+  fd_ = output_pipe[0];
+
+  launchpad_t* lp = NULL;
+  launchpad_create(ZX_HANDLE_INVALID, "sh", &lp);
+  const char* args[] = { "/boot/bin/sh", "-c", command.c_str() };
+  launchpad_load_from_file(lp, args[0]);
+  launchpad_set_args(lp, 3, args);
+  launchpad_clone(lp, LP_CLONE_ALL & (~LP_CLONE_FDIO_STDIO));
+
+  if (!use_console_) {
+    launchpad_clone_fd(lp, output_pipe[1], 1);
+    launchpad_clone_fd(lp, output_pipe[1], 2);
+  }
+
+  const char *errmsg = NULL;
+  if (launchpad_go(lp, &handle_, &errmsg) != ZX_OK)
+    Fatal(errmsg);
+  launchpad_destroy(lp);
+
+  close(output_pipe[1]);
+  return true;
+}
+
+void Subprocess::OnPipeReady() {
+  char buf[4 << 10];
+  ssize_t len = read(fd_, buf, sizeof(buf));
+  if (len > 0) {
+    buf_.append(buf, len);
+  } else {
+    if (len < 0)
+      Fatal("read: %s", strerror(errno));
+    close(fd_);
+    fd_ = -1;
+  }
+}
+
+ExitStatus Subprocess::Finish() {
+  assert(handle_ != ZX_HANDLE_INVALID);
+  zx_status_t status = zx_object_wait_one(handle_, ZX_PROCESS_TERMINATED, ZX_TIME_INFINITE, nullptr);
+  if (status != ZX_OK)
+    Fatal("zx_object_wait_one failed");
+
+  zx_info_process_t info;
+  status = zx_object_get_info(handle_, ZX_INFO_PROCESS, &info, sizeof(info), nullptr, nullptr);
+  if (status != ZX_OK)
+    Fatal("zx_object_get_info failed to get process status");
+
+  zx_handle_close(handle_);
+  handle_ = ZX_HANDLE_INVALID;
+
+  if (info.exited) {
+    if (info.return_code == 0)
+      return ExitSuccess;
+  }
+  return ExitFailure;
+}
+
+bool Subprocess::Done() const {
+  return fd_ == -1;
+}
+
+const string& Subprocess::GetOutput() const {
+  return buf_;
+}
+
+SubprocessSet::SubprocessSet() {
+}
+
+SubprocessSet::~SubprocessSet() {
+  Clear();
+}
+
+Subprocess *SubprocessSet::Add(const string& command, bool use_console) {
+  Subprocess *subprocess = new Subprocess(use_console);
+  if (!subprocess->Start(this, command)) {
+    delete subprocess;
+    return 0;
+  }
+  running_.push_back(subprocess);
+  return subprocess;
+}
+
+bool SubprocessSet::DoWork() {
+  vector<pollfd> fds;
+  nfds_t nfds = 0;
+
+  for (vector<Subprocess*>::iterator i = running_.begin();
+       i != running_.end(); ++i) {
+    int fd = (*i)->fd_;
+    if (fd < 0)
+      continue;
+    pollfd pfd = { fd, POLLIN | POLLPRI, 0 };
+    fds.push_back(pfd);
+    ++nfds;
+  }
+
+  int ret = ppoll(&fds.front(), nfds, NULL, NULL);
+  if (ret == -1) {
+    perror("ninja: ppoll");
+    return false;
+  }
+
+  nfds_t cur_nfd = 0;
+  for (vector<Subprocess*>::iterator i = running_.begin();
+       i != running_.end(); ) {
+    int fd = (*i)->fd_;
+    if (fd < 0)
+      continue;
+    assert(fd == fds[cur_nfd].fd);
+    if (fds[cur_nfd++].revents) {
+      (*i)->OnPipeReady();
+      if ((*i)->Done()) {
+        finished_.push(*i);
+        i = running_.erase(i);
+        continue;
+      }
+    }
+    ++i;
+  }
+
+  return false;
+}
+
+Subprocess* SubprocessSet::NextFinished() {
+  if (finished_.empty())
+    return NULL;
+  Subprocess* subproc = finished_.front();
+  finished_.pop();
+  return subproc;
+}
+
+void SubprocessSet::Clear() {
+  for (vector<Subprocess*>::iterator i = running_.begin();
+       i != running_.end(); ++i)
+    // Since the foreground process is in our process group, it will receive
+    // the interruption signal (i.e. SIGINT or SIGTERM) at the same time as us.
+    if (!(*i)->use_console_)
+      zx_task_kill((*i)->handle_);
+  for (vector<Subprocess*>::iterator i = running_.begin();
+       i != running_.end(); ++i)
+    delete *i;
+  running_.clear();
+}
diff --git a/src/subprocess.h b/src/subprocess.h
index b2d486c..adf6420 100644
--- a/src/subprocess.h
+++ b/src/subprocess.h
@@ -20,8 +20,11 @@
 #include <queue>
 using namespace std;
 
-#ifdef _WIN32
+#if defined(_WIN32)
 #include <windows.h>
+#elif defined(__Fuchsia__)
+#include <zircon/process.h>
+#include <zircon/types.h>
 #else
 #include <signal.h>
 #endif
@@ -58,7 +61,7 @@
 
   string buf_;
 
-#ifdef _WIN32
+#if defined(_WIN32)
   /// Set up pipe_ as the parent-side pipe of the subprocess; return the
   /// other end of the pipe, usable in the child process.
   HANDLE SetupPipe(HANDLE ioport);
@@ -68,6 +71,9 @@
   OVERLAPPED overlapped_;
   char overlapped_buf_[4 << 10];
   bool is_reading_;
+#elif defined(__Fuchsia__)
+  int fd_;
+  zx_handle_t handle_;
 #else
   int fd_;
   pid_t pid_;
@@ -92,9 +98,10 @@
   vector<Subprocess*> running_;
   queue<Subprocess*> finished_;
 
-#ifdef _WIN32
+#if defined(_WIN32)
   static BOOL WINAPI NotifyInterrupted(DWORD dwCtrlType);
   static HANDLE ioport_;
+#elif defined(__Fuchsia__)
 #else
   static void SetInterruptedFlag(int signum);
   static void HandlePendingInterruption();
diff --git a/src/util.cc b/src/util.cc
index ae94d34..9052733 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -368,7 +368,8 @@
 }
 
 void SetCloseOnExec(int fd) {
-#ifndef _WIN32
+#if defined(__Fuchsia__)
+#elif !defined(_WIN32)
   int flags = fcntl(fd, F_GETFD);
   if (flags < 0) {
     perror("fcntl(F_GETFD)");
@@ -563,6 +564,10 @@
     return -0.0f;
   return 1.0 / (1 << SI_LOAD_SHIFT) * si.loads[0];
 }
+#elif defined(__Fuchsia__)
+double GetLoadAverage() {
+  return -0.0f;
+}
 #else
 double GetLoadAverage() {
   double loadavg[3] = { 0.0f, 0.0f, 0.0f };