From 6be3e45aed61f54e902bd2bc6135e3e15ad231f5 Mon Sep 17 00:00:00 2001 From: worksofliam Date: Mon, 21 Jul 2025 11:55:13 -0400 Subject: [PATCH 01/12] Add logic to handle partial builds in makefile Signed-off-by: worksofliam --- cli/src/builders/make/index.ts | 11 +++++++++-- cli/src/targets/index.ts | 29 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/cli/src/builders/make/index.ts b/cli/src/builders/make/index.ts index 4076f19..1c09805 100644 --- a/cli/src/builders/make/index.ts +++ b/cli/src/builders/make/index.ts @@ -188,7 +188,7 @@ export class MakeProject { ``, ...this.generateTargets(specificObjects), ``, - ...this.generateGenericRules() + ...this.generateGenericRules(specificObjects) ]; } @@ -275,9 +275,12 @@ export class MakeProject { return lines; } - public generateGenericRules(): string[] { + public generateGenericRules(partialBuild?: ILEObject[]): string[] { let lines = []; + // If this is a partial build, we only want to generate rules for the specific objects + const filterObjects: ILEObject[]|undefined = partialBuild ? this.targets.getRequiredObjects(partialBuild) : undefined; + for (const entry of Object.entries(this.settings.compiles)) { let [type, data] = entry; @@ -320,6 +323,10 @@ export class MakeProject { if (objects.length > 0) { for (const ileObject of objects) { if (ileObject.reference) continue; + + if (filterObjects && !filterObjects.some(o => o.systemName === ileObject.systemName && o.type === ileObject.type)) { + continue; // Skip this object + } // This is used when your object really has source diff --git a/cli/src/targets/index.ts b/cli/src/targets/index.ts index 0f5a47f..a01bf2b 100644 --- a/cli/src/targets/index.ts +++ b/cli/src/targets/index.ts @@ -696,6 +696,35 @@ export class Targets { return currentItem; } + /** + * Returns a list of all the required objects to build this target + */ + public getRequiredObjects(bases: (ILEObject|ILEObjectTarget)[]) { + let deps: ILEObject[]; + + const addDep = (dep: ILEObject|ILEObjectTarget) => { + if (deps.some(s => s.systemName === dep.systemName && s.type === dep.type)) return; // Already added + if (dep.reference) return; // Skip references + + if (`deps` in dep) { + if (dep.deps && dep.deps.length > 0) { + for (const cDep of dep.deps) { + const d = this.getTarget(cDep) || cDep; + addDep(d); + } + } + } + + deps.push(dep); + } + + for (const required of bases) { + addDep(required); + } + + return deps; + } + /** * This is used when loading in all objects. * SQL sources can have two object names: a system name and a long name From 844680173c93638d00e626a582e5a7ef819256b1 Mon Sep 17 00:00:00 2001 From: worksofliam Date: Mon, 21 Jul 2025 12:39:03 -0400 Subject: [PATCH 02/12] Add tests for getRequiredObjects Signed-off-by: worksofliam --- cli/src/targets/index.ts | 2 +- cli/test/cs_srvpgm.test.ts | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/cli/src/targets/index.ts b/cli/src/targets/index.ts index a01bf2b..1b53b87 100644 --- a/cli/src/targets/index.ts +++ b/cli/src/targets/index.ts @@ -700,7 +700,7 @@ export class Targets { * Returns a list of all the required objects to build this target */ public getRequiredObjects(bases: (ILEObject|ILEObjectTarget)[]) { - let deps: ILEObject[]; + let deps: ILEObject[] = []; const addDep = (dep: ILEObject|ILEObjectTarget) => { if (deps.some(s => s.systemName === dep.systemName && s.type === dep.type)) return; // Already added diff --git a/cli/test/cs_srvpgm.test.ts b/cli/test/cs_srvpgm.test.ts index b240861..4e92053 100644 --- a/cli/test/cs_srvpgm.test.ts +++ b/cli/test/cs_srvpgm.test.ts @@ -51,6 +51,12 @@ describe(`pseudo tests`, () => { expect(empdet.deps.find(f => f.systemName === `EMPLOYEE`)).toBeDefined(); expect(empdet.deps.find(f => f.systemName === `DEPARTMENT`)).toBeDefined(); + const allRequirements = targets.getRequiredObjects([empdet]); + expect(allRequirements.length).toBe(3); + expect(allRequirements.find(f => f.systemName === `EMPLOYEE` && f.type === `FILE`)).toBeDefined(); + expect(allRequirements.find(f => f.systemName === `DEPARTMENT` && f.type === `FILE`)).toBeDefined(); + expect(allRequirements.find(f => f.systemName === `EMPDET` && f.type === `MODULE`)).toBeDefined(); + const employees = targets.getTarget({systemName: `EMPLOYEES`, type: `PGM`}); expect(employees).toBeDefined(); @@ -59,6 +65,16 @@ describe(`pseudo tests`, () => { expect(employees.deps.find(f => f.systemName === `EMPDET` && f.type === `MODULE`)).toBeDefined(); expect(employees.deps.find(f => f.systemName === `EMPS` && f.type === `FILE`)).toBeDefined(); expect(employees.deps.find(f => f.systemName === `EMPLOYEE` && f.type === `FILE`)).toBeDefined(); + + const requiredForEmployees = targets.getRequiredObjects([employees]); + expect(requiredForEmployees.length).toBe(6); + + expect(requiredForEmployees.find(f => f.systemName === `EMPLOYEES` && f.type === `PGM`)).toBeDefined(); + expect(requiredForEmployees.find(f => f.systemName === `EMPDET` && f.type === `MODULE`)).toBeDefined(); + expect(requiredForEmployees.find(f => f.systemName === `EMPLOYEE` && f.type === `FILE`)).toBeDefined(); + expect(requiredForEmployees.find(f => f.systemName === `DEPARTMENT` && f.type === `FILE`)).toBeDefined(); + expect(requiredForEmployees.find(f => f.systemName === `EMPS` && f.type === `FILE`)).toBeDefined(); + expect(requiredForEmployees.find(f => f.systemName === `EMPDET` && f.type === `MODULE`)).toBeDefined }); test('ibmi-bob rules', () => { From 69d645ad08ec24a8fac60cbec7195a678a854e9d Mon Sep 17 00:00:00 2001 From: worksofliam Date: Mon, 21 Jul 2025 13:20:14 -0400 Subject: [PATCH 03/12] Better logic for handling partial builds when building a makefile Signed-off-by: worksofliam --- cli/src/builders/make/index.ts | 67 ++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 12 deletions(-) diff --git a/cli/src/builders/make/index.ts b/cli/src/builders/make/index.ts index 1c09805..d98893d 100644 --- a/cli/src/builders/make/index.ts +++ b/cli/src/builders/make/index.ts @@ -3,7 +3,7 @@ import path from 'path'; import { ILEObject, ILEObjectTarget, ImpactedObject, ObjectType, Targets } from '../../targets'; import { asPosix, fromCl, getFiles, toCl } from '../../utils'; import { warningOut } from '../../cli'; -import { name } from '../../../webpack.config'; +import { name, target } from '../../../webpack.config'; import { FolderOptions, getFolderOptions } from './folderSettings'; import { readAllRules } from './customRules'; import { CompileData, CommandParameters, getTrueBasename } from '../environment'; @@ -21,7 +21,7 @@ interface Step { } export class MakeProject { - private noChildren: boolean = false; + private partialWithImpacts: boolean = false; private settings: iProject = new iProject(); private projectActions: ProjectActions; private actionsEnabled: boolean = false; @@ -32,8 +32,8 @@ export class MakeProject { this.projectActions = new ProjectActions(this.targets, this.rfs); } - public setNoChildrenInBuild(noChildren: boolean) { - this.noChildren = noChildren; + public setPartialWithImpacts(partialWithImpacts: boolean) { + this.partialWithImpacts = partialWithImpacts; } public useActions() { @@ -217,17 +217,18 @@ export class MakeProject { public generateTargets(partialBuild?: ILEObject[]): string[] { let lines = []; + let allParents: ILEObject[]|undefined; + // A 'partial build' means we only want to build specific objects // and we also want to build their parents too. We update `partialBuild` // to include all the parents of the specific objects. if (partialBuild) { + allParents = []; const impacts = partialBuild.map(o => this.targets.getImpactFor(o)); - let allAffected: ILEObject[] = []; - const addImpact = (impactedObj: ImpactedObject) => { - if (!allAffected.some(o => o.systemName === impactedObj.ileObject.systemName && o.type === impactedObj.ileObject.type)) { - allAffected.push(impactedObj.ileObject); + if (!allParents.some(o => o.systemName === impactedObj.ileObject.systemName && o.type === impactedObj.ileObject.type)) { + allParents.push(impactedObj.ileObject); } impactedObj.children.forEach(child => addImpact(child)); @@ -235,9 +236,14 @@ export class MakeProject { impacts.forEach(impact => addImpact(impact)); - partialBuild = allAffected; + if (this.partialWithImpacts) { + // If we want to include the impacts, we need to add them to the partialBuild + partialBuild = allParents; + } } + let allRequirements: ILEObject[]|undefined = partialBuild ? this.targets.getRequiredObjects(partialBuild) : undefined; + const all = partialBuild || [ ...(this.targets.binderRequired() ? [this.targets.getBinderTarget()] : []), ...this.targets.getTargetsOfType(`PGM`), @@ -251,7 +257,19 @@ export class MakeProject { ) } - if (!this.noChildren) { + if (partialBuild) { + // If we don't want the children to get built, we only generate the targets for the specific objects + for (const obj of allRequirements || []) { + if (obj.reference) continue; // Skip references + + const target = this.targets.getTarget(obj); + if (target && target.deps && target.deps.length > 0) { + lines.push( + `$(PREPATH)/${target.systemName}.${target.type}: ${target.deps.filter(d => d.reference !== true).map(dep => `$(PREPATH)/${dep.systemName}.${dep.type}`).join(` `)}` + ) + } + } + } else { // If we don't want the children to get built, we don't generate the dependency targets for (const target of this.targets.getTargets()) { if (target && target.deps.length > 0) { @@ -279,7 +297,32 @@ export class MakeProject { let lines = []; // If this is a partial build, we only want to generate rules for the specific objects - const filterObjects: ILEObject[]|undefined = partialBuild ? this.targets.getRequiredObjects(partialBuild) : undefined; + let allParents: ILEObject[]|undefined; + + // A 'partial build' means we only want to build specific objects + // and we also want to build their parents too. We update `partialBuild` + // to include all the parents of the specific objects. + if (partialBuild) { + allParents = []; + const impacts = partialBuild.map(o => this.targets.getImpactFor(o)); + + const addImpact = (impactedObj: ImpactedObject) => { + if (!allParents.some(o => o.systemName === impactedObj.ileObject.systemName && o.type === impactedObj.ileObject.type)) { + allParents.push(impactedObj.ileObject); + } + + impactedObj.children.forEach(child => addImpact(child)); + } + + impacts.forEach(impact => addImpact(impact)); + + if (this.partialWithImpacts) { + // If we want to include the impacts, we need to add them to the partialBuild + partialBuild = allParents; + } + } + + let allRequirements: ILEObject[]|undefined = partialBuild ? this.targets.getRequiredObjects(partialBuild) : undefined; for (const entry of Object.entries(this.settings.compiles)) { let [type, data] = entry; @@ -324,7 +367,7 @@ export class MakeProject { for (const ileObject of objects) { if (ileObject.reference) continue; - if (filterObjects && !filterObjects.some(o => o.systemName === ileObject.systemName && o.type === ileObject.type)) { + if (allRequirements && !allRequirements.some(o => o.systemName === ileObject.systemName && o.type === ileObject.type)) { continue; // Skip this object } From e8f55d64cb5db0e2791e2d4c9a6e05f7543336f7 Mon Sep 17 00:00:00 2001 From: worksofliam Date: Mon, 21 Jul 2025 13:20:18 -0400 Subject: [PATCH 04/12] Test cases Signed-off-by: worksofliam --- cli/test/make.test.ts | 45 ++++++++++++++++++++++++++++++++++++++- cli/test/project.test.ts | 13 +++++------ cli/test/project2.test.ts | 17 +++++++++------ 3 files changed, 61 insertions(+), 14 deletions(-) diff --git a/cli/test/make.test.ts b/cli/test/make.test.ts index 15ee53b..027b582 100644 --- a/cli/test/make.test.ts +++ b/cli/test/make.test.ts @@ -181,4 +181,47 @@ test(`Multi-module program and service program`, async () => { `\tsystem "CRTSRVPGM SRVPGM($(BIN_LIB)/UTILS) MODULE(JWTHANDLER VALIDATE) SRCSTMF('qsrvsrc/utils.binder') BNDDIR($(BNDDIR))" > .logs/utils.splf`, '\t-system -q "ADDBNDDIRE BNDDIR($(BIN_LIB)/$(APP_BNDDIR)) OBJ((*LIBL/UTILS *SRVPGM *IMMED))"' ].join()); -}) \ No newline at end of file +}); + +test('generateTargets (post-resolve)', async () => { + const targets = await baseTargets(true); + + targets.resolveBinder(); + + const project = new MakeProject(cwd, targets, new ReadFileSystem()); + + const srvpgma = targets.getTarget({systemName: `SRVPGMA`, type: `SRVPGM`}); + const srvpgmaRequirements = targets.getRequiredObjects([srvpgma]); + expect(srvpgmaRequirements.length).toBe(3); + expect(srvpgmaRequirements.map(r => r.systemName)).toEqual([ + `FILEB`, + `MODULEB`, + `SRVPGMA` + ]); + + const targetContent = project.generateTargets([srvpgma]); + + console.log(targetContent.join('\n')); + + expect(targetContent).toEqual( + [ + 'all: .logs .evfevent library $(PREPATH)/SRVPGMA.SRVPGM', + '', + '$(PREPATH)/MODULEB.MODULE: $(PREPATH)/FILEB.FILE', + `$(PREPATH)/SRVPGMA.SRVPGM: $(PREPATH)/MODULEB.MODULE`, + ``, + `.logs:`, + `\tmkdir .logs`, + `.evfevent:`, + `\tmkdir .evfevent`, + `library:`, + `\t-system -q "CRTLIB LIB($(BIN_LIB))"`, + ] + ); + + const rules = project.generateGenericRules([srvpgma]); + expect(rules).toContain(`$(PREPATH)/MODULEB.MODULE: qrpglesrc/moduleB.sqlrpgle`); + expect(rules).toContain(`$(PREPATH)/SRVPGMA.SRVPGM: qsrvsrc/srvpgmA.bnd`); + expect(rules).toContain(`$(PREPATH)/FILEB.FILE: qddssrc/fileB.pf`); + expect(rules.length).toBe(39); +}); \ No newline at end of file diff --git a/cli/test/project.test.ts b/cli/test/project.test.ts index 1a6129a..a00d09a 100644 --- a/cli/test/project.test.ts +++ b/cli/test/project.test.ts @@ -308,6 +308,8 @@ describe(`company_system tests`, () => { const makeProject = new MakeProject(project.cwd, targets, fs); await makeProject.setupSettings(); + makeProject.setPartialWithImpacts(true); + const deptsFile = targets.getTarget({systemName: `DEPTS`, type: `FILE`}); // Generate targets on it's own will have BNDDIR, PGM, etc @@ -323,10 +325,12 @@ describe(`company_system tests`, () => { const makeProject = new MakeProject(project.cwd, targets, fs); await makeProject.setupSettings(); - const deptsFile = targets.getTarget({systemName: `EMPLOYEE`, type: `FILE`}); + makeProject.setPartialWithImpacts(true); + + const empFile = targets.getTarget({systemName: `EMPLOYEE`, type: `FILE`}); // Generate targets on it's own will have BNDDIR, PGM, etc - const headerContent = makeProject.generateTargets([deptsFile]); + const headerContent = makeProject.generateTargets([empFile]); const allTarget = headerContent.find(l => l.startsWith(`all:`)); expect(allTarget).toBeDefined(); @@ -352,7 +356,7 @@ describe(`company_system tests`, () => { const makeProject = new MakeProject(project.cwd, targets, fs); await makeProject.setupSettings(); - makeProject.setNoChildrenInBuild(true); + makeProject.setPartialWithImpacts(true); const deptsFile = targets.getTarget({systemName: `EMPLOYEE`, type: `FILE`}); @@ -372,9 +376,6 @@ describe(`company_system tests`, () => { expect(allTargets).toContain(`$(PREPATH)/DEPTS.PGM`); expect(allTargets).toContain(`$(PREPATH)/SHOWEMPS.PGM`); expect(allTargets).toContain(`$(PREPATH)/GETTOTSAL.SRVPGM`); - - const deptsTargetDeps = headerContent.find(l => l.startsWith(`$(PREPATH)/DEPTS.PGM:`)); - expect(deptsTargetDeps).toBeUndefined(); }); test(`Impact of EMPLOYEES`, () => { diff --git a/cli/test/project2.test.ts b/cli/test/project2.test.ts index c296e7b..dfbd5a0 100644 --- a/cli/test/project2.test.ts +++ b/cli/test/project2.test.ts @@ -302,6 +302,8 @@ describe(`company_system tests`, () => { const makeProject = new MakeProject(project.cwd, targets, fs); await makeProject.setupSettings(); + makeProject.setPartialWithImpacts(true); + const deptsFile = targets.getTarget({systemName: `DEPTS`, type: `FILE`}); // Generate targets on it's own will have BNDDIR, PGM, etc @@ -317,6 +319,8 @@ describe(`company_system tests`, () => { const makeProject = new MakeProject(project.cwd, targets, fs); await makeProject.setupSettings(); + makeProject.setPartialWithImpacts(true); + const deptsFile = targets.getTarget({systemName: `EMPLOYEE`, type: `FILE`}); // Generate targets on it's own will have BNDDIR, PGM, etc @@ -346,18 +350,18 @@ describe(`company_system tests`, () => { const makeProject = new MakeProject(project.cwd, targets, fs); await makeProject.setupSettings(); - makeProject.setNoChildrenInBuild(true); + makeProject.setPartialWithImpacts(true); - const deptsFile = targets.getTarget({systemName: `EMPLOYEE`, type: `FILE`}); + const empFile = targets.getTarget({systemName: `EMPLOYEE`, type: `FILE`}); // Generate targets on it's own will have BNDDIR, PGM, etc - const headerContent = makeProject.generateTargets([deptsFile]); + const headerContent = makeProject.generateTargets([empFile]); const allTarget = headerContent.find(l => l.startsWith(`all: `)); expect(allTarget).toBeDefined(); const allTargets = allTarget.substring(5).split(` `); - expect(allTargets.length).toBe(8); + console.log(allTargets); expect(allTargets[0]).toBe(`.logs`); expect(allTargets[1]).toBe(`.evfevent`); expect(allTargets[2]).toBe(`library`); @@ -366,9 +370,8 @@ describe(`company_system tests`, () => { expect(allTargets).toContain(`$(PREPATH)/DEPTS.PGM`); expect(allTargets).toContain(`$(PREPATH)/SHOWEMPS.PGM`); expect(allTargets).toContain(`$(PREPATH)/GETTOTSAL.SRVPGM`); - - const deptsTargetDeps = headerContent.find(l => l.startsWith(`$(PREPATH)/DEPTS.PGM:`)); - expect(deptsTargetDeps).toBeUndefined(); + + console.log(headerContent.join(`\n`)); }); test(`Impact of EMPLOYEES`, () => { From a801b58c618bd07cdcee9a72b1dc8b8dc2f37e8e Mon Sep 17 00:00:00 2001 From: worksofliam Date: Mon, 21 Jul 2025 13:31:45 -0400 Subject: [PATCH 05/12] Fix bug in getRequiredObjects where targets were missing Signed-off-by: worksofliam --- cli/src/targets/index.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cli/src/targets/index.ts b/cli/src/targets/index.ts index 1b53b87..bf3238c 100644 --- a/cli/src/targets/index.ts +++ b/cli/src/targets/index.ts @@ -706,9 +706,10 @@ export class Targets { if (deps.some(s => s.systemName === dep.systemName && s.type === dep.type)) return; // Already added if (dep.reference) return; // Skip references - if (`deps` in dep) { - if (dep.deps && dep.deps.length > 0) { - for (const cDep of dep.deps) { + const possibleTarget = ('deps' in dep ? dep : this.getTarget(dep)); + if (possibleTarget) { + if (possibleTarget.deps && possibleTarget.deps.length > 0) { + for (const cDep of possibleTarget.deps) { const d = this.getTarget(cDep) || cDep; addDep(d); } From 0a7bc47b275a438e49df9211733ab5cff47e1d78 Mon Sep 17 00:00:00 2001 From: worksofliam Date: Mon, 21 Jul 2025 13:32:00 -0400 Subject: [PATCH 06/12] CLI to support with parents mode in makefile generation Signed-off-by: worksofliam --- cli/.vscode/launch.json | 2 +- cli/src/cli.ts | 2 +- cli/src/index.ts | 15 ++++++++++----- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/cli/.vscode/launch.json b/cli/.vscode/launch.json index fe34933..0d6bd59 100644 --- a/cli/.vscode/launch.json +++ b/cli/.vscode/launch.json @@ -24,7 +24,7 @@ "cwd": "${workspaceFolder:cli}", "program": "${workspaceFolder:cli}/dist/index.js", "sourceMaps": true, - "args": ["-d", "/Users/barry/Repos/ibmi-company_system", "--verbose", "-bf", "make"], + "args": ["-d", "/Users/barry/Repos/ibmi-company_system", "--verbose", "-bf", "make", "-f", "qrpglesrc/employees.pgm.sqlrpgle"], "preLaunchTask": { "type": "npm", "script": "webpack:dev" diff --git a/cli/src/cli.ts b/cli/src/cli.ts index 5a3cbba..bcd4321 100644 --- a/cli/src/cli.ts +++ b/cli/src/cli.ts @@ -11,7 +11,7 @@ export let cliSettings = { fileList: false, lookupFiles: [] as string[], userBranch: ``, - makeFileNoChildren: false, + makefileWithParents: false, assumeSourcesArePrograms: false, }; diff --git a/cli/src/index.ts b/cli/src/index.ts index e0a22f7..d4cef6d 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -64,7 +64,12 @@ async function main() { case '-nc': case '--no-children': - cliSettings.makeFileNoChildren = true; + warningOut(`--no-children is deprecated and is default when doing partial builds.`); + break; + + case `-wp`: + case `--with-parents`: + cliSettings.makefileWithParents = true; break; case '-ap': @@ -127,9 +132,9 @@ async function main() { console.log(``); console.log(`Options specific to '-bf make':`); console.log(``); - console.log(`\t-nc`); - console.log(`\t--no-children\tUsed with '-bf make' and won't include children of`); - console.log(`\t\t\tobjects in the makefile. Useful in conjuction with '-f'.`); + console.log(`\t-wp`); + console.log(`\t--with-parents\tUsed with '-bf make' and will add parents of`); + console.log(`\t\t\tobjects being partially built to the makefile.`); console.log(``); process.exit(0); break; @@ -232,7 +237,7 @@ async function main() { await makeProj.setupSettings(); - makeProj.setNoChildrenInBuild(cliSettings.makeFileNoChildren); + makeProj.setPartialWithImpacts(cliSettings.makefileWithParents); let specificObjects: ILEObject[] | undefined = cliSettings.fileList ? cliSettings.lookupFiles.map(f => targets.getResolvedObject(path.join(cwd, f))).filter(o => o) : undefined; writeFileSync(path.join(cwd, `makefile`), makeProj.getMakefile(specificObjects).join(`\n`)); From abd7c73cea898867455d715221140ea5bf2509db Mon Sep 17 00:00:00 2001 From: worksofliam Date: Mon, 21 Jul 2025 14:54:12 -0400 Subject: [PATCH 07/12] Update docs regarding parents Signed-off-by: worksofliam --- docs/pages/cli/make.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/pages/cli/make.md b/docs/pages/cli/make.md index b78373d..f35c290 100644 --- a/docs/pages/cli/make.md +++ b/docs/pages/cli/make.md @@ -8,6 +8,13 @@ This section only applies if you use `-bf make`. Using `so -bf make` will generate a makefile to rebuild the entire project. This is fine when deploying to new systems. It is also possible to do incremental builds. +To do an incremental build, also referred to as a partial build, you can specify the targets you want to build: + +```bash +so -bf make -f 'qrpglesrc/ord500.pgm.rpgle' +so -bf make -l `git diff --name-only origin/main origin/${GITHUB_HEAD_REF}` +``` + A incremental build means building a specific targets, their parents and optionally their children. Let's assume this is our dependency tree: ``` @@ -22,7 +29,9 @@ Next, assume that we want to do a incremental build of `ORD501.PGM`, which has * one parent: `ORD500.PGM` * two children: `DEPARTMENT.FILE` and `DEPTS.FILE` -So that means that 4 objects are going to be rebuilt. Usually, parents always need to be rebuilt to ensure level checking happens. Sometimes, we don't want to rebuild the children because they haven't changed (and can depend on the library list to find the existing objects). **You can use option `-nc` to ensure no target children get built** as part of the make file. +So that means that 3 objects are going to be rebuilt. Sometimes, we don't want to rebuild the children because they haven't changed (and can depend on the library list to find the existing objects). + +Usually, parents always need to be rebuilt to ensure level checking happens. If you use the `-wp` (with-parents) options, then the `makefile` will also include targets to rebuild the parent objects too (`ORD500`), but the `all` target will only build the specified target (`ORD501`). ### When is a incremental build right? From d3120ee3a4136868e7f4d6a48cbd0ea844ba84be Mon Sep 17 00:00:00 2001 From: worksofliam Date: Wed, 23 Jul 2025 10:51:16 -0400 Subject: [PATCH 08/12] Improvement to API to allow granular partial builds Signed-off-by: worksofliam --- cli/src/builders/make/index.ts | 91 +++++++++++++++++++--------------- cli/test/make.test.ts | 4 ++ cli/test/project.test.ts | 11 ++-- cli/test/project2.test.ts | 17 +++---- 4 files changed, 64 insertions(+), 59 deletions(-) diff --git a/cli/src/builders/make/index.ts b/cli/src/builders/make/index.ts index d98893d..6340ce6 100644 --- a/cli/src/builders/make/index.ts +++ b/cli/src/builders/make/index.ts @@ -20,8 +20,19 @@ interface Step { command: string; } +/** + * parents: this property controls the all target. It will include all the parents of partial build objects. + * partial: if this property is true, the makefile will only include targets for the partial build objects (and optionally their parents) + */ +type PartialOptions = { partial: boolean, parents: boolean }; + +interface PartialTargets { + partial: ILEObject[]; + children: ILEObject[]; +} + export class MakeProject { - private partialWithImpacts: boolean = false; + private partialOptions: PartialOptions = { partial: false, parents: false }; private settings: iProject = new iProject(); private projectActions: ProjectActions; private actionsEnabled: boolean = false; @@ -32,8 +43,8 @@ export class MakeProject { this.projectActions = new ProjectActions(this.targets, this.rfs); } - public setPartialWithImpacts(partialWithImpacts: boolean) { - this.partialWithImpacts = partialWithImpacts; + public setPartialOptions(options: PartialOptions) { + this.partialOptions = options; } public useActions() { @@ -214,15 +225,21 @@ export class MakeProject { ]; } - public generateTargets(partialBuild?: ILEObject[]): string[] { - let lines = []; + /** + * Used to return the objects required to do a partial build. + * If `partial` is true, it will return the object and all objects depending on it recursively. + * If `parents` is true, it will return all parent objects of the partial build objects, and their children/ + */ + private getPartialTargets(partialBuild?: ILEObject[]): PartialTargets|undefined { + if (!partialBuild) { + return; + } let allParents: ILEObject[]|undefined; - // A 'partial build' means we only want to build specific objects - // and we also want to build their parents too. We update `partialBuild` + // we also want to build their parents too. We update `partialBuild` // to include all the parents of the specific objects. - if (partialBuild) { + if (this.partialOptions.parents) { allParents = []; const impacts = partialBuild.map(o => this.targets.getImpactFor(o)); @@ -236,14 +253,28 @@ export class MakeProject { impacts.forEach(impact => addImpact(impact)); - if (this.partialWithImpacts) { - // If we want to include the impacts, we need to add them to the partialBuild - partialBuild = allParents; - } + partialBuild = allParents; + } + + let allChildren: ILEObject[]|undefined = this.partialOptions.partial ? this.targets.getRequiredObjects(partialBuild) : undefined; + + return { + partial: partialBuild, + children: allChildren } + } - let allRequirements: ILEObject[]|undefined = partialBuild ? this.targets.getRequiredObjects(partialBuild) : undefined; + public generateTargets(partialBuild?: ILEObject[]): string[] { + let lines = []; + // A 'partial build' means we only want to build specific objects + const buildObjects = this.getPartialTargets(partialBuild); + + if (buildObjects) { + partialBuild = buildObjects.partial; + } + + // If we are in partial mode, we only want to generate targets for the specific objects const all = partialBuild || [ ...(this.targets.binderRequired() ? [this.targets.getBinderTarget()] : []), ...this.targets.getTargetsOfType(`PGM`), @@ -257,9 +288,9 @@ export class MakeProject { ) } - if (partialBuild) { + if (buildObjects) { // If we don't want the children to get built, we only generate the targets for the specific objects - for (const obj of allRequirements || []) { + for (const obj of buildObjects.children || []) { if (obj.reference) continue; // Skip references const target = this.targets.getTarget(obj); @@ -296,34 +327,12 @@ export class MakeProject { public generateGenericRules(partialBuild?: ILEObject[]): string[] { let lines = []; - // If this is a partial build, we only want to generate rules for the specific objects - let allParents: ILEObject[]|undefined; + const buildObjects = this.getPartialTargets(partialBuild); - // A 'partial build' means we only want to build specific objects - // and we also want to build their parents too. We update `partialBuild` - // to include all the parents of the specific objects. - if (partialBuild) { - allParents = []; - const impacts = partialBuild.map(o => this.targets.getImpactFor(o)); - - const addImpact = (impactedObj: ImpactedObject) => { - if (!allParents.some(o => o.systemName === impactedObj.ileObject.systemName && o.type === impactedObj.ileObject.type)) { - allParents.push(impactedObj.ileObject); - } - - impactedObj.children.forEach(child => addImpact(child)); - } - - impacts.forEach(impact => addImpact(impact)); - - if (this.partialWithImpacts) { - // If we want to include the impacts, we need to add them to the partialBuild - partialBuild = allParents; - } + if (buildObjects) { + partialBuild = buildObjects.partial; } - let allRequirements: ILEObject[]|undefined = partialBuild ? this.targets.getRequiredObjects(partialBuild) : undefined; - for (const entry of Object.entries(this.settings.compiles)) { let [type, data] = entry; @@ -367,7 +376,7 @@ export class MakeProject { for (const ileObject of objects) { if (ileObject.reference) continue; - if (allRequirements && !allRequirements.some(o => o.systemName === ileObject.systemName && o.type === ileObject.type)) { + if (buildObjects && !buildObjects.children.some(o => o.systemName === ileObject.systemName && o.type === ileObject.type)) { continue; // Skip this object } diff --git a/cli/test/make.test.ts b/cli/test/make.test.ts index 027b582..cd748fa 100644 --- a/cli/test/make.test.ts +++ b/cli/test/make.test.ts @@ -199,6 +199,8 @@ test('generateTargets (post-resolve)', async () => { `SRVPGMA` ]); + project.setPartialOptions({partial: true, parents: false}); + const targetContent = project.generateTargets([srvpgma]); console.log(targetContent.join('\n')); @@ -220,6 +222,8 @@ test('generateTargets (post-resolve)', async () => { ); const rules = project.generateGenericRules([srvpgma]); + + console.log(rules.join('\n')); expect(rules).toContain(`$(PREPATH)/MODULEB.MODULE: qrpglesrc/moduleB.sqlrpgle`); expect(rules).toContain(`$(PREPATH)/SRVPGMA.SRVPGM: qsrvsrc/srvpgmA.bnd`); expect(rules).toContain(`$(PREPATH)/FILEB.FILE: qddssrc/fileB.pf`); diff --git a/cli/test/project.test.ts b/cli/test/project.test.ts index a00d09a..8398aca 100644 --- a/cli/test/project.test.ts +++ b/cli/test/project.test.ts @@ -308,7 +308,7 @@ describe(`company_system tests`, () => { const makeProject = new MakeProject(project.cwd, targets, fs); await makeProject.setupSettings(); - makeProject.setPartialWithImpacts(true); + makeProject.setPartialOptions({partial: true, parents: true}); const deptsFile = targets.getTarget({systemName: `DEPTS`, type: `FILE`}); @@ -325,7 +325,7 @@ describe(`company_system tests`, () => { const makeProject = new MakeProject(project.cwd, targets, fs); await makeProject.setupSettings(); - makeProject.setPartialWithImpacts(true); + makeProject.setPartialOptions({partial: false, parents: true}); const empFile = targets.getTarget({systemName: `EMPLOYEE`, type: `FILE`}); @@ -345,18 +345,13 @@ describe(`company_system tests`, () => { expect(allTargets).toContain(`$(PREPATH)/DEPTS.PGM`); expect(allTargets).toContain(`$(PREPATH)/SHOWEMPS.PGM`); expect(allTargets).toContain(`$(PREPATH)/GETTOTSAL.SRVPGM`); - - const deptsTargetDeps = headerContent.find(l => l.startsWith(`$(PREPATH)/DEPTS.PGM:`)); - expect(deptsTargetDeps).toBeDefined(); - - expect(deptsTargetDeps).toContain(`$(PREPATH)/DEPARTMENT.FILE`); }); test(`Makefile targets for partial build (EMPLOYEE table) without children`, async () => { const makeProject = new MakeProject(project.cwd, targets, fs); await makeProject.setupSettings(); - makeProject.setPartialWithImpacts(true); + makeProject.setPartialOptions({partial: false, parents: true}); const deptsFile = targets.getTarget({systemName: `EMPLOYEE`, type: `FILE`}); diff --git a/cli/test/project2.test.ts b/cli/test/project2.test.ts index dfbd5a0..7baee4a 100644 --- a/cli/test/project2.test.ts +++ b/cli/test/project2.test.ts @@ -302,7 +302,7 @@ describe(`company_system tests`, () => { const makeProject = new MakeProject(project.cwd, targets, fs); await makeProject.setupSettings(); - makeProject.setPartialWithImpacts(true); + makeProject.setPartialOptions({partial: false, parents: true}); const deptsFile = targets.getTarget({systemName: `DEPTS`, type: `FILE`}); @@ -319,12 +319,14 @@ describe(`company_system tests`, () => { const makeProject = new MakeProject(project.cwd, targets, fs); await makeProject.setupSettings(); - makeProject.setPartialWithImpacts(true); + makeProject.setPartialOptions({partial: false, parents: true}); - const deptsFile = targets.getTarget({systemName: `EMPLOYEE`, type: `FILE`}); + const employeeFile = targets.getTarget({systemName: `EMPLOYEE`, type: `FILE`}); // Generate targets on it's own will have BNDDIR, PGM, etc - const headerContent = makeProject.generateTargets([deptsFile]); + const headerContent = makeProject.generateTargets([employeeFile]); + + console.log(headerContent.join(`\n`)); const allTarget = headerContent.find(l => l.startsWith(`all:`)); expect(allTarget).toBeDefined(); @@ -339,18 +341,13 @@ describe(`company_system tests`, () => { expect(allTargets).toContain(`$(PREPATH)/DEPTS.PGM`); expect(allTargets).toContain(`$(PREPATH)/SHOWEMPS.PGM`); expect(allTargets).toContain(`$(PREPATH)/GETTOTSAL.SRVPGM`); - - const deptsTargetDeps = headerContent.find(l => l.startsWith(`$(PREPATH)/DEPTS.PGM:`)); - expect(deptsTargetDeps).toBeDefined(); - - expect(deptsTargetDeps).toContain(`$(PREPATH)/DEPARTMENT.FILE`); }); test(`Makefile targets for partial build (EMPLOYEE table) without children`, async () => { const makeProject = new MakeProject(project.cwd, targets, fs); await makeProject.setupSettings(); - makeProject.setPartialWithImpacts(true); + makeProject.setPartialOptions({partial: false, parents: true}); const empFile = targets.getTarget({systemName: `EMPLOYEE`, type: `FILE`}); From b24a1cb32baa717249f09db42a526e1d4a189a95 Mon Sep 17 00:00:00 2001 From: worksofliam Date: Wed, 23 Jul 2025 11:10:09 -0400 Subject: [PATCH 09/12] Add catch when getting objects by extension Signed-off-by: worksofliam --- cli/src/targets/index.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cli/src/targets/index.ts b/cli/src/targets/index.ts index 9422113..5c9b926 100644 --- a/cli/src/targets/index.ts +++ b/cli/src/targets/index.ts @@ -673,8 +673,9 @@ export class Targets { } return Object.values(this.resolvedObjects).filter(obj => - (obj.extension?.toUpperCase() === extension && (obj.type === `PGM`) === shouldBeProgram) || - (anyPrograms === true && obj.type === `PGM` && obj.extension.toUpperCase() === extension) + obj && + ((obj.extension?.toUpperCase() === extension && (obj.type === `PGM`) === shouldBeProgram) || + (anyPrograms === true && obj.type === `PGM` && obj.extension.toUpperCase() === extension)) ); } From 298c8de42a04af98a8e5c902c0a2602400f30a84 Mon Sep 17 00:00:00 2001 From: worksofliam Date: Wed, 23 Jul 2025 11:21:02 -0400 Subject: [PATCH 10/12] Scan for exports in a specific order Signed-off-by: worksofliam --- cli/src/targets/index.ts | 119 +++++++++++++++++++++------------------ 1 file changed, 64 insertions(+), 55 deletions(-) diff --git a/cli/src/targets/index.ts b/cli/src/targets/index.ts index 5c9b926..abd7ce3 100644 --- a/cli/src/targets/index.ts +++ b/cli/src/targets/index.ts @@ -489,67 +489,65 @@ export class Targets { } } - // We loop through all programs and module and study their imports. - // We do this in case they depend on another service programs based on import - for (let currentTarget of allTargets) { - if ([`PGM`, `MODULE`].includes(currentTarget.type) && currentTarget.imports) { - let newImports: ILEObject[] = []; - - // Remove any service program deps so we can resolve them cleanly - currentTarget.deps = currentTarget.deps.filter(d => ![`SRVPGM`].includes(d.type)); - - for (const importName of currentTarget.imports) { - if (currentTarget.exports?.includes(importName.toUpperCase())) { - // This happens when a source copy has the prototype and the implementation (export) - continue; // Don't add imports that are also exports - } + const resolveExportsForObject = (currentTarget: ILEObjectTarget) => { + if (!currentTarget.imports || currentTarget.imports.length === 0) return; // Nothing to resolve - // Find if this import resolves to another object - const possibleSrvPgmDep = this.resolvedExports[importName.toUpperCase()]; - // We can't add a module as a dependency at this step. - if (possibleSrvPgmDep && possibleSrvPgmDep.type === `SRVPGM`) { - // Make sure we haven't imported it before! - if (!newImports.some(i => i.systemName === possibleSrvPgmDep.systemName && i.type === possibleSrvPgmDep.type)) { - newImports.push(possibleSrvPgmDep); - } + let newImports: ILEObject[] = []; - } else if ([`PGM`, `MODULE`].includes(currentTarget.type)) { - // Perhaps we're looking at a program object, which actually should be a multi - // module program, so we do a lookup for additional modules. - const possibleModuleDep = allModules.find(mod => mod.exports && mod.exports.includes(importName.toUpperCase())) - if (possibleModuleDep) { - if (!newImports.some(i => i.systemName === possibleModuleDep.systemName && i.type === possibleModuleDep.type)) { - newImports.push(possibleModuleDep); + // Remove any service program deps so we can resolve them cleanly + currentTarget.deps = currentTarget.deps.filter(d => ![`SRVPGM`].includes(d.type)); - // TODO: consider other IMPORTS that `possibleModuleDep` needs. - } + for (const importName of currentTarget.imports) { + if (currentTarget.exports?.includes(importName.toUpperCase())) { + // This happens when a source copy has the prototype and the implementation (export) + continue; // Don't add imports that are also exports + } + + // Find if this import resolves to another object + const possibleSrvPgmDep = this.resolvedExports[importName.toUpperCase()]; + // We can't add a module as a dependency at this step. + if (possibleSrvPgmDep && possibleSrvPgmDep.type === `SRVPGM`) { + // Make sure we haven't imported it before! + if (!newImports.some(i => i.systemName === possibleSrvPgmDep.systemName && i.type === possibleSrvPgmDep.type)) { + newImports.push(possibleSrvPgmDep); + } + + } else if ([`PGM`, `MODULE`].includes(currentTarget.type)) { + // Perhaps we're looking at a program object, which actually should be a multi + // module program, so we do a lookup for additional modules. + const possibleModuleDep = allModules.find(mod => mod.exports && mod.exports.includes(importName.toUpperCase())); + if (possibleModuleDep) { + if (!newImports.some(i => i.systemName === possibleModuleDep.systemName && i.type === possibleModuleDep.type)) { + newImports.push(possibleModuleDep); + + // TODO: consider other IMPORTS that `possibleModuleDep` needs. } } - }; + } + }; + + // If the program or module has imports that we can resolve, then we add them as deps + if (newImports.length > 0) { + infoOut(`${currentTarget.systemName}.${currentTarget.type} has additional dependencies: ${newImports.map(i => `${i.systemName}.${i.type}`)}`); + currentTarget.deps.push(...newImports); + + if (currentTarget.type === `PGM`) { + // If this program has MODULE dependecies, that means we need to change the way it's compiled + // to be a program made up of many modules, usually done with CRTPGM + if (currentTarget.deps.some(d => d.type === `MODULE`)) { + this.convertBoundProgramToMultiModuleProgram(currentTarget); + + // Then, also include any of the modules dep modules into the currentTarget deps!! + const depTargets = currentTarget.deps + .filter(d => d.type === `MODULE`) + .map(m => this.getTarget(m)); + + // Confusing names, it means: dependencies of the dependencies that are modules + const depDeps = depTargets.map(m => m?.deps).flat().filter(d => d.type === `MODULE`); - // If the program or module has imports that we ca resolve, then we add them as deps - if (newImports.length > 0) { - infoOut(`${currentTarget.systemName}.${currentTarget.type} has additional dependencies: ${newImports.map(i => `${i.systemName}.${i.type}`)}`); - currentTarget.deps.push(...newImports); - - if (currentTarget.type === `PGM`) { - // If this program has MODULE dependecies, that means we need to change the way it's compiled - // to be a program made up of many modules, usually done with CRTPGM - if (currentTarget.deps.some(d => d.type === `MODULE`)) { - this.convertBoundProgramToMultiModuleProgram(currentTarget); - - // Then, also include any of the modules dep modules into the currentTarget deps!! - const depTargets = currentTarget.deps - .filter(d => d.type === `MODULE`) - .map(m => this.getTarget(m)); - - // Confusing names, it means: dependencies of the dependencies that are modules - const depDeps = depTargets.map(m => m?.deps).flat().filter(d => d.type === `MODULE`); - - for (const newDep of depDeps) { - if (newDep && !currentTarget.deps.some(d => d.systemName === newDep.systemName && d.type === newDep.type)) { - currentTarget.deps.push(newDep); - } + for (const newDep of depDeps) { + if (newDep && !currentTarget.deps.some(d => d.systemName === newDep.systemName && d.type === newDep.type)) { + currentTarget.deps.push(newDep); } } } @@ -557,6 +555,17 @@ export class Targets { } } + // Next, resolve the exports for all modules and programs + + for (const module of allModules) { + resolveExportsForObject(module); + } + + const allPrograms = this.getTargetsOfType(`PGM`); + for (const program of allPrograms) { + resolveExportsForObject(program); + } + const commandObjects = this.getResolvedObjects(`CMD`); for (let cmdObject of commandObjects) { // Check if a program exists with the same name. From 65b9c74184b239edb48bdb0ada84db7b828b33a4 Mon Sep 17 00:00:00 2001 From: worksofliam Date: Wed, 23 Jul 2025 11:33:57 -0400 Subject: [PATCH 11/12] More improvements to partial builds for makefiles Signed-off-by: worksofliam --- cli/src/builders/make/index.ts | 10 +++++----- cli/src/cli.ts | 4 ++-- cli/src/index.ts | 20 ++++++++++++++++---- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/cli/src/builders/make/index.ts b/cli/src/builders/make/index.ts index 6340ce6..9d865aa 100644 --- a/cli/src/builders/make/index.ts +++ b/cli/src/builders/make/index.ts @@ -28,7 +28,7 @@ type PartialOptions = { partial: boolean, parents: boolean }; interface PartialTargets { partial: ILEObject[]; - children: ILEObject[]; + children?: ILEObject[]; } export class MakeProject { @@ -231,7 +231,7 @@ export class MakeProject { * If `parents` is true, it will return all parent objects of the partial build objects, and their children/ */ private getPartialTargets(partialBuild?: ILEObject[]): PartialTargets|undefined { - if (!partialBuild) { + if (partialBuild === undefined) { return; } @@ -288,9 +288,9 @@ export class MakeProject { ) } - if (buildObjects) { + if (buildObjects && buildObjects.children) { // If we don't want the children to get built, we only generate the targets for the specific objects - for (const obj of buildObjects.children || []) { + for (const obj of buildObjects.children) { if (obj.reference) continue; // Skip references const target = this.targets.getTarget(obj); @@ -376,7 +376,7 @@ export class MakeProject { for (const ileObject of objects) { if (ileObject.reference) continue; - if (buildObjects && !buildObjects.children.some(o => o.systemName === ileObject.systemName && o.type === ileObject.type)) { + if (buildObjects && buildObjects.children && !buildObjects.children.some(o => o.systemName === ileObject.systemName && o.type === ileObject.type)) { continue; // Skip this object } diff --git a/cli/src/cli.ts b/cli/src/cli.ts index bcd4321..a057ec0 100644 --- a/cli/src/cli.ts +++ b/cli/src/cli.ts @@ -8,9 +8,9 @@ export let cliSettings = { withActions: false, fixIncludes: false, autoRename: false, - fileList: false, - lookupFiles: [] as string[], + lookupFiles: undefined as string[]|undefined, userBranch: ``, + makefileIsPartial: false, makefileWithParents: false, assumeSourcesArePrograms: false, }; diff --git a/cli/src/index.ts b/cli/src/index.ts index d4cef6d..3d10fca 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -67,6 +67,11 @@ async function main() { warningOut(`--no-children is deprecated and is default when doing partial builds.`); break; + case `-ip`: + case `--is-partial`: + cliSettings.makefileIsPartial = true; + break; + case `-wp`: case `--with-parents`: cliSettings.makefileWithParents = true; @@ -84,7 +89,7 @@ async function main() { case `-f`: case `--files`: case `-l`: - cliSettings.fileList = true; + cliSettings.lookupFiles = []; break; case `-h`: @@ -132,6 +137,10 @@ async function main() { console.log(``); console.log(`Options specific to '-bf make':`); console.log(``); + console.log(`\t-ip`); + console.log(`\t--is-partial\tWill only generate targets that are needed for`); + console.log(`\t\t\tthe objects that are being built.`); + console.log(``); console.log(`\t-wp`); console.log(`\t--with-parents\tUsed with '-bf make' and will add parents of`); console.log(`\t\t\tobjects being partially built to the makefile.`); @@ -140,7 +149,7 @@ async function main() { break; default: - if (cliSettings.fileList) { + if (cliSettings.lookupFiles !== undefined) { cliSettings.lookupFiles.push(parms[i]); } break; @@ -237,9 +246,12 @@ async function main() { await makeProj.setupSettings(); - makeProj.setPartialWithImpacts(cliSettings.makefileWithParents); + makeProj.setPartialOptions({ + partial: cliSettings.makefileIsPartial, + parents: cliSettings.makefileWithParents + }) - let specificObjects: ILEObject[] | undefined = cliSettings.fileList ? cliSettings.lookupFiles.map(f => targets.getResolvedObject(path.join(cwd, f))).filter(o => o) : undefined; + let specificObjects: ILEObject[] | undefined = cliSettings.lookupFiles ? cliSettings.lookupFiles.map(f => targets.getResolvedObject(path.join(cwd, f))).filter(o => o) : undefined; writeFileSync(path.join(cwd, `makefile`), makeProj.getMakefile(specificObjects).join(`\n`)); break; From d8cf61335cefa09df4c860582a030c14b677c672 Mon Sep 17 00:00:00 2001 From: worksofliam Date: Wed, 23 Jul 2025 11:41:09 -0400 Subject: [PATCH 12/12] Updated docs Signed-off-by: worksofliam --- docs/pages/cli/make.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/pages/cli/make.md b/docs/pages/cli/make.md index bef7350..7a8c330 100644 --- a/docs/pages/cli/make.md +++ b/docs/pages/cli/make.md @@ -8,13 +8,6 @@ This section only applies if you use `-bf make`. Using `so -bf make` will generate a makefile to rebuild the entire project. This is fine when deploying to new systems. It is also possible to do incremental builds. -To do an incremental build, also referred to as a partial build, you can specify the targets you want to build: - -```bash -so -bf make -f 'qrpglesrc/ord500.pgm.rpgle' -so -bf make -l `git diff --name-only origin/main origin/${GITHUB_HEAD_REF}` -``` - A incremental build means building a specific targets, their parents and optionally their children. Let's assume this is our dependency tree: ``` @@ -33,6 +26,18 @@ So that means that 3 objects are going to be rebuilt. Sometimes, we don't want t Usually, parents always need to be rebuilt to ensure level checking happens. If you use the `-wp` (with-parents) options, then the `makefile` will also include targets to rebuild the parent objects too (`ORD500`), but the `all` target will only build the specified target (`ORD501`). +### Parameters for increment builds + +When you use `so -bf make`, you can specify the following parameters to control the incremental build: + +* `-f`/`-l` to specify the list of sources to build. This can be a single file or a list of files. + * `so -bf make -f qrpglesrc/employees.pgm.sqlrpgle` will build the `EMPLOYEES.PGM` object. +* With `-ip` (is-partial), then only the specified objects and its dependents will be put into the `makefile`. + * `so -bf make -f qrpglesrc/employees.pgm.sqlrpgle -ip` + * This will generate a makefile only for the specific objects. +* With `-wp` (with-parents), then the parents of the specified objects will also be included in the `makefile`. + * `so -bf make -f qrpglesrc/employees.pgm.sqlrpgle -ip -wp` + ### General rule for builds To ensure library lists are supported correctly, the following rules are recommended: