Skip to content

Commit c9f6f50

Browse files
edrioukbrondani
andauthored
Reload active solution when used dbgconf files change (#382)
* Reload active solution when used dbgconf files change Watch dbgconf changes and resolve referenced dbgconf paths from cbuild files. Only files used by the active solution trigger reloads, avoiding unnecessary converts for unrelated debugger configs. * Remove unused pattern from test * fix test * Update src/solutions/solution-manager.ts Co-authored-by: Daniel Brondani <daniel.brondani@arm.com> * remove and ignore .cmsis-dev files --------- Co-authored-by: Daniel Brondani <daniel.brondani@arm.com>
1 parent 5ababc8 commit c9f6f50

7 files changed

Lines changed: 128 additions & 5 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,5 @@ test-results/**
4242
tools
4343
guide
4444
schemas
45-
src/json-rpc/interface/*
45+
src/json-rpc/interface/*
46+
.cmsis-dev

src/solutions/active-solution-tracker.test.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@
1515
*/
1616

1717
import 'jest';
18-
import { ActiveSolutionTrackerImpl, COMMAND_ACTIVATE_SOLUTION, COMMAND_DEACTIVATE_SOLUTION, solutionFileWatchPattern } from './active-solution-tracker';
18+
import {
19+
ActiveSolutionTrackerImpl,
20+
COMMAND_ACTIVATE_SOLUTION,
21+
COMMAND_DEACTIVATE_SOLUTION,
22+
dbgconfFileWatchPattern,
23+
solutionFileWatchPattern,
24+
} from './active-solution-tracker';
1925
import * as vscode from 'vscode';
2026
import { WorkspaceFolder } from 'vscode';
2127
import { URI } from 'vscode-uri';
@@ -611,6 +617,7 @@ describe('ActiveSolutionTracker solution file watching', () => {
611617

612618
it('registers a file watcher on activation', () => {
613619
expect(fileWatcherProvider.watchFiles).toHaveBeenCalledWith(solutionFileWatchPattern, expect.any(Object), expect.anything());
620+
expect(fileWatcherProvider.watchFiles).toHaveBeenCalledWith(dbgconfFileWatchPattern, expect.any(Object), expect.anything());
614621
});
615622

616623
it('fires onActiveSolutionFilesChanged when the csolution file is modified', async () => {
@@ -636,6 +643,22 @@ describe('ActiveSolutionTracker solution file watching', () => {
636643
expect(changeListener).toHaveBeenCalledWith(packFile);
637644
});
638645

646+
it('fires onActiveSolutionFilesChanged when a dbgconf file is modified', async () => {
647+
const dbgconfFile = path.join(solutionRoot, 'DebugConfig', 'My.dbgconf');
648+
fileWatcherProvider.mockFireEvent(dbgconfFileWatchPattern, dbgconfFile, 'change');
649+
650+
expect(changeListener).toHaveBeenCalledTimes(1);
651+
expect(changeListener).toHaveBeenCalledWith(dbgconfFile);
652+
});
653+
654+
it('fires onActiveSolutionFilesChanged when a dbgconf file in .cmsis is modified', async () => {
655+
const dbgconfFile = path.join(solutionRoot, '.cmsis', 'My.dbgconf');
656+
fileWatcherProvider.mockFireEvent(dbgconfFileWatchPattern, dbgconfFile, 'change');
657+
658+
expect(changeListener).toHaveBeenCalledTimes(1);
659+
expect(changeListener).toHaveBeenCalledWith(dbgconfFile);
660+
});
661+
639662
it('fires onActiveSolutionFilesChanged when an inactive csolution file is modified', async () => {
640663
const inactiveSolution = path.join(__dirname, 'another-solution', 'NotMy.csolution.yaml');
641664
fileWatcherProvider.mockFireEvent(solutionFileWatchPattern, inactiveSolution, 'change');

src/solutions/active-solution-tracker.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ export const COMMAND_GET_SOLUTION_FILE = `${manifest.PACKAGE_NAME}.getSolutionFi
3434
/** @deprecated */
3535
export const COMMAND_GET_SOLUTION_PATH = `${manifest.PACKAGE_NAME}.getSolutionPath`;
3636

37-
3837
// this pattern covers csolution, project, layer and global generator files
3938
export const solutionFileWatchPattern =
4039
'**/{cdefault,*.csolution,*.cproject,*.clayer,*.cgen}.{yaml,yml}';
4140

41+
export const dbgconfFileWatchPattern = '**/*.dbgconf';
4242

4343
interface SolutionDetails {
4444
displayName: string;
@@ -57,7 +57,7 @@ export interface ActiveSolutionTracker {
5757
readonly onDidChangeActiveSolution: vscode.Event<void>;
5858

5959
/**
60-
* Event fired when a watched solution YAML file changes.
60+
* Event fired when a watched solution-related file changes.
6161
*/
6262
readonly onActiveSolutionFilesChanged: vscode.Event<string>;
6363

@@ -121,6 +121,9 @@ export class ActiveSolutionTrackerImpl implements ActiveSolutionTracker {
121121
onChange: this.handleActiveSolutionFileChange,
122122
onCreate: this.handleActiveSolutionFileChange,
123123
}, this),
124+
this.fileWatcherProvider.watchFiles(dbgconfFileWatchPattern, {
125+
onChange: this.handleActiveSolutionFileChange,
126+
}, this),
124127
this.commandsProvider.registerCommand(COMMAND_OPEN_SOLUTION, this.handleActivateSolution, this),
125128
this.commandsProvider.registerCommand(COMMAND_ACTIVATE_SOLUTION, this.handleActivateSolution, this),
126129
this.commandsProvider.registerCommand(COMMAND_DEACTIVATE_SOLUTION, this.handleDeactivateSolution, this),

src/solutions/csolution.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,4 +718,41 @@ describe('CSolution', () => {
718718
expect(files).toHaveLength(1 + projectPaths.length + layerPaths.length);
719719
});
720720
});
721+
722+
describe('getUsedDbgconfFiles', () => {
723+
it('returns empty list when no solution is loaded', () => {
724+
const csolution = new CSolution();
725+
726+
expect(csolution.getUsedDbgconfFiles()).toEqual([]);
727+
});
728+
729+
it('returns deduplicated absolute dbgconf paths from cbuild files', async () => {
730+
const csolution = new CSolution();
731+
const fileName = path.join(testDataHandler.tmpDir, 'solutions', 'WestSupport', 'solution.csolution.yml');
732+
733+
await csolution.load(fileName);
734+
735+
expect(csolution.getUsedDbgconfFiles()).toEqual([
736+
path.join(testDataHandler.tmpDir, 'solutions', 'WestSupport', '.cmsis', 'solution+CM0.dbgconf'),
737+
]);
738+
});
739+
740+
it('returns all dbgconf file entries from a cbuild file', () => {
741+
const csolution = new CSolution();
742+
const cbuildPath = path.join(testDataHandler.tmpDir, 'solutions', 'multiple-dbgconf.cbuild.yml');
743+
csolution.cbuildYmlRoot.set(cbuildPath, parseYamlToCTreeItem(YAML.stringify({
744+
build: {
745+
dbgconf: [
746+
{ file: 'first.dbgconf' },
747+
{ file: 'debug/second.dbgconf' },
748+
],
749+
},
750+
}), cbuildPath) as CTreeItem);
751+
752+
expect(csolution.getUsedDbgconfFiles()).toEqual([
753+
path.join(testDataHandler.tmpDir, 'solutions', 'first.dbgconf'),
754+
path.join(testDataHandler.tmpDir, 'solutions', 'debug', 'second.dbgconf'),
755+
]);
756+
});
757+
});
721758
});

src/solutions/csolution.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,26 @@ export class CSolution {
8787
return ymlFiles;
8888
}
8989

90+
public getUsedDbgconfFiles(): string[] {
91+
const dbgconfFiles: string[] = [];
92+
const seen = new Set<string>();
93+
94+
for (const cbuild of this.cbuildYmlRoot.values()) {
95+
for (const dbgConfigFile of cbuild.getChildItem()?.getGrandChildren('dbgconf') ?? []) {
96+
const fileName = dbgConfigFile.getValue('file');
97+
if (!fileName) {
98+
continue;
99+
}
100+
const filePath = cbuild.resolvePath(fileName);
101+
if (!seen.has(filePath)) {
102+
seen.add(filePath);
103+
dbgconfFiles.push(filePath);
104+
}
105+
}
106+
}
107+
return dbgconfFiles;
108+
}
109+
90110
cbuildPackFile = new CbuildPackFile();
91111

92112
cbuilds?: ITreeItem<CTreeItem>[];

src/solutions/solution-manager.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,42 @@ describe('SolutionManager', () => {
316316
);
317317
});
318318

319+
it('reloads the solution when a changed dbgconf file is used by the active solution', async () => {
320+
await activateTestSolution();
321+
const dbgconfPath = path.join(path.dirname(testSolutionPath), '.cmsis', 'Used.Debug+B-U585I-IOT02A.dbgconf');
322+
jest.spyOn(solutionManager.getCsolution()!, 'getUsedDbgconfFiles').mockReturnValue([dbgconfPath]);
323+
convertMock.mockClear();
324+
loadBuildFilesListener.mockClear();
325+
326+
changeSolutionFilesEmitter.fire(dbgconfPath);
327+
await waitTimeout(100);
328+
329+
expect(convertMock).toHaveBeenCalledTimes(1);
330+
expect(convertMock).toHaveBeenCalledWith(
331+
expect.objectContaining({
332+
solutionPath: testSolutionPath,
333+
updateRte: true,
334+
restartRpc: false,
335+
lockAbort: false,
336+
}),
337+
);
338+
expect(loadBuildFilesListener).toHaveBeenCalledTimes(1);
339+
});
340+
341+
it('does not reload when a changed dbgconf file is not used by the active solution', async () => {
342+
await activateTestSolution();
343+
const dbgconfPath = path.join(path.dirname(testSolutionPath), '.cmsis', 'Inactive.Debug+B-U585I-IOT02A.dbgconf');
344+
jest.spyOn(solutionManager.getCsolution()!, 'getUsedDbgconfFiles').mockReturnValue([
345+
path.join(path.dirname(testSolutionPath), '.cmsis', 'Used.Debug+B-U585I-IOT02A.dbgconf'),
346+
]);
347+
convertMock.mockClear();
348+
349+
changeSolutionFilesEmitter.fire(dbgconfPath);
350+
await waitTimeout(100);
351+
352+
expect(convertMock).not.toHaveBeenCalled();
353+
});
354+
319355
it('does not reload for a watched YAML file inside the solution directory when it is not in the active solution model', async () => {
320356
await activateTestSolution();
321357
const unrelatedProjectPath = path.join(path.dirname(testSolutionPath), 'Unrelated.cproject.yml');

src/solutions/solution-manager.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,10 @@ export class SolutionManagerImpl implements SolutionManager {
180180

181181
private handleActiveSolutionFilesChanged(changedPath: string): void {
182182
const solutionFiles = this.csolution?.getSolutionYmlFiles();
183-
if (!solutionFiles?.some(solutionFile => pathsEqual(solutionFile, changedPath))) {
183+
const isSolutionYmlFile = solutionFiles?.some(solutionFile => pathsEqual(solutionFile, changedPath));
184+
const isUsedDbgconfFile = changedPath.toLowerCase().endsWith('.dbgconf')
185+
&& this.csolution?.getUsedDbgconfFiles().some(dbgconfFile => pathsEqual(dbgconfFile, changedPath));
186+
if (!isSolutionYmlFile && !isUsedDbgconfFile) {
184187
return;
185188
}
186189
this.debouncedHandleActiveSolutionFileChange();

0 commit comments

Comments
 (0)