diff --git a/client/src/__tests__/unit-tests/driver/scanner.test.ts b/client/src/__tests__/unit-tests/driver/scanner.test.ts index e0027fc2..dcd29d84 100644 --- a/client/src/__tests__/unit-tests/driver/scanner.test.ts +++ b/client/src/__tests__/unit-tests/driver/scanner.test.ts @@ -103,6 +103,19 @@ describe('BitBakeProjectScanner', () => { ) }) + it('can detected skipped recipes', async () => { + const recipes = bitBakeProjectScanner.scanResult._recipes + // systemd is skipped in the poky distribution, because systemV is used instead + const systemdRecipe = recipes.find((recipe) => recipe.name === 'systemd') + expect(systemdRecipe).toEqual( + expect.objectContaining( + { + skipped: expect.stringContaining('skipped:') + } + ) + ) + }) + it('can get recipes appends', async () => { const recipes = bitBakeProjectScanner.scanResult._recipes const busyboxRecipe = recipes.find((recipe) => recipe.name === 'busybox') diff --git a/client/src/__tests__/unit-tests/ui/bitbake-recipes-view.test.ts b/client/src/__tests__/unit-tests/ui/bitbake-recipes-view.test.ts index 74ed5a34..42725d5e 100644 --- a/client/src/__tests__/unit-tests/ui/bitbake-recipes-view.test.ts +++ b/client/src/__tests__/unit-tests/ui/bitbake-recipes-view.test.ts @@ -48,7 +48,8 @@ describe('BitbakeDriver Recipes View', () => { ext: '.bbappend', name: 'base-files' } - ] + ], + skipped: 'skipped: because reasons' } ], _includes: [], @@ -66,6 +67,7 @@ describe('BitbakeDriver Recipes View', () => { expect(rootTreeItem?.length).toStrictEqual(2) const recipeItem = (rootTreeItem as BitbakeRecipeTreeItem[])[0] expect(recipeItem.label).toStrictEqual('base-files') + expect(recipeItem.description).toEqual('skipped: because reasons') const filesItems = await treeDataProvider.getChildren(recipeItem) expect(filesItems).toBeDefined() @@ -75,7 +77,7 @@ describe('BitbakeDriver Recipes View', () => { mockVscodeEvents() const bitbakeRecipesView = new BitbakeRecipesView(bitbakeWorkspace, bitBakeProjectScanner) - bitbakeRecipesView.registerView(contextMock) bitBakeProjectScanner.onChange.emit(BitBakeProjectScanner.EventType.SCAN_COMPLETE, scanResult) + bitbakeRecipesView.registerView(contextMock) }) }) diff --git a/client/src/driver/BitBakeProjectScanner.ts b/client/src/driver/BitBakeProjectScanner.ts index f35d52bd..3602f096 100644 --- a/client/src/driver/BitBakeProjectScanner.ts +++ b/client/src/driver/BitBakeProjectScanner.ts @@ -48,7 +48,6 @@ export class BitBakeProjectScanner { onChange: EventEmitter = new EventEmitter() private _bitbakeScanResult: BitbakeScanResult = { _classes: [], _includes: [], _layers: [], _overrides: [], _recipes: [], _workspaces: [], _confFiles: [] } - private static readonly _scanResultVersion: number = 1 private readonly _bitbakeDriver: BitbakeDriver private _languageClient: LanguageClient | undefined @@ -77,10 +76,6 @@ export class BitBakeProjectScanner { this._bitbakeScanResult = scanResult } - get scanResultVersion (): number { - return BitBakeProjectScanner._scanResultVersion - } - get bitbakeDriver (): BitbakeDriver { return this._bitbakeDriver } @@ -337,15 +332,18 @@ You should adjust your docker volumes to use the same URIs as those present on y * Example: * zstd: meta 1.5.5 + virt-viewer: + meta-virtualization 11.0 (skipped: one of 'wayland x11' needs to be in DISTRO_FEATURES) The ones that are followed by (skipped) are not included. */ - const recipeRegex = /(?.+):\r?\n((?:\s+(?\S+)\s+(?\S+)(?:\s+\(skipped\))?\r?\n)+)/g + const recipeRegex = /(?.+):\r?\n((?:\s+(?\S+)\s+(?\S+)(?\s+\(skipped[^\r\n]*\))?\r?\n)+)/g for (const match of outputRecipeSection.matchAll(recipeRegex)) { const name = match.groups?.name const layerName = match.groups?.layer const version = match.groups?.version + const skipped = match.groups?.skipped if (name === undefined) { logger.error('[scanForRecipes] recipeName is undefined') @@ -375,7 +373,8 @@ You should adjust your docker volumes to use the same URIs as those present on y name, extraInfo, layerInfo, - version + version, + skipped } this._bitbakeScanResult._recipes.push(element) @@ -408,25 +407,35 @@ You should adjust your docker volumes to use the same URIs as those present on y const output = await this.executeBitBakeCommand('bitbake-layers show-recipes -f') const lines = output.split(/\r?\n/g) // Example output (indentation or skipped means not used): + // We don't want to report the indented recipes, which are superseeded + // However skipped recipes are still in context, even though not buildable. + // Their skipped reason is already captured in ElementInfo.skipped /* === Available recipes: === * /home/deribaucourt/Workspace/yocto-vscode/yocto/yocto-build/sources/poky/meta/recipes-core/busybox/busybox_1.36.2.bb * /home/deribaucourt/Workspace/yocto-vscode/yocto/yocto-build/sources/poky/meta/recipes-core/busybox/busybox_1.36.1.bb * /home/deribaucourt/Workspace/yocto-vscode/yocto/yocto-build/sources/poky/meta/recipes-core/busybox/busybox_1.36.3.bb * /home/deribaucourt/Workspace/yocto-vscode/yocto/yocto-build/sources/poky/meta-selftest/recipes-test/images/wic-image-minimal.bb (skipped: ...) */ - const regex = /^([\w/._-]+\.bb)$/ - const matches = lines.filter((line) => regex.test(line)) - if (matches === null) { return } + const regex = /^(?[\w/._-]+\.bb)(?:\s+\(skipped[^\r\n]*\))?$/ + const filenames = [] + for (const line of lines) { + const match = line.match(regex) + const filename = match?.groups?.filename + if (filename !== null && filename !== undefined) { + filenames.push(filename) + } + } + if (filenames.length === 0) { return } // Sort by decreasing length to avoid partial matches - matches.sort((a, b) => b.length - a.length) + filenames.sort((a, b) => b.length - a.length) this._bitbakeScanResult._recipes.sort((a, b) => b.name.length - a.name.length) - await this.assignRecipesPaths(matches, this._bitbakeScanResult._recipes, (a: string, b: string) => a === b) + await this.assignRecipesPaths(filenames, this._bitbakeScanResult._recipes, (a: string, b: string) => a === b) // Some recipes change their PN like gcc-source -> gcc-source-13.2 // We allow the recipe to be found by just including part of the name const recipesWithoutPaths = this._bitbakeScanResult._recipes.filter((recipe) => recipe.path === undefined) - await this.assignRecipesPaths(matches, recipesWithoutPaths, (a: string, b: string) => a.includes(b)) + await this.assignRecipesPaths(filenames, recipesWithoutPaths, (a: string, b: string) => a.includes(b)) } private async assignRecipesPaths (filePaths: string[], recipesArray: ElementInfo[], nameMatchFunc: (a: string, b: string) => boolean): Promise { diff --git a/client/src/extension.ts b/client/src/extension.ts index 0f878a84..b7e6a8a7 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -24,7 +24,7 @@ import { BitbakeTerminalProfileProvider } from './ui/BitbakeTerminalProfile' import { BitbakeTerminalLinkProvider } from './ui/BitbakeTerminalLinkProvider' import { extractRecipeName } from './lib/src/utils/files' import { BitbakeConfigPicker } from './ui/BitbakeConfigPicker' -import { type BitbakeScanResult, scanContainsData } from './lib/src/types/BitbakeScanResult' +import { type BitbakeScanResult, SCAN_RESULT_VERSION, scanContainsData } from './lib/src/types/BitbakeScanResult' import { reviewDiagnostics } from './language/diagnosticsSupport' import { embeddedLanguageDocsManager } from './language/EmbeddedLanguageDocsManager' import { NotificationMethod } from './lib/src/types/notifications' @@ -274,7 +274,7 @@ export async function activate (context: vscode.ExtensionContext): Promise void context.workspaceState.update('bitbake.ScanResult', scanResult).then(() => { logger.debug('BitBake scan result saved to workspace state') }) - void context.workspaceState.update('bitbake.ScanResultVersion', bitBakeProjectScanner.scanResultVersion).then(() => { + void context.workspaceState.update('bitbake.ScanResultVersion', SCAN_RESULT_VERSION).then(() => { logger.debug('BitBake scan result version saved to workspace state') }) }) @@ -284,7 +284,7 @@ export async function activate (context: vscode.ExtensionContext): Promise const needToScan = lastBitbakeScanResult === undefined || lastBitbakeScanResultVersion === undefined || - lastBitbakeScanResultVersion !== bitBakeProjectScanner.scanResultVersion + lastBitbakeScanResultVersion !== SCAN_RESULT_VERSION if (needToScan) { logger.debug('No valid scan result found, scanning the project') diff --git a/client/src/lib/src/types/BitbakeScanResult.ts b/client/src/lib/src/types/BitbakeScanResult.ts index 51b36c89..f366bf49 100644 --- a/client/src/lib/src/types/BitbakeScanResult.ts +++ b/client/src/lib/src/types/BitbakeScanResult.ts @@ -4,6 +4,11 @@ * ------------------------------------------------------------------------------------------ */ import { type ParsedPath } from 'path' + +// Make sure to increment this number when the structure of the scan data changes +// This will invalidate previous scan data saved for the workspace +export const SCAN_RESULT_VERSION: number = 2 + export interface LayerInfo { name: string path: string @@ -20,6 +25,7 @@ export interface ElementInfo { appends?: PathInfo[] overlayes?: PathInfo[] version?: string + skipped?: string } export interface DevtoolWorkspaceInfo { diff --git a/client/src/ui/BitbakeRecipesView.ts b/client/src/ui/BitbakeRecipesView.ts index 4df62dd3..deb1c867 100644 --- a/client/src/ui/BitbakeRecipesView.ts +++ b/client/src/ui/BitbakeRecipesView.ts @@ -25,10 +25,20 @@ export class BitbakeRecipesView { } export class BitbakeRecipeTreeItem extends vscode.TreeItem { - constructor (public readonly label: string, public readonly collapsibleState: vscode.TreeItemCollapsibleState) { + // Explicit that label will be string to the TypeScript compiler + public readonly label: string + + constructor (public readonly recipe: ElementInfo | string, public readonly collapsibleState: vscode.TreeItemCollapsibleState) { + const label = typeof recipe === 'string' ? recipe : recipe.name super(label, collapsibleState) + this.label = label this.contextValue = 'bitbakeRecipeCtx' this.iconPath = new vscode.ThemeIcon('library') + if (typeof recipe !== 'string' && recipe?.skipped !== undefined) { + this.tooltip = recipe.skipped + this.description = recipe.skipped + this.iconPath = new vscode.ThemeIcon('warning') + } } } @@ -122,8 +132,17 @@ class BitbakeTreeDataProvider implements vscode.TreeDataProvider() + if ((this.bitbakeScanResults?._recipes) != null) { + this.bitbakeScanResults._recipes.forEach((recipe: ElementInfo) => { + recipeInfoMap.set(recipe.name, recipe) + }) + } + return this.bitbakeWorkspace.activeRecipes.map((recipe: string) => { - return new BitbakeRecipeTreeItem(recipe, vscode.TreeItemCollapsibleState.Collapsed) + const recipeInfo = recipeInfoMap.get(recipe) + return new BitbakeRecipeTreeItem(recipeInfo ?? recipe, vscode.TreeItemCollapsibleState.Collapsed) }).sort((a, b) => a.label.localeCompare(b.label)) }