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

Fix: Fix parseAllRecipes #15

Merged
merged 2 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
205 changes: 87 additions & 118 deletions server/src/BitBakeProjectScanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,32 +166,28 @@ export class BitBakeProjectScanner {
private scanAvailableLayers (): void {
this._layers = new Array < LayerInfo >()

const output: string = this.executeBitBakeCommand('bitbake-layers show-layers')

if (output.length > 0) {
try {
let tempStr: string[] = output.split('\n')
tempStr = tempStr.slice(2)

for (const element of tempStr) {
const tempElement: string[] = element.split(/\s+/)
const layerElement = {
name: tempElement[0],
path: tempElement[1],
priority: parseInt(tempElement[2])
}

if ((layerElement.name !== undefined) && (layerElement.path !== undefined) && layerElement.priority !== undefined) {
this._layers.push(layerElement)
}
const commandResult = this.executeBitBakeCommand('bitbake-layers show-layers')

if (commandResult.status === 0) {
const output = commandResult.stdout.toString()
const outputLines = output.split('\n')

for (const element of outputLines.slice(2)) {
const tempElement: string[] = element.split(/\s+/)
const layerElement = {
name: tempElement[0],
path: tempElement[1],
priority: parseInt(tempElement[2])
}
} catch (error) {
if (typeof error !== 'string') {
throw error

if ((layerElement.name !== undefined) && (layerElement.path !== undefined) && layerElement.priority !== undefined) {
this._layers.push(layerElement)
}
logger.error(`can not scan available layers error: ${error}`)
this._outputParser.parse(error)
}
} else {
const error = commandResult.stderr.toString()
logger.error(`can not scan available layers error: ${error}`)
this._outputParser.parse(error)
}
}

Expand Down Expand Up @@ -225,75 +221,57 @@ export class BitBakeProjectScanner {
scanForRecipes (): void {
this._recipes = new Array < ElementInfo >()

const output: string = this.executeBitBakeCommand('bitbake-layers show-recipes')

if (output.length > 0) {
const outerReg: RegExp = /(.+):\n((?:\s+\S+\s+\S+(?:\s+\(skipped\))?\n)+)/g
const innerReg: RegExp = /\s+(\S+)\s+(\S+(?:\s+\(skipped\))?)\n/g
let match: RegExpExecArray | null
const commandResult = this.executeBitBakeCommand('bitbake-layers show-recipes')
const output = commandResult.output.toString()

while ((match = outerReg.exec(output)) !== null) {
if (match.index === outerReg.lastIndex) {
outerReg.lastIndex++
}

let matchInner: RegExpExecArray | null
const extraInfoString: string[] = new Array < string >()
let layerName: string
let version: string = ''
const outerReg: RegExp = /(.+):\n((?:\s+\S+\s+\S+(?:\s+\(skipped\))?\n)+)/g
const innerReg: RegExp = /\s+(\S+)\s+(\S+(?:\s+\(skipped\))?)\n/g

while ((matchInner = innerReg.exec(match[2])) !== null) {
if (matchInner.index === innerReg.lastIndex) {
innerReg.lastIndex++
}

if (extraInfoString.length === 0) {
layerName = matchInner[1]
version = matchInner[2]
}
for (const match of output.matchAll(outerReg)) {
const extraInfoString: string[] = new Array < string >()
let layerName: string
let version: string = ''

extraInfoString.push(`layer: ${matchInner[1]}`)
extraInfoString.push(`version: ${matchInner[2]} `)
for (const matchInner of match[2].matchAll(innerReg)) {
if (extraInfoString.length === 0) {
layerName = matchInner[1]
version = matchInner[2]
}

const layer = this._layers.find((obj: LayerInfo): boolean => {
return obj.name === layerName
})
extraInfoString.push(`layer: ${matchInner[1]}`)
extraInfoString.push(`version: ${matchInner[2]} `)
}

const element: ElementInfo = {
name: match[1],
extraInfo: extraInfoString.join('\n'),
layerInfo: layer,
version
}
const layer = this._layers.find((obj: LayerInfo): boolean => {
return obj.name === layerName
})

this._recipes.push(element)
const element: ElementInfo = {
name: match[1],
extraInfo: extraInfoString.join('\n'),
layerInfo: layer,
version
}

this._recipes.push(element)
}

this.scanForRecipesPath()
}

parseAllRecipes (): boolean {
logger.debug('parseAllRecipes')
let parsingOutput: string
let parsingSuccess: boolean = true

try {
parsingOutput = this.executeBitBakeCommand('bitbake -p')
} catch (error) {
if (typeof error !== 'string') {
throw error
}
logger.error(`parsing all recipes is abborted: ${error}`)
parsingOutput = error
}

if (parsingOutput.length > 0) {
this._outputParser.parse(parsingOutput)
if (this._outputParser.errorsFound()) {
this._outputParser.reportProblems()
parsingSuccess = false
const commandResult = this.executeBitBakeCommand('bitbake -p')
const output = commandResult.output.toString()
this._outputParser.parse(output)
if (this._outputParser.errorsFound()) {
this._outputParser.reportProblems()
parsingSuccess = false
} else {
if (commandResult.status !== 0) {
logger.warn('Unhandled parsing error:', output)
}
}
return parsingSuccess
Expand Down Expand Up @@ -322,80 +300,71 @@ export class BitBakeProjectScanner {
logger.info(`${recipesWithOutPath.length} recipes must be examined more deeply.`)

for (const recipeWithOutPath of recipesWithOutPath) {
const output: string = this.executeBitBakeCommand(`bitbake-layers show-recipes -f ${recipeWithOutPath.name}`)
const commandResult = this.executeBitBakeCommand(`bitbake-layers show-recipes -f ${recipeWithOutPath.name}`)
const output = commandResult.output.toString()
const regExp: RegExp = /(\s.*\.bb)/g
let match: RegExpExecArray | null

if (output.length > 0) {
while ((match = regExp.exec(output)) !== null) {
if (match.index === regExp.lastIndex) {
regExp.lastIndex++
}

recipeWithOutPath.path = path.parse(match[0].trim())
}
for (const match of output.matchAll(regExp)) {
recipeWithOutPath.path = path.parse(match[0].trim())
}
}
}
}

private scanRecipesAppends (): void {
const output: string = this.executeBitBakeCommand('bitbake-layers show-appends')
const commandResult = this.executeBitBakeCommand('bitbake-layers show-appends')
const output = commandResult.output.toString()

if (output.length > 0) {
const outerReg: RegExp = /(\S.*\.bb):(?:\s*\/\S*.bbappend)+/g
const outerReg: RegExp = /(\S.*\.bb):(?:\s*\/\S*.bbappend)+/g

let match: RegExpExecArray | null
for (const match of output.matchAll(outerReg)) {
const fullRecipeNameAsArray: string[] = match[1].split('_')

while ((match = outerReg.exec(output)) !== null) {
if (match.index === outerReg.lastIndex) {
outerReg.lastIndex++
}
let matchInner: RegExpExecArray | null
const fullRecipeNameAsArray: string[] = match[1].split('_')

if (fullRecipeNameAsArray.length > 0) {
const recipeName: string = fullRecipeNameAsArray[0]

const recipe: ElementInfo | undefined = this.recipes.find((obj: ElementInfo): boolean => {
return obj.name === recipeName
})
if (fullRecipeNameAsArray.length > 0) {
const recipeName: string = fullRecipeNameAsArray[0]

if (recipe !== undefined) {
const innerReg: RegExp = /(\S*\.bbappend)/g

while ((matchInner = innerReg.exec(match[0])) !== null) {
if (matchInner.index === innerReg.lastIndex) {
innerReg.lastIndex++
}
const recipe: ElementInfo | undefined = this.recipes.find((obj: ElementInfo): boolean => {
return obj.name === recipeName
})

if (recipe.appends === undefined) {
recipe.appends = new Array < PathInfo >()
}
if (recipe !== undefined) {
const innerReg: RegExp = /(\S*\.bbappend)/g

recipe.appends.push(path.parse(matchInner[0]))
for (const matchInner of match[0].matchAll(innerReg)) {
if (recipe.appends === undefined) {
recipe.appends = new Array < PathInfo >()
}

recipe.appends.push(path.parse(matchInner[0]))
}
}
}
}
}

private executeBitBakeCommand (command: string): string {
private executeBitBakeCommand (command: string): childProcess.SpawnSyncReturns<Buffer> {
const scriptContent: string = this.generateBitBakeCommand(command)
return this.executeCommand(scriptContent)
const commandResult = this.executeCommand(scriptContent)

if (commandResult.status === 127) {
// Likely "bitbake: not found"
// TODO: Show a proper error message to help the user configuring the extension
throw new Error(commandResult.output.toString())
}

return commandResult
}

private executeCommand (command: string): string {
return childProcess.execSync(command).toString()
private executeCommand (command: string): childProcess.SpawnSyncReturns<Buffer> {
return childProcess.spawnSync(command, { shell: true })
}

private generateBitBakeCommand (bitbakeCommand: string): string {
const scriptFileBuffer: string[] = []

if (fs.existsSync(this._pathToEnvScript)) {
logger.info('using env script')
scriptFileBuffer.push('source ./' + this._pathToEnvScript + ' ' + this._pathToBuildFolder + ' > /dev/null')
scriptFileBuffer.push(`. ./${this._pathToEnvScript} ${this._pathToBuildFolder} > /dev/null`)
} else {
logger.info('not using env script')
scriptFileBuffer.push(
Expand Down
76 changes: 26 additions & 50 deletions server/src/OutputParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
import logger from 'winston'

import {
DiagnosticSeverity,
type Connection
} from 'vscode-languageserver'

import {
type ProblemType,
ProblemsContainer
} from './ProblemsContainer'

Expand All @@ -22,63 +22,39 @@ export class OutputParser {
}

parse (message: string): void {
const regex: RegExp = /\s(WARNING:|ERROR:)\s(.*)/g
let m
this.clearAllProblemsAndReport()

while ((m = regex.exec(message)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++
const severityRegex: RegExp = /\b(WARNING:|ERROR:)/
let currentSeverity: DiagnosticSeverity = DiagnosticSeverity.Error // dummy initializer which will never be used
let currentMessageLines: string[] = []
for (const line of message.split(/\r?\n/g).reverse()) {
currentMessageLines.push(line)
const unparsedSeverity = line.match(severityRegex)?.[1]
if (unparsedSeverity === 'ERROR:') {
currentSeverity = DiagnosticSeverity.Error
} else if (unparsedSeverity === 'WARNING:') {
currentSeverity = DiagnosticSeverity.Warning
}

let problemType: ProblemType
if (m[1] === 'ERROR:') {
problemType = 'error'
} else if (m[1] === 'WARNING:') {
problemType = 'warning'
} else {
return
if (unparsedSeverity !== undefined) { // if we reached the first line of a problem
this.addProblem(currentSeverity, currentMessageLines.reverse().join('\n'))
currentMessageLines = []
}

const tempProblemContainer: ProblemsContainer[] = ProblemsContainer.createProblemContainer(problemType, m[2])

tempProblemContainer.forEach((container: ProblemsContainer) => {
const element: ProblemsContainer | undefined = this._problems.find((other: ProblemsContainer) => {
if (other.url === container.url) {
return true
} else {
return false
}
})

if (element !== undefined) {
element.problems = element.problems.concat(container.problems)
} else {
this._problems.push(container)
}
})
}
}

errorsFound (): boolean {
let errorFound: boolean = false
const BreakException = new Error()

try {
this._problems.forEach((container: ProblemsContainer) => {
if (container.containsErrors()) {
errorFound = true
throw BreakException
}
})
} catch (error) {
if (error !== BreakException) {
throw error
private addProblem (severity: DiagnosticSeverity, message: string): void {
const tempProblemContainer: ProblemsContainer[] = ProblemsContainer.createProblemContainer(severity, message)
tempProblemContainer.forEach((container: ProblemsContainer) => {
const element = this._problems.find((other) => other.url === container.url)
if (element !== undefined) {
element.problems = element.problems.concat(container.problems)
} else {
this._problems.push(container)
}
}
})
}

return errorFound
errorsFound (): boolean {
return this._problems.some((container) => container.containsErrors())
}

reportProblems (): void {
Expand Down
Loading