2020-01-02 15:13:47 -05:00
|
|
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
2020-03-08 13:09:22 +01:00
|
|
|
import { File } from "./files.ts";
|
|
|
|
import { close } from "./ops/resources.ts";
|
2020-04-28 12:32:43 +02:00
|
|
|
import { Closer, Reader, Writer } from "./io.ts";
|
2019-09-02 17:07:11 -04:00
|
|
|
import { readAll } from "./buffer.ts";
|
2020-03-09 15:18:02 +01:00
|
|
|
import { kill, runStatus as runStatusOp, run as runOp } from "./ops/process.ts";
|
2018-11-15 20:07:40 -08:00
|
|
|
|
|
|
|
export type ProcessStdio = "inherit" | "piped" | "null";
|
|
|
|
|
|
|
|
// TODO Maybe extend VSCode's 'CommandOptions'?
|
|
|
|
// See https://code.visualstudio.com/docs/editor/tasks-appendix#_schema-for-tasksjson
|
|
|
|
export interface RunOptions {
|
2020-03-22 03:14:18 +05:30
|
|
|
cmd: string[];
|
2018-11-15 20:07:40 -08:00
|
|
|
cwd?: string;
|
2019-02-16 00:37:04 +09:00
|
|
|
env?: { [key: string]: string };
|
2019-06-22 01:00:14 +02:00
|
|
|
stdout?: ProcessStdio | number;
|
|
|
|
stderr?: ProcessStdio | number;
|
|
|
|
stdin?: ProcessStdio | number;
|
2018-11-15 20:07:40 -08:00
|
|
|
}
|
|
|
|
|
2019-08-26 14:50:21 +02:00
|
|
|
async function runStatus(rid: number): Promise<ProcessStatus> {
|
2020-03-09 15:18:02 +01:00
|
|
|
const res = await runStatusOp(rid);
|
2019-03-10 04:30:38 +11:00
|
|
|
|
2019-08-26 14:50:21 +02:00
|
|
|
if (res.gotSignal) {
|
|
|
|
const signal = res.exitSignal;
|
2019-03-10 04:30:38 +11:00
|
|
|
return { signal, success: false };
|
|
|
|
} else {
|
2019-08-26 14:50:21 +02:00
|
|
|
const code = res.exitCode;
|
2019-03-10 04:30:38 +11:00
|
|
|
return { code, success: code === 0 };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-15 20:07:40 -08:00
|
|
|
export class Process {
|
|
|
|
readonly rid: number;
|
|
|
|
readonly pid: number;
|
2020-04-28 12:32:43 +02:00
|
|
|
readonly stdin?: Writer & Closer;
|
|
|
|
readonly stdout?: Reader & Closer;
|
|
|
|
readonly stderr?: Reader & Closer;
|
2018-11-15 20:07:40 -08:00
|
|
|
|
|
|
|
// @internal
|
2019-08-26 14:50:21 +02:00
|
|
|
constructor(res: RunResponse) {
|
|
|
|
this.rid = res.rid;
|
|
|
|
this.pid = res.pid;
|
2018-11-15 20:07:40 -08:00
|
|
|
|
2019-08-26 14:50:21 +02:00
|
|
|
if (res.stdinRid && res.stdinRid > 0) {
|
|
|
|
this.stdin = new File(res.stdinRid);
|
2018-11-15 20:07:40 -08:00
|
|
|
}
|
|
|
|
|
2019-08-26 14:50:21 +02:00
|
|
|
if (res.stdoutRid && res.stdoutRid > 0) {
|
|
|
|
this.stdout = new File(res.stdoutRid);
|
2018-11-15 20:07:40 -08:00
|
|
|
}
|
|
|
|
|
2019-08-26 14:50:21 +02:00
|
|
|
if (res.stderrRid && res.stderrRid > 0) {
|
|
|
|
this.stderr = new File(res.stderrRid);
|
2018-11-15 20:07:40 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-20 14:38:34 +01:00
|
|
|
status(): Promise<ProcessStatus> {
|
2020-03-16 10:22:16 +01:00
|
|
|
return runStatus(this.rid);
|
2018-11-15 20:07:40 -08:00
|
|
|
}
|
|
|
|
|
2018-12-01 02:44:05 +08:00
|
|
|
async output(): Promise<Uint8Array> {
|
|
|
|
if (!this.stdout) {
|
|
|
|
throw new Error("Process.output: stdout is undefined");
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
return await readAll(this.stdout);
|
|
|
|
} finally {
|
|
|
|
this.stdout.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-28 21:09:46 +01:00
|
|
|
async stderrOutput(): Promise<Uint8Array> {
|
|
|
|
if (!this.stderr) {
|
|
|
|
throw new Error("Process.stderrOutput: stderr is undefined");
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
return await readAll(this.stderr);
|
|
|
|
} finally {
|
|
|
|
this.stderr.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-15 20:07:40 -08:00
|
|
|
close(): void {
|
|
|
|
close(this.rid);
|
|
|
|
}
|
2019-04-21 18:26:56 -07:00
|
|
|
|
|
|
|
kill(signo: number): void {
|
|
|
|
kill(this.pid, signo);
|
|
|
|
}
|
2018-11-15 20:07:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface ProcessStatus {
|
|
|
|
success: boolean;
|
|
|
|
code?: number;
|
|
|
|
signal?: number; // TODO: Make this a string, e.g. 'SIGTERM'.
|
|
|
|
}
|
|
|
|
|
2019-06-22 01:00:14 +02:00
|
|
|
function isRid(arg: unknown): arg is number {
|
|
|
|
return !isNaN(arg as number);
|
|
|
|
}
|
|
|
|
|
2019-08-26 14:50:21 +02:00
|
|
|
interface RunResponse {
|
|
|
|
rid: number;
|
|
|
|
pid: number;
|
|
|
|
stdinRid: number | null;
|
|
|
|
stdoutRid: number | null;
|
|
|
|
stderrRid: number | null;
|
|
|
|
}
|
2020-03-10 16:08:58 +00:00
|
|
|
export function run({
|
2020-03-22 03:14:18 +05:30
|
|
|
cmd,
|
2020-03-10 16:08:58 +00:00
|
|
|
cwd = undefined,
|
|
|
|
env = {},
|
|
|
|
stdout = "inherit",
|
|
|
|
stderr = "inherit",
|
2020-03-29 04:03:49 +11:00
|
|
|
stdin = "inherit",
|
2020-03-10 16:08:58 +00:00
|
|
|
}: RunOptions): Process {
|
|
|
|
const res = runOp({
|
2020-03-22 03:14:18 +05:30
|
|
|
cmd: cmd.map(String),
|
2020-03-10 16:08:58 +00:00
|
|
|
cwd,
|
|
|
|
env: Object.entries(env),
|
|
|
|
stdin: isRid(stdin) ? "" : stdin,
|
|
|
|
stdout: isRid(stdout) ? "" : stdout,
|
|
|
|
stderr: isRid(stderr) ? "" : stderr,
|
|
|
|
stdinRid: isRid(stdin) ? stdin : 0,
|
|
|
|
stdoutRid: isRid(stdout) ? stdout : 0,
|
2020-03-29 04:03:49 +11:00
|
|
|
stderrRid: isRid(stderr) ? stderr : 0,
|
2020-03-10 16:08:58 +00:00
|
|
|
}) as RunResponse;
|
2018-11-15 20:07:40 -08:00
|
|
|
return new Process(res);
|
|
|
|
}
|