digital-ide/src/global/prjInfo.ts
2023-07-08 03:50:42 +08:00

566 lines
17 KiB
TypeScript

/* eslint-disable @typescript-eslint/naming-convention */
import * as vscode from 'vscode';
import * as fs from 'fs';
import * as fspath from 'path';
import { ToolChainType, LibraryState, XilinxIP,
validToolChainType, validXilinxIP, validLibraryState } from './enum';
import { PrjInfoSchema } from './propertySchema';
import assert = require('assert');
import * as hdlPath from '../hdlFs/path';
type AbsPath = string;
type RelPath = string;
type Path = AbsPath | RelPath;
type SimPath = {sim: Path};
type SrcPath = {src: Path};
type DataPath = {data: Path};
type OptionalPickType<T> = {[P in keyof T]?: T[P]};
type PrjInfoMeta = Record<keyof PrjInfoSchema, any>;
type RawPrjInfoMeta = OptionalPickType<PrjInfoMeta>;
const PrjInfoDefaults: PrjInfoMeta = {
get toolChain() {
return ToolChainType.Xilinx;
},
get prjName() {
return {
PL: 'template',
PS: 'template'
};
},
get IP_REPO() {
return [];
},
get soc() {
return {
core: '',
bd: '',
os: '',
app: ''
};
},
get enableShowLog() {
return false;
}
get device() {
return 'none';
},
get arch() {
return {
prjPath: '',
hardware: {
src: '',
sim: '',
data: ''
},
software: {
src: '',
data: ''
}
};
},
get library() {
return {
state: LibraryState.Unknown,
hardware: {
common: [],
custom: []
}
};
}
};
interface PrjName {
PL: RelPath
PS: RelPath
};
interface Arch {
prjPath: AbsPath
hardware: SrcPath & SimPath & DataPath
software: SrcPath & DataPath
};
interface Soc {
core: string
bd: string
os: string
app: string
};
interface Library {
state: LibraryState,
hardware: {
common: RelPath[],
custom: (RelPath | AbsPath)[]
}
}
interface RawPrjInfo extends RawPrjInfoMeta {
toolChain?: ToolChainType
prjName?: PrjName
IP_REPO?: XilinxIP[]
soc?: Soc
enableShowLog?: boolean
device?: string
arch?: Arch
library?: Library
};
function toSlash(path: Path): Path {
return path.replace(/\\/g,"\/");
}
function resolve(...paths: Path[]): AbsPath {
const absPath = fspath.resolve(...paths);
return toSlash(absPath);
}
function join(...paths: string[]): AbsPath {
const joinedPath = fspath.join(...paths);
return toSlash(joinedPath);
}
class PrjInfo implements PrjInfoMeta {
private _extensionPath: AbsPath = '';
private _workspacePath: AbsPath = '';
// toolChain is the tool chain used in the project
// which is supposed to support xilinx, intel, custom
private _toolChain: ToolChainType = PrjInfoDefaults.toolChain;
// project name, include pl and ps
private readonly _prjName: PrjName = PrjInfoDefaults.prjName;
private _IP_REPO: XilinxIP[] = PrjInfoDefaults.IP_REPO;
private readonly _soc: Soc = PrjInfoDefaults.soc;
private _enableShowLog: boolean = PrjInfoDefaults.enableShowLog;
private _device: string = PrjInfoDefaults.device;
// structure of the project, including path of source of hardware design, testBench
private readonly _arch: Arch = PrjInfoDefaults.arch;
// library to manage
private readonly _library: Library = PrjInfoDefaults.library;
public get toolChain(): ToolChainType {
return this._toolChain;
}
public get prjName(): PrjName {
return this._prjName;
}
public get arch(): Arch {
return this._arch;
}
public get library(): Library {
return this._library;
}
public get IP_REPO() : XilinxIP[] {
return this._IP_REPO;
}
public get soc(): Soc {
return this._soc;
}
public get enableShowLog(): boolean {
return this._enableShowLog;
}
public get device(): string {
return this._device;
}
public get INSIDE_BOOT_TYPE(): string {
return 'microphase';
}
/**
* replace token like ${workspace} in path
* @param path
*/
private replacePathToken(path: AbsPath): AbsPath {
const workspacePath = this._workspacePath;
assert(workspacePath);
this.setDefaultValue(this.prjName, 'PL', 'template');
this.setDefaultValue(this.prjName, 'PS', 'template');
const plname = this.prjName.PL;
const psname = this.prjName.PS;
// TODO : packaging the replacer
return path.replace(new RegExp('${workspace}', 'g'), workspacePath)
.replace(new RegExp('${plname}', 'g'), plname)
.replace(new RegExp('${psname}', 'g'), psname);
}
/**
* uniform a absolute path
* @param path
*/
public uniformisePath(path: AbsPath): AbsPath {
const slashPath = toSlash(path);
const replacedPath = this.replacePathToken(slashPath);
return replacedPath;
}
/**
* resolve path with workspacePath as root
* @param path
* @param check if true, check the existence of path
* @param root root of path, root and path will be joined
* @returns
*/
private resolvePath(path: Path, check: boolean = false, root?: AbsPath): AbsPath | undefined {
let uniformPath = '';
if (fspath.isAbsolute(path)) {
uniformPath = path;
} else {
const rootPath = root ? root : this._workspacePath;
uniformPath = fspath.resolve(rootPath, path);
}
uniformPath = toSlash(uniformPath);
if (check) {
if (fs.existsSync(uniformPath)) {
return uniformPath;
} else {
vscode.window.showErrorMessage('path ' + uniformPath + ' not exist!');
return undefined;
}
} else {
return uniformPath;
}
}
public updateToolChain(toolChain?: ToolChainType) {
if (toolChain) {
if (!validToolChainType(toolChain)) {
vscode.window.showErrorMessage('expect toolChain to be "xilinx", "intel", "custom"');
return;
}
this._toolChain = toolChain;
}
}
public updatePathWisely<T extends string>(obj: Record<T, AbsPath | AbsPath[]>,
attr: T,
path?: Path | Path[],
root?: AbsPath,
defaultPath: Path | Path[] = '') {
if (path) {
if (path instanceof Array) {
const actualPaths = [];
for (const p of path) {
const actualPath = this.resolvePath(p, true, root);
if (actualPath) {
actualPaths.push(actualPath);
}
}
obj[attr] = actualPaths;
} else {
const actualPath = this.resolvePath(path, true, root);
if (actualPath) {
obj[attr] = actualPath;
}
}
} else {
obj[attr] = defaultPath;
}
}
public updatePrjName(prjName?: PrjName) {
if (prjName) {
if (prjName.PL) {
this._prjName.PL = prjName.PL;
}
if (prjName.PS) {
this._prjName.PS = prjName.PS;
}
}
}
public updateIP_REPO(IP_REPO?: XilinxIP[]) {
if (IP_REPO) {
if (IP_REPO instanceof Array) {
const invalidIPs = IP_REPO.filter(ip => !validXilinxIP(ip));
if (invalidIPs.length > 0) {
vscode.window.showErrorMessage('detect invalid IPs:' + invalidIPs);
} else {
this._IP_REPO = IP_REPO;
}
} else {
vscode.window.showErrorMessage('expect IP_REPO to be list');
}
}
}
public updateSoc(soc?: Soc) {
if (soc) {
if (soc.core) {
this._soc.core = soc.core;
}
if (soc.bd) {
this._soc.bd = soc.bd;
}
if (soc.os) {
this._soc.os = soc.os;
}
if (soc.app) {
this._soc.app = soc.app;
}
}
}
public updateEnableShowLog(enableShowLog?: boolean) {
if (enableShowLog) {
this._enableShowLog = enableShowLog;
}
}
public updateDevice(device?: string) {
if (device) {
this._device = device;
}
}
/**
* assign defaultValue to obj[attr] if boolean of obj[attr] is false or 'none'
* @param obj
* @param attr
* @param defaultValue
*/
private setDefaultValue<T extends string, K>(obj: Record<T, K>,
attr: T,
defaultValue: K) {
const value: K = obj[attr];
let isNull = !Boolean(value);
if (typeof value === 'string') {
isNull ||= value === 'none';
}
if (isNull) {
obj[attr] = defaultValue;
}
}
private checkDirExist(dir: AbsPath) {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
}
public updateArch(arch?: Arch) {
const workspacePath = this._workspacePath;
if (arch) {
this.updatePathWisely(this.arch, 'prjPath', arch.prjPath);
if (arch.hardware) {
this.updatePathWisely(this.arch.hardware, 'src', arch.hardware.src);
this.updatePathWisely(this.arch.hardware, 'sim', arch.hardware.sim);
this.updatePathWisely(this.arch.hardware, 'data', arch.hardware.data);
}
if (arch.software) {
this.updatePathWisely(this.arch.software, 'src', arch.software.src);
this.updatePathWisely(this.arch.software, 'data', arch.software.data);
}
} else {
let hardwarePath: AbsPath = join(workspacePath, 'user');
let softwarePath: AbsPath = join(workspacePath, 'user', 'Software');
const socCore = this._soc.core;
if (socCore && socCore !== 'none') {
hardwarePath = join(hardwarePath, 'Hardware');
}
this.arch.prjPath = join(workspacePath, 'prj');
this.arch.hardware.src = join(hardwarePath, 'src');
this.arch.hardware.sim = join(hardwarePath, 'sim');
this.arch.hardware.data = join(hardwarePath, 'data');
this.arch.software.src = join(softwarePath, 'src');
this.arch.software.data = join(softwarePath, 'data');
}
// // if path is '', set as workspace
this.setDefaultValue(this.arch.hardware, 'src', workspacePath);
this.setDefaultValue(this.arch.hardware, 'sim', this.arch.hardware.src);
this.setDefaultValue(this.arch.hardware, 'data', workspacePath);
this.setDefaultValue(this.arch.software, 'src', workspacePath);
this.setDefaultValue(this.arch.software, 'data', workspacePath);
this.setDefaultValue(this.arch, 'prjPath', workspacePath);
// check existence
this.checkDirExist(this.arch.hardware.sim);
this.checkDirExist(this.arch.hardware.src);
this.checkDirExist(this.arch.hardware.data);
this.checkDirExist(this.arch.software.src);
this.checkDirExist(this.arch.software.data);
this.checkDirExist(this.arch.prjPath);
}
public updateLibrary(library?: Library) {
if (library) {
if (library.state) {
if (!validLibraryState(library.state)) {
vscode.window.showErrorMessage('expect library.state to be "local", "remote"');
this._library.state = LibraryState.Unknown;
} else {
this._library.state = library.state;
}
} else {
this._library.state = library.state;
}
if (library.hardware) {
const commonPath = this.libCommonPath;
const customPath = this.libCustomPath;
this.library.hardware.common = library.hardware.common ? library.hardware.common : [];
// this.updatePathWisely(this.library.hardware, 'common', library.hardware.common, commonPath, []);
this.updatePathWisely(this.library.hardware, 'custom', library.hardware.custom, customPath, []);
} else {
this._library.hardware = library.hardware;
}
} else {
this._library.hardware = PrjInfoDefaults.library.hardware;
this._library.state = PrjInfoDefaults.library.state;
}
}
public appendLibraryCommonPath(relPath: RelPath) {
this._library.hardware.common.push(relPath);
}
public appendLibraryCustomPath(relPath: RelPath) {
this._library.hardware.custom.push(relPath);
}
public getLibraryCommonPaths(absolute: boolean = true): Path[] {
if (absolute) {
const commonFolder = hdlPath.join(this.libCommonPath, 'Empty');
return this._library.hardware.common.map<Path>(relPath => hdlPath.rel2abs(commonFolder, relPath));
}
return this._library.hardware.common;
}
public getLibraryCustomPaths(absolute: boolean = true): Path[] {
const libCustomPath = this.libCustomPath;
if (libCustomPath === '') {
return [];
}
if (absolute) {
const configFolder = hdlPath.join(libCustomPath, 'Empty');
return this._library.hardware.custom.map<Path>(relPath => hdlPath.rel2abs(configFolder, relPath));
}
return this._library.hardware.custom;
}
/**
* merge the input uncomplete prjInfo into this
* cover the value that exist in rawPrjInfo recursively
* reserve the value that not covered in rawPrjInfo
* @param rawPrjInfo
*/
public merge(rawPrjInfo: RawPrjInfo) {
this.updateToolChain(rawPrjInfo.toolChain);
this.updatePrjName(rawPrjInfo.prjName);
this.updateIP_REPO(rawPrjInfo.IP_REPO);
this.updateSoc(rawPrjInfo.soc);
this.updateEnableShowLog(rawPrjInfo.enableShowLog);
this.updateDevice(rawPrjInfo.device);
this.updateArch(rawPrjInfo.arch);
this.updateLibrary(rawPrjInfo.library);
}
/**
* config init path in prjInfo
* @param extensionPath
* @param workspacePath
*/
public initContextPath(extensionPath: AbsPath, workspacePath: AbsPath) {
this._extensionPath = toSlash(extensionPath);
this._workspacePath = toSlash(workspacePath);
}
public get libCommonPath(): AbsPath {
const libPath = join(this._extensionPath, 'lib', 'common');
if (!fs.existsSync(libPath)) {
vscode.window.showErrorMessage('common lib path: "' + libPath + '" in extension is invalid, maybe extension has been corrupted, reinstall the extension');
}
return libPath;
}
public get libCustomPath(): AbsPath {
const libPath = vscode.workspace.getConfiguration().get('prj.lib.custom.path', this._workspacePath);
if (!fs.existsSync(libPath)) {
return '';
}
return libPath;
}
public get hardwareSimPath(): AbsPath {
const simPath = this._arch.hardware.sim;
if (fspath.isAbsolute(simPath)) {
return simPath;
}
const workspace = this._workspacePath;
return hdlPath.join(workspace, simPath);
}
public get hardwareSrcPath(): AbsPath {
const srcPath = this._arch.hardware.src;
if (fspath.isAbsolute(srcPath)) {
return srcPath;
}
const workspace = this._workspacePath;
return hdlPath.join(workspace, srcPath);
}
public json(): RawPrjInfo {
return {
toolChain: this._toolChain,
prjName: this._prjName,
IP_REPO: this._IP_REPO,
soc: this._soc,
enableShowLog: this._enableShowLog,
device: this._device,
arch: this._arch,
library: this._library,
};
}
};
export {
PrjInfo,
PrjInfoDefaults,
PrjName,
Arch,
Soc,
Library,
RawPrjInfo,
toSlash,
resolve,
SimPath,
SrcPath,
DataPath
};