Skip to content

Commit

Permalink
Merge remote branch 'origin/master' into edge
Browse files Browse the repository at this point in the history
  • Loading branch information
automatic-merge committed Jan 24, 2024
2 parents ebf640b + feba33a commit fdc3f69
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 94 deletions.
6 changes: 2 additions & 4 deletions integration/vscode/ada/src/ExtensionState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { createClient } from './clients';
import { GnatTaskProvider } from './gnatTaskProvider';
import { GprTaskProvider } from './gprTaskProvider';
import { registerTaskProviders } from './taskProviders';
import { getCustomEnvSettingName } from './helpers';
import { TERMINAL_ENV_SETTING_NAME } from './helpers';

/**
* This class encapsulates all state that should be maintained throughout the
Expand Down Expand Up @@ -106,9 +106,7 @@ export class ExtensionState {
// React to changes made in the environment variables, showing
// a popup to reload the VS Code window and thus restart the
// Ada extension.
const env_config_name = getCustomEnvSettingName();

if (e.affectsConfiguration(env_config_name)) {
if (e.affectsConfiguration(TERMINAL_ENV_SETTING_NAME)) {
void this.showReloadWindowPopup();
}
};
Expand Down
4 changes: 2 additions & 2 deletions integration/vscode/ada/src/clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { existsSync } from 'fs';
import * as vscode from 'vscode';
import { LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient/node';
import { logger } from './extension';
import { logErrorAndThrow, setCustomEnvironment } from './helpers';
import { logErrorAndThrow, setTerminalEnvironment } from './helpers';

export function createClient(
context: vscode.ExtensionContext,
Expand Down Expand Up @@ -63,7 +63,7 @@ export function createClient(
// Copy this process's environment
const serverEnv: NodeJS.ProcessEnv = { ...process.env };
// Set custom environment
setCustomEnvironment(serverEnv);
setTerminalEnvironment(serverEnv);

logger.debug(`Environment for ${name}:`);
for (const key in serverEnv) {
Expand Down
96 changes: 74 additions & 22 deletions integration/vscode/ada/src/debugConfigProvider.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import assert from 'assert';
import * as vscode from 'vscode';
import { adaExtState } from './extension';
import { AdaMain, getAdaMains, getProjectFile } from './helpers';
import { AdaMain, exe, getAdaMains, getEvaluatedTerminalEnv, getProjectFile } from './helpers';
import { BUILD_PROJECT_TASK_NAME, getBuildTaskName } from './taskProviders';
import path from 'path';
import { existsSync } from 'fs';

/**
* Ada Configuration for a debug session
*/
interface AdaConfig extends vscode.DebugConfiguration {
export interface AdaConfig extends vscode.DebugConfiguration {
MIMode: string;
program: string;
cwd?: string;
Expand All @@ -20,8 +22,25 @@ interface AdaConfig extends vscode.DebugConfiguration {
ignoreFailures: boolean;
}[];
processId?: string;
miDebuggerPath?: string;
}

export const adaDynamicDebugConfigProvider = {
async provideDebugConfigurations(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_folder?: vscode.WorkspaceFolder
): Promise<vscode.DebugConfiguration[]> {
const quickpick = await createQuickPickItems('Build & Debug');

const configs: AdaConfig[] = quickpick.flatMap((i) => {
assert(i.adaMain);
return [initializeConfig(i.adaMain), createAttachConfig(i.adaMain)];
});

return configs;
},
};

/**
* Initialize debugging support for Ada projects.
*
Expand All @@ -48,35 +67,66 @@ export function initializeDebugging(ctx: vscode.ExtensionContext) {
ctx.subscriptions.push(
vscode.debug.registerDebugConfigurationProvider(
'ada',
{
async provideDebugConfigurations(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_folder: vscode.WorkspaceFolder | undefined
): Promise<vscode.DebugConfiguration[]> {
const quickpick = await createQuickPickItems('Build & Debug');

const configs: AdaConfig[] = quickpick.flatMap((i) => {
assert(i.adaMain);
return [initializeConfig(i.adaMain), createAttachConfig(i.adaMain)];
});

return configs;
},
},
adaDynamicDebugConfigProvider,
// The 'Dynamic' trigger type only works if the package.json lists
// "onDebugDynamicConfigurations:ada" as part of the
// activationEvents.
vscode.DebugConfigurationProviderTriggerKind.Dynamic
)
);

// TODO it is also possible to register another provider with trigger kind
// 'Dynamic', however the role of such a provider is unclear. In practical
// experiments it ends up never being called. The above provider is enough
// to make it possible to launch debug sessions without a launch.json.

return provider;
}

let cachedGdb: string | undefined | null = undefined;

/**
*
* @returns the full path to the `gdb` executable, taking into consideration the
* `PATH` variable in the `terminal.integrated.env.*` setting if set. Otherwise,
* the `PATH` variable of the current process environment is considered.
*
* The current process environment is unlikely to change during the lifetime of
* the extension, and we already prompt the User to reload the window in case
* the `terminal.integrated.env.*` variables change. For this reason, we compute
* the value only on the first call, and cache it for subsequent calls to return
* it efficiently.
*/
function getOrFindGdb(): string | undefined {
if (cachedGdb == undefined) {
/**
* If undefined yet, try to compute it.
*/
const env = getEvaluatedTerminalEnv();
let pathVal: string;
if (env && 'PATH' in env) {
pathVal = env.PATH;
} else if ('PATH' in process.env) {
pathVal = process.env.PATH ?? '';
} else {
pathVal = '';
}

const gdb = pathVal
.split(path.delimiter)
.map<string>((v) => path.join(v, 'gdb' + exe))
.find(existsSync);

if (gdb) {
// Found
cachedGdb = gdb;
return cachedGdb;
} else {
// Not found. Assign null to cache to avoid recomputing at every call.
cachedGdb = null;
}
}

// When returning, coerce null to undefined because the distinction doesn't
// matter on the caller side.
return cachedGdb ?? undefined;
}

/**
* Initialize a debug configuration based on 'cppdbg' for the given executable
* if specified. Otherwise the program field includes
Expand Down Expand Up @@ -109,6 +159,7 @@ function initializeConfig(main: AdaMain, name?: string): AdaConfig {
MIMode: 'gdb',
preLaunchTask: main ? getBuildTaskName(main) : BUILD_PROJECT_TASK_NAME,
setupCommands: setupCmd,
miDebuggerPath: getOrFindGdb(),
};

return config;
Expand Down Expand Up @@ -364,5 +415,6 @@ function createAttachConfig(adaMain: AdaMain): AdaConfig {
* to trigger an unwanted rebuild, so we don't set a preLaunchTask.
*/
// preLaunchTask: adaMain ? getBuildTaskName(adaMain) : BUILD_PROJECT_TASK_NAME,
miDebuggerPath: getOrFindGdb(),
};
}
10 changes: 5 additions & 5 deletions integration/vscode/ada/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ import { initializeDebugging } from './debugConfigProvider';
import { initializeTestView } from './gnattest';
import {
assertSupportedEnvironments,
getCustomEnvSettingName,
getEvaluatedCustomEnv,
TERMINAL_ENV_SETTING_NAME,
getEvaluatedTerminalEnv,
startedInDebugMode,
} from './helpers';

Expand Down Expand Up @@ -127,15 +127,15 @@ async function activateExtension(context: vscode.ExtensionContext) {
assertSupportedEnvironments(logger);

// Log the environment that the extension (and all VS Code) will be using
const customEnv = getEvaluatedCustomEnv();
const customEnv = getEvaluatedTerminalEnv();
if (customEnv && Object.keys(customEnv).length > 0) {
logger.info(`Custom environment variables from ${getCustomEnvSettingName()}`);
logger.info(`Custom environment variables from ${TERMINAL_ENV_SETTING_NAME}`);
for (const varName in customEnv) {
const varValue: string = customEnv[varName];
logger.info(`${varName}=${varValue}`);
}
} else {
logger.debug('No custom environment variables set in %s', getCustomEnvSettingName());
logger.debug('No custom environment variables set in %s', TERMINAL_ENV_SETTING_NAME);
}

// Create the Ada and GPR clients.
Expand Down
70 changes: 34 additions & 36 deletions integration/vscode/ada/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,39 +109,33 @@ export function substituteVariables(str: string, recursive = false): string {
return str;
}

/*
Environment setting helper functions
*/

export function getCustomEnv() {
const env_config_name = getCustomEnvSettingName();
/**
* Name of the `terminal.integrated.env.*` setting applicable to the current platform.
*/
export const TERMINAL_ENV_SETTING_NAME =
'terminal.integrated.env.' +
(platform() == 'darwin' ? 'osx' : platform() == 'win32' ? 'windows' : 'linux');

/**
*
* @returns the value of the applicable `terminal.integrated.env.*` setting,
* without evaluation of macros such as `${env:...}`.
*/
export function getTerminalEnv() {
const custom_env = vscode.workspace
.getConfiguration()
.get<{ [name: string]: string }>(env_config_name);
.get<{ [name: string]: string }>(TERMINAL_ENV_SETTING_NAME);

return custom_env;
}

export function getCustomEnvSettingName() {
const user_platform = platform();
let env_config_name = 'terminal.integrated.env';

switch (user_platform) {
case 'darwin':
env_config_name += '.osx';
break;
case 'win32':
env_config_name += '.windows';
break;
default:
env_config_name += '.linux';
}
return env_config_name;
}

export function getEvaluatedCustomEnv() {
const custom_env = getCustomEnv();
/**
*
* @returns the value of the applicable `terminal.integrated.env.*` setting,
* after evaluation of macros such as `${env:...}`.
*/
export function getEvaluatedTerminalEnv() {
const custom_env = getTerminalEnv();

if (custom_env) {
for (const var_name in custom_env) {
Expand All @@ -160,17 +154,13 @@ export function getEvaluatedCustomEnv() {
* Read the environment variables specified in the vscode setting
* `terminal.integrated.env.<os>` and set them in the given ProcessEnv object.
*
* If no targetEnv is given, `process.env` is used as a target environment.
* The targetEnv can be `process.env` to apply the changes to the environment of
* the running process.
*/
export function setCustomEnvironment(targetEnv?: NodeJS.ProcessEnv) {
if (!targetEnv) {
targetEnv = process.env;
}

export function setTerminalEnvironment(targetEnv: NodeJS.ProcessEnv) {
// Retrieve the user's custom environment variables if specified in their
// settings/workspace: we'll then launch any child process with this custom
// environment
const custom_env = getEvaluatedCustomEnv();
// settings/workspace
const custom_env = getEvaluatedTerminalEnv();

if (custom_env) {
for (const var_name in custom_env) {
Expand All @@ -183,7 +173,7 @@ export function setCustomEnvironment(targetEnv?: NodeJS.ProcessEnv) {
export function assertSupportedEnvironments(mainChannel: winston.Logger) {
// Get the ALS environment variable from the custom environment, or from the
// process environment
const customEnv = getEvaluatedCustomEnv();
const customEnv = getEvaluatedTerminalEnv();
const als = customEnv?.ALS ?? process.env.ALS;
if (als) {
// The User provided an external ALS executable. Do not perform any
Expand Down Expand Up @@ -350,3 +340,11 @@ export function startedInDebugMode() {
}
return false;
}

/**
* This constant is set to the string `.exe` on Windows, and to the empty string
* otherwise. It is intended for computingk executable filenames conveniently by
* simply appending the constant at the end of the name and obtaining a result
* compatible with the running platform.
*/
export const exe: '.exe' | '' = process.platform == 'win32' ? '.exe' : '';
42 changes: 42 additions & 0 deletions integration/vscode/ada/test/suite/general/debug.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import assert from 'assert';
import { suite, test } from 'mocha';
import { AdaConfig, adaDynamicDebugConfigProvider } from '../../../src/debugConfigProvider';
import { activate } from '../utils';

suite('Debug Configurations', function () {
this.beforeAll(async () => {
await activate();
});

test('GDB path is explicitely set in offered debug config', async () => {
const firstConfig = (await adaDynamicDebugConfigProvider.provideDebugConfigurations()).at(
0
) as AdaConfig;

assert.notEqual(firstConfig.miDebuggerPath, undefined);
});

test('GDB path is the same for all configs', async () => {
const configs =
(await adaDynamicDebugConfigProvider.provideDebugConfigurations()) as AdaConfig[];

assert(configs.length > 1);
const miDebuggerPath = configs.at(0)?.miDebuggerPath;
assert.notEqual(miDebuggerPath, '');
for (let i = 0; i < configs.length; i++) {
const c = configs[i];
assert.equal(c.miDebuggerPath, miDebuggerPath);
}
});

test('Two debug configs per main are proposed', async () => {
const configs =
(await adaDynamicDebugConfigProvider.provideDebugConfigurations()) as AdaConfig[];
const expected = `
Ada: Debug main - src/main1.adb
Ada: Attach debugger to running process - src/main1.adb
Ada: Debug main - src/test.adb
Ada: Attach debugger to running process - src/test.adb`.trim();
assert.equal(configs.map((v) => `${v.name}`).join('\n'), expected);
});
});
5 changes: 2 additions & 3 deletions integration/vscode/ada/test/suite/general/tasks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { existsSync } from 'fs';
import { suite, test } from 'mocha';
import * as vscode from 'vscode';
import { adaExtState } from '../../../src/extension';
import { getProjectFile } from '../../../src/helpers';
import { exe, getProjectFile } from '../../../src/helpers';
import {
CustomTaskDefinition,
PROJECT_FROM_CONFIG,
createAdaTaskProvider,
createSparkTaskProvider,
} from '../../../src/taskProviders';
import { activate, exe } from '../utils';
import { activate } from '../utils';

suite('GPR Tasks Provider', function () {
let projectPath: string;
Expand Down Expand Up @@ -125,7 +125,6 @@ ada: Build and run main - src/test.adb - kind: buildAndRunMain`.trim();
const status = await runTaskAndGetResult(task);
assert.equal(status, 0);

console.info(`cwd=${process.cwd()}`);
/**
* Check that the executable is produced. The project defines a
* different name for the executable produced by main1.adb.
Expand Down
1 change: 0 additions & 1 deletion integration/vscode/ada/test/suite/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,3 @@ export function runMochaTestsuite(suiteName: string, suiteDirectory: string) {
}
});
}
export const exe = process.platform == 'win32' ? '.exe' : '';
Loading

0 comments on commit fdc3f69

Please sign in to comment.