roku-debug 0.23.16 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/dist/Exceptions.d.ts +4 -0
- package/dist/Exceptions.js.map +1 -1
- package/dist/FileUtils.js +4 -9
- package/dist/FileUtils.js.map +1 -1
- package/dist/LaunchConfiguration.d.ts +20 -2
- package/dist/PerfettoManager.d.ts +7 -1
- package/dist/PerfettoManager.js +11 -6
- package/dist/PerfettoManager.js.map +1 -1
- package/dist/RendezvousTracker.d.ts +2 -2
- package/dist/RendezvousTracker.js +14 -41
- package/dist/RendezvousTracker.js.map +1 -1
- package/dist/RokuECP.d.ts +11 -4
- package/dist/RokuECP.js +48 -62
- package/dist/RokuECP.js.map +1 -1
- package/dist/SceneGraphDebugCommandController.d.ts +28 -2
- package/dist/SceneGraphDebugCommandController.js +100 -4
- package/dist/SceneGraphDebugCommandController.js.map +1 -1
- package/dist/adapters/DebugProtocolAdapter.d.ts +6 -0
- package/dist/adapters/DebugProtocolAdapter.js +30 -11
- package/dist/adapters/DebugProtocolAdapter.js.map +1 -1
- package/dist/adapters/TelnetAdapter.d.ts +6 -0
- package/dist/adapters/TelnetAdapter.js +21 -13
- package/dist/adapters/TelnetAdapter.js.map +1 -1
- package/dist/adapters/TelnetRequestPipeline.d.ts +3 -4
- package/dist/adapters/TelnetRequestPipeline.js.map +1 -1
- package/dist/debugProtocol/DebugProtocolClientReplaySession.js +1 -1
- package/dist/debugProtocol/DebugProtocolClientReplaySession.js.map +1 -1
- package/dist/debugProtocol/client/DebugProtocolClient.d.ts +14 -2
- package/dist/debugProtocol/client/DebugProtocolClient.js +33 -18
- package/dist/debugProtocol/client/DebugProtocolClient.js.map +1 -1
- package/dist/debugProtocol/client/DebugProtocolClientPlugin.d.ts +2 -3
- package/dist/debugSession/BrightScriptDebugSession.d.ts +68 -9
- package/dist/debugSession/BrightScriptDebugSession.js +286 -110
- package/dist/debugSession/BrightScriptDebugSession.js.map +1 -1
- package/dist/debugSession/Events.d.ts +5 -5
- package/dist/debugSession/Events.js +15 -2
- package/dist/debugSession/Events.js.map +1 -1
- package/dist/interfaces.d.ts +8 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/managers/BreakpointManager.d.ts +9 -5
- package/dist/managers/BreakpointManager.js +22 -11
- package/dist/managers/BreakpointManager.js.map +1 -1
- package/dist/managers/LocationManager.js +2 -2
- package/dist/managers/LocationManager.js.map +1 -1
- package/dist/managers/ProjectManager.d.ts +56 -1
- package/dist/managers/ProjectManager.js +146 -23
- package/dist/managers/ProjectManager.js.map +1 -1
- package/dist/util.d.ts +46 -2
- package/dist/util.js +71 -1
- package/dist/util.js.map +1 -1
- package/package.json +6 -6
|
@@ -313,6 +313,13 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
313
313
|
this.sendEvent(new debugadapter_1.BreakpointEvent(eventName, event));
|
|
314
314
|
}
|
|
315
315
|
}
|
|
316
|
+
/**
|
|
317
|
+
* A short human-readable identifier for the target device, safe for log and error messages
|
|
318
|
+
* (never includes credentials like the rceToken)
|
|
319
|
+
*/
|
|
320
|
+
get deviceLabel() {
|
|
321
|
+
return util_1.util.getDeviceLabel(this.launchConfiguration.device);
|
|
322
|
+
}
|
|
316
323
|
get enableDebugProtocol() {
|
|
317
324
|
var _a;
|
|
318
325
|
return (_a = this.launchConfiguration) === null || _a === void 0 ? void 0 : _a.enableDebugProtocol;
|
|
@@ -477,9 +484,23 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
477
484
|
normalizeLaunchConfig(config) {
|
|
478
485
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
|
|
479
486
|
var _w, _x;
|
|
487
|
+
//`device` is the canonical way to address the target device; `host` is a deprecated alias.
|
|
488
|
+
//this is the ONLY place the debugger reads the top-level `host` field: whatever was supplied
|
|
489
|
+
//is resolved to a concrete device config here, and everything downstream uses `device`.
|
|
490
|
+
if (!config.device && config.host) {
|
|
491
|
+
config.device = { host: config.host };
|
|
492
|
+
}
|
|
493
|
+
//the deprecated field is now consumed. Delete it so the runtime object matches the resolved
|
|
494
|
+
//type and nothing downstream (including the configs echoed back to the client) carries it.
|
|
495
|
+
delete config.host;
|
|
496
|
+
//an RCE device config without a token picks one up from the environment (the extension
|
|
497
|
+
//injects ROKU_RCE_TOKEN into this process so the token does not have to travel through the
|
|
498
|
+
//launch config over DAP). The custom events that echo this config back to the client scrub
|
|
499
|
+
//the token out again (see Events.ts), so it never rides the DAP wire in either direction.
|
|
500
|
+
config.device = util_1.util.hydrateRceTokenFromEnv(config.device);
|
|
480
501
|
(_a = config.cwd) !== null && _a !== void 0 ? _a : (config.cwd = process.cwd());
|
|
481
502
|
(_b = config.outDir) !== null && _b !== void 0 ? _b : (config.outDir = (0, FileUtils_1.standardizePath) `${config.cwd}/out`);
|
|
482
|
-
(_c = config.stagingDir) !== null && _c !== void 0 ? _c : (config.stagingDir = (
|
|
503
|
+
(_c = config.stagingDir) !== null && _c !== void 0 ? _c : (config.stagingDir = util_1.util.getStagingDir({ outDir: config.outDir, cwd: config.cwd }));
|
|
483
504
|
(_d = config.componentLibrariesPort) !== null && _d !== void 0 ? _d : (config.componentLibrariesPort = 8080);
|
|
484
505
|
(_e = config.packagePort) !== null && _e !== void 0 ? _e : (config.packagePort = 80);
|
|
485
506
|
(_f = config.remotePort) !== null && _f !== void 0 ? _f : (config.remotePort = 8060);
|
|
@@ -505,11 +526,16 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
505
526
|
return config;
|
|
506
527
|
}
|
|
507
528
|
async launchRequest(response, config) {
|
|
508
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
529
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
509
530
|
const logEnd = this.logger.timeStart('log', '[launchRequest] launch');
|
|
510
531
|
try {
|
|
511
532
|
this.resetSessionState();
|
|
512
533
|
this.launchConfiguration = this.normalizeLaunchConfig(config);
|
|
534
|
+
//fail fast when the launch config supplied no device addressing at all, rather than
|
|
535
|
+
//failing later with a confusing dns or connection error for an undefined host
|
|
536
|
+
if (!this.launchConfiguration.device) {
|
|
537
|
+
return await this.shutdown(`Launch config does not specify a target device. Please supply the 'device' option (or the deprecated 'host' option).`);
|
|
538
|
+
}
|
|
513
539
|
this.setupProcessErrorHandlers();
|
|
514
540
|
//prebake some threads for our ProjectManager to use later on (1 for the main project, and 1 for every complib)
|
|
515
541
|
BscProjectWorkerPool_1.bscProjectWorkerPool.preload(1 + ((_c = (_b = (_a = this.launchConfiguration) === null || _a === void 0 ? void 0 : _a.componentLibraries) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0));
|
|
@@ -518,12 +544,13 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
518
544
|
logging_1.logger.logLevel = this.launchConfiguration.logLevel;
|
|
519
545
|
}
|
|
520
546
|
this.sendLaunchProgress('start', 'Finding device on network');
|
|
521
|
-
//do a DNS lookup for the host to fix issues with roku rejecting ECP
|
|
547
|
+
//do a DNS lookup for the host to fix issues with roku rejecting ECP. Only local devices
|
|
548
|
+
//are addressed by host; other device types (like the Roku Cloud Emulator) pass through unchanged
|
|
522
549
|
try {
|
|
523
|
-
this.launchConfiguration.
|
|
550
|
+
this.launchConfiguration.device = await this.rokuDeploy.withDnsResolvedHost(this.launchConfiguration.device);
|
|
524
551
|
}
|
|
525
552
|
catch (e) {
|
|
526
|
-
return this.shutdown(`Could not resolve ip address for host '${this.
|
|
553
|
+
return this.shutdown(`Could not resolve ip address for host '${this.deviceLabel}'`);
|
|
527
554
|
}
|
|
528
555
|
// fetch device info if not supplied via launch config
|
|
529
556
|
try {
|
|
@@ -531,20 +558,20 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
531
558
|
this.deviceInfo = roku_deploy_1.rokuDeploy.enhanceDeviceInfo(this.launchConfiguration.deviceInfo);
|
|
532
559
|
}
|
|
533
560
|
else {
|
|
534
|
-
this.deviceInfo = await roku_deploy_1.rokuDeploy.getDeviceInfo({
|
|
561
|
+
this.deviceInfo = await roku_deploy_1.rokuDeploy.getDeviceInfo({ device: this.launchConfiguration.device, ecpPort: this.launchConfiguration.remotePort, enhance: true, timeout: 4000 });
|
|
535
562
|
}
|
|
536
563
|
if (this.deviceInfo.ecpSettingMode === 'limited') {
|
|
537
|
-
return await this.shutdown(`
|
|
564
|
+
return await this.shutdown(`To allow the debugger to communicate properly, please ensure on the Roku device that 'Settings' > 'System' > 'Advanced system settings' > 'Control by mobile apps' is set to "Enabled" or "Permissive". Current mode: Limited (device: ${this.deviceLabel})`);
|
|
538
565
|
}
|
|
539
566
|
}
|
|
540
567
|
catch (e) {
|
|
541
568
|
if (e instanceof roku_deploy_1.EcpNetworkAccessModeDisabledError) {
|
|
542
|
-
return this.shutdown(`
|
|
569
|
+
return this.shutdown(`To allow the debugger to communicate properly, please ensure on the Roku device that 'Settings' > 'System' > 'Advanced system settings' > 'Control by mobile apps' is set to "Enabled" or "Permissive". Current mode: Disabled (device: ${this.deviceLabel})`);
|
|
543
570
|
}
|
|
544
|
-
return this.shutdown(`Unable to connect to roku at '${this.
|
|
571
|
+
return this.shutdown(`Unable to connect to roku at '${this.deviceLabel}'. Verify the device address is correct and that the device is powered on and reachable.`);
|
|
545
572
|
}
|
|
546
573
|
if (this.deviceInfo && !this.deviceInfo.developerEnabled) {
|
|
547
|
-
return await this.shutdown(`Developer mode is not enabled for
|
|
574
|
+
return await this.shutdown(`Developer mode is not enabled for device '${this.deviceLabel}'.`);
|
|
548
575
|
}
|
|
549
576
|
// everything is ready, send the response to the launch request so the UI can update and configuration can begin
|
|
550
577
|
this.sendResponse(response);
|
|
@@ -571,10 +598,10 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
571
598
|
}
|
|
572
599
|
packageEnd();
|
|
573
600
|
if (this.enableDebugProtocol) {
|
|
574
|
-
util_1.util.log(`Connecting to Roku via the BrightScript debug protocol at ${this.
|
|
601
|
+
util_1.util.log(`Connecting to Roku via the BrightScript debug protocol at ${this.deviceLabel}:${this.launchConfiguration.controlPort}`);
|
|
575
602
|
}
|
|
576
603
|
else {
|
|
577
|
-
util_1.util.log(`Connecting to Roku via telnet at ${this.
|
|
604
|
+
util_1.util.log(`Connecting to Roku via telnet at ${this.deviceLabel}:${this.launchConfiguration.brightScriptConsolePort}`);
|
|
578
605
|
}
|
|
579
606
|
//activate rendezvous tracking (if enabled). Log the error and move on if it crashes, this shouldn't bring down the session.
|
|
580
607
|
try {
|
|
@@ -588,6 +615,15 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
588
615
|
this.sendLaunchProgress('update', 'Connecting to debug server');
|
|
589
616
|
const connectAdapterEnd = this.logger.timeStart('log', 'Connect adapter');
|
|
590
617
|
this.createRokuAdapter(this.rendezvousTracker);
|
|
618
|
+
// Manipulating complibs on-device autolaunches the dev app and often triggers compile errors,
|
|
619
|
+
// so if we have at least one installable complib, delete the dev app and any complibs to avoid all that.
|
|
620
|
+
if ((_j = this.launchConfiguration.componentLibraries) === null || _j === void 0 ? void 0 : _j.some(x => x.install)) {
|
|
621
|
+
this.sendLaunchProgress('update', 'Removing existing dev app and component libraries');
|
|
622
|
+
await roku_deploy_1.rokuDeploy.deleteAllSideloadedPlugins({
|
|
623
|
+
device: this.launchConfiguration.device,
|
|
624
|
+
password: this.launchConfiguration.password
|
|
625
|
+
});
|
|
626
|
+
}
|
|
591
627
|
await this.connectRokuAdapter();
|
|
592
628
|
connectAdapterEnd();
|
|
593
629
|
// Capabilities that depend on the adapter or device version. The exception-breakpoint
|
|
@@ -615,8 +651,8 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
615
651
|
util_1.util.log('Encountered an issue during the launch process');
|
|
616
652
|
util_1.util.log(e === null || e === void 0 ? void 0 : e.stack);
|
|
617
653
|
//send any compile errors to the client
|
|
618
|
-
await ((
|
|
619
|
-
const message = (e instanceof Exceptions_1.SocketConnectionInUseError) ? e.message : ((
|
|
654
|
+
await ((_k = this.rokuAdapter) === null || _k === void 0 ? void 0 : _k.sendErrors());
|
|
655
|
+
const message = (e instanceof Exceptions_1.SocketConnectionInUseError) ? e.message : ((_l = e === null || e === void 0 ? void 0 : e.stack) !== null && _l !== void 0 ? _l : e);
|
|
620
656
|
await this.shutdown(message, true);
|
|
621
657
|
}
|
|
622
658
|
else {
|
|
@@ -633,7 +669,7 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
633
669
|
try {
|
|
634
670
|
await this.runAutomaticSceneGraphCommands(this.launchConfiguration.autoRunSgDebugCommands);
|
|
635
671
|
//press the home button to ensure we're at the home screen
|
|
636
|
-
await this.rokuDeploy.
|
|
672
|
+
await this.rokuDeploy.keyPress({ device: this.launchConfiguration.device, key: 'Home', ecpPort: this.launchConfiguration.remotePort });
|
|
637
673
|
//pass the log level down thought the adapter to the RendezvousTracker and ChanperfTracker
|
|
638
674
|
this.rokuAdapter.setConsoleOutput(this.launchConfiguration.consoleOutput);
|
|
639
675
|
//pass along the console output
|
|
@@ -649,7 +685,7 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
649
685
|
}
|
|
650
686
|
this.rokuAdapter.on('device-unresponsive', async (data) => {
|
|
651
687
|
const stopDebuggerAction = 'Stop Debugger';
|
|
652
|
-
const message = `Roku device ${this.
|
|
688
|
+
const message = `Roku device ${this.deviceLabel} is not responding and may not recover.` +
|
|
653
689
|
(data.lastCommand ? `\n\nActive command:\n"${util_1.util.truncate(data.lastCommand, 30)}"` : '');
|
|
654
690
|
this.logger.log(message, data);
|
|
655
691
|
const response = await this.showPopupMessage(message, 'warn', false, [stopDebuggerAction]);
|
|
@@ -697,10 +733,20 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
697
733
|
//profiling supports connecting to the socket BEFORE a channel is published, so go ahead and connect now
|
|
698
734
|
await this.tryProfilingConnectOnStart();
|
|
699
735
|
//all setBreakpoints requests have arrived by this point (configurationDone is the DAP signal
|
|
700
|
-
//that the client has finished sending configuration).
|
|
736
|
+
//that the client has finished sending configuration). Validate breakpoints across every project
|
|
737
|
+
//in one pass (and inject the STOPs for telnet) - validating per project would fail every other
|
|
738
|
+
//project's breakpoints. Must finish before postfixing renames the complib files.
|
|
739
|
+
await this.writeBreakpoints();
|
|
740
|
+
//postfix each complib's own files now (still no zips. those are sealed after cross-project
|
|
741
|
+
//references are rewritten).
|
|
742
|
+
await this.postfixComponentLibraries(this.launchConfiguration.componentLibraries);
|
|
743
|
+
//now that EVERY project (main + all complibs) is postfixed, rewrite cross-project `Library`
|
|
744
|
+
//references to point at the postfixed file names. Must run before any project zip is sealed.
|
|
745
|
+
await this.projectManager.applyLibraryReferencePostfixes();
|
|
746
|
+
//now zip the main project, also zip and upload installable complibs, and start the webserver for non-installed complibs.
|
|
701
747
|
await Promise.all([
|
|
702
|
-
this.
|
|
703
|
-
this.
|
|
748
|
+
this.zipMainProject(),
|
|
749
|
+
this.zipServeAndInstallComponentLibraries(this.launchConfiguration.componentLibraries, this.launchConfiguration.componentLibrariesPort)
|
|
704
750
|
]);
|
|
705
751
|
this.sendLaunchProgress('update', 'Uploading to Roku');
|
|
706
752
|
await this.publish();
|
|
@@ -717,13 +763,13 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
717
763
|
if (!error) {
|
|
718
764
|
if (this.rokuAdapter.connected) {
|
|
719
765
|
this.logger.info('Host connection was established before the main public process was completed');
|
|
720
|
-
this.logger.log(`deployed to Roku@${this.
|
|
766
|
+
this.logger.log(`deployed to Roku@${this.deviceLabel}`);
|
|
721
767
|
}
|
|
722
768
|
else {
|
|
723
769
|
this.logger.info('Main public process was completed but we are still waiting for a connection to the host');
|
|
724
770
|
this.rokuAdapter.on('connected', (status) => {
|
|
725
771
|
if (status) {
|
|
726
|
-
this.logger.log(`deployed to Roku@${this.
|
|
772
|
+
this.logger.log(`deployed to Roku@${this.deviceLabel}`);
|
|
727
773
|
}
|
|
728
774
|
});
|
|
729
775
|
}
|
|
@@ -738,7 +784,7 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
738
784
|
//if we are at a breakpoint, continue
|
|
739
785
|
await this.rokuAdapter.continue();
|
|
740
786
|
//kill the app on the roku
|
|
741
|
-
// await this.rokuDeploy.
|
|
787
|
+
// await this.rokuDeploy.keyPress({ device: this.launchConfiguration.device, key: 'Home', ecpPort: this.launchConfiguration.remotePort });
|
|
742
788
|
//convert a hostname to an ip address
|
|
743
789
|
const deepLinkUrl = await util_1.util.resolveUrl(this.launchConfiguration.deepLinkUrl);
|
|
744
790
|
//send the deep link http request
|
|
@@ -764,10 +810,10 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
764
810
|
* Activate all required functionality for profiling
|
|
765
811
|
*/
|
|
766
812
|
async initializeProfiling() {
|
|
767
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
813
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
768
814
|
// Initialize PerfettoManager
|
|
769
815
|
this.perfettoManager = new PerfettoManager_1.PerfettoManager({
|
|
770
|
-
|
|
816
|
+
device: this.launchConfiguration.device,
|
|
771
817
|
rootDir: this.launchConfiguration.rootDir,
|
|
772
818
|
remotePort: this.launchConfiguration.remotePort,
|
|
773
819
|
...(_a = this.launchConfiguration.profiling) === null || _a === void 0 ? void 0 : _a.tracing
|
|
@@ -798,20 +844,20 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
798
844
|
if (((_c = (_b = this.launchConfiguration.profiling) === null || _b === void 0 ? void 0 : _b.tracing) === null || _c === void 0 ? void 0 : _c.enable) && this.supportsPerfettoTracing) {
|
|
799
845
|
this.logger.info('Enabling perfetto tracing because it is supported by the device and enabled in the launch configuration');
|
|
800
846
|
try {
|
|
801
|
-
await this.perfettoManager.enableTracing();
|
|
847
|
+
await ((_e = (_d = this.perfettoManager) === null || _d === void 0 ? void 0 : _d.enableTracing) === null || _e === void 0 ? void 0 : _e.call(_d));
|
|
802
848
|
}
|
|
803
849
|
catch (e) {
|
|
804
850
|
this.logger.error('Failed to enable perfetto tracing', e);
|
|
805
851
|
}
|
|
806
852
|
//tracing is expicitly DISabled. turn it off
|
|
807
853
|
}
|
|
808
|
-
else if (((
|
|
854
|
+
else if (((_g = (_f = this.launchConfiguration.profiling) === null || _f === void 0 ? void 0 : _f.tracing) === null || _g === void 0 ? void 0 : _g.enable) === false && this.supportsPerfettoTracing) {
|
|
809
855
|
this.logger.info('Disabling perfetto tracing because it is disabled in the launch configuration');
|
|
810
856
|
//TODO implement a way to disable perfetto tracing on the device
|
|
811
857
|
//tracing was requested but the device firmware does not meet the minimum requirement
|
|
812
858
|
}
|
|
813
|
-
else if (((
|
|
814
|
-
const firmwareVersion = (
|
|
859
|
+
else if (((_j = (_h = this.launchConfiguration.profiling) === null || _h === void 0 ? void 0 : _h.tracing) === null || _j === void 0 ? void 0 : _j.enable) && !this.supportsPerfettoTracing) {
|
|
860
|
+
const firmwareVersion = (_l = (_k = this.deviceInfo) === null || _k === void 0 ? void 0 : _k.softwareVersion) !== null && _l !== void 0 ? _l : 'unknown';
|
|
815
861
|
const message = `Perfetto profiling is not available: device firmware ${firmwareVersion} is below the minimum required version (15.2). The Perfetto profiling buttons will not be available during this session.`;
|
|
816
862
|
this.logger.warn(message);
|
|
817
863
|
this.showPopupMessage(message, 'warn').catch(e => this.logger.error('Failed to show Perfetto unavailable notification', e));
|
|
@@ -825,17 +871,17 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
825
871
|
* If profiling was marked "connectOnStart", try connecting right away
|
|
826
872
|
*/
|
|
827
873
|
async tryProfilingConnectOnStart() {
|
|
828
|
-
var _a, _b, _c, _d, _e, _f;
|
|
874
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
829
875
|
if (((_b = (_a = this.launchConfiguration.profiling) === null || _a === void 0 ? void 0 : _a.tracing) === null || _b === void 0 ? void 0 : _b.connectOnStart) && this.supportsPerfettoTracing) {
|
|
830
876
|
try {
|
|
831
|
-
await this.perfettoManager.startTracing();
|
|
877
|
+
await ((_d = (_c = this.perfettoManager) === null || _c === void 0 ? void 0 : _c.startTracing) === null || _d === void 0 ? void 0 : _d.call(_c));
|
|
832
878
|
}
|
|
833
879
|
catch (e) {
|
|
834
880
|
this.logger.error('Failed to start perfetto tracing on start', e);
|
|
835
881
|
}
|
|
836
882
|
}
|
|
837
|
-
else if (((
|
|
838
|
-
const firmwareVersion = (
|
|
883
|
+
else if (((_f = (_e = this.launchConfiguration.profiling) === null || _e === void 0 ? void 0 : _e.tracing) === null || _f === void 0 ? void 0 : _f.connectOnStart) && !this.supportsPerfettoTracing) {
|
|
884
|
+
const firmwareVersion = (_h = (_g = this.deviceInfo) === null || _g === void 0 ? void 0 : _g.softwareVersion) !== null && _h !== void 0 ? _h : 'unknown';
|
|
839
885
|
const message = `Perfetto profiling is not available: device firmware ${firmwareVersion} is below the minimum required version (15.2). Tracing will not start automatically.`;
|
|
840
886
|
this.logger.warn(message);
|
|
841
887
|
this.showPopupMessage(message, 'warn').catch(e => this.logger.error('Failed to show Perfetto unavailable notification', e));
|
|
@@ -915,14 +961,17 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
915
961
|
this.sendEvent(new Events_1.DiagnosticsEvent(diagnostics));
|
|
916
962
|
}
|
|
917
963
|
async publish() {
|
|
918
|
-
var _a, _b;
|
|
964
|
+
var _a, _b, _c;
|
|
919
965
|
const uploadingEnd = this.logger.timeStart('log', 'Uploading zip');
|
|
920
966
|
let packageIsPublished = false;
|
|
921
967
|
//delete any currently installed dev channel (if enabled to do so)
|
|
922
968
|
try {
|
|
923
969
|
if (this.launchConfiguration.deleteDevChannelBeforeInstall === true) {
|
|
924
|
-
await this.rokuDeploy.
|
|
925
|
-
|
|
970
|
+
await this.rokuDeploy.deleteDevChannel({
|
|
971
|
+
device: this.launchConfiguration.device,
|
|
972
|
+
password: this.launchConfiguration.password,
|
|
973
|
+
username: this.launchConfiguration.username,
|
|
974
|
+
packagePort: this.launchConfiguration.packagePort
|
|
926
975
|
});
|
|
927
976
|
}
|
|
928
977
|
}
|
|
@@ -937,25 +986,28 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
937
986
|
}
|
|
938
987
|
const isConnected = this.rokuAdapter.once('app-ready');
|
|
939
988
|
const options = {
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
989
|
+
device: this.launchConfiguration.device,
|
|
990
|
+
password: this.launchConfiguration.password,
|
|
991
|
+
username: this.launchConfiguration.username,
|
|
992
|
+
packagePort: this.launchConfiguration.packagePort,
|
|
993
|
+
ecpPort: this.launchConfiguration.remotePort,
|
|
994
|
+
//sideload the zip that was already built from the staging folder (or supplied via packagePath)
|
|
995
|
+
zip: (_c = this.launchConfiguration.packagePath) !== null && _c !== void 0 ? _c : util_1.util.getOutputZipPath({ outDir: this.launchConfiguration.outDir }),
|
|
943
996
|
// enable the debug protocol if true
|
|
944
997
|
remoteDebug: this.enableDebugProtocol,
|
|
945
998
|
//necessary for capturing compile errors from the protocol (has no effect on telnet)
|
|
946
999
|
remoteDebugConnectEarly: false,
|
|
947
1000
|
//we don't want to fail if there were compile errors...we'll let our compile error processor handle that
|
|
948
1001
|
failOnCompileError: true,
|
|
1002
|
+
//deleting the dev channel (when enabled) was already handled at the start of this function
|
|
1003
|
+
deleteDevChannel: false,
|
|
1004
|
+
//the device was already sent to the home screen during configurationDone
|
|
1005
|
+
close: false,
|
|
949
1006
|
//pass any upload form overrides the client may have configured
|
|
950
1007
|
packageUploadOverrides: this.launchConfiguration.packageUploadOverrides
|
|
951
1008
|
};
|
|
952
|
-
//if packagePath is specified, use that info instead of outDir and outFile
|
|
953
|
-
if (this.launchConfiguration.packagePath) {
|
|
954
|
-
options.outDir = path.dirname(this.launchConfiguration.packagePath);
|
|
955
|
-
options.outFile = path.basename(this.launchConfiguration.packagePath);
|
|
956
|
-
}
|
|
957
1009
|
//publish the package to the target Roku
|
|
958
|
-
const publishPromise = this.rokuDeploy.
|
|
1010
|
+
const publishPromise = this.rokuDeploy.sideload(options).then(() => {
|
|
959
1011
|
packageIsPublished = true;
|
|
960
1012
|
}).catch(async (e) => {
|
|
961
1013
|
var _a, _b;
|
|
@@ -1118,7 +1170,7 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
1118
1170
|
}
|
|
1119
1171
|
async runAutomaticSceneGraphCommands(commands) {
|
|
1120
1172
|
if (commands) {
|
|
1121
|
-
let connection = new SceneGraphDebugCommandController_1.SceneGraphDebugCommandController(this.launchConfiguration.
|
|
1173
|
+
let connection = new SceneGraphDebugCommandController_1.SceneGraphDebugCommandController(this.launchConfiguration.device, this.launchConfiguration.sceneGraphDebugCommandsPort);
|
|
1122
1174
|
try {
|
|
1123
1175
|
await connection.connect();
|
|
1124
1176
|
for (let command of this.launchConfiguration.autoRunSgDebugCommands) {
|
|
@@ -1186,28 +1238,29 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
1186
1238
|
await this.handleEntryBreakpoint();
|
|
1187
1239
|
}
|
|
1188
1240
|
/**
|
|
1189
|
-
*
|
|
1190
|
-
* Runs after the DAP `InitializedEvent` so client-side `setBreakpoints` requests have
|
|
1191
|
-
* before any STOPs are written
|
|
1241
|
+
* Validate every breakpoint against the staged projects and (telnet only) write STOP statements into the
|
|
1242
|
+
* staged .brs files. Runs after the DAP `InitializedEvent` so client-side `setBreakpoints` requests have
|
|
1243
|
+
* landed before any STOPs are written. Validates the main project and every component library in ONE pass:
|
|
1244
|
+
* a breakpoint is only failed when it can't be placed in any project, so validating per project would fail
|
|
1245
|
+
* every other project's breakpoints. Must complete before `postfixComponentLibraries` renames the complib
|
|
1246
|
+
* files so telnet STOPs are written to the original file names.
|
|
1192
1247
|
*/
|
|
1193
|
-
async
|
|
1194
|
-
var _a;
|
|
1248
|
+
async writeBreakpoints() {
|
|
1195
1249
|
//add breakpoint lines to source files and then publish
|
|
1196
1250
|
util_1.util.log('Adding stop statements for active breakpoints');
|
|
1197
1251
|
//validate breakpoints for all debugger types (and write `stop` statements for telnet — decided internally)
|
|
1198
|
-
await this.breakpointManager.
|
|
1252
|
+
await this.breakpointManager.validateAndWriteBreakpointsForProjects(this.projectManager.getAllProjects());
|
|
1253
|
+
}
|
|
1254
|
+
/**
|
|
1255
|
+
* Create the main project's zip package (or run the configured packageTask). Must run AFTER
|
|
1256
|
+
* `applyLibraryReferencePostfixes` so any rewritten `Library` statements are included in the zip.
|
|
1257
|
+
*/
|
|
1258
|
+
async zipMainProject() {
|
|
1259
|
+
var _a;
|
|
1199
1260
|
if (this.launchConfiguration.packageTask) {
|
|
1200
1261
|
util_1.util.log(`Executing task '${this.launchConfiguration.packageTask}' to assemble the app`);
|
|
1201
1262
|
await this.sendCustomRequest('executeTask', { task: this.launchConfiguration.packageTask });
|
|
1202
|
-
const
|
|
1203
|
-
...this.launchConfiguration
|
|
1204
|
-
};
|
|
1205
|
-
//if packagePath is specified, use that info instead of outDir and outFile
|
|
1206
|
-
if (this.launchConfiguration.packagePath) {
|
|
1207
|
-
options.outDir = path.dirname(this.launchConfiguration.packagePath);
|
|
1208
|
-
options.outFile = path.basename(this.launchConfiguration.packagePath);
|
|
1209
|
-
}
|
|
1210
|
-
const packagePath = (_a = this.launchConfiguration.packagePath) !== null && _a !== void 0 ? _a : roku_deploy_1.rokuDeploy.getOutputZipFilePath(options);
|
|
1263
|
+
const packagePath = (_a = this.launchConfiguration.packagePath) !== null && _a !== void 0 ? _a : util_1.util.getOutputZipPath({ outDir: this.launchConfiguration.outDir });
|
|
1211
1264
|
if (!fsExtra.pathExistsSync(packagePath)) {
|
|
1212
1265
|
return this.shutdown(`Cancelling debug session. Package does not exist at '${packagePath}'`);
|
|
1213
1266
|
}
|
|
@@ -1223,6 +1276,7 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
1223
1276
|
* @param command name of the command to execute
|
|
1224
1277
|
*/
|
|
1225
1278
|
async customRequest(command, response, args) {
|
|
1279
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1226
1280
|
if (command === 'rendezvous.clearHistory') {
|
|
1227
1281
|
this.rokuAdapter.clearRendezvousHistory();
|
|
1228
1282
|
}
|
|
@@ -1236,11 +1290,11 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
1236
1290
|
this.emit('popupMessageEventResponse', args);
|
|
1237
1291
|
}
|
|
1238
1292
|
else if (command === 'captureHeapSnapshot') {
|
|
1239
|
-
this.perfettoManager.captureHeapSnapshot().catch((e) => this.logger.error('Failed to capture heap snapshot', e));
|
|
1293
|
+
(_b = (_a = this.perfettoManager) === null || _a === void 0 ? void 0 : _a.captureHeapSnapshot) === null || _b === void 0 ? void 0 : _b.call(_a).catch((e) => this.logger.error('Failed to capture heap snapshot', e));
|
|
1240
1294
|
}
|
|
1241
1295
|
else if (command === 'startPerfettoTracing') {
|
|
1242
1296
|
try {
|
|
1243
|
-
await this.perfettoManager.startTracing();
|
|
1297
|
+
await ((_d = (_c = this.perfettoManager) === null || _c === void 0 ? void 0 : _c.startTracing) === null || _d === void 0 ? void 0 : _d.call(_c));
|
|
1244
1298
|
}
|
|
1245
1299
|
catch (e) {
|
|
1246
1300
|
response.success = false;
|
|
@@ -1249,7 +1303,7 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
1249
1303
|
}
|
|
1250
1304
|
else if (command === 'stopPerfettoTracing') {
|
|
1251
1305
|
try {
|
|
1252
|
-
await this.perfettoManager.stopTracing();
|
|
1306
|
+
await ((_f = (_e = this.perfettoManager) === null || _e === void 0 ? void 0 : _e.stopTracing) === null || _f === void 0 ? void 0 : _f.call(_e));
|
|
1253
1307
|
}
|
|
1254
1308
|
catch (e) {
|
|
1255
1309
|
response.success = false;
|
|
@@ -1291,32 +1345,43 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
1291
1345
|
await Promise.all(this.projectManager.componentLibraryProjects.map(compLibProject => compLibProject.stage()));
|
|
1292
1346
|
}
|
|
1293
1347
|
/**
|
|
1294
|
-
*
|
|
1295
|
-
*
|
|
1296
|
-
*
|
|
1297
|
-
*
|
|
1348
|
+
* Postfix each staged complib's own files (rename its `.brs` files with the `__lib<index>` postfix and
|
|
1349
|
+
* rewrite its `uri=` references). Runs in `configurationDoneRequest` AFTER `writeBreakpoints` so any
|
|
1350
|
+
* telnet STOPs have already been written to the original file names. Kept separate from zipping so the
|
|
1351
|
+
* cross-project `Library` reference rewrite can run after EVERY project is postfixed but before any zip
|
|
1352
|
+
* is sealed.
|
|
1298
1353
|
*/
|
|
1299
|
-
async
|
|
1354
|
+
async postfixComponentLibraries(componentLibraries) {
|
|
1355
|
+
if (!componentLibraries || componentLibraries.length === 0) {
|
|
1356
|
+
return;
|
|
1357
|
+
}
|
|
1358
|
+
//postfix each complib's own files in parallel
|
|
1359
|
+
await Promise.all(this.projectManager.componentLibraryProjects.map(compLibProject => compLibProject.postfixFiles()));
|
|
1360
|
+
}
|
|
1361
|
+
/**
|
|
1362
|
+
* Seal every component library's zip, install the installable ones (in order), and start static file
|
|
1363
|
+
* hosting. Must run AFTER `applyLibraryReferencePostfixes` so each zip contains the rewritten `Library`
|
|
1364
|
+
* statements.
|
|
1365
|
+
*/
|
|
1366
|
+
async zipServeAndInstallComponentLibraries(componentLibraries, port) {
|
|
1367
|
+
var _a;
|
|
1300
1368
|
if (!componentLibraries || componentLibraries.length === 0) {
|
|
1301
1369
|
return;
|
|
1302
1370
|
}
|
|
1303
1371
|
const componentLibrariesOutDir = (0, FileUtils_1.standardizePath) `${this.launchConfiguration.outDir}/component-libraries`;
|
|
1304
|
-
// Add breakpoint lines to the staging files and before publishing
|
|
1305
|
-
util_1.util.log('Adding stop statements for active breakpoints in Component Libraries');
|
|
1306
|
-
//validate breakpoints (and write STOPs for telnet), postfix, and zip each complib in parallel — each targets distinct files
|
|
1307
|
-
const packagePromises = this.projectManager.componentLibraryProjects.map(async (compLibProject) => {
|
|
1308
|
-
await this.breakpointManager.validateAndWriteBreakpointsForProject(compLibProject);
|
|
1309
|
-
await compLibProject.postfixFiles();
|
|
1310
|
-
await compLibProject.zipPackage({ retainStagingFolder: true });
|
|
1311
|
-
});
|
|
1312
1372
|
const needToDeleteComplibs = this.projectManager.componentLibraryProjects.some(x => x.install);
|
|
1313
1373
|
if (needToDeleteComplibs) {
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1374
|
+
//deleteAllComponentLibraries pauses compile-error reporting (to swallow deletion-induced errors) and
|
|
1375
|
+
//leaves it paused. Now that deletion is done and we're about to put libraries back on the device, settle
|
|
1376
|
+
//(draining any lingering deletion output) and resume reporting so real install errors are surfaced.
|
|
1377
|
+
await this.deleteAllComponentLibraries();
|
|
1378
|
+
}
|
|
1379
|
+
//zip each complib in parallel
|
|
1380
|
+
const packagePromises = this.projectManager.componentLibraryProjects.map(compLibProject => compLibProject.zipPackage({ retainStagingFolder: true }));
|
|
1381
|
+
//install component libraries strictly in their declared order (which must be dependency order:
|
|
1382
|
+
//a library may only depend on libraries declared before it). Each install is fully awaited before the
|
|
1383
|
+
//next begins, and a failure aborts the launch. Installing out of order, or continuing past a failed
|
|
1384
|
+
//install, leaves the device with dangling `Library` references that fail to compile.
|
|
1320
1385
|
for (let i = 0; i < this.projectManager.componentLibraryProjects.length; i++) {
|
|
1321
1386
|
const compLibProject = this.projectManager.componentLibraryProjects[i];
|
|
1322
1387
|
if (compLibProject.install === true) {
|
|
@@ -1326,25 +1391,29 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
1326
1391
|
await this.sendCustomRequest('executeTask', { task: componentLibraries[i].packageTask });
|
|
1327
1392
|
}
|
|
1328
1393
|
const options = {
|
|
1329
|
-
|
|
1394
|
+
device: this.launchConfiguration.device,
|
|
1330
1395
|
password: this.launchConfiguration.password,
|
|
1331
1396
|
username: this.launchConfiguration.username || 'rokudev',
|
|
1332
|
-
|
|
1397
|
+
packagePort: this.launchConfiguration.packagePort,
|
|
1398
|
+
ecpPort: this.launchConfiguration.remotePort,
|
|
1399
|
+
zip: (_a = componentLibraries[i].packagePath) !== null && _a !== void 0 ? _a : util_1.util.getOutputZipPath({ outDir: compLibProject.outDir, outFile: compLibProject.outFile }),
|
|
1333
1400
|
failOnCompileError: true,
|
|
1334
|
-
outDir: compLibProject.outDir,
|
|
1335
|
-
outFile: compLibProject.outFile,
|
|
1336
1401
|
appType: 'dcl',
|
|
1402
|
+
//installing a component library should never close or delete the sideloaded channel
|
|
1403
|
+
close: false,
|
|
1404
|
+
deleteDevChannel: false,
|
|
1337
1405
|
packageUploadOverrides: componentLibraries[i].packageUploadOverrides || {}
|
|
1338
1406
|
};
|
|
1339
|
-
|
|
1340
|
-
options.outDir = path.dirname(componentLibraries[i].packagePath);
|
|
1341
|
-
options.outFile = path.basename(componentLibraries[i].packagePath);
|
|
1342
|
-
}
|
|
1407
|
+
util_1.util.log(`Installing component library ${i} (${compLibProject.outFile})`);
|
|
1343
1408
|
try {
|
|
1344
|
-
await roku_deploy_1.rokuDeploy.
|
|
1409
|
+
await roku_deploy_1.rokuDeploy.sideload(options);
|
|
1410
|
+
util_1.util.log(`Installed component library ${i} (${compLibProject.outFile})`);
|
|
1345
1411
|
}
|
|
1346
1412
|
catch (error) {
|
|
1347
|
-
|
|
1413
|
+
//do NOT continue installing further libraries (or publishing the main app) - a failed install
|
|
1414
|
+
//here means later libraries and the main app would reference a library that isn't on the device
|
|
1415
|
+
this.logger.error(`Error installing component library ${i} (${compLibProject.outFile})`, error);
|
|
1416
|
+
throw error;
|
|
1348
1417
|
}
|
|
1349
1418
|
}
|
|
1350
1419
|
}
|
|
@@ -1357,6 +1426,106 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
1357
1426
|
hostingPromise
|
|
1358
1427
|
]);
|
|
1359
1428
|
}
|
|
1429
|
+
/**
|
|
1430
|
+
* Delete every component library installed on the device.
|
|
1431
|
+
*
|
|
1432
|
+
* A complib can only depend on complibs the user declared BEFORE it, so the user's configured order is already
|
|
1433
|
+
* dependency order. Deleting in the REVERSE of that order therefore deletes each complib before the ones it
|
|
1434
|
+
* depends on, avoiding compile errors entirely. We use that reverse order as the delete priority.
|
|
1435
|
+
*
|
|
1436
|
+
* Anything still installed that the user did NOT configure (e.g. leftovers from a previous session) has no known
|
|
1437
|
+
* order, so we fall back to compile-error tolerance for those: deleting a complib while another still references
|
|
1438
|
+
* it fails with a compile error, which we ignore and retry later (after its dependent is gone). We finish when
|
|
1439
|
+
* every complib is gone, and fail if the only complibs left are ones that can never be deleted.
|
|
1440
|
+
*/
|
|
1441
|
+
async deleteAllComponentLibraries() {
|
|
1442
|
+
var _a;
|
|
1443
|
+
const deviceOptions = {
|
|
1444
|
+
device: this.launchConfiguration.device,
|
|
1445
|
+
password: this.launchConfiguration.password,
|
|
1446
|
+
username: this.launchConfiguration.username || 'rokudev'
|
|
1447
|
+
};
|
|
1448
|
+
//The user's configured complibs, in REVERSE declaration order (dependents before their dependencies). A
|
|
1449
|
+
//complib's position here is its delete priority; complibs the user didn't configure aren't in this list.
|
|
1450
|
+
const deletePriority = this.projectManager.componentLibraryProjects
|
|
1451
|
+
.map(complib => complib.outFile)
|
|
1452
|
+
.reverse();
|
|
1453
|
+
//sort key for a complib: its index in `deletePriority`, or Infinity (delete last) if it wasn't configured
|
|
1454
|
+
const priorityOf = (fileName) => {
|
|
1455
|
+
const index = deletePriority.indexOf(fileName);
|
|
1456
|
+
return index === -1 ? Infinity : index;
|
|
1457
|
+
};
|
|
1458
|
+
const maxAttempts = 5;
|
|
1459
|
+
//how many times we've tried to delete each complib, and the ones we've given up on after maxAttempts
|
|
1460
|
+
const attempts = new Map();
|
|
1461
|
+
const failed = new Set();
|
|
1462
|
+
while (true) {
|
|
1463
|
+
//re-fetch the installed complibs after each iteration: deleting one complib can cascade-delete others, so never delete a stale entry
|
|
1464
|
+
const installed = (await roku_deploy_1.rokuDeploy.listSideloadedPlugins(deviceOptions)).filter(x => x.appType === 'dcl');
|
|
1465
|
+
const attemptCount = (complib) => { var _a; return (_a = attempts.get(complib.archiveFileName)) !== null && _a !== void 0 ? _a : 0; };
|
|
1466
|
+
//of the complibs we haven't given up on, pick the next to delete: fewest attempts first (spreads retries
|
|
1467
|
+
//evenly so a dependent gets deleted before we circle back to a complib that previously failed), and among
|
|
1468
|
+
//equal attempts, follow the reverse-configured delete priority (dependents before dependencies).
|
|
1469
|
+
const candidates = installed.filter(complib => !failed.has(complib.archiveFileName));
|
|
1470
|
+
const next = candidates.sort((a, b) => attemptCount(a) - attemptCount(b) ||
|
|
1471
|
+
priorityOf(a.archiveFileName) - priorityOf(b.archiveFileName))[0];
|
|
1472
|
+
//nothing left to try — done if the device is clear, otherwise hard-fail with whatever remains
|
|
1473
|
+
if (!next) {
|
|
1474
|
+
if (installed.length > 0) {
|
|
1475
|
+
throw new Error(`Failed to delete existing component libraries on device; ${installed.length} still installed: ${installed.map(x => x.archiveFileName).join(', ')}`);
|
|
1476
|
+
}
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
const fileName = next.archiveFileName;
|
|
1480
|
+
attempts.set(fileName, ((_a = attempts.get(fileName)) !== null && _a !== void 0 ? _a : 0) + 1);
|
|
1481
|
+
try {
|
|
1482
|
+
await roku_deploy_1.rokuDeploy.deleteComponentLibrary({ ...deviceOptions, fileName: fileName });
|
|
1483
|
+
}
|
|
1484
|
+
catch (error) {
|
|
1485
|
+
//re-throw anything that isn't a dependency compile error (auth, network, etc.)
|
|
1486
|
+
if (!this.isComponentLibraryDependencyCompileError(error)) {
|
|
1487
|
+
throw error;
|
|
1488
|
+
}
|
|
1489
|
+
//Deleting a complib that a dependent still references produces a compile error - but the delete may
|
|
1490
|
+
//STILL have succeeded (the device reports both a compile error and 'Delete Succeeded'). If it actually
|
|
1491
|
+
//deleted, treat it as done. Only if it did NOT succeed do we defer and retry it later.
|
|
1492
|
+
if (this.wasComponentLibraryDeleteSuccessful(error)) {
|
|
1493
|
+
this.logger.trace(`Deleted '${fileName}' (device reported a compile error but the delete succeeded)`);
|
|
1494
|
+
}
|
|
1495
|
+
else {
|
|
1496
|
+
//another not-yet-deleted complib still depends on this one. Give up on it once it's out of
|
|
1497
|
+
//attempts; otherwise leave it pending so we retry after its dependents are gone.
|
|
1498
|
+
if (attempts.get(fileName) >= maxAttempts) {
|
|
1499
|
+
failed.add(fileName);
|
|
1500
|
+
}
|
|
1501
|
+
this.logger.log(`Deferring delete of '${fileName}'; another component library still depends on it`);
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
//the Roku doesn't appreciate back-to-back deletes; give it a moment between requests
|
|
1505
|
+
await util_1.util.sleep(10);
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
/**
|
|
1509
|
+
* Is this error the device reporting a compile failure (which, during complib deletion, means another
|
|
1510
|
+
* installed complib still references the one we tried to delete)?
|
|
1511
|
+
*/
|
|
1512
|
+
isComponentLibraryDependencyCompileError(error) {
|
|
1513
|
+
var _a;
|
|
1514
|
+
const message = `${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : ''}`;
|
|
1515
|
+
return /compile error|compilation failed/i.test(message);
|
|
1516
|
+
}
|
|
1517
|
+
/**
|
|
1518
|
+
* Did the component-library delete actually succeed, even though the device also reported a compile error?
|
|
1519
|
+
* When we delete a complib that a dependent still references, the device reports BOTH a compile error AND a
|
|
1520
|
+
* `Delete Succeeded` message - meaning the complib really was removed. roku-deploy attaches the parsed device
|
|
1521
|
+
* messages (`{ errors, infos, successes }`) to the thrown error as `rokuMessages`; we look for the success there.
|
|
1522
|
+
* Absence of that success message means the delete did NOT happen, so it should be treated as a failure/retry.
|
|
1523
|
+
*/
|
|
1524
|
+
wasComponentLibraryDeleteSuccessful(error) {
|
|
1525
|
+
var _a, _b;
|
|
1526
|
+
const successes = (_b = (_a = error === null || error === void 0 ? void 0 : error.rokuMessages) === null || _a === void 0 ? void 0 : _a.successes) !== null && _b !== void 0 ? _b : [];
|
|
1527
|
+
return successes.some(message => /delete succeeded/i.test(message));
|
|
1528
|
+
}
|
|
1360
1529
|
sourceRequest(response, args) {
|
|
1361
1530
|
this.logger.log('sourceRequest');
|
|
1362
1531
|
let old = this.sendResponse;
|
|
@@ -1877,7 +2046,7 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
1877
2046
|
else if (v.type === '$$Registry') {
|
|
1878
2047
|
// This is a special scope variable used to load registry data via an ECP call
|
|
1879
2048
|
// Send the registry ECP call for the `dev` app as side loaded apps are always `dev`
|
|
1880
|
-
await (0, ecpRegistryUtils_1.populateVariableFromRegistryEcp)({
|
|
2049
|
+
await (0, ecpRegistryUtils_1.populateVariableFromRegistryEcp)({ remotePort: this.launchConfiguration.remotePort, device: this.launchConfiguration.device, appId: 'dev' }, v, this.variables, this.getEvaluateRefId.bind(this));
|
|
1881
2050
|
}
|
|
1882
2051
|
}
|
|
1883
2052
|
catch (error) {
|
|
@@ -2548,14 +2717,14 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
2548
2717
|
*/
|
|
2549
2718
|
async disconnectRequest(response, args, request) {
|
|
2550
2719
|
//return to the home screen — best effort. The device may already be powered off or unreachable
|
|
2551
|
-
//at disconnect time; without a guard
|
|
2720
|
+
//at disconnect time; without a guard the home key press rejects (EHOSTDOWN / ECONNREFUSED / etc)
|
|
2552
2721
|
//and because @vscode/debugadapter dispatches this method without awaiting the returned Promise,
|
|
2553
2722
|
//that rejection escapes as an unhandledRejection and crashes the DAP process.
|
|
2554
2723
|
//See https://github.com/rokucommunity/vscode-brightscript-language/issues/807
|
|
2555
2724
|
// https://github.com/rokucommunity/roku-debug/issues/332
|
|
2556
2725
|
if (!this.enableDebugProtocol) {
|
|
2557
2726
|
try {
|
|
2558
|
-
await this.rokuDeploy.
|
|
2727
|
+
await this.rokuDeploy.keyPress({ device: this.launchConfiguration.device, key: 'Home', ecpPort: this.launchConfiguration.remotePort });
|
|
2559
2728
|
}
|
|
2560
2729
|
catch (e) {
|
|
2561
2730
|
this.logger.warn('Failed to press home button during disconnect; device may be unreachable', e);
|
|
@@ -2594,8 +2763,8 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
2594
2763
|
}
|
|
2595
2764
|
try {
|
|
2596
2765
|
let appStateResult = await RokuECP_1.rokuECP.getAppState({
|
|
2597
|
-
host: this.launchConfiguration.host,
|
|
2598
2766
|
remotePort: this.launchConfiguration.remotePort,
|
|
2767
|
+
device: this.launchConfiguration.device,
|
|
2599
2768
|
appId: 'dev',
|
|
2600
2769
|
requestOptions: { timeout: 300 }
|
|
2601
2770
|
});
|
|
@@ -2606,8 +2775,8 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
2606
2775
|
// If the app supports Instant Resume and is running in the background or the app does not support Instant Resume and is running, sending this command terminates the app.
|
|
2607
2776
|
// This means that we might need to send this command twice to terminate the app.
|
|
2608
2777
|
await RokuECP_1.rokuECP.exitApp({
|
|
2609
|
-
host: this.launchConfiguration.host,
|
|
2610
2778
|
remotePort: this.launchConfiguration.remotePort,
|
|
2779
|
+
device: this.launchConfiguration.device,
|
|
2611
2780
|
appId: 'dev',
|
|
2612
2781
|
requestOptions: { timeout: 300 }
|
|
2613
2782
|
});
|
|
@@ -2918,9 +3087,12 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
2918
3087
|
else {
|
|
2919
3088
|
const lastId = this.launchProgressId;
|
|
2920
3089
|
this.sendEvent(new debugadapter_1.ProgressUpdateEvent(lastId, message));
|
|
2921
|
-
setTimeout(() => {
|
|
3090
|
+
const endTimer = setTimeout(() => { var _a; return (_a = this.flushLaunchProgressEnd) === null || _a === void 0 ? void 0 : _a.call(this); }, 1000); // add a slight delay before ending the progress to improve UX
|
|
3091
|
+
this.flushLaunchProgressEnd = () => {
|
|
3092
|
+
clearTimeout(endTimer);
|
|
3093
|
+
this.flushLaunchProgressEnd = undefined;
|
|
2922
3094
|
this.sendEvent(new debugadapter_1.ProgressEndEvent(lastId, message));
|
|
2923
|
-
}
|
|
3095
|
+
};
|
|
2924
3096
|
this.launchProgressId = undefined;
|
|
2925
3097
|
}
|
|
2926
3098
|
}
|
|
@@ -3001,7 +3173,7 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
3001
3173
|
var _a;
|
|
3002
3174
|
if (this.shutdownPromise === undefined) {
|
|
3003
3175
|
this.logger.log('[shutdown] Beginning shutdown sequence', errorMessage);
|
|
3004
|
-
//Backstop: if the graceful shutdown hangs (e.g.
|
|
3176
|
+
//Backstop: if the graceful shutdown hangs (e.g. a home key press against an unreachable
|
|
3005
3177
|
//device), force-exit anyway so we never leave an orphaned adapter running forever
|
|
3006
3178
|
const forceExitTimer = setTimeout(() => {
|
|
3007
3179
|
this.logger.error('[shutdown] graceful shutdown timed out; forcing exit');
|
|
@@ -3018,9 +3190,13 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
3018
3190
|
return this.shutdownPromise;
|
|
3019
3191
|
}
|
|
3020
3192
|
async _shutdown(errorMessage, modal = false) {
|
|
3021
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
3193
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
|
|
3022
3194
|
// Ensure any active launch progress bar is dismissed before showing error messages or the terminated event.
|
|
3023
3195
|
this.sendLaunchProgress('end', 'Complete');
|
|
3196
|
+
// 'end' defers its ProgressEndEvent for UX; deliver it right now (whether from the line above or from an
|
|
3197
|
+
// earlier 'end' whose delay has not elapsed yet) - the adapter exits before a pending timer would fire,
|
|
3198
|
+
// which would leave the client's progress notification stuck open
|
|
3199
|
+
(_a = this.flushLaunchProgressEnd) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
3024
3200
|
//send the message FIRST before anything else. This improves the chances that the message will be displayed to the user
|
|
3025
3201
|
try {
|
|
3026
3202
|
if (errorMessage) {
|
|
@@ -3035,13 +3211,13 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
3035
3211
|
}
|
|
3036
3212
|
// stop perfetto tracing if it's running
|
|
3037
3213
|
try {
|
|
3038
|
-
await this.perfettoManager.stopTracing();
|
|
3214
|
+
await ((_c = (_b = this.perfettoManager) === null || _b === void 0 ? void 0 : _b.stopTracing) === null || _c === void 0 ? void 0 : _c.call(_b));
|
|
3039
3215
|
}
|
|
3040
3216
|
catch (e) {
|
|
3041
3217
|
this.logger.error('Error stopping perfetto tracing', e);
|
|
3042
3218
|
}
|
|
3043
3219
|
try {
|
|
3044
|
-
await ((
|
|
3220
|
+
await ((_e = (_d = this.perfettoManager) === null || _d === void 0 ? void 0 : _d.dispose) === null || _e === void 0 ? void 0 : _e.call(_d));
|
|
3045
3221
|
}
|
|
3046
3222
|
catch (e) {
|
|
3047
3223
|
this.logger.error('Error disposing perfetto manager', e);
|
|
@@ -3049,11 +3225,11 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
3049
3225
|
//close the debugger connection
|
|
3050
3226
|
try {
|
|
3051
3227
|
this.logger.log('Destroy rokuAdapter');
|
|
3052
|
-
await ((
|
|
3228
|
+
await ((_g = (_f = this.rokuAdapter) === null || _f === void 0 ? void 0 : _f.destroy) === null || _g === void 0 ? void 0 : _g.call(_f));
|
|
3053
3229
|
//press the home button to return to the home screen
|
|
3054
3230
|
try {
|
|
3055
3231
|
this.logger.log('Press home button');
|
|
3056
|
-
await this.rokuDeploy.
|
|
3232
|
+
await this.rokuDeploy.keyPress({ device: this.launchConfiguration.device, key: 'Home', ecpPort: this.launchConfiguration.remotePort });
|
|
3057
3233
|
}
|
|
3058
3234
|
catch (e) {
|
|
3059
3235
|
this.logger.error(e);
|
|
@@ -3063,25 +3239,25 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
3063
3239
|
this.logger.error(e);
|
|
3064
3240
|
}
|
|
3065
3241
|
try {
|
|
3066
|
-
(
|
|
3242
|
+
(_j = (_h = this.projectManager) === null || _h === void 0 ? void 0 : _h.dispose) === null || _j === void 0 ? void 0 : _j.call(_h);
|
|
3067
3243
|
}
|
|
3068
3244
|
catch (e) {
|
|
3069
3245
|
this.logger.error(e);
|
|
3070
3246
|
}
|
|
3071
3247
|
try {
|
|
3072
|
-
(
|
|
3248
|
+
(_k = this.componentLibraryServer) === null || _k === void 0 ? void 0 : _k.stop();
|
|
3073
3249
|
}
|
|
3074
3250
|
catch (e) {
|
|
3075
3251
|
this.logger.error(e);
|
|
3076
3252
|
}
|
|
3077
3253
|
try {
|
|
3078
|
-
await ((
|
|
3254
|
+
await ((_m = (_l = this.rendezvousTracker) === null || _l === void 0 ? void 0 : _l.destroy) === null || _m === void 0 ? void 0 : _m.call(_l));
|
|
3079
3255
|
}
|
|
3080
3256
|
catch (e) {
|
|
3081
3257
|
this.logger.error(e);
|
|
3082
3258
|
}
|
|
3083
3259
|
try {
|
|
3084
|
-
await ((
|
|
3260
|
+
await ((_p = (_o = this.sourceMapManager) === null || _o === void 0 ? void 0 : _o.destroy) === null || _p === void 0 ? void 0 : _p.call(_o));
|
|
3085
3261
|
}
|
|
3086
3262
|
catch (e) {
|
|
3087
3263
|
this.logger.error(e);
|
|
@@ -3089,7 +3265,7 @@ class BrightScriptDebugSession extends debugadapter_1.LoggingDebugSession {
|
|
|
3089
3265
|
try {
|
|
3090
3266
|
//if configured, delete the staging directory
|
|
3091
3267
|
if (!this.launchConfiguration.retainStagingFolder) {
|
|
3092
|
-
const stagingDirs = (
|
|
3268
|
+
const stagingDirs = (_r = (_q = this.projectManager) === null || _q === void 0 ? void 0 : _q.getStagingDirs()) !== null && _r !== void 0 ? _r : [];
|
|
3093
3269
|
this.logger.info('deleting staging folders', stagingDirs);
|
|
3094
3270
|
for (let stagingDir of stagingDirs) {
|
|
3095
3271
|
try {
|