diff --git a/package.json b/package.json index 2d07d243..d1b39847 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { - "@pybricks/mpy-cross-4": "^1.0.2", + "@pybricks/mpy-cross-4": "^2.0.0", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", diff --git a/src/actions/mpy.ts b/src/actions/mpy.ts index d39b8d56..2bbbc6ce 100644 --- a/src/actions/mpy.ts +++ b/src/actions/mpy.ts @@ -1,8 +1,6 @@ -import MpyCross from '@pybricks/mpy-cross-4'; +import { compile as mpyCrossCompile } from '@pybricks/mpy-cross-4'; import { Action } from 'redux'; - -// this starts the mpy-cross wasm runtime and leaves it running in the background -const mpy = MpyCross({ arguments: ['-mno-unicode'] }); +import { ThunkAction } from 'redux-thunk'; export enum MpyActionType { Compiled = 'mpy.action.compile', @@ -12,11 +10,18 @@ export interface MpyCompiledAction extends Action { /** * The compiled .mpy data. */ - data: Uint8Array; + data?: Uint8Array; + /** + * Error output. + */ + err?: string; } -export function compile(script: string): MpyCompiledAction { - // TODO: figure out how to capture stderr and emit error action on failure - const data = mpy.compile(script); - return { type: MpyActionType.Compiled, data }; +type MpyCompileAction = ThunkAction, {}, {}, Action>; + +export function compile(script: string, options?: string[]): MpyCompileAction { + return async function (): Promise { + const result = await mpyCrossCompile('main.py', script, options); + return { type: MpyActionType.Compiled, data: result.mpy, err: result.err }; + }; } diff --git a/src/components/RunButton.tsx b/src/components/RunButton.tsx index 0b92e1a2..3be4f853 100644 --- a/src/components/RunButton.tsx +++ b/src/components/RunButton.tsx @@ -1,9 +1,10 @@ import { Ace } from 'ace-builds'; -import { batch, connect } from 'react-redux'; +import { connect } from 'react-redux'; import { AnyAction } from 'redux'; import { ThunkDispatch } from 'redux-thunk'; import { downloadAndRun } from '../actions/hub'; import { compile } from '../actions/mpy'; +import * as notification from '../actions/notification'; import { RootState } from '../reducers'; import { HubRuntimeState } from '../reducers/hub'; import ActionButton, { ActionButtonProps } from './ActionButton'; @@ -27,11 +28,19 @@ const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({ console.error('No current editor'); return; } - batch(() => { - const script = c.getValue(); - const mpy = dispatch(compile(script)); - dispatch(downloadAndRun(mpy.data)); - }); + const script = c.getValue(); + // TODO: need to get options from hub because they depend on firmware compile options + dispatch(compile(script, ['-mno-unicode'])) + .then((mpy) => { + if (mpy.data) { + dispatch(downloadAndRun(mpy.data)); + } else { + dispatch( + notification.add('error', mpy.err || 'Unknown compiler error.'), + ); + } + }) + .catch((err) => console.error(err)); }, }); diff --git a/src/sagas/bootloader.ts b/src/sagas/bootloader.ts index 0692c5b9..cd3fa740 100644 --- a/src/sagas/bootloader.ts +++ b/src/sagas/bootloader.ts @@ -8,6 +8,7 @@ import { delay, fork, put, + putResolve, race, take, takeEvery, @@ -177,7 +178,7 @@ interface FirmwareMetadata { 'device-id': HubType; 'checksum-type': 'sum' | 'crc32'; 'mpy-abi-version': number; - 'mpy-cross-options': Array; + 'mpy-cross-options': string[]; 'user-mpy-offset': number; 'max-firmware-size': number; } @@ -213,8 +214,13 @@ function* flashFirmware(action: BootloaderFlashFirmwareAction): Generator { ); } - // TODO: pass metadata["mpy-cross-options"] to compiler - const mpy = (yield put(compile(main))) as MpyCompiledAction; + const mpy = (yield putResolve( + (compile(main, metadata['mpy-cross-options']) as unknown) as Action, + )) as MpyCompiledAction; + + if (!mpy.data) { + throw Error(mpy.err); + } // compute offset for checksum - must be aligned to 4-byte boundary const checksumOffset = diff --git a/yarn.lock b/yarn.lock index 9dc1b3a8..df725d8b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1254,12 +1254,10 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.4.0.tgz#0e1bdf8d021e7ea58affade33d9d607e11365915" integrity sha512-NMrDy6EWh9TPdSRiHmHH2ye1v5U0gBD7pRYwSwJvomx7Bm4GG04vu63dYiVzebLOx2obPpJugew06xVP0Nk7hA== -"@pybricks/mpy-cross-4@^1.0.2": - version "1.0.2" - resolved "https://npm.pkg.github.com/download/@pybricks/mpy-cross-4/1.0.2/0d1c1c7789469794795680f14930bc66922dbe55caf59b3e58df8cb8c70365df#441d5ff59498d2d5c78adc74d0a70a6575e8c426" - integrity sha512-iy6nVtIDRqU2SJ/aGKizfjPIk9PZQSSkOTTI7L7+HNPeF4zY/uhQSOI+ZFFaYSNoxFOrMIu9qn1Jsh3w08HO+A== - dependencies: - "@types/emscripten" "^1.39.3" +"@pybricks/mpy-cross-4@^2.0.0": + version "2.0.0" + resolved "https://npm.pkg.github.com/download/@pybricks/mpy-cross-4/2.0.0/ee1d8da09629b14ea43bc6ac7a685b48fe191404958215e3b267478c6d750aa8#4bdf4c397652ca69ecf7ecb0749d76afc928f39b" + integrity sha512-qSUDm8WMCq/ZH0wdWpgnR9fHDBnLsTQ+6CNc1ARX4orD0Wezpp/N34ya45G2RGyy6dFB/YAqYcK/daGhb6Ka8Q== "@redux-saga/core@^1.1.3": version "1.1.3" @@ -1506,11 +1504,6 @@ resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== -"@types/emscripten@^1.39.3": - version "1.39.3" - resolved "https://registry.yarnpkg.com/@types/emscripten/-/emscripten-1.39.3.tgz#cac2f24b4739c344c42f928a4c1af1aab245364e" - integrity sha512-F+LPHVwVWp6bkhRq7sMdW0JsQMXd5ZuPbjwU/X6HaAtvIjRmYL2z4BCIopnBRcrzZLorVFf8gxY/GR7szLuPEw== - "@types/eslint-visitor-keys@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"