Skip to content

Commit

Permalink
Merge branch 'VSC-449-ha_lazy_cmake' into 'master'
Browse files Browse the repository at this point in the history
VSC-449 Lazy cmake projects

See merge request ide/vscode/iar-vsc-build!22
  • Loading branch information
HampusAdolfsson committed Apr 22, 2024
2 parents 2eb42d1 + 8b1d92e commit 7b192c4
Show file tree
Hide file tree
Showing 17 changed files with 496 additions and 134 deletions.
9 changes: 9 additions & 0 deletions media/toolbarview.css
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ body {
height: 18px;
}

span.codicon {
width: 18px;
height: 18px;
}

.center-contents {
text-align: center;
width: fit-content;
Expand All @@ -46,6 +51,10 @@ body {
width: 100%;
}

.hidden {
display: none;
}

.segment {
margin: 0 .5em;
position: relative;
Expand Down
28 changes: 27 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,16 @@
"dark": "media/icons/Build-dark.svg",
"light": "media/icons/Build-light.svg"
}
},
{
"command": "iar-build.configure",
"title": "Configure project",
"category": "IAR Build"
},
{
"command": "iar-build.reconfigure",
"title": "Force reconfiguration",
"category": "IAR Build"
}
],
"viewsContainers": {
Expand Down Expand Up @@ -229,6 +239,14 @@
"command": "iar-build.reloadProject",
"when": "view == iar-project"
},
{
"command": "iar-build.configure",
"when": "view == iar-project && iar-build.canConfigureProject"
},
{
"command": "iar-build.reconfigure",
"when": "view == iar-project && iar-build.canConfigureProject"
},
{
"command": "iar-build.addFileToRoot",
"when": "view == iar-project && iar-build.canModifyProjectTree",
Expand Down Expand Up @@ -368,6 +386,14 @@
{
"command": "iar-build.buildBatch",
"when": "false"
},
{
"command": "iar-build.configure",
"when": "iar-build.canConfigureProject"
},
{
"command": "iar-build.reconfigure",
"when": "iar-build.canConfigureProject"
}
]
},
Expand Down Expand Up @@ -697,7 +723,7 @@
"csv-parse": "4.8.8",
"escape-html": "^1.0.3",
"fast-deep-equal": "2.0.1",
"iar-vsc-common": "git@github.com:IARSystems/iar-vsc-common.git#ede169fb",
"iar-vsc-common": "git@github.com:IARSystems/iar-vsc-common.git#5c4aea5f",
"jsdom": "^19.0.0",
"rxjs": "^7.5.2",
"sanitize-html": "^2.6.1",
Expand Down
52 changes: 52 additions & 0 deletions src/extension/command/configure.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

import { ExtensionState } from "../extensionstate";
import { IarVsc } from "../main";
import { CMAKE_CMSIS_LOG_NAME } from "../ui/logservicehandler";
import { CommandBase } from "./command";

/**
* Configures a selected CMake or CMSIS-Toolbox project
*/
export class ConfigureCommand extends CommandBase<void> {
public static readonly ID = "iar-build.configure";

constructor() {
super(ConfigureCommand.ID);
}

async executeImpl() {
const workspace = await ExtensionState.getInstance().workspace.getValue();
if (workspace?.isExtendedWorkspace()) {
const project = await workspace.getExtendedProject();
if (project) {
IarVsc.outputChannelProvider.getOutputChannel(CMAKE_CMSIS_LOG_NAME).show(true);
await project.configure();
}
}
}
}

/**
* Force reconfigures a selected CMake or CMSIS-Toolbox project
*/
export class ReconfigureCommand extends CommandBase<void> {
public static readonly ID = "iar-build.reconfigure";

constructor() {
super(ReconfigureCommand.ID);
}

async executeImpl() {
const workspace = await ExtensionState.getInstance().workspace.getValue();
if (workspace?.isExtendedWorkspace()) {
const project = await workspace.getExtendedProject();
if (project) {
IarVsc.outputChannelProvider.getOutputChannel(CMAKE_CMSIS_LOG_NAME).show(true);
await project.reconfigure();
}
}
}
}
11 changes: 10 additions & 1 deletion src/extension/extensionstate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,19 @@ class StateImpl implements State, Disposable {
this.workspace.onValueDidChange(newWorkspace => {
logger.debug(`Loaded workspace '${newWorkspace?.name}'`);
if (newWorkspace) {
newWorkspace.projects.addOnSelectedHandler(() => {
newWorkspace.projects.addOnSelectedHandler(async() => {
if (!this.workbenches.selected && newWorkspace.projects.selected) {
StateImpl.selectWorkbenchMatchingProject(newWorkspace.projects.selected, this.workbenches);
}

if (newWorkspace.isExtendedWorkspace()) {
const proj = await newWorkspace.getExtendedProject();
Vscode.commands.executeCommand("setContext",
"iar-build.canConfigureProject",
(await proj?.isCmakeOrCmsisProject()) ?? false);
} else {
Vscode.commands.executeCommand("setContext", "iar-build.canConfigureProject", false);
}
});
if (!this.workbenches.selected && newWorkspace.projects.selected) {
StateImpl.selectWorkbenchMatchingProject(newWorkspace.projects.selected, this.workbenches);
Expand Down
143 changes: 74 additions & 69 deletions src/extension/intellisense/workspaceintellisenseprovider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { WorkbenchFeatures } from "iar-vsc-common/workbenchfeatureregistry";
import { EwWorkspace } from "../../iar/workspace/ewworkspace";
import { EwwFile } from "../../iar/workspace/ewwfile";
import { logger } from "iar-vsc-common/logger";
import { ProjectLock } from "../../iar/projectlock";

/**
* Generates and holds intellisense information ({@link IntellisenseInfo}) for a collection of projects (a "workspace").
Expand Down Expand Up @@ -312,17 +313,19 @@ namespace ConfigGenerator {
extraArgs = [...extraArgs, "-varfile", argVarFile];
}

// VSC-192 Invoke iarbuild and clean up any backups created
await BackupUtils.doWithBackupCheck(project.path.toString(), async() => {
const builderProc = spawn(
workbench.builderPath.toString(),
[project.path.toString(), "-jsondb", config.name, "-output", jsonPath].concat(extraArgs),
{ cwd: workspaceFolder });
builderProc.stdout.on("data", data => output?.append(data.toString()));
builderProc.on("error", (err) => {
return Promise.reject(err);
await ProjectLock.runExclusive(project.path, async() => {
// VSC-192 Invoke iarbuild and clean up any backups created
await BackupUtils.doWithBackupCheck(project.path.toString(), async() => {
const builderProc = spawn(
workbench.builderPath.toString(),
[project.path.toString(), "-jsondb", config.name, "-output", jsonPath].concat(extraArgs),
{ cwd: workspaceFolder });
builderProc.stdout.on("data", data => output?.append(data.toString()));
builderProc.on("error", (err) => {
return Promise.reject(err);
});
await ProcessUtils.waitForExit(builderProc);
});
await ProcessUtils.waitForExit(builderProc);
});

// Parse the json file for compilation flags
Expand Down Expand Up @@ -360,73 +363,75 @@ namespace ConfigGenerator {

// VSC-386 We use a mutex here to throttle the CPU usage
return mutex.runExclusive(() => {
// VSC-192 clean up any backups created by iarbuild
return BackupUtils.doWithBackupCheck(project.path.toString(), async() => {
const builderProc = spawn(
workbench.builderPath.toString(),
[project.path.toString(), "-dryrun", config.name, "-log", "all"].concat(extraArgs),
{ cwd: workspaceFolder },
);
builderProc.on("error", (err) => {
return Promise.reject(err);
});
const exitPromise = ProcessUtils.waitForExit(builderProc);

// Parse output from iarbuild to find all compiler invocations
const compInvs = await new Promise<string[][]>((resolve, _reject) => {
const compilerInvocations: string[][] = [];
const lineReader = readline.createInterface({
input: builderProc.stdout,
return ProjectLock.runExclusive(project.path, () => {
// VSC-192 clean up any backups created by iarbuild
return BackupUtils.doWithBackupCheck(project.path.toString(), async() => {
const builderProc = spawn(
workbench.builderPath.toString(),
[project.path.toString(), "-dryrun", config.name, "-log", "all"].concat(extraArgs),
{ cwd: workspaceFolder },
);
builderProc.on("error", (err) => {
return Promise.reject(err);
});
lineReader.on("line", (line: string) => {
if (line.startsWith(">")) { // this is a compiler invocation
line = line.slice(1); // get rid of the >
const endOfFirstArg = line.search(/\s+/);
const args = [line.slice(0, endOfFirstArg)]; // first arg (compiler name) is unquoted, so handle it specially
let argsRaw = line.slice(endOfFirstArg).trim();
argsRaw = argsRaw.replace(/"\\(\s+)"/g, "\"$1\""); // IarBuild inserts some weird backslashes we want to get rid of
const argDelimRegex = /"\s+"/;
let match: RegExpExecArray | null;
while ((match = argDelimRegex.exec(argsRaw)) !== null) {
const unquotedArg = stripQuotes(argsRaw.slice(0, match.index + 1));
// Quotes inside parameters are escaped on the command line, but we want the unescaped parameters
const arg = unquotedArg.replace(/\\"/g, "\"");
args.push(arg);
argsRaw = argsRaw.slice(match.index + 1);
const exitPromise = ProcessUtils.waitForExit(builderProc);

// Parse output from iarbuild to find all compiler invocations
const compInvs = await new Promise<string[][]>((resolve, _reject) => {
const compilerInvocations: string[][] = [];
const lineReader = readline.createInterface({
input: builderProc.stdout,
});
lineReader.on("line", (line: string) => {
if (line.startsWith(">")) { // this is a compiler invocation
line = line.slice(1); // get rid of the >
const endOfFirstArg = line.search(/\s+/);
const args = [line.slice(0, endOfFirstArg)]; // first arg (compiler name) is unquoted, so handle it specially
let argsRaw = line.slice(endOfFirstArg).trim();
argsRaw = argsRaw.replace(/"\\(\s+)"/g, "\"$1\""); // IarBuild inserts some weird backslashes we want to get rid of
const argDelimRegex = /"\s+"/;
let match: RegExpExecArray | null;
while ((match = argDelimRegex.exec(argsRaw)) !== null) {
const unquotedArg = stripQuotes(argsRaw.slice(0, match.index + 1));
// Quotes inside parameters are escaped on the command line, but we want the unescaped parameters
const arg = unquotedArg.replace(/\\"/g, "\"");
args.push(arg);
argsRaw = argsRaw.slice(match.index + 1);
}
args.push(stripQuotes(argsRaw));
compilerInvocations.push(args);
} else if (line.match(/^Linking/) || line.match(/^/)) { // usually the promise finishes here
lineReader.removeAllListeners();
resolve(compilerInvocations);
return;
}
args.push(stripQuotes(argsRaw));
compilerInvocations.push(args);
} else if (line.match(/^Linking/) || line.match(/^/)) { // usually the promise finishes here
output?.appendLine(line);
});
lineReader.on("close", () => { // safeguard in case the builder crashes
output?.appendLine("WARN: Builder closed without reaching linking stage.");
lineReader.removeAllListeners();
resolve(compilerInvocations);
});
}); /* new Promise */
await exitPromise; // Make sure iarbuild exits without error

// Find the filename of each compiler invocation, and add to the map
const compilerInvocationsMap = new Map<string, string[]>();
compInvs.forEach(compInv => {
if (compInv?.[0] === undefined || compInv[1] === undefined) return;
const file = compInv[1];
if (LanguageUtils.determineLanguage(file) === undefined) {
output?.appendLine("Skipping file of unsupported type: " + file);
return;
}
output?.appendLine(line);
});
lineReader.on("close", () => { // safeguard in case the builder crashes
output?.appendLine("WARN: Builder closed without reaching linking stage.");
lineReader.removeAllListeners();
resolve(compilerInvocations);
// generateFromCompilerArgs expects the first arg to be an absolute path to a compiler
compInv[0] = Path.join(workbench.path.toString(), `${config.targetId}/bin/${compInv[0]}`);
compilerInvocationsMap.set(OsUtils.normalizePath(file), compInv);
});
}); /* new Promise */
await exitPromise; // Make sure iarbuild exits without error

// Find the filename of each compiler invocation, and add to the map
const compilerInvocationsMap = new Map<string, string[]>();
compInvs.forEach(compInv => {
if (compInv?.[0] === undefined || compInv[1] === undefined) return;
const file = compInv[1];
if (LanguageUtils.determineLanguage(file) === undefined) {
output?.appendLine("Skipping file of unsupported type: " + file);
return;
}
// generateFromCompilerArgs expects the first arg to be an absolute path to a compiler
compInv[0] = Path.join(workbench.path.toString(), `${config.targetId}/bin/${compInv[0]}`);
compilerInvocationsMap.set(OsUtils.normalizePath(file), compInv);
});

return compilerInvocationsMap;
}); /* /doWithBackupCheck */
return compilerInvocationsMap;
}); /* /doWithBackupCheck */
});
}); /* /runExclusive */
}

Expand Down
3 changes: 3 additions & 0 deletions src/extension/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { TreeBatchBuildView } from "./ui/batchbuildview";
import { BatchBuild } from "./ui/batchbuildcommands";
import { ErrorUtils } from "../utils/utils";
import { OutputChannelRegistry } from "../utils/outputchannelregistry";
import { ConfigureCommand, ReconfigureCommand } from "./command/configure";

export function activate(context: vscode.ExtensionContext): BuildExtensionApi {
logger.init("IAR Build");
Expand All @@ -46,6 +47,8 @@ export function activate(context: vscode.ExtensionContext): BuildExtensionApi {
// --- create and register commands
GetSettingsCommand.initCommands(context);
new ReloadProjectCommand().register(context);
new ConfigureCommand().register(context);
new ReconfigureCommand().register(context);
new AddFileToRootCommand().register(context);
new AddGroupToRootCommand().register(context);
new AddFileCommand().register(context);
Expand Down
Loading

0 comments on commit 7b192c4

Please sign in to comment.