diff --git a/core/config.mk b/core/config.mk index 15dea31d11..58461df33b 100644 --- a/core/config.mk +++ b/core/config.mk @@ -484,7 +484,6 @@ endif prebuilt_sdk_tools := prebuilts/sdk/tools prebuilt_sdk_tools_bin := $(prebuilt_sdk_tools)/$(HOST_OS)/bin -# Always use prebuilts for ckati and makeparallel prebuilt_build_tools := prebuilts/build-tools prebuilt_build_tools_wrappers := prebuilts/build-tools/common/bin prebuilt_build_tools_jars := prebuilts/build-tools/common/framework @@ -542,7 +541,6 @@ DEPMOD := $(HOST_OUT_EXECUTABLES)/depmod FILESLIST := $(SOONG_HOST_OUT_EXECUTABLES)/fileslist FILESLIST_UTIL :=$= build/make/tools/fileslist_util.py HOST_INIT_VERIFIER := $(HOST_OUT_EXECUTABLES)/host_init_verifier -MAKEPARALLEL := $(prebuilt_build_tools_bin)/makeparallel SOONG_JAVAC_WRAPPER := $(SOONG_HOST_OUT_EXECUTABLES)/soong_javac_wrapper SOONG_ZIP := $(SOONG_HOST_OUT_EXECUTABLES)/soong_zip MERGE_ZIPS := $(SOONG_HOST_OUT_EXECUTABLES)/merge_zips diff --git a/core/main.mk b/core/main.mk index 16cba7114c..608f11b51f 100644 --- a/core/main.mk +++ b/core/main.mk @@ -10,22 +10,11 @@ SHELL := /bin/bash endif ifndef KATI - -host_prebuilts := linux-x86 -ifeq ($(shell uname),Darwin) -host_prebuilts := darwin-x86 +$(warning Calling make directly is no longer supported.) +$(warning Either use 'envsetup.sh; m' or 'build/soong/soong_ui.bash --make-mode') +$(error done) endif -.PHONY: run_soong_ui -run_soong_ui: - +@prebuilts/build-tools/$(host_prebuilts)/bin/makeparallel --ninja build/soong/soong_ui.bash --make-mode $(MAKECMDGOALS) - -.PHONY: $(MAKECMDGOALS) -$(sort $(MAKECMDGOALS)) : run_soong_ui - @#empty - -else # KATI - $(info [1/1] initializing build system ...) # Absolute path of the present working direcotry. @@ -1897,5 +1886,3 @@ ndk: $(SOONG_OUT_DIR)/ndk.timestamp $(call dist-write-file,$(KATI_PACKAGE_MK_DIR)/dist.mk) $(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] writing build rules ...) - -endif # KATI diff --git a/core/ninja_config.mk b/core/ninja_config.mk index e33ebf598d..b1f4b03862 100644 --- a/core/ninja_config.mk +++ b/core/ninja_config.mk @@ -55,7 +55,7 @@ PARSE_TIME_MAKE_GOALS := \ include $(wildcard vendor/*/build/ninja_config.mk) # Any Android goals that need to be built. -ANDROID_GOALS := $(filter-out $(KATI_OUTPUT_PATTERNS) $(CKATI) $(MAKEPARALLEL),\ +ANDROID_GOALS := $(filter-out $(KATI_OUTPUT_PATTERNS),\ $(sort $(ORIGINAL_MAKECMDGOALS) $(MAKECMDGOALS))) # Goals we need to pass to Ninja. NINJA_GOALS := $(filter-out $(NINJA_EXCLUDE_GOALS), $(ANDROID_GOALS)) diff --git a/tools/makeparallel/.gitignore b/tools/makeparallel/.gitignore deleted file mode 100644 index a7d6181959..0000000000 --- a/tools/makeparallel/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -makeparallel -*.o -*.d -test.out diff --git a/tools/makeparallel/Android.bp b/tools/makeparallel/Android.bp deleted file mode 100644 index 898db68610..0000000000 --- a/tools/makeparallel/Android.bp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2016 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. - -cc_binary_host { - name: "makeparallel", - srcs: [ - "makeparallel.cpp", - ], - cflags: ["-Wall", "-Werror"], -} diff --git a/tools/makeparallel/Makefile b/tools/makeparallel/Makefile deleted file mode 100644 index 82a4abfac2..0000000000 --- a/tools/makeparallel/Makefile +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright 2015 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. - -# Find source file location from path to this Makefile -MAKEPARALLEL_SRC_PATH := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) -ifndef MAKEPARALLEL_SRC_PATH - MAKEPARALLEL_SRC_PATH := . -endif - -# Set defaults if they weren't set by the including Makefile -MAKEPARALLEL_CXX ?= $(CXX) -MAKEPARALLEL_LD ?= $(CXX) -MAKEPARALLEL_INTERMEDIATES_PATH ?= . -MAKEPARALLEL_BIN_PATH ?= . - -MAKEPARALLEL_CXX_SRCS := \ - makeparallel.cpp - -MAKEPARALLEL_CXXFLAGS := -Wall -Werror -MMD -MP - -MAKEPARALLEL_CXX_SRCS := $(addprefix $(MAKEPARALLEL_SRC_PATH)/,\ - $(MAKEPARALLEL_CXX_SRCS)) - -MAKEPARALLEL_CXX_OBJS := $(patsubst $(MAKEPARALLEL_SRC_PATH)/%.cpp,$(MAKEPARALLEL_INTERMEDIATES_PATH)/%.o,$(MAKEPARALLEL_CXX_SRCS)) - -MAKEPARALLEL := $(MAKEPARALLEL_BIN_PATH)/makeparallel - -ifeq ($(shell uname),Linux) -MAKEPARALLEL_LIBS := -lrt -lpthread -endif - -# Rule to build makeparallel into MAKEPARALLEL_BIN_PATH -$(MAKEPARALLEL): $(MAKEPARALLEL_CXX_OBJS) - @mkdir -p $(dir $@) - $(MAKEPARALLEL_LD) -std=c++11 $(MAKEPARALLEL_CXXFLAGS) -o $@ $^ $(MAKEPARALLEL_LIBS) - -# Rule to build source files into object files in MAKEPARALLEL_INTERMEDIATES_PATH -$(MAKEPARALLEL_CXX_OBJS): $(MAKEPARALLEL_INTERMEDIATES_PATH)/%.o: $(MAKEPARALLEL_SRC_PATH)/%.cpp - @mkdir -p $(dir $@) - $(MAKEPARALLEL_CXX) -c -std=c++11 $(MAKEPARALLEL_CXXFLAGS) -o $@ $< - -makeparallel_clean: - rm -rf $(MAKEPARALLEL) - rm -rf $(MAKEPARALLEL_INTERMEDIATES_PATH)/*.o - rm -rf $(MAKEPARALLEL_INTERMEDIATES_PATH)/*.d - -.PHONY: makeparallel_clean - --include $(MAKEPARALLEL_INTERMEDIATES_PATH)/*.d - -.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="" $(MAKEPARALLEL_TEST) -j1 - @EXPECTED="-j$$(($$(nproc) + 2))" $(MAKEPARALLEL_TEST) -j - @EXPECTED="" $(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="" $(MAKEPARALLEL_TEST) A=-j1234 - - @EXPECTED="-j1234 args" ARGS="args" $(MAKEPARALLEL_TEST) -j1234 diff --git a/tools/makeparallel/Makefile.test b/tools/makeparallel/Makefile.test deleted file mode 100644 index cf53684c0d..0000000000 --- a/tools/makeparallel/Makefile.test +++ /dev/null @@ -1,12 +0,0 @@ -MAKEPARALLEL ?= ./makeparallel - -.PHONY: test -test: - @+echo MAKEFLAGS=$${MAKEFLAGS}; \ - result=$$($(MAKEPARALLEL) echo $(ARGS)); \ - echo result: $${result}; \ - if [ "$${result}" = "$(EXPECTED)" ]; then \ - echo SUCCESS && echo; \ - else \ - echo FAILED expected $(EXPECTED) && false; \ - fi diff --git a/tools/makeparallel/README.md b/tools/makeparallel/README.md deleted file mode 100644 index 2e5fbf9f6f..0000000000 --- a/tools/makeparallel/README.md +++ /dev/null @@ -1,54 +0,0 @@ - - -makeparallel -============ -makeparallel communicates with the [GNU make jobserver](http://make.mad-scientist.net/papers/jobserver-implementation/) -in order claim all available jobs, and then passes the number of jobs -claimed to a subprocess with `-j`. - -The number of available jobs is determined by reading tokens from the jobserver -until a read would block. If the makeparallel rule is the only one running the -number of jobs will be the total size of the jobserver pool, i.e. the value -passed to make with `-j`. Any jobs running in parallel with with the -makeparellel rule will reduce the measured value, and thus reduce the -parallelism available to the subprocess. - -To run a multi-thread or multi-process binary inside GNU make using -makeparallel, add -```Makefile - +makeparallel subprocess arguments -``` -to a rule. For example, to wrap ninja in make, use something like: -```Makefile - +makeparallel ninja -f build.ninja -``` - -To determine the size of the jobserver pool, add -```Makefile - +makeparallel echo > make.jobs -``` -to a rule that is guarantee to run alone (i.e. all other rules are either -dependencies of the makeparallel rule, or the depend on the makeparallel -rule. The output file will contain the `-j` flag passed to the parent -make process, or `-j1` if no flag was found. Since GNU make will run -makeparallel during the execution phase, after all variables have been -set and evaluated, it is not possible to get the output of makeparallel -into a make variable. Instead, use a shell substitution to read the output -file directly in a recipe. For example: -```Makefile - echo Make was started with $$(cat make.jobs) -``` diff --git a/tools/makeparallel/makeparallel.cpp b/tools/makeparallel/makeparallel.cpp deleted file mode 100644 index 66babdf13d..0000000000 --- a/tools/makeparallel/makeparallel.cpp +++ /dev/null @@ -1,421 +0,0 @@ -// Copyright (C) 2015 The Android Open Source Project -// -// 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. - -// makeparallel communicates with the GNU make jobserver -// (http://make.mad-scientist.net/papers/jobserver-implementation/) -// in order claim all available jobs, and then passes the number of jobs -// claimed to a subprocess with -j. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#ifdef __linux__ -#include -#endif - -#ifdef __APPLE__ -#include -#define error(code, eval, fmt, ...) errc(eval, code, fmt, ##__VA_ARGS__) -// Darwin does not interrupt syscalls by default. -#define TEMP_FAILURE_RETRY(exp) (exp) -#endif - -// Throw an error if fd is not valid. -static void CheckFd(int fd) { - int ret = fcntl(fd, F_GETFD); - if (ret < 0) { - if (errno == EBADF) { - error(errno, 0, "no jobserver pipe, prefix recipe command with '+'"); - } else { - error(errno, errno, "fnctl failed"); - } - } -} - -// Extract flags from MAKEFLAGS that need to be propagated to subproccess -static std::vector ReadMakeflags() { - std::vector args; - - const char* makeflags_env = getenv("MAKEFLAGS"); - if (makeflags_env == nullptr) { - 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; - - // 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); - - // Drop the first argument if it is empty - while (args.size() > 0 && args[0].size() == 0) { - args.erase(args.begin()); - } - - // 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]; - } - } - - return args; -} - -static bool ParseMakeflags(std::vector& args, - int* in_fd, int* out_fd, bool* parallel, bool* keep_going) { - - std::vector 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())); - } - - 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; -} - -// Read a single byte from fd, with timeout in milliseconds. Returns true if -// a byte was read, false on timeout. Throws away the read value. -// Non-reentrant, uses timer and signal handler global state, plus static -// variable to communicate with signal handler. -// -// Uses a SIGALRM timer to fire a signal after timeout_ms that will interrupt -// the read syscall if it hasn't yet completed. If the timer fires before the -// read the read could block forever, so read from a dup'd fd and close it from -// the signal handler, which will cause the read to return EBADF if it occurs -// after the signal. -// The dup/read/close combo is very similar to the system described to avoid -// a deadlock between SIGCHLD and read at -// http://make.mad-scientist.net/papers/jobserver-implementation/ -static bool ReadByteTimeout(int fd, int timeout_ms) { - // global variable to communicate with the signal handler - static int dup_fd = -1; - - // dup the fd so the signal handler can close it without losing the real one - dup_fd = dup(fd); - if (dup_fd < 0) { - error(errno, errno, "dup failed"); - } - - // set up a signal handler that closes dup_fd on SIGALRM - struct sigaction action = {}; - action.sa_flags = SA_SIGINFO, - action.sa_sigaction = [](int, siginfo_t*, void*) { - close(dup_fd); - }; - struct sigaction oldaction = {}; - int ret = sigaction(SIGALRM, &action, &oldaction); - if (ret < 0) { - error(errno, errno, "sigaction failed"); - } - - // queue a SIGALRM after timeout_ms - const struct itimerval timeout = {{}, {0, timeout_ms * 1000}}; - ret = setitimer(ITIMER_REAL, &timeout, NULL); - if (ret < 0) { - error(errno, errno, "setitimer failed"); - } - - // start the blocking read - char buf; - int read_ret = read(dup_fd, &buf, 1); - int read_errno = errno; - - // cancel the alarm in case it hasn't fired yet - const struct itimerval cancel = {}; - ret = setitimer(ITIMER_REAL, &cancel, NULL); - if (ret < 0) { - error(errno, errno, "reset setitimer failed"); - } - - // remove the signal handler - ret = sigaction(SIGALRM, &oldaction, NULL); - if (ret < 0) { - error(errno, errno, "reset sigaction failed"); - } - - // clean up the dup'd fd in case the signal never fired - close(dup_fd); - dup_fd = -1; - - if (read_ret == 0) { - error(EXIT_FAILURE, 0, "EOF on jobserver pipe"); - } else if (read_ret > 0) { - return true; - } else if (read_errno == EINTR || read_errno == EBADF) { - return false; - } else { - error(read_errno, read_errno, "read failed"); - } - abort(); -} - -// Measure the size of the jobserver pool by reading from in_fd until it blocks -static int GetJobserverTokens(int in_fd) { - int tokens = 0; - pollfd pollfds[] = {{in_fd, POLLIN, 0}}; - int ret; - while ((ret = TEMP_FAILURE_RETRY(poll(pollfds, 1, 0))) != 0) { - if (ret < 0) { - error(errno, errno, "poll failed"); - } else if (pollfds[0].revents != POLLIN) { - error(EXIT_FAILURE, 0, "unexpected event %d\n", pollfds[0].revents); - } - - // There is probably a job token in the jobserver pipe. There is a chance - // another process reads it first, which would cause a blocking read to - // block forever (or until another process put a token back in the pipe). - // The file descriptor can't be set to O_NONBLOCK as that would affect - // all users of the pipe, including the parent make process. - // ReadByteTimeout emulates a non-blocking read on a !O_NONBLOCK socket - // using a SIGALRM that fires after a short timeout. - bool got_token = ReadByteTimeout(in_fd, 10); - if (!got_token) { - // No more tokens - break; - } else { - tokens++; - } - } - - // This process implicitly gets a token, so pool size is measured size + 1 - return tokens; -} - -// Return tokens to the jobserver pool. -static void PutJobserverTokens(int out_fd, int tokens) { - // Return all the tokens to the pipe - char buf = '+'; - for (int i = 0; i < tokens; i++) { - int ret = TEMP_FAILURE_RETRY(write(out_fd, &buf, 1)); - if (ret < 0) { - error(errno, errno, "write failed"); - } else if (ret == 0) { - error(EXIT_FAILURE, 0, "EOF on jobserver pipe"); - } - } -} - -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--; - } - - if (argc < 2) { - error(EXIT_FAILURE, 0, "expected command to run"); - } - - const char* path = argv[1]; - std::vector args({argv[1]}); - - std::vector makeflags = ReadMakeflags(); - if (ParseMakeflags(makeflags, &in_fd, &out_fd, ¶llel, &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; - if (parallel) { - if (tokens == 0) { - if (ninja) { - // ninja is parallel by default - jarg = ""; - } else { - // make -j with no argument, guess a reasonable parallelism like ninja does - jarg = "-j" + std::to_string(sysconf(_SC_NPROCESSORS_ONLN) + 2); - } - } else { - jarg = "-j" + std::to_string(tokens + 1); - } - } - - - 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 (jarg != "") { - args.push_back(strdup(jarg.c_str())); - } - } - if (keep_going) { - args.push_back(strdup("-k0")); - } - } else { - if (jarg != "") { - args.push_back(strdup(jarg.c_str())); - } - } - - args.insert(args.end(), &argv[2], &argv[argc]); - - args.push_back(nullptr); - - static pid_t pid; - - // Set up signal handlers to forward SIGTERM to child. - // Assume that all other signals are sent to the entire process group, - // and that we'll wait for our child to exit instead of handling them. - struct sigaction action = {}; - action.sa_flags = SA_RESTART; - action.sa_handler = [](int signal) { - if (signal == SIGTERM && pid > 0) { - kill(pid, signal); - } - }; - - int ret = 0; - if (!ret) ret = sigaction(SIGHUP, &action, NULL); - if (!ret) ret = sigaction(SIGINT, &action, NULL); - if (!ret) ret = sigaction(SIGQUIT, &action, NULL); - if (!ret) ret = sigaction(SIGTERM, &action, NULL); - if (!ret) ret = sigaction(SIGALRM, &action, NULL); - if (ret < 0) { - error(errno, errno, "sigaction failed"); - } - - pid = fork(); - if (pid < 0) { - error(errno, errno, "fork failed"); - } else if (pid == 0) { - // child - unsetenv("MAKEFLAGS"); - unsetenv("MAKELEVEL"); - - // make 3.81 sets the stack ulimit to unlimited, which may cause problems - // for child processes - struct rlimit rlim{}; - if (getrlimit(RLIMIT_STACK, &rlim) == 0 && rlim.rlim_cur == RLIM_INFINITY) { - rlim.rlim_cur = 8*1024*1024; - setrlimit(RLIMIT_STACK, &rlim); - } - - int ret = execvp(path, args.data()); - if (ret < 0) { - error(errno, errno, "exec %s failed", path); - } - abort(); - } - - // parent - - siginfo_t status = {}; - int exit_status = 0; - ret = waitid(P_PID, pid, &status, WEXITED); - if (ret < 0) { - error(errno, errno, "waitpid failed"); - } else if (status.si_code == CLD_EXITED) { - exit_status = status.si_status; - } else { - exit_status = -(status.si_status); - } - - if (tokens > 0) { - PutJobserverTokens(out_fd, tokens); - } - exit(exit_status); -}