Skip to content

Commit

Permalink
Implemented new class based architecture for environment #5
Browse files Browse the repository at this point in the history
Before, the whol environment was in one file with local variables...

The new structure has classes and static classes which each represent a single environment component. This makes the code more flexible for new features and additions to single components.

At the same time some components now offer more flexibility for changes in file namings and diretcory structures.

The extension logger was used in all new components #12

The stray logger entry on startup is also gone now #28.
  • Loading branch information
Radeonmann committed Jan 25, 2022
1 parent 02d84fe commit 75fe22d
Show file tree
Hide file tree
Showing 19 changed files with 938 additions and 766 deletions.
4 changes: 2 additions & 2 deletions src/BRCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/

import * as vscode from 'vscode';
import * as BREnvironment from './Environment/BREnvironment';
import { Environment } from './Environment/Environment';
import * as BRDialogs from './UI/BrDialogs';


Expand Down Expand Up @@ -32,7 +32,7 @@ function registerContributedCommands(context: vscode.ExtensionContext) {
context.subscriptions.push(disposable);
// Update configuration of installed AS versions from file system search
disposable = vscode.commands.registerCommand('vscode-brautomationtools.updateAvailableAutomationStudioVersions',
BREnvironment.updateAvailableAutomationStudioVersions);
Environment.automationStudio.updateVersions);
context.subscriptions.push(disposable);
}

Expand Down
4 changes: 2 additions & 2 deletions src/BrAsBuildTaskProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
import * as vscode from 'vscode';
import * as childProcess from 'child_process';
import * as BrAsProjectWorkspace from './BRAsProjectWorkspace';
import * as BrEnvironment from './Environment/BREnvironment';
import * as BrDialogs from './UI/BrDialogs';
import { logger } from './BrLog';
import { extensionConfiguration } from './BRConfiguration';
import { timeDiffString } from './Tools/Helpers';
import { Environment } from './Environment/Environment';


/**
Expand Down Expand Up @@ -297,7 +297,7 @@ class BrAsBuildTerminal implements vscode.Pseudoterminal {
this.done(43);
return;
}
const buildExe = await BrEnvironment.getBrAsBuilExe(asProject.asVersion);
const buildExe = (await Environment.automationStudio.getVersion(asProject.asVersion))?.buildExe.exePath;
if (!buildExe) {
this.writeLine(`ERROR: BR.AS.Build.exe not found for AS Version: ${asProject.asVersion}`);
this.done(44);
Expand Down
4 changes: 1 addition & 3 deletions src/BrAsTransferTaskProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ import * as uriTools from './Tools/UriTools';
import * as fileTools from './Tools/FileTools';
import * as Dialogs from './UI/Dialogs';
import * as BrAsProjectWorkspace from './BRAsProjectWorkspace';
import * as BrEnvironment from './Environment/BREnvironment';
import * as BrDialogs from './UI/BrDialogs';
import * as BrConfiguration from './BRConfiguration';
import { logger } from './BrLog';
import { Environment } from './Environment/Environment';

Expand Down Expand Up @@ -409,7 +407,7 @@ class BrPviTransferTerminal implements vscode.Pseudoterminal {
}
// Get PVITransfer.exe in highest version
// TODO Maybe start process in PviTransferExe.ts
const pviTransferExe = (await Environment.getPviVersion())?.pviTransfer.executable;
const pviTransferExe = (await Environment.pvi.getVersion())?.pviTransfer.exePath;
if (!pviTransferExe) {
this.writeLine(`ERROR: No PVI version found`);
this.done(70);
Expand Down
183 changes: 183 additions & 0 deletions src/Environment/AutomationStudioVersion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import * as vscode from 'vscode';
import * as uriTools from '../Tools/UriTools';
import * as semver from 'semver';
import { logger } from '../BrLog';
import { GccInstallation } from './GccInstallation';
import { BrAsBuildExe } from './BrAsBuildExe';

/**
* Representation of an Automation Studio version
*/
export class AutomationStudioVersion {

/**
* Gets all Automation Studio versions which are located in the installRoot.
* @param installRoot The root directory containing multiple Automation Studio installations. e.g. `C:\BrAutomation`
* @returns An array with all found versions
*/
public static async searchVersionsInDir(installRoot: vscode.Uri): Promise<AutomationStudioVersion[]> {
// Get matching subdirectories
const asDirRegExp = /^AS(\d)(\d+)$/;
const subDirs = await uriTools.listSubDirectories(installRoot, asDirRegExp);
// create AutomationStudioVersion from matching subdirectories
const versions: AutomationStudioVersion[] = [];
for (const dir of subDirs) {
const asVersion = await this.createFromDir(dir);
if (asVersion !== undefined) {
versions.push(asVersion);
}
}
// done
return versions;
}

/**
* Creates an Automation Studio version from a specified root directory
* @param asRoot The root directory containing a single Automation Studio installation. e.g. `C:\BrAutomation\AS410`
* @returns The version which was parsed from the root URI
*/
public static async createFromDir(asRoot: vscode.Uri): Promise<AutomationStudioVersion | undefined> {
// Create and initialize object
try {
const asVersion = new AutomationStudioVersion(asRoot);
await asVersion.#initialize();
logger.info(`Automation Studio Version V${asVersion.version.version} found in '${asVersion.rootPath.fsPath}'`);
return asVersion;
} catch (error) {
if (error instanceof Error) {
logger.error(`Failed to get Automation Studio in path '${asRoot.fsPath}': ${error.message}`);
} else {
logger.error(`Failed to get Automation Studio in path '${asRoot.fsPath}'`);
}
return undefined;
}
}

/** Object is not ready to use after constructor due to async operations,
* #initialize() has to be called for the object to be ready to use! */
private constructor(asRoot: vscode.Uri) {
this.#rootPath = asRoot;
// other properties rely on async and will be initialized in #initialize()
}

/** Async operations to finalize object construction */
async #initialize(): Promise<void> {
this.#version = await parseAutomationStudioVersion(this.#rootPath);
// Find BR.AS.Build.exe
this.#buildExe = await searchAutomationStudioBuildExe(this.#rootPath);
if (!this.#buildExe) {
throw new Error('Cannot find BR.AS.Build.exe');
}
// find gcc versions
const gccInstallRoot = vscode.Uri.joinPath(this.#rootPath, './AS/gnuinst');
this.#gccInstallation = await GccInstallation.searchAutomationStudioGnuinst(gccInstallRoot);
// init done
this.#isInitialized = true;
}
#isInitialized = false;

/** The root URI of the Automation Studio version */
public get rootPath(): vscode.Uri {
if (!this.#isInitialized) { throw new Error(`Use of not initialized ${AutomationStudioVersion.name} object`); }
return this.#rootPath;
}
#rootPath: vscode.Uri;

/** The Automation Studio version */
public get version(): semver.SemVer {
if (!this.#isInitialized || !this.#version) { throw new Error(`Use of not initialized ${AutomationStudioVersion.name} object`); }
return this.#version;
}
#version: semver.SemVer | undefined;

/** The Automation Studio project build tool */
public get buildExe(): BrAsBuildExe {
if (!this.#isInitialized || !this.#buildExe) { throw new Error(`Use of not initialized ${AutomationStudioVersion.name} object`); }
return this.#buildExe;
}
#buildExe: BrAsBuildExe | undefined;

/** gcc compiler versions available in this Automation Studio version */
public get gccInstallation(): GccInstallation {
if (!this.#isInitialized || !this.#gccInstallation) { throw new Error(`Use of not initialized ${AutomationStudioVersion.name} object`); }
return this.#gccInstallation;
}
#gccInstallation: GccInstallation | undefined;

/** toJSON required as getter properties are not shown in JSON.stringify() otherwise */
public toJSON(): any {
return {
rootPath: this.rootPath.toString(true),
version: this.version.version,
buildExe: this.buildExe,
gccInstallation: this.gccInstallation,
};
}
}

/**
* Trys to parse the version if an Automation Studio installation. The info is gathered from info files and the rootPath name.
* @param asRoot Root path of the AS installation. e.g. `C:\BrAutomation\AS410`
* @returns The parsed version, or V0.0.0 if parsing failed
*/
async function parseAutomationStudioVersion(asRoot: vscode.Uri): Promise<semver.SemVer> {
let version: semver.SemVer | undefined = undefined;
// Try to get version from ./BrSetup/VInfo/ProductInfo.ini
const prodInfoPath = uriTools.pathJoin(asRoot, 'BrSetup/VInfo/ProductInfo.ini');
try {
const prodInfoDoc = await vscode.workspace.openTextDocument(prodInfoPath);
const prodInfoText = prodInfoDoc.getText();
const versionRegex = /^Version=[\D]*([\d.]*)[\D]*$/gm;
const versionMatch = versionRegex.exec(prodInfoText);
if (versionMatch) {
version = semver.coerce(versionMatch[0]) ?? undefined;
}
} catch (error) {
// no reaction required
}
if (version) {
return version;
} else {
logger.warning(`Failed to get AS Version from '${prodInfoPath.toString(true)}'. Will try to parse from directory name`);
}
// Try parse version from root directory name if get from file failed
const dirName = uriTools.pathBasename(asRoot);
const asDirRegExp = /^AS(\d)(\d+)$/;
const match = asDirRegExp.exec(dirName);
if (match && match.length >= 3) {
version = semver.coerce(`${match![1]}.${match![2]}.0`) ?? undefined;
}
if (version) {
return version;
} else {
logger.warning(`Failed to parse AS Version from directory name '${asRoot.toString(true)}'. AS will be listed as V0.0.0`);
}
// set to V0.0.0 as backup, so AS is still available but with wrong version...
return new semver.SemVer('0.0.0');
}

/**
* Search for Br.As.Build.exe in the Automation Studio installation
* @param asRoot Root path of the AS installation. e.g. `C:\BrAutomation\AS410`
* @returns The first found Br.As.Build.exe, or undefined if no such was found.
*/
async function searchAutomationStudioBuildExe(asRoot: vscode.Uri): Promise<BrAsBuildExe | undefined> {
// english
const buildExeUriEn = uriTools.pathJoin(asRoot, 'Bin-en/BR.AS.Build.exe');
if (await uriTools.exists(buildExeUriEn)) {
return new BrAsBuildExe(buildExeUriEn);
}
// german
const buildExeUriDe = uriTools.pathJoin(asRoot, 'Bin-de/BR.AS.Build.exe');
if (await uriTools.exists(buildExeUriDe)) {
return new BrAsBuildExe(buildExeUriDe);
}
// slower search if none was found yet
const searchPattern = new vscode.RelativePattern(asRoot, '**/BR.AS.Build.exe');
const searchResult = await vscode.workspace.findFiles(searchPattern);
if (searchResult.length > 0) {
return new BrAsBuildExe(searchResult[0]);
}
// none was found
return undefined;
}
Loading

0 comments on commit 75fe22d

Please sign in to comment.