pxt-core
Version:
Microsoft MakeCode provides Blocks / JavaScript / Python tools and editors
733 lines (732 loc) • 27.6 kB
JavaScript
;
/// <reference path="../built/pxtlib.d.ts"/>
/// <reference path="../built/pxtsim.d.ts"/>
Object.defineProperty(exports, "__esModule", { value: true });
exports.compileWithLocalCompileService = exports.buildDalConst = exports.codalGitAsync = exports.buildHexAsync = exports.setThisBuild = exports.thisBuild = exports.buildEngines = void 0;
global.pxt = pxt;
const nodeutil = require("./nodeutil");
const fs = require("fs");
const path = require("path");
const child_process = require("child_process");
const util_1 = require("util");
const hid = require("./hid");
var U = pxt.Util;
function noopAsync() { return Promise.resolve(); }
exports.buildEngines = {
yotta: {
id: "yotta",
updateEngineAsync: () => runYottaAsync(["update"]),
buildAsync: () => runYottaAsync(["build"]),
setPlatformAsync: () => runYottaAsync(["target", pxt.appTarget.compileService.yottaTarget]),
patchHexInfo: patchYottaHexInfo,
prepBuildDirAsync: noopAsync,
buildPath: "built/yt",
moduleConfig: "module.json",
deployAsync: msdDeployCoreAsync,
appPath: "source"
},
dockeryotta: {
id: "dockeryotta",
updateEngineAsync: () => runDockerYottaAsync(["yotta", "update"]),
buildAsync: () => runDockerYottaAsync(["yotta", "build"]),
setPlatformAsync: () => runDockerYottaAsync(["yotta", "target", pxt.appTarget.compileService.yottaTarget]),
patchHexInfo: patchYottaHexInfo,
prepBuildDirAsync: noopAsync,
buildPath: "built/dockeryt",
moduleConfig: "module.json",
deployAsync: msdDeployCoreAsync,
appPath: "source"
},
platformio: {
id: "platformio",
updateEngineAsync: noopAsync,
buildAsync: () => runPlatformioAsync(["run"]),
setPlatformAsync: noopAsync,
patchHexInfo: patchPioHexInfo,
prepBuildDirAsync: noopAsync,
buildPath: "built/pio",
moduleConfig: "platformio.ini",
deployAsync: platformioDeployAsync,
appPath: "src"
},
codal: {
id: "codal",
updateEngineAsync: updateCodalBuildAsync,
buildAsync: () => runBuildCmdAsync("python", "build.py"),
setPlatformAsync: noopAsync,
patchHexInfo: patchCodalHexInfo,
prepBuildDirAsync: prepCodalBuildDirAsync,
buildPath: "built/codal",
moduleConfig: "codal.json",
deployAsync: msdDeployCoreAsync,
appPath: "pxtapp"
},
dockercodal: {
id: "dockercodal",
updateEngineAsync: updateCodalBuildAsync,
buildAsync: () => runDockerAsync(["python", "build.py"]),
setPlatformAsync: noopAsync,
patchHexInfo: patchCodalHexInfo,
prepBuildDirAsync: prepCodalBuildDirAsync,
buildPath: "built/dockercodal",
moduleConfig: "codal.json",
deployAsync: msdDeployCoreAsync,
appPath: "pxtapp"
},
dockermake: {
id: "dockermake",
updateEngineAsync: () => runBuildCmdAsync(nodeutil.addCmd("npm"), "install"),
buildAsync: () => runDockerAsync(["make", "-j8"]),
setPlatformAsync: noopAsync,
patchHexInfo: patchDockermakeHexInfo,
prepBuildDirAsync: noopAsync,
buildPath: "built/dockermake",
moduleConfig: "package.json",
deployAsync: msdDeployCoreAsync,
outputPath: "bld/pxt-app.elf",
appPath: "pxtapp"
},
dockercross: {
id: "dockercross",
updateEngineAsync: () => runBuildCmdAsync(nodeutil.addCmd("npm"), "install"),
buildAsync: () => runDockerAsync(["make"]),
setPlatformAsync: noopAsync,
patchHexInfo: patchDockerCrossHexInfo,
prepBuildDirAsync: noopAsync,
buildPath: "built/dockercross",
moduleConfig: "package.json",
deployAsync: noopAsync,
appPath: "pxtapp"
},
dockerespidf: {
id: "dockerespidf",
updateEngineAsync: noopAsync,
buildAsync: () => runDockerAsync(["make"]),
setPlatformAsync: noopAsync,
patchHexInfo: patchDockerEspIdfHexInfo,
prepBuildDirAsync: noopAsync,
buildPath: "built/dockerespidf",
moduleConfig: "sdkconfig.defaults",
deployAsync: noopAsync,
appPath: "main"
},
cs: {
id: "cs",
updateEngineAsync: noopAsync,
buildAsync: () => runBuildCmdAsync(getCSharpCommand(), "-t:library", "-out:pxtapp.dll", "lib.cs"),
setPlatformAsync: noopAsync,
patchHexInfo: patchCSharpDll,
prepBuildDirAsync: noopAsync,
buildPath: "built/cs",
moduleConfig: "module.json",
deployAsync: buildFinalCsAsync,
appPath: "pxtapp"
},
};
// once we have a different build engine, set this appropriately
exports.thisBuild = exports.buildEngines['yotta'];
function setThisBuild(b) {
if (pxt.appTarget.compileService.dockerImage && !process.env["PXT_NODOCKER"]) {
if (b === exports.buildEngines["codal"])
b = exports.buildEngines["dockercodal"];
if (b === exports.buildEngines["yotta"])
b = exports.buildEngines["dockeryotta"];
}
pxt.debug(`set build engine: ${b.id}`);
exports.thisBuild = b;
}
exports.setThisBuild = setThisBuild;
function patchYottaHexInfo(extInfo) {
let buildEngine = exports.thisBuild;
let hexPath = buildEngine.buildPath + "/build/" + pxt.appTarget.compileService.yottaTarget.split("@")[0]
+ "/source/" + pxt.appTarget.compileService.yottaBinary;
return {
hex: fs.readFileSync(hexPath, "utf8").split(/\r?\n/)
};
}
function patchCodalHexInfo(extInfo) {
let bin = pxt.appTarget.compileService.codalBinary;
let hexPath = exports.thisBuild.buildPath + "/build/" + bin + ".hex";
return {
hex: fs.readFileSync(hexPath, "utf8").split(/\r?\n/)
};
}
function patchDockermakeHexInfo(extInfo) {
let hexPath = exports.thisBuild.buildPath + "/bld/pxt-app.hex";
return {
hex: fs.readFileSync(hexPath, "utf8").split(/\r?\n/)
};
}
function patchDockerCrossHexInfo(extInfo) {
let hexPath = exports.thisBuild.buildPath + "/bld/all.tgz.b64";
return {
hex: fs.readFileSync(hexPath, "utf8").split(/\r?\n/)
};
}
function patchDockerEspIdfHexInfo(extInfo) {
let hexPath = exports.thisBuild.buildPath + "/build/pxtapp.b64";
return {
hex: fs.readFileSync(hexPath, "utf8").split(/\r?\n/)
};
}
function patchCSharpDll(extInfo) {
let hexPath = exports.thisBuild.buildPath + "/lib.cs";
return {
hex: [fs.readFileSync(hexPath, "utf8")]
};
}
function pioFirmwareHex() {
let buildEngine = exports.buildEngines['platformio'];
return buildEngine.buildPath + "/.pioenvs/myenv/firmware.hex";
}
function patchPioHexInfo(extInfo) {
return {
hex: fs.readFileSync(pioFirmwareHex(), "utf8").split(/\r?\n/)
};
}
function platformioDeployAsync(r) {
if (pxt.appTarget.compile.useUF2)
return msdDeployCoreAsync(r);
else
return platformioUploadAsync(r);
}
function platformioUploadAsync(r) {
// TODO maybe platformio has some option to do this?
let buildEngine = exports.buildEngines['platformio'];
let prevHex = fs.readFileSync(pioFirmwareHex());
fs.writeFileSync(pioFirmwareHex(), r.outfiles[pxtc.BINARY_HEX]);
return runPlatformioAsync(["run", "--target", "upload", "--target", "nobuild", "-v"])
.finally(() => {
pxt.log('Restoring ' + pioFirmwareHex());
fs.writeFileSync(pioFirmwareHex(), prevHex);
});
}
function buildHexAsync(buildEngine, mainPkg, extInfo, forceBuild) {
let tasks = Promise.resolve();
let buildCachePath = buildEngine.buildPath + "/buildcache.json";
let buildCache = {};
if (fs.existsSync(buildCachePath)) {
buildCache = nodeutil.readJson(buildCachePath);
}
if (!forceBuild && (buildCache.sha == extInfo.sha && !process.env["PXT_RUNTIME_DEV"])) {
pxt.debug("Skipping C++ build.");
return tasks;
}
pxt.debug("writing build files to " + buildEngine.buildPath);
let allFiles = U.clone(extInfo.generatedFiles);
U.jsonCopyFrom(allFiles, extInfo.extensionFiles);
let writeFiles = () => {
for (let f of nodeutil.allFiles(buildEngine.buildPath + "/" + buildEngine.appPath, { maxDepth: 8, allowMissing: true })) {
let bn = f.slice(buildEngine.buildPath.length);
bn = bn.replace(/\\/g, "/").replace(/^\//, "/");
if (U.startsWith(bn, "/" + buildEngine.appPath + "/") && !allFiles[bn]) {
pxt.log("removing stale " + bn);
fs.unlinkSync(f);
}
}
U.iterMap(allFiles, (fn, v) => {
fn = buildEngine.buildPath + fn;
nodeutil.mkdirP(path.dirname(fn));
let existing = null;
if (fs.existsSync(fn))
existing = fs.readFileSync(fn, "utf8");
if (existing !== v)
nodeutil.writeFileSync(fn, v);
});
};
tasks = tasks
.then(buildEngine.prepBuildDirAsync)
.then(writeFiles);
let saveCache = () => fs.writeFileSync(buildCachePath, JSON.stringify(buildCache, null, 4) + "\n");
let modSha = U.sha256(extInfo.generatedFiles["/" + buildEngine.moduleConfig]);
let needDal = false;
if (buildCache.modSha !== modSha || forceBuild) {
tasks = tasks
.then(buildEngine.setPlatformAsync)
.then(buildEngine.updateEngineAsync)
.then(() => {
buildCache.sha = "";
buildCache.modSha = modSha;
saveCache();
needDal = true;
});
}
else {
pxt.debug(`Skipping C++ build update.`);
}
tasks = tasks
.then(buildEngine.buildAsync)
.then(() => {
buildCache.sha = extInfo.sha;
saveCache();
if (needDal)
buildDalConst(buildEngine, mainPkg, true);
});
return tasks;
}
exports.buildHexAsync = buildHexAsync;
function runYottaAsync(args) {
let ypath = process.env["YOTTA_PATH"];
let ytCommand = "yotta";
let env = U.clone(process.env);
if (/;[A-Z]:\\/.test(ypath)) {
for (let pp of ypath.split(";")) {
let q = path.join(pp, "yotta.exe");
if (fs.existsSync(q)) {
ytCommand = q;
env["PATH"] = env["PATH"] + ";" + ypath;
break;
}
}
}
pxt.log("*** " + ytCommand + " " + args.join(" "));
let child = child_process.spawn("yotta", args, {
cwd: exports.thisBuild.buildPath,
stdio: "inherit",
env: env
});
return new Promise((resolve, reject) => {
child.on("close", (code) => {
if (code === 0)
resolve();
else
reject(new Error("yotta " + args.join(" ") + ": exit code " + code));
});
});
}
function runPlatformioAsync(args) {
pxt.log("*** platformio " + args.join(" "));
let child = child_process.spawn("platformio", args, {
cwd: exports.thisBuild.buildPath,
stdio: "inherit",
env: process.env
});
return new Promise((resolve, reject) => {
child.on("close", (code) => {
if (code === 0)
resolve();
else
reject(new Error("platformio " + args.join(" ") + ": exit code " + code));
});
});
}
function runDockerAsync(args, flags) {
if (process.env["PXT_NODOCKER"] == "force") {
const cmd = args.shift();
return nodeutil.spawnAsync({
cmd,
args,
cwd: exports.thisBuild.buildPath
});
}
else {
let fullpath = process.cwd() + "/" + exports.thisBuild.buildPath + "/";
let cs = pxt.appTarget.compileService;
let dargs = cs.dockerArgs || ["-u", "build"];
let mountArg = fullpath + ":/src";
// this speeds up docker build a lot on macOS,
// see https://docs.docker.com/docker-for-mac/osxfs-caching/
if (process.platform == "darwin")
mountArg += ":delegated";
let fullArgs = ["--rm", "-v", mountArg, "-w", "/src", ...dargs, cs.dockerImage, ...args];
if (flags) {
fullArgs = [...flags, ...fullArgs];
}
return nodeutil.spawnAsync({
cmd: "docker",
args: ["run", ...fullArgs],
cwd: exports.thisBuild.buildPath
});
}
}
function runDockerYottaAsync(args) {
let fullpath = process.cwd() + "/" + exports.thisBuild.buildPath + "/";
fs.copyFileSync(path.join(__dirname, "prepYotta.js"), path.join(fullpath, "prepYotta.js"));
let argVariable = args.join(" ");
return runDockerAsync(["/bin/bash", "-c", `node prepYotta.js; ${argVariable}`], ["--env", "GITHUB_ACCESS_TOKEN"]);
}
let parseCppInt = pxt.cpp.parseCppInt;
function codalGitAsync(...args) {
return nodeutil.spawnAsync({
cmd: "git",
args: args,
cwd: exports.thisBuild.buildPath
});
}
exports.codalGitAsync = codalGitAsync;
function prepCodalBuildDirAsync() {
if (fs.existsSync(exports.thisBuild.buildPath + "/.git/config"))
return Promise.resolve();
let cs = pxt.appTarget.compileService;
let pkg = "https://github.com/" + cs.githubCorePackage;
nodeutil.mkdirP("built");
return nodeutil.runGitAsync("clone", pkg, exports.thisBuild.buildPath)
.then(() => codalGitAsync("checkout", cs.gittag));
}
function runBuildCmdAsync(cmd, ...args) {
return nodeutil.spawnAsync({
cmd,
args,
cwd: exports.thisBuild.buildPath,
});
}
function updateCodalBuildAsync() {
let cs = pxt.appTarget.compileService;
return codalGitAsync("checkout", cs.gittag)
.then(() => /v\d+/.test(cs.gittag) ? Promise.resolve() : codalGitAsync("pull"), e => codalGitAsync("checkout", "master")
.then(() => codalGitAsync("pull")))
.then(() => codalGitAsync("checkout", cs.gittag));
}
// TODO: DAL specific code should be lifted out
function buildDalConst(buildEngine, mainPkg, rebuild = false, create = false) {
var _a;
const constName = "dal.d.ts";
let constPath = constName;
const config = mainPkg && mainPkg.config;
const corePackage = (_a = config === null || config === void 0 ? void 0 : config.dalDTS) === null || _a === void 0 ? void 0 : _a.corePackage;
if (corePackage)
constPath = path.join(corePackage, constName);
let vals = {};
let done = {};
let excludeSyms = [];
function expandInt(s) {
s = s.trim();
let existing = U.lookup(vals, s);
if (existing != null && existing != "?")
s = existing;
let mm = /^\((.*)\)/.exec(s);
if (mm)
s = mm[1];
let m = /^(\w+)\s*([\+\|])\s*(.*)$/.exec(s);
if (m) {
let k = expandInt(m[1]);
if (k != null)
return m[2] == "+" ? k + expandInt(m[3]) : k | expandInt(m[3]);
}
let pp = parseCppInt(s);
if (pp != null)
return pp;
return null;
}
function extractConstants(fileName, src, dogenerate = false) {
let lineNo = 0;
// let err = (s: string) => U.userError(`${fileName}(${lineNo}): ${s}\n`)
let outp = "";
let inEnum = false;
let enumVal = 0;
let defineVal = (n, v) => {
if (excludeSyms.some(s => U.startsWith(n, s)))
return;
let parsed = expandInt(v);
if (parsed != null) {
v = parsed.toString();
let curr = U.lookup(vals, n);
if (curr == null || curr == v) {
vals[n] = v;
if (dogenerate && !done[n]) {
outp += ` ${n} = ${v},\n`;
done[n] = v;
}
}
else {
vals[n] = "?";
// TODO: DAL-specific code
if (dogenerate && !/^MICROBIT_DISPLAY_(ROW|COLUMN)_COUNT|PXT_VTABLE_SHIFT$/.test(n))
pxt.log(`${fileName}(${lineNo}): #define conflict, ${n}`);
}
}
};
src.split(/\r?\n/).forEach(ln => {
++lineNo;
ln = ln.replace(/\/\/.*/, "").replace(/\/\*.*/g, "");
let m = /^\s*#define\s+(\w+)\s+(.*)$/.exec(ln);
if (m) {
defineVal(m[1], m[2]);
}
if (inEnum && /}/.test(ln))
inEnum = false;
if (/^\s*enum\s+(\w+)/.test(ln)) {
inEnum = true;
enumVal = -1;
}
const shouldExpand = inEnum && (m = /^\s*(\w+)\s*(=\s*(.*?))?,?\s*$/.exec(ln));
if (shouldExpand) {
let v = m[3];
if (v) {
enumVal = expandInt(v);
if (enumVal == null) {
pxt.log(`${fileName}(${lineNo}): invalid enum initializer, ${ln}`);
inEnum = false;
return;
}
}
else {
enumVal++;
v = enumVal + "";
}
defineVal(m[1], v);
}
});
return outp;
}
if (mainPkg && (create ||
(mainPkg.getFiles().indexOf(constName) >= 0 && (rebuild || !fs.existsSync(constName))))) {
pxt.log(`rebuilding ${constName} into ${constPath}...`);
let files = [];
let foundConfig = false;
for (let d of mainPkg.sortedDeps()) {
if (d.config.dalDTS) {
if (d.config.dalDTS.includeDirs)
for (let dn of d.config.dalDTS.includeDirs) {
dn = buildEngine.buildPath + "/" + dn;
if (U.endsWith(dn, ".h"))
files.push(dn);
else {
let here = nodeutil.allFiles(dn, { maxDepth: 20 }).filter(fn => U.endsWith(fn, ".h"));
U.pushRange(files, here);
}
}
excludeSyms = d.config.dalDTS.excludePrefix || excludeSyms;
foundConfig = true;
}
}
if (!foundConfig) {
let incPath = buildEngine.buildPath + "/yotta_modules/microbit-dal/inc/";
if (!fs.existsSync(incPath))
incPath = buildEngine.buildPath + "/yotta_modules/codal/inc/";
if (!fs.existsSync(incPath))
incPath = buildEngine.buildPath;
if (!fs.existsSync(incPath))
U.userError("cannot find " + incPath);
files = nodeutil.allFiles(incPath, { maxDepth: 20 })
.filter(fn => U.endsWith(fn, ".h"))
.filter(fn => fn.indexOf("/mbed-classic/") < 0)
.filter(fn => fn.indexOf("/mbed-os/") < 0);
}
files.sort(U.strcmp);
let fc = {};
for (let fn of files) {
if (U.endsWith(fn, "Config.h"))
continue;
fc[fn] = fs.readFileSync(fn, "utf8");
}
files = Object.keys(fc);
// pre-pass - detect conflicts
for (let fn of files) {
extractConstants(fn, fc[fn]);
}
// stabilize
for (let fn of files) {
extractConstants(fn, fc[fn]);
}
let consts = "// Auto-generated. Do not edit.\ndeclare const enum DAL {\n";
for (let fn of files) {
let v = extractConstants(fn, fc[fn], true);
if (v) {
consts += " // " + fn.replace(/\\/g, "/").replace(buildEngine.buildPath, "") + "\n";
consts += v;
}
}
consts += "}\n";
fs.writeFileSync(constPath, consts);
}
}
exports.buildDalConst = buildDalConst;
const writeFileAsync = (0, util_1.promisify)(fs.writeFile);
const cpExecAsync = (0, util_1.promisify)(child_process.exec);
const readDirAsync = (0, util_1.promisify)(fs.readdir);
function buildFinalCsAsync(res) {
return nodeutil.spawnAsync({
cmd: getCSharpCommand(),
args: ["-out:pxtapp.exe", "binary.cs"],
cwd: "built",
});
}
function getCSharpCommand() {
return process.platform == "win32" ? "mcs.bat" : "mcs";
}
function msdDeployCoreAsync(res) {
const firmwareName = [pxtc.BINARY_UF2, pxtc.BINARY_HEX, pxtc.BINARY_ELF].filter(f => !!res.outfiles[f])[0];
if (!firmwareName) { // something went wrong heres
pxt.reportError("compile", `firmware missing from built files (${Object.keys(res.outfiles).join(', ')})`);
return Promise.resolve();
}
const firmware = res.outfiles[firmwareName];
const encoding = firmwareName == pxtc.BINARY_HEX
? "utf8" : "base64";
function copyDeployAsync() {
return getBoardDrivesAsync()
.then(drives => filterDrives(drives))
.then(drives => {
if (drives.length == 0)
throw new Error("cannot find any drives to deploy to");
pxt.log(`copying ${firmwareName} to ` + drives.join(", "));
const writeHexFile = (drivename) => {
return writeFileAsync(path.join(drivename, firmwareName), firmware, encoding)
.then(() => pxt.debug(" wrote to " + drivename))
.catch((e) => {
throw new Error(`failed writing to ${drivename}; ${e.message}`);
});
};
return U.promiseMapAll(drives, d => writeHexFile(d))
.then(() => drives.length);
}).then(() => { });
}
function hidDeployAsync() {
const f = firmware;
const blocks = pxtc.UF2.parseFile(U.stringToUint8Array(atob(f)));
return hid.initAsync()
.then(dev => dev.flashAsync(blocks));
}
let p = Promise.resolve();
if (pxt.appTarget.compile
&& pxt.appTarget.compile.useUF2
&& !pxt.appTarget.serial.noDeploy
&& hid.isInstalled(true)) {
// try hid or simply bail out
p = p.then(() => hidDeployAsync())
.catch(e => copyDeployAsync());
}
else {
p = p.then(() => copyDeployAsync());
}
return p;
}
function getBoardDrivesAsync() {
if (process.platform == "win32") {
const rx = new RegExp("^([A-Z]:)\\s+(\\d+).* " + pxt.appTarget.compile.deployDrives);
return cpExecAsync("wmic PATH Win32_LogicalDisk get DeviceID, VolumeName, FileSystem, DriveType")
.then(({ stdout, stderr }) => {
let res = [];
stdout
.split(/\n/)
.forEach(ln => {
let m = rx.exec(ln);
if (m && m[2] == "2") {
res.push(m[1] + "/");
}
});
return res;
});
}
else if (process.platform == "darwin") {
const rx = new RegExp(pxt.appTarget.compile.deployDrives);
return readDirAsync("/Volumes")
.then(lst => lst.filter(s => rx.test(s)).map(s => "/Volumes/" + s + "/"));
}
else if (process.platform == "linux") {
const rx = new RegExp(pxt.appTarget.compile.deployDrives);
const user = process.env["USER"];
if (nodeutil.existsDirSync(`/media/${user}`))
return readDirAsync(`/media/${user}`)
.then(lst => lst.filter(s => rx.test(s)).map(s => `/media/${user}/${s}/`));
return Promise.resolve([]);
}
else {
return Promise.resolve([]);
}
}
function filterDrives(drives) {
const marker = pxt.appTarget.compile.deployFileMarker;
if (!marker)
return drives;
return drives.filter(d => {
try {
return fs.existsSync(path.join(d, marker));
}
catch (e) {
return false;
}
});
}
async function compileWithLocalCompileService(extinfo) {
const resp = await runDockerCompileAsync(extinfo.compileData);
if (resp.hexfile) {
pxt.log("Compile successful");
}
else {
pxt.log("Compile failed");
pxt.log(resp.stderr);
pxt.log(resp.stdout);
}
return resp.hexfile && {
hex: resp.hexfile.split(/\r?\n/)
};
}
exports.compileWithLocalCompileService = compileWithLocalCompileService;
async function runDockerCompileAsync(data) {
const compileReq = JSON.parse(Buffer.from(data, "base64").toString("utf8"));
const deploymentConfig = JSON.parse(fs.readFileSync(path.resolve("../../../pxt-deployment-config/production/config.json"), "utf8"));
const compileServiceConfig = deploymentConfig.compileServices.find(cs => cs.id === compileReq.config);
const isPlatformio = !!compileServiceConfig.board;
if (!(isPlatformio || compileServiceConfig.hexfile)) {
const tag = compileReq.tag || "";
if (compileServiceConfig.repourl) {
if (/^[\w.\-]+$/.test(tag)) {
compileServiceConfig.repourl = compileServiceConfig.repourl.replace(/#.*/g, "") + "#" + compileReq.tag;
}
}
const moduleName = compileServiceConfig.binary.replace(/-combined/, "").replace(/\.hex$/, "");
const modulejson = {
"name": moduleName,
"version": "0.0.0",
"description": "Auto-generated. Do not edit.",
"license": "n/a",
"dependencies": compileReq.dependencies || {},
"targetDependencies": {},
"bin": "./source"
};
if (compileServiceConfig.repourl) {
let repoSlug = compileServiceConfig.repourl.replace(/^https?:\/\/[^\/]+\//, "").replace(/\.git#/, "#");
let pkgName = repoSlug.replace(/#.*/, "").replace(/^.*\//, "");
modulejson.dependencies[pkgName] = repoSlug;
}
compileReq.replaceFiles["/module.json"] = JSON.stringify(modulejson, null, 2) + "\n";
}
const mappedFiles = (Object.keys(compileReq.replaceFiles).map(filename => {
return {
name: filename.replace(/^\/+/, ""),
text: compileReq.replaceFiles[filename]
};
}));
let image = compileServiceConfig.image;
if (!image) {
if (isPlatformio) {
image = "pext/platformio:latest";
}
else {
image = "mcr.microsoft.com/makecode/yotta:main-gcc5";
}
}
let hexFile = compileServiceConfig.hexfile;
if (!hexFile && !isPlatformio) {
hexFile = "source/" + compileServiceConfig.binary;
}
let gittag = "";
if (compileServiceConfig.clone) {
gittag = compileReq.tag;
}
else if (compileServiceConfig.repourl) {
gittag = compileServiceConfig.repourl.replace(/.*#/, "");
}
const compileRequest = {
op: "buildex",
files: mappedFiles,
gittag: gittag,
empty: true,
hexfile: hexFile,
target: compileServiceConfig.target,
platformio: isPlatformio,
clone: compileServiceConfig.clone,
buildcmd: compileServiceConfig.buildcmd,
image: image,
githubToken: process.env["GITHUB_ACCESS_TOKEN"]
};
const stdout = await nodeutil.spawnWithPipeAsync({
cmd: "docker",
args: ["run", "-i", "--env", "LOCAL_BUILD='TRUE'", pxt.appTarget.compileService.dockerImage],
input: JSON.stringify(compileRequest)
});
const resp = JSON.parse(stdout.toString("utf8"));
return resp;
}