Skip to content

Conversation

@afreof
Copy link

@afreof afreof commented Sep 8, 2025

support bitbake client debugging

This is a simple implementation of a command to generate a debug configuration for the currently open BitBake client script. It creates a launch.json entry with the necessary environment setup and allows specifying arguments.

Example with debugging bitbake-getvar:

  • Open the bitbake-getvar script.
  • Press Ctrl + Shift + P and run "BitBake: Debug Current File"
  • Enter arguments like MACHINE
  • This will create a launch.json entry and start a debug session.
    If the debug sessions stops at with bb.utils.lock_timeout(self.rlock)
    just press "play" again to continue. Debugging bitbake with tinfoil
    is a bit tricky due to its architecture with the server and clients.
    But it is possible to set breakpoints in the client code.

Note about debugging the server: The server could be debugged by attaching a debugger to the running server process. however, that is more complex to set up. Not sure this is still possible due to the double forking that happens when the server is started and the heartbeat mechanism. So for now, this command focuses on debugging client scripts.

Maybe it would be better to have a dedicated debug configuration provider class instead of generating launch.json directly. But this is more advanced.

Adrian Freihofer added 3 commits September 8, 2025 23:55
This is a simple implementation of a command to generate a debug
configuration for the currently open BitBake client script. It creates a
launch.json entry with the necessary environment setup and allows
specifying arguments.

Example with debugging bitbake-getvar:
- Open the bitbake-getvar script.
- Press Ctrl + Shift + P and run "BitBake: Debug Current File"
- Enter arguments like `MACHINE`
- This will create a launch.json entry and start a debug session.
  If the debug sessions stops at with bb.utils.lock_timeout(self.rlock)
  just press "play" again to continue. Debugging bitbake with tinfoil
  is a bit tricky due to its architecture with the server and clients.
  But it is possible to set breakpoints in the client code.

Note about debugging the server: The server could be debugged by
attaching a debugger to the running server process. however, that is
more complex to set up. Not sure this is still possible due to the
double forking that happens when the server is started and the heartbeat
mechanism. So for now, this command focuses on debugging client scripts.

Maybe it would be better to have a dedicated debug configuration provider
class instead of generating launch.json directly. But this is more advanced.
This resolves a bunch of "unresolved import" errors for scripts like
devtool or wic.
@afreof afreof force-pushed the launch-configuration-for-bb-clients branch from ebe16c9 to 4f097e8 Compare September 8, 2025 21:59
Copy link
Member

@deribaucourt deribaucourt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello, this looks quite useful when bitbake fails to do what we want!
Thanks for this idea and contribution, and welcome to the niche world of Typescript development for Yocto 😄
I left some comments to better follow our design guidelines.

// Utility to source BitBake environment script and return key env vars as dict
export async function getBitbakeEnvironmentVars(workspaceFolder: string): Promise<Record<string, string> | undefined> {
const config = vscode.workspace.getConfiguration('bitbake');
const pathToEnvScript = config.get<string>('pathToEnvScript');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the bitbakeDriver.getBuildConfig() method to access this. It also supports when pathToEnvScript is defined through "bitbake.buildConfigurations". You can look for examples of commands passing the bitbakeDriver ref in client/src/ui/BitbakeCommands.ts.
Look for other such spots in this PR.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be fixed.

Copy link
Member

@deribaucourt deribaucourt Sep 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but by calling exec instead of runBitBakeTerminal you won't have compatibility with commandWrapper configurated environments. This is another reason to use runBitBakeTerminal than a background terminal.

We have a vast amount of users using commandWrapper (e.g. kas) instead of providing a pathToEnvScript. This extension systematically should use the appropriate environments when possible. It is conveniently provided by the BitbakeDriver.

}

// Generates a tasks.json file with a bitbake-server task
export function generateTasksJson(taskName: string, workspaceFolder: string) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add unit tests for the JSON files creation method. It could create them from our integrationTest poky setup and look for specific keys, or compare with a pre-defined file.

{
label: taskName,
type: 'shell',
command: `sh -c '. "${pathToEnvScript}" >/dev/null 2>&1 && bitbake --server-only -T 120'`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't support dockerized environments where bitbake has to be run in the commandWrapper. Look if you can't use the bitbakeDriver shell commands prefix.

label: taskName,
type: 'shell',
command: `sh -c '. "${pathToEnvScript}" >/dev/null 2>&1 && bitbake --server-only -T 120'`,
group: 'build',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this should be in the launch.json as group extensionHost. You can actually have a look in this extension's launch.json configuration which allows spawning the process, then attaching to it in a similar manner to what you do on bitbake. It could be more elegant.

fs.mkdirSync(vscodeDir);
}
const tasksJsonPath = path.join(vscodeDir, 'tasks.json');
fs.writeFileSync(tasksJsonPath, JSON.stringify(tasksJson, null, 4));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid overwriting user configuration. So far what we do it add to the existing JSON. This would be safer.
you could look for the existing task with the same name and override just that part of the JSON.
Do the same in launch.json.

}

let programPath: string;
if (path.isAbsolute(program)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This relative path logic should be defined in a separate utility function. It could stay in this file.

name: `Debug ${programName}`,
type: 'debugpy',
request: 'launch',
program: programPath,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likely problematic with dockerized environments as well. Maybe we don't want to support dockerized environments for this feature's first release? I'd be ok with that since it's a more advanced command, likely to be used in native dev environments.

"@types/node": "^22.10.5",
"@types/vscode": "^1.92.0",
"@types/node": "^22.18.1",
"@types/semver": "^7.7.1",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

semver is already part of client/package.json.

},
"engines": {
"vscode": "^1.92.0"
"vscode": "^1.103.0"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe leave out this commit updating refs outside of this PR?
I'm working on that separately and I don't think you need it for making this feature work

@deribaucourt
Copy link
Member

I approved the CI run, but it has been failing due to low bandwidth recently, let's hope we don't get a false positive now 🤞.

import { finishProcessExecution } from '../utils/ProcessUtils'
import { type LanguageClient } from 'vscode-languageclient/node'
import { getVariableValue } from '../language/languageClient'
import { createDebugConfiguration } from '../utils/launchConfigGenerator';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we rename this in the same vein as the command? DebugConfiguration is too broad, ti could refer to too many things in this repository's context. Suggestion createBitBakeClientScriptDebugConfiguration, BitBakeClientScriptLaunchConfigGenerator. I honestly prefere it to be too long than too broad 😅.

@@ -0,0 +1,160 @@
import * as fs from 'fs';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the CI says, you should run eslint to automatically fix some linting issues. If you install the eslint vscode extension, it could run automatically when you save documents.

}
// Compose shell command to source env script and print env vars
const fullEnvScriptPath = path.isAbsolute(pathToEnvScript) ? pathToEnvScript : path.resolve(workspaceFolder, pathToEnvScript);
const shellCmd = `sh -c '. "${fullEnvScriptPath}" >/dev/null 2>&1 && printf "BUILDDIR=%s\\nBBPATH=%s\\nPATH=%s\\nPYTHONPATH=%s\\n" "$BUILDDIR" "$BBPATH" "$PATH" "$PYTHONPATH"'`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is part of getting information from the BitBake environment, which we all group in the "Scanner". It's OK to run this quick command here, but if tomorrow we also want to use BUILDDIR, or PATH somewhere else in the code, we are going to start duplicating these commands which is not a good thing for performance or maintainability. Hence why I recommend to move this in the scanner part next to the $OVERRIDES scanning.

const fullEnvScriptPath = path.isAbsolute(pathToEnvScript) ? pathToEnvScript : path.resolve(workspaceFolder, pathToEnvScript);
const shellCmd = `sh -c '. "${fullEnvScriptPath}" >/dev/null 2>&1 && printf "BUILDDIR=%s\\nBBPATH=%s\\nPATH=%s\\nPYTHONPATH=%s\\n" "$BUILDDIR" "$BBPATH" "$PATH" "$PYTHONPATH"'`;
return new Promise((resolve) => {
exec(shellCmd, { cwd: workspaceFolder, env: process.env }, (error, stdout, stderr) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By convention our extension doesn't spawn background commands and I think it should remain that way because due to commandWrapper, we may manipulate tricky environments which can have side effects or where errors may be hard to understand without terminal UI.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants