mirror of
https://github.com/denoland/deno.git
synced 2024-11-27 16:10:57 -05:00
137 lines
3.7 KiB
TypeScript
137 lines
3.7 KiB
TypeScript
|
// 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 };
|
||
|
}
|
||
|
}
|