diff --git a/localtypings/pxtarget.d.ts b/localtypings/pxtarget.d.ts index 6e91644dbf4d..ee4495e1a508 100644 --- a/localtypings/pxtarget.d.ts +++ b/localtypings/pxtarget.d.ts @@ -738,6 +738,7 @@ declare namespace ts.pxtc { flashChecksumAddr?: number; ramSize?: number; patches?: pxt.Map; // semver range -> upgrade policies + pyPatches?: pxt.Map; // semver range -> upgrade policies openocdScript?: string; uf2Family?: string; onStartText?: boolean; diff --git a/pxtcompiler/simpledriver.ts b/pxtcompiler/simpledriver.ts index 8b7aef04fd70..b0e200e8f3cc 100644 --- a/pxtcompiler/simpledriver.ts +++ b/pxtcompiler/simpledriver.ts @@ -172,6 +172,9 @@ namespace pxt { return mainPkg.getCompileOptionsAsync(target) }).then(opts => { patchTS(mainPkg.targetVersion(), opts) + if (mainPkg.getPreferredEditor() === pxt.PYTHON_PROJECT_NAME) { + patchPY(mainPkg.targetVersion(), opts) + } prepPythonOptions(opts) return opts }) @@ -194,16 +197,24 @@ namespace pxt { } export function patchTS(version: string, opts: pxtc.CompileOptions) { + patchText(version, opts, ".ts"); + } + + export function patchPY(version: string, opts: pxtc.CompileOptions) { + patchText(version, opts, ".py"); + } + + export function patchText(version: string, opts: pxtc.CompileOptions, extension: string) { if (!version) return - pxt.debug(`applying TS patches relative to ${version}`) + pxt.debug(`applying ${extension.replace('.', '')} patches relative to ${version}`) for (let fn of Object.keys(opts.fileSystem)) { - if (fn.indexOf("/") == -1 && U.endsWith(fn, ".ts")) { - const ts = opts.fileSystem[fn] - const ts2 = pxt.patching.patchJavaScript(version, ts) - if (ts != ts2) { - pxt.debug(`applying TS patch to ${fn}`) - opts.fileSystem[fn] = ts2 + if (fn.indexOf("/") == -1 && U.endsWith(fn, extension)) { + const initial = opts.fileSystem[fn] + const patched = pxt.patching.patchPython(version, initial) + if (initial != patched) { + pxt.debug(`applying ${extension.replace('.', '')} patch to ${fn}`) + opts.fileSystem[fn] = patched } } } diff --git a/pxtlib/patch.ts b/pxtlib/patch.ts index 1fb6b426bfa6..99b22b7b3b3d 100644 --- a/pxtlib/patch.ts +++ b/pxtlib/patch.ts @@ -2,6 +2,16 @@ namespace pxt.patching { export function computePatches(version: string, kind?: string): ts.pxtc.UpgradePolicy[] { const patches = pxt.appTarget.compile ? pxt.appTarget.compile.patches : undefined; if (!patches) return undefined; + return parsePatches(version, patches, kind); + } + + export function computePyPatches(version: string, kind?: string): ts.pxtc.UpgradePolicy[] { + const patches = pxt.appTarget.compile ? pxt.appTarget.compile.pyPatches : undefined; + if (!patches) return undefined; + return parsePatches(version, patches, kind); + } + + function parsePatches(version: string, patches: Map, kind?: string): ts.pxtc.UpgradePolicy[] { const v = pxt.semver.tryParse(version || "0.0.0") || pxt.semver.tryParse("0.0.0"); let r: ts.pxtc.UpgradePolicy[] = []; Object.keys(patches) @@ -30,6 +40,15 @@ namespace pxt.patching { export function patchJavaScript(pkgTargetVersion: string, fileContents: string): string { const upgrades = pxt.patching.computePatches(pkgTargetVersion); + return patchTextCode(pkgTargetVersion, fileContents, upgrades); + } + + export function patchPython(pkgTargetVersion: string, fileContents: string): string { + const upgrades = pxt.patching.computePyPatches(pkgTargetVersion); + return patchTextCode(pkgTargetVersion, fileContents, upgrades); + } + + function patchTextCode(pkgTargetVersion: string, fileContents: string, upgrades: pxtc.UpgradePolicy[]): string { let updatedContents = fileContents; if (upgrades) { upgrades.filter(u => u.type === "api").forEach(rule => { diff --git a/webapp/src/compiler.ts b/webapp/src/compiler.ts index 17c96264fac0..e899329de597 100644 --- a/webapp/src/compiler.ts +++ b/webapp/src/compiler.ts @@ -835,7 +835,12 @@ export function applyUpgradesAsync(): Promise { }); } - const upgradeOp = epkg.header.editor !== pxt.BLOCKS_PROJECT_NAME ? upgradeFromTSAsync : upgradeFromBlocksAsync; + const upgradeOp = + epkg.header.editor !== pxt.BLOCKS_PROJECT_NAME + ? epkg.header.editor !== pxt.PYTHON_PROJECT_NAME + ? upgradeFromTSAsync + : upgradeFromPythonAsync + : upgradeFromBlocksAsync; let projectNeverCompiled = false; @@ -953,6 +958,37 @@ function upgradeFromTSAsync(): Promise { }); } +function upgradeFromPythonAsync(): Promise { + const mainPkg = pkg.mainPkg; + const project = pkg.getEditorPkg(mainPkg); + const targetVersion = project.header.targetVersion; + + const patchedFiles: pxt.Map = {}; + pxt.Util.values(project.files).filter(isPyFile).forEach(file => { + const patched = pxt.patching.patchPython(targetVersion, file.content); + if (patched != file.content) { + patchedFiles[file.name] = patched; + } + }); + + pxt.debug("Applying upgrades to Python") + + return checkPatchAsync(patchedFiles) + .then(() => { + return { + success: true, + editor: pxt.PYTHON_PROJECT_NAME, + patchedFiles + }; + }) + .catch(e => { + return { + success: false, + errorCodes: e.errorCodes + }; + }); +} + interface UpgradeError extends Error { errorCodes?: pxt.Map; } @@ -1000,6 +1036,10 @@ function isTsFile(file: pkg.File) { return pxt.Util.endsWith(file.getName(), ".ts"); } +function isPyFile(file: pkg.File) { + return pxt.Util.endsWith(file.getName(), ".py"); +} + export function updatePackagesAsync(packages: pkg.EditorPackage[], token?: pxt.Util.CancellationToken): Promise { const epkg = pkg.mainEditorPkg(); let backup: pxt.workspace.Header;