Skip to content
Everframe Docs
Documentation

Build instrumentation

One build step before packaging. Errors are recorded with a stack, then re-thrown, so your channel behaves exactly as before.

Updated

The instrument step writes an instrumented copy of your channel to a separate folder. Your sources are never modified. Add it to your release and beta deploys, and keep day-to-day development builds uninstrumented:

{
  "scripts": {
    "everframe:build": "everframe-roku instrument ./ --out ./.everframe-build --bundle-library",
    "deploy:dev": "roku-deploy",
    "deploy:release": "npm run everframe:build && cd .everframe-build && roku-deploy"
  }
}

.everframe-build is a complete channel folder — the step copies the whole channel, including rokudeploy.json. That is why deploy:release runs roku-deploy from inside it. roku-deploy 3.x ignores command-line arguments and reads its options only from rokudeploy.json / bsconfig.json in the current directory, so roku-deploy --rootDir ./.everframe-build would silently deploy your uninstrumented channel. Keep the cd in the same script as the deploy tool.

You still load the library and call start() as in Install; --bundle-library copies the library zip into <out>/components/. The Main() lines from the install page are not needed in an instrumented build — the step adds the equivalent call itself. Keep them in your sources anyway if your uninstrumented builds should report crashes; the two do not conflict.

Add .everframe-build/ to .gitignore.

What gets wrapped

Each targeted function body is wrapped in try … catch … end try on the same source line, so line numbers in stack traces still match your original files. The catch records the error and then re-throws the original exception: an error that would have crashed your channel still crashes it, and an error your own code catches further up is still caught there.

  • Main / RunUserInterface in source/
  • init of every component
  • onKeyEvent
  • onChange handlers declared on interface fields
  • functions declared in a component <interface>
  • callbacks registered with observeField / observeFieldScoped
  • Task functionName targets

The last four follow extends in both directions: a callback named in a base component is wrapped where a subclass implements it, and the other way round.

Functions that cannot be wrapped safely are skipped and listed in the output. That includes any function containing a label (a goto target), because BrightScript does not allow labels inside a try block.

Instrumentation also records key presses and the scene’s init as breadcrumbs, and sets the current screen for components matching --screens.

Effect on debugging

Instrumentation changes how a crash looks in the BrightScript debugger. The original exception is caught and re-thrown from the end of the wrapped function, so:

  • the debugger stops on the wrapped function’s end sub / end function line, not on the line that failed
  • the stack has already unwound to that function, so the local variables of the failing line are gone

The report sent to Everframe still carries the original error, message and backtrace. This is the reason to instrument release and beta builds only.

Options

everframe-roku instrument <channelDir> --out <dir> \
  [--exclude <glob>]... \
  [--mechanisms main,init,key,observer,task,callfunc] \
  [--screens <globs>|none] \
  [--bundle-library] [--dry-run]
OptionMeaning
--out <dir>Where the instrumented copy is written. Required unless --dry-run. May be a subdirectory of the channel; must not be the channel directory itself or a parent of it
--exclude <glob>Skip files whose channel-relative path matches. Repeatable, for example --exclude "components/vendor/**"
--mechanismsComma-separated subset of main, init, key, observer, task, callfunc. Default is all of them
--screensComma-separated globs (* and ?) for automatic screens. Default *Screen,*View,*Page; none turns it off. Needs the init mechanism
--bundle-libraryCopy the library zip into <out>/components/
--dry-runPrint what would be wrapped and which components are tracked as screens, without writing anything

--out is emptied on every run, so files deleted from your channel do not linger in the build. To make that safe, each run writes a .everframe-build marker file into --out, and the CLI only empties a directory that has this marker. If --out exists, is not empty and has no marker, the command fails with refusing to overwrite a directory everframe-roku did not create.

Other tools

  • VS Code BrightScript extension — add a task that runs everframe:build, and a launch configuration whose rootDir is ${workspaceFolder}/.everframe-build with that task as its preLaunchTask. Keep your normal launch configuration for debugging.
  • CI or signed packaging — run everframe:build, then zip or deploy the contents of .everframe-build with your own tooling.