Skip to content

Commit d576ee6

Browse files
* Full bash syntax support for pre-/post-build commands on UNIX systems
* Environment variables for pre-/post-build commands which give the user access to several build process parameters like the sketch, the output directory, serial port, build mode (verify, upload, analyze, ...), board type and workspace path Addresses microsoft#786
1 parent 4fb0d4c commit d576ee6

File tree

2 files changed

+46
-19
lines changed

2 files changed

+46
-19
lines changed

src/arduino/arduino.ts

+42-15
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,7 @@ export class ArduinoApp {
133133
public async setPref(key, value) {
134134
try {
135135
await util.spawn(this._settings.commandPath,
136-
null,
137-
["--pref", `${key}=${value}`, "--save-prefs"]);
136+
["--pref", `${key}=${value}`, "--save-prefs"]);
138137
} catch (ex) {
139138
}
140139
}
@@ -414,21 +413,34 @@ Please make sure the folder is not occupied by other procedures .`);
414413
* @returns True if successful, false on error.
415414
*/
416415
protected async runPrePostBuildCommand(dc: DeviceContext,
416+
environment: any,
417417
what: "pre" | "post"): Promise<boolean> {
418418
const cmdline = what === "pre"
419419
? dc.prebuild
420420
: dc.postbuild;
421421

422422
if (cmdline) {
423423
arduinoChannel.info(`Running ${what}-build command: "${cmdline}"`);
424-
// TODO 2020-02-27, EW: We could call bash -c "cmd" here at least for
425-
// UNIX systems. Windows users must live with their poor system :)
426-
const args = cmdline.split(/\s+/);
427-
const cmd = args.shift();
424+
let cmd: string;
425+
let args: string[];
426+
// pre-/post-build commands feature full bash support on UNIX systems.
427+
// Windows users must live with their poor system unless someone is
428+
// willing to fight with the annoying Windows cmd escaping -- good luck!
429+
if (os.platform() === "win32") {
430+
args = cmdline.split(/\s+/);
431+
cmd = args.shift();
432+
} else {
433+
args = ["-c", cmdline];
434+
cmd = "bash";
435+
}
428436
try {
429437
await util.spawn(cmd,
430438
args,
431-
{ shell: true, cwd: ArduinoWorkspace.rootPath },
439+
{
440+
shell: os.platform() === "win32",
441+
cwd: ArduinoWorkspace.rootPath,
442+
env: {...environment},
443+
},
432444
{ channel: arduinoChannel.channel });
433445
} catch (ex) {
434446
const msg = ex.error
@@ -533,23 +545,38 @@ Please make sure the folder is not occupied by other procedures .`);
533545
arduinoChannel.start(`${mode} sketch '${dc.sketch}'`);
534546

535547
if (buildDir || dc.output) {
536-
const outputPath = path.resolve(ArduinoWorkspace.rootPath, buildDir || dc.output);
537-
const dirPath = path.dirname(outputPath);
548+
buildDir = path.resolve(ArduinoWorkspace.rootPath, buildDir || dc.output);
549+
const dirPath = path.dirname(buildDir);
538550
if (!util.directoryExistsSync(dirPath)) {
539-
logger.notifyUserError("InvalidOutPutPath", new Error(constants.messages.INVALID_OUTPUT_PATH + outputPath));
551+
logger.notifyUserError("InvalidOutPutPath", new Error(constants.messages.INVALID_OUTPUT_PATH + buildDir));
540552
return false;
541553
}
542-
args.push("--pref", `build.path=${outputPath}`);
543-
arduinoChannel.info(`Please see the build logs in output path: ${outputPath}`);
554+
args.push("--pref", `build.path=${buildDir}`);
555+
arduinoChannel.info(`Please see the build logs in output path: ${buildDir}`);
544556
} else {
545557
const msg = "Output path is not specified. Unable to reuse previously compiled files. Build will be slower. See README.";
546558
arduinoChannel.warning(msg);
547559
}
548560

561+
// Environment variables passed to pre- and post-build commands
562+
const env = {
563+
VSCA_BUILD_MODE: mode,
564+
VSCA_SKETCH: dc.sketch,
565+
VSCA_BOARD: boardDescriptor,
566+
VSCA_WORKSPACE_DIR: ArduinoWorkspace.rootPath,
567+
VSCA_LOG_LEVEL: verbose ? constants.LogLevel.Verbose : constants.LogLevel.Info,
568+
};
569+
if (dc.port) {
570+
env["VSCA_SERIAL"] = dc.port;
571+
}
572+
if (buildDir) {
573+
env["VSCA_BUILD_DIR"] = buildDir;
574+
}
575+
549576
// TODO EW: What should we do with pre-/post build commands when running
550577
// analysis? Some could use it to generate/manipulate code which could
551578
// be a prerequisite for a successful build
552-
if (!await this.runPrePostBuildCommand(dc, "pre")) {
579+
if (!await this.runPrePostBuildCommand(dc, env, "pre")) {
553580
return false;
554581
}
555582

@@ -566,7 +593,7 @@ Please make sure the folder is not occupied by other procedures .`);
566593
const cleanup = async (result: "ok" | "error") => {
567594
let ret = true;
568595
if (result === "ok") {
569-
ret = await this.runPrePostBuildCommand(dc, "post");
596+
ret = await this.runPrePostBuildCommand(dc, env, "post");
570597
}
571598
await cocopa.conclude();
572599
if (mode === BuildMode.Upload || mode === BuildMode.UploadProgrammer) {
@@ -618,7 +645,7 @@ Please make sure the folder is not occupied by other procedures .`);
618645
undefined,
619646
{ stdout: stdoutcb, stderr: stderrcb },
620647
).then(async () => {
621-
const ret = await cleanup("ok");
648+
const ret = await cleanup("ok");
622649
if (ret) {
623650
arduinoChannel.end(`${mode} sketch '${dc.sketch}'${os.EOL}`);
624651
}

src/common/util.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

4-
import * as childProcess from "child_process";
4+
import * as child_process from "child_process";
55
import * as fs from "fs";
66
import * as iconv from "iconv-lite";
77
import * as os from "os";
@@ -187,18 +187,18 @@ export function isArduinoFile(filePath): boolean {
187187

188188
export function spawn(command: string,
189189
args: string[] = [],
190-
options: any = {},
190+
options: child_process.SpawnOptions = {},
191191
output?: {channel?: vscode.OutputChannel,
192192
stdout?: (string) => void,
193193
stderr?: (string) => void}): Thenable<object> {
194194
return new Promise((resolve, reject) => {
195195
options.cwd = options.cwd || path.resolve(path.join(__dirname, ".."));
196-
const child = childProcess.spawn(command, args, options);
196+
const child = child_process.spawn(command, args, options);
197197

198198
let codepage = "65001";
199199
if (os.platform() === "win32") {
200200
try {
201-
const chcp = childProcess.execSync("chcp.com");
201+
const chcp = child_process.execSync("chcp.com");
202202
codepage = chcp.toString().split(":").pop().trim();
203203
} catch (error) {
204204
arduinoChannel.warning(`Defaulting to code page 850 because chcp.com failed.\

0 commit comments

Comments
 (0)