Skip to content

Commit

Permalink
Run 'autoproj watch' as a child process instead of a task [TESTS MISS…
Browse files Browse the repository at this point in the history
…ING]
  • Loading branch information
g-arjones committed May 10, 2024
1 parent acd8287 commit f7cee0e
Show file tree
Hide file tree
Showing 13 changed files with 343 additions and 259 deletions.
5 changes: 2 additions & 3 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
- Add command to enable debug symbols (by creating rb file in overrides.d)
- Add output channel
- Run `autoproj watch` with `child_process.spawn` instead of as a task (and monitor it)
- Combine autoproj-package and autoproj-workspace in a single task provider
- Support adding parent folders (i.e. `drivers/`) in task provider
- Refactor `extension.test.ts` and `commands.test.ts` to use a real workspace instead of mocks (see `cpptools.test.ts`)
- Add linter
- Add icons to package and workspace picker in "Add package to workspace command"
- Consider vscode-rdbg integration
- Set "python.experiments.optOutFrom": ["pythonTestAdapter"]
- Install `ruby-lsp` automatically
- Create a Gemfile in `.autoproj/vscode-autoproj` that does `eval_gemfile` on `install/gems/Gemfile` and adds `ruby-lsp` and `debug`
- Do a `bundler install` after that
- See `tasks.Handler` how to create a progress view
- On startup (also manifest changes / folder added / folder removed?):
- On startup:
- If `.autoproj/vscode-autoproj/Gemfile.lock` does not exist
- Does nothing
- If `.autoproj/vscode-autoproj/Gemfile.lock` exists
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,6 @@
"enum": [
"build",
"osdeps",
"watch",
"update-config",
"update-environment",
"update",
Expand Down
104 changes: 32 additions & 72 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,46 +6,32 @@ import * as vscode from "vscode";
import * as autoproj from "./autoproj";
import * as commands from "./commands";
import * as tasks from "./tasks";
import * as watcher from "./watcher";
import * as watcher from "./fileWatcher";
import * as wrappers from "./wrappers";
import * as cpptools from "./cpptools";
import { ShimsWriter } from "./shimsWriter";
import { WatchManager } from "./workspaceWatcher";

export class EventHandler implements vscode.Disposable {
private _wrapper: wrappers.VSCode;
private _watcher: watcher.FileWatcher;
private _fileWatcher: watcher.FileWatcher;
private _workspaces: autoproj.Workspaces;
private _workspaceRootToPid: Map<string, number>;
private _cppConfigurationProvider: cpptools.CppConfigurationProvider;
private _shimsWriter: ShimsWriter;
private _watchManager: WatchManager;

constructor(wrapper: wrappers.VSCode, fileWatcher: watcher.FileWatcher,
workspaces: autoproj.Workspaces, cppConfigurationProvider: cpptools.CppConfigurationProvider) {
constructor(wrapper: wrappers.VSCode, workspaces: autoproj.Workspaces,
cppConfigurationProvider: cpptools.CppConfigurationProvider, watchManager: WatchManager) {
this._wrapper = wrapper;
this._watcher = fileWatcher;
this._fileWatcher = new watcher.FileWatcher();
this._workspaces = workspaces;
this._cppConfigurationProvider = cppConfigurationProvider;
this._shimsWriter = new ShimsWriter();
this._workspaceRootToPid = new Map();
this._watchManager = watchManager;
}

public dispose() {
for (const [, pid] of this._workspaceRootToPid) {
try {
this._wrapper.killProcess(pid, "SIGINT");
} catch (error) {
// either the user terminated the task or "autoproj watch" failed
}
}
this._workspaceRootToPid.clear();
}

public onDidStartTaskProcess(event: vscode.TaskProcessStartEvent) {
const task = event.execution.task;
if (task.definition.type === tasks.TaskType.Workspace &&
task.definition.mode === tasks.WorkspaceTaskMode.Watch) {
this._workspaceRootToPid.set(task.definition.workspace, event.processId);
}
this._fileWatcher.dispose();
}

public onDidOpenTextDocument(event: vscode.TextDocument) {
Expand All @@ -63,18 +49,21 @@ export class EventHandler implements vscode.Disposable {
public async onManifestChanged(ws: autoproj.Workspace): Promise<void> {
try {
await ws.reload();
this._cppConfigurationProvider.notifyChanges();
} catch (err) {
this._wrapper.showErrorMessage(`Could not load installation manifest: ${err.message}`);
}
this._watchManager.start(ws);
this._cppConfigurationProvider.notifyChanges();
}

private async _writeShim(callback: () => Promise<void>, name: string, ws: autoproj.Workspace) {
public async writeShims(workspace: autoproj.Workspace) {
try {
await callback();
await this._shimsWriter.writeOpts(workspace);
await this._shimsWriter.writePython(workspace);
await this._shimsWriter.writeGdb(workspace);
await this._shimsWriter.writeRuby(workspace);
} catch (err) {
const wsName = path.basename(ws.root);
await this._wrapper.showErrorMessage(`Could create ${name} shim in '${wsName}' workspace: ${err.message}`);
await this._wrapper.showErrorMessage(`Could create file:: ${err.message}`);
}
}

Expand All @@ -83,32 +72,13 @@ export class EventHandler implements vscode.Disposable {
if (added && workspace) {
try {
await workspace.info();
this._cppConfigurationProvider.notifyChanges();
await this._writeShim(() => this._shimsWriter.writePython(workspace), "python", workspace);
await this._writeShim(() => this._shimsWriter.writeGdb(workspace), "gdb", workspace);
await this._writeShim(() => this._shimsWriter.writeRuby(workspace), "ruby", workspace);
} catch (err) {
this._wrapper.showErrorMessage(`Could not load installation manifest: ${err.message}`);
}
try {
const allTasks = await this._wrapper.fetchTasks(tasks.WORKSPACE_TASK_FILTER);
const watchTask = allTasks.find((task) => task.definition.mode === tasks.WorkspaceTaskMode.Watch &&
task.definition.workspace === workspace.root);

if (watchTask) {
const execution = vscode.tasks.taskExecutions.find(
(execution: vscode.TaskExecution) => execution.task.definition == watchTask.definition
);
if (!execution) {
this._wrapper.executeTask(watchTask);
}
} else {
this._wrapper.showErrorMessage("Internal error: Could not find watch task");
}
} catch (err) {
this._wrapper.showErrorMessage(`Could not start autoproj watch task: ${err.message}`);
}
this._cppConfigurationProvider.notifyChanges();
this.watchManifest(workspace);
await this.writeShims(workspace);
this._watchManager.start(workspace);
}
}

Expand All @@ -117,47 +87,39 @@ export class EventHandler implements vscode.Disposable {
const deletedWs = this._workspaces.deleteFolder(folder.uri.fsPath);
if (deletedWs) {
this.unwatchManifest(deletedWs);

const pid = this._workspaceRootToPid.get(deletedWs.root);
if (pid) {
try {
this._wrapper.killProcess(pid, "SIGINT");
} catch (error) {
// either the user stopped the task or it "autoproj watch" failed
}
this._workspaceRootToPid.delete(deletedWs.root);
}
await this._watchManager.stop(deletedWs);
}
}

public watchManifest(ws: autoproj.Workspace): void {
const manifestPath = autoproj.installationManifestPath(ws.root);
try {
this._watcher.startWatching(manifestPath, () => this.onManifestChanged(ws));
this._fileWatcher.startWatching(manifestPath, () => this.onManifestChanged(ws));
} catch (err) {
this._wrapper.showErrorMessage(err.message);
}
}

public unwatchManifest(ws: autoproj.Workspace): void {
try {
this._watcher.stopWatching(autoproj.installationManifestPath(ws.root));
this._fileWatcher.stopWatching(autoproj.installationManifestPath(ws.root));
} catch (err) {
this._wrapper.showErrorMessage(err.message);
}
}
}

export async function setupExtension(subscriptions: any[], vscodeWrapper: wrappers.VSCode) {
const fileWatcher = new watcher.FileWatcher();
export async function setupExtension(subscriptions: vscode.Disposable[], vscodeWrapper: wrappers.VSCode) {
const workspaces = new autoproj.Workspaces(null);
const autoprojTaskProvider = new tasks.AutoprojProvider(workspaces, vscodeWrapper);
const autoprojPackageTaskProvider = new tasks.AutoprojPackageTaskProvider(autoprojTaskProvider);
const autoprojWorkspaceTaskProvider = new tasks.AutoprojWorkspaceTaskProvider(autoprojTaskProvider);
const autoprojCommands = new commands.Commands(workspaces, vscodeWrapper);
const cppConfigurationProvider = new cpptools.CppConfigurationProvider(workspaces);
const eventHandler = new EventHandler(vscodeWrapper, fileWatcher, workspaces, cppConfigurationProvider);
const outputChannel = vscode.window.createOutputChannel("Autoproj", { log: true });
const watchManager = new WatchManager(outputChannel, vscodeWrapper);
const tasksHandler = new tasks.Handler(vscodeWrapper, workspaces);
const eventHandler = new EventHandler(vscodeWrapper, workspaces, cppConfigurationProvider, watchManager);

subscriptions.push(vscode.tasks.registerTaskProvider("autoproj-workspace", autoprojWorkspaceTaskProvider));
subscriptions.push(vscode.tasks.registerTaskProvider("autoproj-package", autoprojPackageTaskProvider));
Expand All @@ -172,18 +134,16 @@ export async function setupExtension(subscriptions: any[], vscodeWrapper: wrappe

subscriptions.push(eventHandler);
subscriptions.push(workspaces);
subscriptions.push(fileWatcher);
subscriptions.push(tasksHandler);
subscriptions.push(cppConfigurationProvider);
subscriptions.push(vscode.tasks.onDidStartTaskProcess((event) => {
eventHandler.onDidStartTaskProcess(event);
tasksHandler.onDidStartTaskProcess(event);
}));
subscriptions.push(outputChannel);
subscriptions.push(watchManager);
subscriptions.push(vscode.tasks.onDidStartTaskProcess((event) => { tasksHandler.onDidStartTaskProcess(event); }));
subscriptions.push(vscode.tasks.onDidEndTaskProcess((event) => tasksHandler.onDidEndTaskProcess(event)));
subscriptions.push(vscode.workspace.onDidChangeConfiguration((event) => autoprojTaskProvider.reloadTasks()));
subscriptions.push(vscode.workspace.onDidOpenTextDocument((event: vscode.TextDocument) => {
eventHandler.onDidOpenTextDocument(event);
}));
subscriptions.push(vscode.tasks.onDidEndTaskProcess((event) => tasksHandler.onDidEndTaskProcess(event)));
subscriptions.push(vscode.workspace.onDidChangeConfiguration((event) => autoprojTaskProvider.reloadTasks()));
subscriptions.push(vscode.workspace.onDidChangeWorkspaceFolders((event) => {
event.added.forEach((folder) => eventHandler.onWorkspaceFolderAdded(folder));
event.removed.forEach((folder) => eventHandler.onWorkspaceFolderRemoved(folder));
Expand Down
File renamed without changes.
13 changes: 13 additions & 0 deletions src/logging.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as vscode from "vscode";

export function getLogger(channel: vscode.LogOutputChannel, name: string): vscode.LogOutputChannel {
return new Proxy(channel, {
get(target, prop, receiver) {
const methods = ["trace", "debug", "info", "warn", "error", "replace", "append", "appendLine"];
if (methods.includes(String(prop))) {
return (msg, ...args) => target[prop].call(target, `[${name}] ${msg}`, ...args)
}
return target[prop];
}
});
}
15 changes: 14 additions & 1 deletion src/shimsWriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ exec ${autoproj.autoprojExePath(workspace.root)} exec --use-cache ruby "$@"
}

export class ShimsWriter {
public static readonly RELATIVE_SHIMS_PATH = path.join(".autoproj", "vscode-autoproj", "bin");
public static readonly RELATIVE_OPTS_PATH = path.join(".autoproj", "vscode-autoproj");
public static readonly RELATIVE_SHIMS_PATH = path.join(ShimsWriter.RELATIVE_OPTS_PATH, "bin");

constructor() {
}
Expand Down Expand Up @@ -71,4 +72,16 @@ export class ShimsWriter {
public async writeRuby(workspace: autoproj.Workspace): Promise<void> {
return this._writeShim(workspace, "ruby", rubyGenerator);
}

public async writeOpts(workspace: autoproj.Workspace): Promise<void> {
const optsPath = path.join(workspace.root, ShimsWriter.RELATIVE_OPTS_PATH, "rubyopt.rb");
const contents = [
"# AUTOGENERATED BY THE VSCODE AUTOPROJ EXTENSION",
"STDOUT.sync = true",
"STDERR.sync = true",
]

await this._mkShimDir(workspace);
await fs.writeFile(optsPath, contents.join("\n"));
}
}
1 change: 0 additions & 1 deletion src/tasks/definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export enum TaskType {

export enum WorkspaceTaskMode {
Osdeps = "osdeps",
Watch = "watch",
Build = "build",
UpdateConfig = "update-config",
UpdateEnvironment = "update-environment",
Expand Down
16 changes: 0 additions & 16 deletions src/tasks/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ function runAutoproj(ws, ...args) {
export class AutoprojProvider implements vscode.TaskProvider {
public workspaces: autoproj.Workspaces;

private _watchTasks: Map<string, vscode.Task>;
private _buildTasks: Map<string, vscode.Task>;
private _nodepsBuildTasks: Map<string, vscode.Task>;
private _rebuildTasks: Map<string, vscode.Task>;
Expand All @@ -41,10 +40,6 @@ export class AutoprojProvider implements vscode.TaskProvider {
return this._getCache(this._buildTasks, path);
}

public async watchTask(path: string): Promise<vscode.Task> {
return this._getCache(this._watchTasks, path);
}

public async forceBuildTask(path: string): Promise<vscode.Task> {
return this._getCache(this._forceBuildTasks, path);
}
Expand Down Expand Up @@ -80,7 +75,6 @@ export class AutoprojProvider implements vscode.TaskProvider {
public reloadTasks(): void {
this._allTasks = [];

this._watchTasks = new Map<string, vscode.Task>();
this._buildTasks = new Map<string, vscode.Task>();
this._nodepsBuildTasks = new Map<string, vscode.Task>();
this._forceBuildTasks = new Map<string, vscode.Task>();
Expand Down Expand Up @@ -145,7 +139,6 @@ export class AutoprojProvider implements vscode.TaskProvider {
const updateConfig = this.isTaskEnabled(TaskType.Workspace, WorkspaceTaskMode.UpdateConfig);
const osdeps = this.isTaskEnabled(TaskType.Workspace, WorkspaceTaskMode.Osdeps);

this._addTask(ws.root, this._createWatchTask(`${ws.name}: Watch`, ws), this._watchTasks);
this._addTask(ws.root, this._createUpdateEnvironmentTask(`${ws.name}: Update Environment`, ws),
this._updateEnvironmentTasks);

Expand Down Expand Up @@ -243,15 +236,6 @@ export class AutoprojProvider implements vscode.TaskProvider {
return this._createWorkspaceTask(name, ws, "osdeps", defs, ["osdeps", "--color", ...args]);
}

private _createWatchTask(name, ws, defs = {}, args: string[] = []) {
const task = this._createWorkspaceTask(name, ws, "watch", defs, ["watch", "--show-events", ...args]);
task.isBackground = true;
task.presentationOptions = {
reveal: vscode.TaskRevealKind.Never,
};
return task;
}

// vscode currently does not support workspace and global tasks
// so we just use scope = vscode.workspaces.workspace[0] (this was the behavior of the,
// now deprecated constructor)
Expand Down

0 comments on commit f7cee0e

Please sign in to comment.