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/builders/make/index.ts b/cli/src/builders/make/index.ts index 4076f19..9d865aa 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'; @@ -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 noChildren: 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 setNoChildrenInBuild(noChildren: boolean) { - this.noChildren = noChildren; + public setPartialOptions(options: PartialOptions) { + this.partialOptions = options; } public useActions() { @@ -188,7 +199,7 @@ export class MakeProject { ``, ...this.generateTargets(specificObjects), ``, - ...this.generateGenericRules() + ...this.generateGenericRules(specificObjects) ]; } @@ -214,20 +225,27 @@ 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 === undefined) { + return; + } - // A 'partial build' means we only want to build specific objects - // and we also want to build their parents too. We update `partialBuild` + let allParents: ILEObject[]|undefined; + + // 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)); - 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 +253,28 @@ export class MakeProject { impacts.forEach(impact => addImpact(impact)); - partialBuild = allAffected; + partialBuild = allParents; + } + + let allChildren: ILEObject[]|undefined = this.partialOptions.partial ? this.targets.getRequiredObjects(partialBuild) : undefined; + + return { + partial: partialBuild, + children: allChildren } + } + + 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`), @@ -251,7 +288,19 @@ export class MakeProject { ) } - if (!this.noChildren) { + 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) { + 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) { @@ -275,9 +324,15 @@ export class MakeProject { return lines; } - public generateGenericRules(): string[] { + public generateGenericRules(partialBuild?: ILEObject[]): string[] { let lines = []; + const buildObjects = this.getPartialTargets(partialBuild); + + if (buildObjects) { + partialBuild = buildObjects.partial; + } + for (const entry of Object.entries(this.settings.compiles)) { let [type, data] = entry; @@ -320,6 +375,10 @@ export class MakeProject { if (objects.length > 0) { for (const ileObject of objects) { if (ileObject.reference) continue; + + if (buildObjects && buildObjects.children && !buildObjects.children.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/cli.ts b/cli/src/cli.ts index 5a3cbba..a057ec0 100644 --- a/cli/src/cli.ts +++ b/cli/src/cli.ts @@ -8,10 +8,10 @@ export let cliSettings = { withActions: false, fixIncludes: false, autoRename: false, - fileList: false, - lookupFiles: [] as string[], + lookupFiles: undefined as string[]|undefined, userBranch: ``, - makeFileNoChildren: false, + makefileIsPartial: false, + makefileWithParents: false, assumeSourcesArePrograms: false, }; diff --git a/cli/src/index.ts b/cli/src/index.ts index e0a22f7..3d10fca 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -64,7 +64,17 @@ 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 `-ip`: + case `--is-partial`: + cliSettings.makefileIsPartial = true; + break; + + case `-wp`: + case `--with-parents`: + cliSettings.makefileWithParents = true; break; case '-ap': @@ -79,7 +89,7 @@ async function main() { case `-f`: case `--files`: case `-l`: - cliSettings.fileList = true; + cliSettings.lookupFiles = []; break; case `-h`: @@ -127,15 +137,19 @@ 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-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.`); console.log(``); process.exit(0); break; default: - if (cliSettings.fileList) { + if (cliSettings.lookupFiles !== undefined) { cliSettings.lookupFiles.push(parms[i]); } break; @@ -232,9 +246,12 @@ async function main() { await makeProj.setupSettings(); - makeProj.setNoChildrenInBuild(cliSettings.makeFileNoChildren); + 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; diff --git a/cli/src/targets/index.ts b/cli/src/targets/index.ts index 91627fe..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 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); - } + 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); } } } @@ -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. @@ -673,8 +682,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)) ); } @@ -713,6 +723,36 @@ 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 + + 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); + } + } + } + + 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 diff --git a/cli/test/cs_srvpgm.test.ts b/cli/test/cs_srvpgm.test.ts index 20f7b21..dbd4652 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', () => { diff --git a/cli/test/make.test.ts b/cli/test/make.test.ts index 50be27b..83bda47 100644 --- a/cli/test/make.test.ts +++ b/cli/test/make.test.ts @@ -181,4 +181,51 @@ test(`Multi-module program and service program`, async () => { `\tsystem "CRTSRVPGM SRVPGM($(BIN_LIB)/UTILS) MODULE(JWTHANDLER VALIDATE) SRCSTMF('qsrvsrc/utils.binder') BNDDIR($(APP_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` + ]); + + project.setPartialOptions({partial: true, parents: false}); + + 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]); + + 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`); + 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 95fee32..67b7d7c 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.setPartialOptions({partial: true, parents: 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.setPartialOptions({partial: false, parents: 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(); @@ -341,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.setNoChildrenInBuild(true); + makeProject.setPartialOptions({partial: false, parents: true}); const deptsFile = targets.getTarget({systemName: `EMPLOYEE`, type: `FILE`}); @@ -372,9 +371,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 c54ff99..35a66f2 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.setPartialOptions({partial: false, parents: true}); + const deptsFile = targets.getTarget({systemName: `DEPTS`, type: `FILE`}); // Generate targets on it's own will have BNDDIR, PGM, etc @@ -317,10 +319,14 @@ describe(`company_system tests`, () => { const makeProject = new MakeProject(project.cwd, targets, fs); await makeProject.setupSettings(); - const deptsFile = targets.getTarget({systemName: `EMPLOYEE`, type: `FILE`}); + makeProject.setPartialOptions({partial: false, parents: true}); + + 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(); @@ -335,29 +341,24 @@ 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.setNoChildrenInBuild(true); + makeProject.setPartialOptions({partial: false, parents: 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 +367,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`, () => { diff --git a/docs/pages/cli/make.md b/docs/pages/cli/make.md index 8a3ff5d..7a8c330 100644 --- a/docs/pages/cli/make.md +++ b/docs/pages/cli/make.md @@ -22,7 +22,21 @@ 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`). + +### 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