Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

View skipped recipes #322

Merged
13 changes: 13 additions & 0 deletions client/src/__tests__/unit-tests/driver/scanner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ describe('BitbakeDriver Recipes View', () => {
ext: '.bbappend',
name: 'base-files'
}
]
],
skipped: 'skipped: because reasons'
}
],
_includes: [],
Expand All @@ -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()
Expand All @@ -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)
})
})
35 changes: 22 additions & 13 deletions client/src/driver/BitBakeProjectScanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -77,10 +76,6 @@ export class BitBakeProjectScanner {
this._bitbakeScanResult = scanResult
}

get scanResultVersion (): number {
return BitBakeProjectScanner._scanResultVersion
}

get bitbakeDriver (): BitbakeDriver {
return this._bitbakeDriver
}
Expand Down Expand Up @@ -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 = /(?<name>.+):\r?\n((?:\s+(?<layer>\S+)\s+(?<version>\S+)(?:\s+\(skipped\))?\r?\n)+)/g
const recipeRegex = /(?<name>.+):\r?\n((?:\s+(?<layer>\S+)\s+(?<version>\S+)(?<skipped>\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')
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 = /^(?<filename>[\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<void> {
Expand Down
6 changes: 3 additions & 3 deletions client/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -274,7 +274,7 @@ export async function activate (context: vscode.ExtensionContext): Promise<void>
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')
})
})
Expand All @@ -284,7 +284,7 @@ export async function activate (context: vscode.ExtensionContext): Promise<void>

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')
Expand Down
6 changes: 6 additions & 0 deletions client/src/lib/src/types/BitbakeScanResult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -20,6 +25,7 @@ export interface ElementInfo {
appends?: PathInfo[]
overlayes?: PathInfo[]
version?: string
skipped?: string
}

export interface DevtoolWorkspaceInfo {
Expand Down
23 changes: 21 additions & 2 deletions client/src/ui/BitbakeRecipesView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
}
}
}

Expand Down Expand Up @@ -122,8 +132,17 @@ class BitbakeTreeDataProvider implements vscode.TreeDataProvider<BitbakeRecipeTr
}

private getBitbakeRecipes (): BitbakeRecipeTreeItem[] {
// Prepare a map for quick lookup. This avoids going through the list of recipes multiple times
const recipeInfoMap = new Map<string, ElementInfo>()
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))
}

Expand Down