@profoundlogic/codermake
v3.0.0
Published
Make wrapper framework for IBM i development that simplifies build rules
Readme
codermake
A make wrapper framework for IBM i development that simplifies build rules and abstracts platform-specific complexity.
Features
- Simplified Rules: Write clean, maintainable build rules in
Rules.mkfiles - Platform Agnostic: Same rules work on IBM i and Unix-like systems (Linux, macOS, FreeBSD, etc.)
- Automatic Discovery: Automatically finds and processes Rules.mk files in your project
- Recipe Inference: Automatically determines build recipes based on file extensions
- Remote Builds: Build from Unix-like systems to IBM i via SSH
- Compile Options: Set default compile parameters (e.g.
TGTRLS,DBGVIEW) per project via.codermake/config.json
Installation
npm install -g @profoundlogic/codermakeQuick Start
1. Set up environment variables
On IBM i (local build):
export BUILD_LIBRARY=DEVLIBOn Unix-like systems (remote build via SSH):
export IBMI_BUILD_LIBRARY=DEVLIB
export IBMI_HOST=ibmi.example.com
export IBMI_USER=devuser
export IBMI_KEY=/path/to/ssh/key # OptionalMulti-library output mode (alternative to BUILD_LIBRARY / IBMI_BUILD_LIBRARY, requires multi-library layout):
# Map source-tree library directories to IBM i libraries
export CODERMAKE_LIBRARY_MAP="LIBA=APPLIBA LIBB=APPLIBB"
# Or use the natural mapping (LIBA → LIBA, LIBB → LIBB)
export CODERMAKE_LIBRARY_MAP=auto
# Optional: replace the user portion of the IBM i library list at build time
export CODERMAKE_LIBRARY_LIST="APPLIBA APPLIBB"CODERMAKE_LIBRARY_MAP is mutually exclusive with BUILD_LIBRARY / IBMI_BUILD_LIBRARY. See Multi-library output below.
2. Create a Rules.mk file
Create src/Rules.mk:
# Simple program with display file
hellor.pgm: hellor.rpgle hellod.file
# Display file
hellod.file: hellod.dspf3. Build
codermakeRules.mk Syntax
Basic Rule Format
target: prerequisitesSupported File Types
Output Objects:
.pgm- Program.module- Module.srvpgm- Service Program (from modules, SQL procedures withPROGRAM TYPE SUB, or SQL UDFs).file- File (display, physical, logical, printer, SQL table/index/view).menu- Menu.msgf- Message File.bnddir- Binding Directory
Source Files:
.rpgle- ILE RPG source.sqlrpgle- SQL RPG source.clle- ILE CL source.cl- OPM CL source.clp- OPM CL source (alternate).clp38- System/38 compatible OPM CL source (QSYS38/CRTCLPGM).rpg- RPG/400 (RPG III) source (CRTRPGPGM).rpg38- System/38 compatible RPG III source (QSYS38/CRTRPGPGM).dspf- Display file DDS.pf- Physical file DDS.lf- Logical file DDS.prtf- Printer file DDS.dspf38- System/38 compatible display file DDS (QSYS38/CRTDSPF).pf38- System/38 compatible physical file DDS (QSYS38/CRTPF).lf38- System/38 compatible logical file DDS (QSYS38/CRTLF).prtf38- System/38 compatible printer file DDS (QSYS38/CRTPRTF).table.sql- SQL table definition.index.sql- SQL index definition.view.sql- SQL view definition.proc.sql- SQL procedure definition (*PGM, or*SRVPGMwithPROGRAM TYPE SUB).udf.sql- SQL function definition (CREATE FUNCTION ... LANGUAGE SQL→*SRVPGM).trg.sql- SQL trigger definition (CREATE TRIGGER→*PGM).json- Rich Display File (RDF) JSON (when first prerequisite of.filetarget).msgf- Message file CL commands (when first prerequisite of.msgftarget).bnddir- Binding directory CL commands (when first prerequisite of.bnddirtarget).exportsor.bnd- Service program export list
Note: .msgf and .bnddir files serve dual purposes:
- As source: When appearing as the first normal prerequisite of a matching target type (e.g.,
app.msgf: app.msgf) - As object: In all other contexts (e.g., as prerequisites of other target types or non-first prerequisites)
Examples
Simple Program:
mypgm.pgm: source.rpgleService Program:
mymod.module: source.rpgle
mysrvpgm.srvpgm: mymod.module myexports.exports
# Without export list (exports all procedures via EXPORT(*ALL)):
mysrvpgm.srvpgm: mymod.moduleProgram from Modules (with automatic binding):
calc.module: calc.rpgle
calc.pgm: calc.module utils.srvpgm # BNDSRVPGM(utils) added automatically
calcd.pgm: calcd.module | app.bnddir # BNDDIR(app) added automatically.srvpgm and .bnddir prerequisites work as either normal or order-only; the automatic BNDSRVPGM()/BNDDIR() parameters are generated either way. This only applies to module-based programs (CRTPGM/CRTSRVPGM), not source-based programs (e.g., CRTBNDRPG).
Program with Dependencies:
hellor.pgm: hellor.rpgle hellod.file custr.srvpgmNote: This is a source-based program (CRTBNDRPG), so custr.srvpgm is a build-ordering dependency only — no BNDSRVPGM() parameter is generated. Binding is typically handled via a BNDDIR control specification in the RPG source.
Rich Display File from JSON:
mydisplay.file: mydisplay.jsonCL Programs:
# ILE CL
hellocl.pgm: hellocl.clle
# OPM CL
simpgm.pgm: simpgm.clp
# CL Module
mathmod.module: mathmod.clle
mathpgm.pgm: mathmod.modulePrinter File:
helloprt.file: helloprt.prtfSystem/38 Compatible DDS:
# .pf38/.lf38/.dspf38/.prtf38 use dedicated recipes that call the QSYS38
# CRTPF/CRTLF/CRTDSPF/CRTPRTF command variants. The temporary source member is
# tagged with the matching System/38 source type (PF38/LF38/DSPF38/PRTF38),
# derived automatically from the file extension.
custp38.file: custp38.pf38
custl38.file: custl38.lf38 custp38.file
hello38.file: hello38.dspf38
rpt38.file: rpt38.prtf38RPG III and System/38 CL Programs:
# RPG/400 (RPG III) — CRTRPGPGM ... REPLACE(*YES)
myrpg.pgm: myrpg.rpg
# System/38 compatible RPG III — QSYS38/CRTRPGPGM (delete-then-create)
myrpg38.pgm: myrpg38.rpg38
# System/38 compatible OPM CL — QSYS38/CRTCLPGM (delete-then-create)
mycl38.pgm: mycl38.clp38.rpg, .rpg38, and .clp38 cannot compile directly from stream files, so
codermake creates a temporary source member (QRPGSRC/QCLSRC) before
compiling. RPG III /COPY directives always reference source members, so any
/COPY prerequisite is created as a source member too. List each /COPY
target as a prerequisite; its path in the source tree determines the source
file (parent directory), the member name (base name), and — in multi-library
output mode — the target library (leading library-directory segment):
# QCPYSRC,SHARED is staged as a source member (QCPYSRC/SHARED) before compiling
myrpg.pgm: myrpg.rpg qcpysrc/shared.rpgSQL Objects:
employee.file: employee.table.sql
empaudit.file: empaudit.table.sql
empview.file: empview.view.sql employee.file
empname.file: empname.index.sql employee.file
getcount.pgm: getcount.proc.sql employee.file # SQL procedure -> *PGM
getcounts.srvpgm: getcounts.proc.sql employee.file # SQL procedure (PROGRAM TYPE SUB) -> *SRVPGM
getname.srvpgm: getname.udf.sql employee.file # SQL UDF (LANGUAGE SQL) -> *SRVPGM
emptrg.pgm: emptrg.trg.sql employee.file empaudit.file # SQL trigger -> *PGMSQL routines and triggers are built with RUNSQLSTM: codermake only runs the statement, and IBM i creates and names the resulting object. Name SQL routines to match the build target so the generated *PGM/*SRVPGM object name lines up with the rule. For triggers, use the trigger name or PROGRAM NAME to align the generated *PGM with the .pgm target.
Order-Only Prerequisites:
mypgm.pgm: source.rpgle | mybnddir.bnddirOrder-only prerequisites must exist before building the target but do not trigger a rebuild when they change. Note: for source-based programs like the above (CRTBNDRPG), no automatic BNDDIR() parameter is generated — the binding directory is typically specified via a BNDDIR control specification in the RPG source instead.
Command-Line Options
codermake [options] [targets...]Options
--help- Show help message--version- Show version--debug- Enable debug output--print-rules- Print preprocessed rules and exit--print-makefile- Generate standalone Makefile for debugging--license-show- Show license status--license-set=<key>- Install a license key
All other options are passed through to GNU Make. See make --help for additional options.
Examples
Build all targets:
codermakeBuild specific target:
codermake hellor.pgmParallel build:
codermake -j4Dry run:
codermake -nClean:
codermake cleanDebug preprocessing:
codermake --print-rulesCompile Options
By default, codermake supplies a fixed set of parameters to each IBM i compile
command. You can set your own defaults — such as a target release or debug
view — with an optional configuration file at .codermake/config.json in your
project root (the directory you run codermake from). No file is required; when
it is absent, nothing changes.
{
"targetRelease": "V7R4M0",
"compileOptions": {
"crtbndrpg": { "dbgview": "*all" },
"crtrpgpgm": { "option": ["*srcdbg"] },
"runsqlstm": { "commit": "*chg" },
"crtsqlrpgi": { "dbgview": "*source", "compileopt": { "optimize": "*full" } }
}
}targetReleasesetsTGTRLS, applied to each compile command that accepts it.compileOptionsis keyed by CL command name (case-insensitive, e.g.crtbndrpg,qsys38/crtclpgm). Each entry adds keyword parameters to that command. An entry applies to every recipe that uses the command.
Some parameters are reserved — codermake controls them because changing
them would break the build (e.g. PGM, SRCSTMF, OUTPUT(*PRINT), TGTCCSID)
— and setting a reserved parameter is reported as an error. OPTION and
COMPILEOPT are augmentable: your values are merged with the parts
codermake requires (for example OPTION(*SOURCE ...) always keeps *SOURCE).
Verify what will run without compiling:
codermake --print-makefile # shows the effective compile parameters
codermake -n <target> # dry-run the expanded compile commandProject Structure
Flat layout
project/
├── src1/
│ ├── Rules.mk # Build rules for src1/
│ ├── hellor.rpgle
│ └── hellod.dspf
├── src2/
│ ├── Rules.mk # Build rules for src2/
│ └── custr.rpgle
|── tmp/ # Build outputs (gitignored)
| └── logs/ # Compiler output logs
└── build/ # Dummy IBM i objects (Unix-type systems only, gitignored)Multi-library layout
For large IBM i codebases organized by library, a two-level directory structure is supported. Library-equivalent directories contain traditional source-file directories:
project/
├── LIBA/
│ ├── qrpglesrc/
│ │ ├── Rules.mk # Source-file-level Rules.mk
│ │ └── mypgm.rpgle
│ └── qddssrc/
│ ├── Rules.mk
│ └── myscreen.dspf
├── LIBB/
│ ├── Rules.mk # Library-level Rules.mk (also supported)
│ └── qrpglesrc/
│ └── otherpgm.rpgle
├── tmp/
└── build/Rules.mk can be placed at either the library level or source-file level. Multi-library layout is auto-detected when either (a) any Rules.mk exists at depth 2, or (b) a depth-1 Rules.mk dir contains subdirectories (the typical IBM i library layout, with source-file subdirs like qrpglesrc/, qddssrc/).
For library-level Rules.mk files, paths like qrpglesrc/mypgm.rpgle are automatically prefixed with the library directory. Cross-library references (paths starting with another library's directory name) pass through unchanged.
By default a multi-library project still builds into a single output library (set via BUILD_LIBRARY / IBMI_BUILD_LIBRARY). To build each source-tree library directory into its own IBM i output library, see Multi-library output below.
Multi-library output
When the project has a multi-library layout, CODERMAKE_LIBRARY_MAP enables per-library output:
export CODERMAKE_LIBRARY_MAP="LIBA=APPLIBA LIBB=APPLIBB"Each library-equivalent source directory builds into the IBM i library named on the right side of =. The sentinel value auto enables a "natural" mapping where each directory maps to a library of the same name:
export CODERMAKE_LIBRARY_MAP=autoCODERMAKE_LIBRARY_MAP is mutually exclusive with BUILD_LIBRARY / IBMI_BUILD_LIBRARY. Setting both, or neither, is an error. Setting CODERMAKE_LIBRARY_MAP on a flat-layout project is also an error.
Cross-library object references
In multi-library output mode, a Rules.mk in one library can refer to objects produced by another library using the qualifier syntax <libraryDir>/<bareobj>.<ext>:
# In LIBA/qrpglesrc/Rules.mk
crossbnd.pgm: crossbnd.rpgle | LIBB/utils.srvpgmThe qualifier works on both prerequisites and targets. Source files in another library still use the existing 3-segment form (LIBB/qcpysrc/shared.rpgle).
A target can also be qualified to redirect the output library — useful when a single source tree compiles into multiple deployment libraries:
# Source in LIBA's tree, output goes to LIBB
LIBB/installer.pgm: installer.rpgleOptional: control the IBM i library list at build time
CODERMAKE_LIBRARY_LIST (only valid with CODERMAKE_LIBRARY_MAP) replaces the user portion of the IBM i library list with the listed libraries in declared order:
export CODERMAKE_LIBRARY_LIST="APPLIBA APPLIBB"This is useful when BNDSRVPGM() and BNDDIR() parameters need to resolve unambiguously across mapped libraries. Unqualified service-program and binding-directory references resolve via the library list at bind/runtime, by design — this preserves the developer workflow of placing a private library copy higher on the runtime liblist to override without rebuilding.
When CODERMAKE_LIBRARY_LIST is not set, codermake leaves the build job's library list untouched (whatever the job description / profile inherits).
Portability across modes
The same Rules.mk works in both single-library output mode (BUILD_LIBRARY / IBMI_BUILD_LIBRARY) and multi-library output mode (CODERMAKE_LIBRARY_MAP). In single-library mode, qualifier syntax collapses to the single output library — useful for containerized agentic builds that use one disposable library while the production IBM i build uses per-library output.
RPG /COPY resolution in multi-library projects
Set CODERMAKE_INCLUDE_PATH to control the search order for unqualified /COPY and /INCLUDE member-style references:
export CODERMAKE_INCLUDE_PATH="LIBA LIBB LIBC"Library-qualified references (e.g., /COPY LIBB/QCPYSRC,MEMBER) resolve directly regardless of this setting.
How It Works
- Platform Detection: Detects IBM i vs Unix-like system
- Rules Discovery: Finds all
Rules.mkfiles in subdirectories (flat or multi-library layout) - Preprocessing: Transforms simplified rules into full GNU Make syntax
- Recipe Inference: Automatically determines build recipes from file extensions
- Execution: Invokes GNU make with generated rules
RPG /COPY and /INCLUDE Preprocessing
codermake automatically handles legacy RPG /COPY and /INCLUDE directives written in source member style. Source member references are converted to IFS stream file paths during compilation, allowing the same source to work in both source member and stream file environments.
See design/RPG_COPY_PREPROCESSING.md for details.
Requirements
- Node.js: >= 22.0.0
- GNU Make: >= 4.0
- SSH: For remote builds from Unix-like to IBM i
Author
Profound Logic Software
