2020-04-28 14:30:49 +00:00
|
|
|
import * as cp from 'child_process';
|
|
|
|
import * as readline from 'readline';
|
2020-04-30 12:25:04 +00:00
|
|
|
import { OutputChannel } from 'vscode';
|
2020-04-28 14:30:49 +00:00
|
|
|
|
|
|
|
interface CompilationArtifact {
|
|
|
|
fileName: string;
|
|
|
|
name: string;
|
|
|
|
kind: string;
|
|
|
|
isTest: boolean;
|
|
|
|
}
|
|
|
|
|
|
|
|
export class Cargo {
|
|
|
|
rootFolder: string;
|
2020-04-30 12:25:04 +00:00
|
|
|
env?: Record<string, string>;
|
|
|
|
output: OutputChannel;
|
2020-04-28 14:30:49 +00:00
|
|
|
|
2020-04-30 12:25:04 +00:00
|
|
|
public constructor(cargoTomlFolder: string, output: OutputChannel, env: Record<string, string> | undefined = undefined) {
|
2020-04-28 14:30:49 +00:00
|
|
|
this.rootFolder = cargoTomlFolder;
|
2020-04-30 12:25:04 +00:00
|
|
|
this.output = output;
|
|
|
|
this.env = env;
|
2020-04-28 14:30:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public async artifactsFromArgs(cargoArgs: string[]): Promise<CompilationArtifact[]> {
|
2020-04-30 15:41:48 +00:00
|
|
|
const artifacts: CompilationArtifact[] = [];
|
2020-04-28 14:30:49 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
await this.runCargo(cargoArgs,
|
|
|
|
message => {
|
2020-04-30 15:41:48 +00:00
|
|
|
if (message.reason === 'compiler-artifact' && message.executable) {
|
|
|
|
const isBinary = message.target.crate_types.includes('bin');
|
|
|
|
const isBuildScript = message.target.kind.includes('custom-build');
|
2020-04-28 14:30:49 +00:00
|
|
|
if ((isBinary && !isBuildScript) || message.profile.test) {
|
|
|
|
artifacts.push({
|
|
|
|
fileName: message.executable,
|
|
|
|
name: message.target.name,
|
|
|
|
kind: message.target.kind[0],
|
|
|
|
isTest: message.profile.test
|
2020-04-30 15:41:48 +00:00
|
|
|
});
|
2020-04-28 14:30:49 +00:00
|
|
|
}
|
|
|
|
}
|
2020-04-30 15:41:48 +00:00
|
|
|
else if (message.reason === 'compiler-message') {
|
2020-04-30 12:25:04 +00:00
|
|
|
this.output.append(message.message.rendered);
|
|
|
|
}
|
2020-04-28 14:30:49 +00:00
|
|
|
},
|
2020-04-30 12:25:04 +00:00
|
|
|
stderr => {
|
|
|
|
this.output.append(stderr);
|
2020-04-28 14:30:49 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
catch (err) {
|
2020-04-30 12:25:04 +00:00
|
|
|
this.output.show(true);
|
2020-04-28 14:30:49 +00:00
|
|
|
throw new Error(`Cargo invocation has failed: ${err}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
return artifacts;
|
|
|
|
}
|
|
|
|
|
2020-04-30 15:53:34 +00:00
|
|
|
public async executableFromArgs(args: string[]): Promise<string> {
|
2020-04-30 15:57:40 +00:00
|
|
|
const cargoArgs = [...args]; // to remain args unchanged
|
2020-04-28 14:30:49 +00:00
|
|
|
cargoArgs.push("--message-format=json");
|
|
|
|
|
2020-04-30 15:41:48 +00:00
|
|
|
const artifacts = await this.artifactsFromArgs(cargoArgs);
|
2020-04-28 14:30:49 +00:00
|
|
|
|
2020-04-30 15:41:48 +00:00
|
|
|
if (artifacts.length === 0) {
|
2020-04-28 14:30:49 +00:00
|
|
|
throw new Error('No compilation artifacts');
|
|
|
|
} else if (artifacts.length > 1) {
|
|
|
|
throw new Error('Multiple compilation artifacts are not supported.');
|
|
|
|
}
|
|
|
|
|
|
|
|
return artifacts[0].fileName;
|
|
|
|
}
|
|
|
|
|
|
|
|
runCargo(
|
|
|
|
cargoArgs: string[],
|
|
|
|
onStdoutJson: (obj: any) => void,
|
|
|
|
onStderrString: (data: string) => void
|
|
|
|
): Promise<number> {
|
|
|
|
return new Promise<number>((resolve, reject) => {
|
2020-04-30 15:41:48 +00:00
|
|
|
const cargo = cp.spawn('cargo', cargoArgs, {
|
2020-04-28 14:30:49 +00:00
|
|
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
|
|
cwd: this.rootFolder,
|
|
|
|
env: this.env,
|
|
|
|
});
|
|
|
|
|
|
|
|
cargo.on('error', err => {
|
|
|
|
reject(new Error(`could not launch cargo: ${err}`));
|
|
|
|
});
|
|
|
|
cargo.stderr.on('data', chunk => {
|
|
|
|
onStderrString(chunk.toString());
|
|
|
|
});
|
|
|
|
|
2020-04-30 15:41:48 +00:00
|
|
|
const rl = readline.createInterface({ input: cargo.stdout });
|
2020-04-28 14:30:49 +00:00
|
|
|
rl.on('line', line => {
|
2020-04-30 15:41:48 +00:00
|
|
|
const message = JSON.parse(line);
|
2020-04-28 14:30:49 +00:00
|
|
|
onStdoutJson(message);
|
|
|
|
});
|
|
|
|
|
|
|
|
cargo.on('exit', (exitCode, _) => {
|
2020-04-30 15:41:48 +00:00
|
|
|
if (exitCode === 0)
|
2020-04-28 14:30:49 +00:00
|
|
|
resolve(exitCode);
|
|
|
|
else
|
|
|
|
reject(new Error(`exit code: ${exitCode}.`));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|