summaryrefslogtreecommitdiff
path: root/js/process.ts
diff options
context:
space:
mode:
Diffstat (limited to 'js/process.ts')
-rw-r--r--js/process.ts136
1 files changed, 136 insertions, 0 deletions
diff --git a/js/process.ts b/js/process.ts
new file mode 100644
index 000000000..0a1393ed0
--- /dev/null
+++ b/js/process.ts
@@ -0,0 +1,136 @@
+// Copyright 2018 the Deno authors. All rights reserved. MIT license.
+import * as dispatch from "./dispatch";
+import * as flatbuffers from "./flatbuffers";
+import * as msg from "gen/msg_generated";
+import { assert, unreachable } from "./util";
+import { close, File } from "./files";
+import { ReadCloser, WriteCloser } from "./io";
+
+/** How to handle subsubprocess stdio.
+ *
+ * "inherit" The default if unspecified. The child inherits from the
+ * corresponding parent descriptor.
+ *
+ * "piped" A new pipe should be arranged to connect the parent and child
+ * subprocesses.
+ *
+ * "null" This stream will be ignored. This is the equivalent of attaching the
+ * stream to /dev/null.
+ */
+export type ProcessStdio = "inherit" | "piped" | "null";
+
+// TODO Maybe extend VSCode's 'CommandOptions'?
+// tslint:disable-next-line:max-line-length
+// See https://code.visualstudio.com/docs/editor/tasks-appendix#_schema-for-tasksjson
+export interface RunOptions {
+ args: string[];
+ cwd?: string;
+ stdout?: ProcessStdio;
+ stderr?: ProcessStdio;
+ stdin?: ProcessStdio;
+}
+
+export class Process {
+ readonly rid: number;
+ readonly pid: number;
+ readonly stdin?: WriteCloser;
+ readonly stdout?: ReadCloser;
+ readonly stderr?: ReadCloser;
+
+ // @internal
+ constructor(res: msg.RunRes) {
+ this.rid = res.rid();
+ this.pid = res.pid();
+
+ if (res.stdinRid() > 0) {
+ this.stdin = new File(res.stdinRid());
+ }
+
+ if (res.stdoutRid() > 0) {
+ this.stdout = new File(res.stdoutRid());
+ }
+
+ if (res.stderrRid() > 0) {
+ this.stderr = new File(res.stderrRid());
+ }
+ }
+
+ async status(): Promise<ProcessStatus> {
+ return await runStatus(this.rid);
+ }
+
+ close(): void {
+ close(this.rid);
+ }
+}
+
+export interface ProcessStatus {
+ success: boolean;
+ code?: number;
+ signal?: number; // TODO: Make this a string, e.g. 'SIGTERM'.
+}
+
+function stdioMap(s: ProcessStdio): msg.ProcessStdio {
+ switch (s) {
+ case "inherit":
+ return msg.ProcessStdio.Inherit;
+ case "piped":
+ return msg.ProcessStdio.Piped;
+ case "null":
+ return msg.ProcessStdio.Null;
+ default:
+ return unreachable();
+ }
+}
+
+export function run(opt: RunOptions): Process {
+ const builder = flatbuffers.createBuilder();
+ const argsOffset = msg.Run.createArgsVector(
+ builder,
+ opt.args.map(a => builder.createString(a))
+ );
+ const cwdOffset = opt.cwd == null ? -1 : builder.createString(opt.cwd);
+ msg.Run.startRun(builder);
+ msg.Run.addArgs(builder, argsOffset);
+ if (opt.cwd != null) {
+ msg.Run.addCwd(builder, cwdOffset);
+ }
+ if (opt.stdin) {
+ msg.Run.addStdin(builder, stdioMap(opt.stdin!));
+ }
+ if (opt.stdout) {
+ msg.Run.addStdout(builder, stdioMap(opt.stdout!));
+ }
+ if (opt.stderr) {
+ msg.Run.addStderr(builder, stdioMap(opt.stderr!));
+ }
+ const inner = msg.Run.endRun(builder);
+ const baseRes = dispatch.sendSync(builder, msg.Any.Run, inner);
+ assert(baseRes != null);
+ assert(msg.Any.RunRes === baseRes!.innerType());
+ const res = new msg.RunRes();
+ assert(baseRes!.inner(res) != null);
+
+ return new Process(res);
+}
+
+async function runStatus(rid: number): Promise<ProcessStatus> {
+ const builder = flatbuffers.createBuilder();
+ msg.RunStatus.startRunStatus(builder);
+ msg.RunStatus.addRid(builder, rid);
+ const inner = msg.RunStatus.endRunStatus(builder);
+
+ const baseRes = await dispatch.sendAsync(builder, msg.Any.RunStatus, inner);
+ assert(baseRes != null);
+ assert(msg.Any.RunStatusRes === baseRes!.innerType());
+ const res = new msg.RunStatusRes();
+ assert(baseRes!.inner(res) != null);
+
+ if (res.gotSignal()) {
+ const signal = res.exitSignal();
+ return { signal, success: false };
+ } else {
+ const code = res.exitCode();
+ return { code, success: code === 0 };
+ }
+}