Skip to content

Command enhancement #101

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/src/builders/imd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ export class ImpactMarkdown {

this.targets.getResolvedObjects().forEach(ileObject => {

let logs = this.targets.logger.getLogsFor(ileObject.relativePath);
let logs = this.targets.logger.getLogsFor(ileObject.relativePath) || [];
let parents = this.targets.getTargets().filter(t => t.deps.some(d => d.systemName === ileObject.systemName && d.type === ileObject.type));
let children = this.targets.getTarget(ileObject)?.deps || [];

Expand Down
54 changes: 31 additions & 23 deletions cli/src/builders/make/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { iProject } from '../iProject';
export class MakeProject {
private noChildren: boolean = false;
private settings: iProject = new iProject();
private folderSettings: {[folder: string]: FolderOptions} = {};
private folderSettings: { [folder: string]: FolderOptions } = {};

constructor(private cwd: string, private targets: Targets) {
this.setupSettings();
Expand Down Expand Up @@ -207,32 +207,30 @@ export class MakeProject {
if (objects.length > 0) {
for (const ileObject of objects) {
if (ileObject.reference) continue;

// This is used when your object really has source

// This is used when your object really has source
const possibleTarget: ILEObjectTarget = this.targets.getTarget(ileObject) || (ileObject as ILEObjectTarget);
const customAttributes = this.getObjectAttributes(data, possibleTarget);

lines.push(...MakeProject.generateSpecificTarget(data, possibleTarget, customAttributes));
}

} else
if (data.sourceOptional) {
// This is usually used as a generic target.
lines.push(
`$(PREPATH)/%.${data.becomes}: ${data.targetSource ? asPosix(data.targetSource) : ``}`,
...(data.preCommands ? data.preCommands.map(cmd => `\t${cmd}`) : []),
...(data.command ?
[
`\tliblist -c $(BIN_LIB);\\`,
`\tliblist -a $(LIBL);\\`,
`\tsystem "${toCl(data.command, data.parameters)}"` // TODO: write the spool file somewhere?
]
: []
),
...(data.postCommands ? data.postCommands.map(cmd => `\t${cmd}`) : []),
);
}
if (data.sourceOptional) {
// This is usually used as a generic target.
lines.push(
`$(PREPATH)/%.${data.becomes}: ${data.targetSource ? asPosix(data.targetSource) : ``}`,
...(data.preCommands ? data.preCommands.map(cmd => `\t${cmd}`) : []),
...(data.command ?
[
`\tliblist -c $(BIN_LIB);\\`,
`\tliblist -a $(LIBL);\\`,
`\tsystem "${toCl(data.command, data.parameters)}"` // TODO: write the spool file somewhere?
]
: []
),
...(data.postCommands ? data.postCommands.map(cmd => `\t${cmd}`) : []),
);
}
}

lines.push(``);
Expand All @@ -249,6 +247,19 @@ export class MakeProject {
const qsysTempName: string | undefined = (parentName && parentName.length > 10 ? parentName.substring(0, 10) : parentName);

const resolve = (command: string) => {

if (ileObject.overrides) {
for (const [key, value] of Object.entries(ileObject.overrides)) {
const regex = new RegExp(`(${key.toUpperCase()}.*)\\$[\\*<]`, `g`);
const match = regex.exec(command);
if (match)
command = command.replace(regex, `$1${value}`);
else {
command = `${command} ${key.toUpperCase()}(${value})`;
}
}
}

command = command.replace(new RegExp(`\\*CURLIB`, `g`), `$(BIN_LIB)`);
command = command.replace(new RegExp(`\\$\\*`, `g`), ileObject.systemName);
command = command.replace(new RegExp(`\\$<`, `g`), asPosix(ileObject.relativePath));
Expand All @@ -263,13 +274,11 @@ export class MakeProject {
command = command.replace(new RegExp(`\\*${objType}S`, `g`), specificDeps.map(d => d.systemName).join(` `));
}
}

return command;
}

// TODO: resolve the parameters from the Rules.mk
const objectKey = `${ileObject.systemName}.${ileObject.type}`;

if (customAttributes) {
data.parameters = {
...data.parameters,
Expand All @@ -278,7 +287,6 @@ export class MakeProject {
}

let sourceFileCcsid = `*JOB`;

if (data.parameters.memberCcsid) {
sourceFileCcsid = data.parameters.memberCcsid;
delete data.parameters.memberCcsid;
Expand Down
123 changes: 100 additions & 23 deletions cli/src/targets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import fss from 'fs';
import Cache from "vscode-rpgle/language/models/cache";
import { IncludeStatement } from "vscode-rpgle/language/parserTypes";
import { infoOut, warningOut } from './cli';
import { DefinitionType, File, Module, CLParser } from 'vscode-clle/language';
import { DefinitionType, File, Module, CLParser, Token } from 'vscode-clle/language';
import { DisplayFile as dds } from "vscode-displayfile/src/dspf";
import Document from "vscode-db2i/src/language/sql/document";
import { ObjectRef, StatementType } from 'vscode-db2i/src/language/sql/types';
Expand Down Expand Up @@ -33,6 +33,10 @@ const sqlTypeExtension = {
const bindingDirectoryTarget: ILEObject = { systemName: `$(APP_BNDDIR)`, type: `BNDDIR` };

const TextRegex = /\%TEXT.*(?=\n|\*)/gm
const ParameterRegexs = [
/\%(PGM)(.*)(?=\n|\*)/gm,
/\%(VLDCKR)(.*)(?=\n|\*)/gm
];

export interface ILEObject {
systemName: string;
Expand All @@ -55,6 +59,7 @@ export interface ILEObject {

export interface ILEObjectTarget extends ILEObject {
deps: ILEObject[];
overrides?: CommandParameters;
}

export interface TargetSuggestions {
Expand All @@ -77,6 +82,11 @@ interface FileOptions {
text?: string;
}

interface CommandParameters {
pgm?: string;
vldckr?: string;
}

/**
* This class is responsible for storing all the targets
* and their dependencies. It also handles the parsing
Expand Down Expand Up @@ -337,6 +347,7 @@ export class Targets {
try {
[textMatch] = content.match(TextRegex);
if (textMatch) {
console.log(textMatch);
if (textMatch.startsWith(`%TEXT`)) textMatch = textMatch.substring(5);
if (textMatch.endsWith(`*`)) textMatch = textMatch.substring(0, textMatch.length - 1);
textMatch = textMatch.trim();
Expand Down Expand Up @@ -392,7 +403,7 @@ export class Targets {
this.createSrvPgmTarget(filePath, module, options);
}
else if (cmdExtensions.includes(ext)) {
this.createCmdTarget(filePath, options);
this.createCmdTarget(filePath, content, options);
}
} catch (e) {
this.logger.fileLog(relative, {
Expand All @@ -415,10 +426,53 @@ export class Targets {

}

private createCmdTarget(localPath, options: FileOptions = {}) {
this.resolvePathToObject(localPath, options.text);
private createCmdTarget(localPath: string, content: string, options: FileOptions = {}) {
const ileObject = this.resolvePathToObject(localPath, options.text);
const target: ILEObjectTarget = {
...ileObject,
deps: []
};
const params: CommandParameters = {};
const clDocs = new CLParser();
const tokens = clDocs.parseDocument(content);

ParameterRegexs.forEach((regex) => {
try {
const match = regex.exec(content);
if (match) {
console.log(`Parameter match: ${match}`);
params[match[1].toLowerCase()] = match[2].trim() ?? '';
}
} catch (e) { }
});

if (params?.pgm) {
const possibleChildObject = this.searchForObject({ systemName: params.pgm, type: `PGM` });
if (possibleChildObject) target.deps.push(this.createOrAppend(possibleChildObject));
};

// Since cmd source doesn't explicity contains deps, we resolve later on
if (params?.vldckr) {
const possibleChildObject = this.searchForObject({ systemName: params.vldckr, type: `PGM` });
if (possibleChildObject) target.deps.push(this.createOrAppend(possibleChildObject));
} else {
// Get program dependency from VLDCKR parameter in the command definition
tokens.forEach((token, index) => {
if (token.type === 'parameter' && token.value === 'VLDCKR') {
const firstBlockToken = tokens.slice(index + 1).find(token => token.type === 'block');
if (firstBlockToken) {
const wordToken = firstBlockToken.block.find(token => token.type === 'word');
if (wordToken && wordToken.value) {
const possibleChildObject = this.searchForObject({ systemName: wordToken.value, type: `PGM` });
if (possibleChildObject)
target.deps.push(this.createOrAppend(possibleChildObject));
}
}
}
});
}

target.overrides = params;
this.addNewTarget(target);
}

private createSrvPgmTarget(localPath: string, module: Module, options: FileOptions = {}) {
Expand Down Expand Up @@ -716,10 +770,6 @@ export class Targets {
}
});

// We also look to see if there is a `.cmd` object with the same name
const possibleCommandObject = this.searchForObject({ systemName: ileObject.systemName, type: `CMD` });
if (possibleCommandObject) this.createOrAppend(possibleCommandObject, target);

if (target.deps.length > 0)
infoOut(`Depends on: ${target.deps.map(d => `${d.systemName}.${d.type}`).join(` `)}`);

Expand Down Expand Up @@ -989,7 +1039,7 @@ export class Targets {

if (cache.includes && cache.includes.length > 0) {
ileObject.headers = [];

cache.includes.forEach((include: IncludeStatement) => {
// RPGLE includes are always returned as posix paths
// even on Windows. We need to do some magic to convert here for Windows systems
Expand Down Expand Up @@ -1054,6 +1104,9 @@ export class Targets {
deps: []
};

// const target = this.createOrAppend(ileObject);


// This usually means .pgm is in the name
if (ileObject.type === `PGM` && cache.keyword[`NOMAIN`]) {
const possibleName = pathDetail.name.toLowerCase().endsWith(`.pgm`) ? pathDetail.name.substring(0, pathDetail.name.length - 4) : pathDetail.name;
Expand Down Expand Up @@ -1301,15 +1354,14 @@ export class Targets {
});
}

// TODO: did we duplicate this?
// We also look to see if there is a `.cmd` object with the same name
const resolvedObject = this.searchForObject({ systemName: ileObject.systemName, type: `CMD` });
if (resolvedObject) this.createOrAppend(resolvedObject, target);

if (target.deps.length > 0)
infoOut(`Depends on: ${target.deps.map(d => `${d.systemName}.${d.type}`).join(` `)}`);

this.addNewTarget(target);
// For each target dep call createOrAppend
const currentTarget = this.createOrAppend(target);

// Merge dependencies into the current target
this.mergeDependencies(currentTarget, target.deps);
}

getTarget(object: ILEObject): ILEObjectTarget | undefined {
Expand Down Expand Up @@ -1450,15 +1502,20 @@ export class Targets {
...cmdObject,
deps: [programObject]
}

this.addNewTarget(newTarget);
} else {

this.removeObject(cmdObject);
this.logger.fileLog(cmdObject.relativePath, {
message: `Removed as target because no program was found with a matching name.`,
type: `info`
});
const commandObject = this.getTarget(cmdObject);
// Check if command object has no deps defined or deps count is 0
if (commandObject?.overrides?.pgm)
continue;
else {
// If no program is found, we remove the command object
this.removeObject(cmdObject);
this.logger.fileLog(cmdObject.relativePath, {
message: `Removed as target because no program was found with a matching name.`,
type: `info`
});
}
}
}
}
Expand Down Expand Up @@ -1515,6 +1572,26 @@ export class Targets {
return existingTarget;
}

public mergeDependencies(parentObject: ILEObject, deps: ILEObject[]) {
let existingTarget = this.targets[`${parentObject.systemName}.${parentObject.type}`];

if (!existingTarget) {
existingTarget = {
...parentObject,
deps: []
};

this.addNewTarget(existingTarget);
}

// We need to make sure we don't add duplicates
for (const dep of deps) {
if (!existingTarget.deps.some(d => d.systemName === dep.systemName && d.type === dep.type)) {
existingTarget.deps.push(dep);
}
}
}

private addNewTarget(dep: ILEObjectTarget) {
this.targets[`${dep.systemName}.${dep.type}`] = dep;
}
Expand Down
6 changes: 6 additions & 0 deletions cli/test/fixtures/company_system/qcmdsrc/mycmd.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* %PGM MYPGM */
/* %VLDCKR VMYCMD */
/* %TEXT Text of my command */

CMD PROMPT('My command')
PARM KWD(MODE) TYPE(*CHAR) LEN(50)
18 changes: 18 additions & 0 deletions cli/test/fixtures/company_system/qrpglesrc/vmycmd.pgm.rpgle
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
**free

ctl-opt dftactgrp(*no);

/INCLUDE 'qrpgleref/constants.rpgleinc'

dcl-s mytext char(50);

Dcl-PR printf Int(10) extproc('printf');
input Pointer value options(*string);
End-PR;

mytext = 'Hello to all you people';
printf(mytext);

dsply mytext;

return;
Loading
Loading