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 };