npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@profoundlogic/codermake

v4.0.1

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.mk files
  • Platform Agnostic: Same rules work on IBM i and Unix-like systems (Linux, macOS, FreeBSD, etc.)
  • Automatic Discovery: Automatically finds and processes the Rules.mk files of every library directory 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/codermake

Quick Start

1. Set up environment variables

On IBM i (local build):

export BUILD_LIBRARY=DEVLIB

On 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  # Optional

Multi-library output mode (alternative to BUILD_LIBRARY / IBMI_BUILD_LIBRARY):

# Map the project's 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 library directory and its Rules.mk

Each top-level directory of the project is an IBM i library, holding one directory per source physical file:

project/
└── mylib/
    ├── Rules.mk
    ├── qrpglesrc/
    │   └── hellor.rpgle
    └── qddssrc/
        └── hellod.dspf

Create mylib/Rules.mk:

# Display file
hellod.file: qddssrc/hellod.dspf

# Simple program with display file
hellor.pgm: qrpglesrc/hellor.rpgle qddssrc/hellod.dspf | hellod.file

See Project Layout for the full layout.

3. Build

codermake

Rules.mk Syntax

Basic Rule Format

target: prerequisites

The first normal prerequisite is the source that creates the object, and it is what selects the compile command codermake runs. Every normal prerequisite after it is a dependency: it forces a rebuild when it changes but never changes the command. Order-only prerequisites (after |) do neither — they only have to exist first.

Paths in Rules.mk

A Rules.mk belongs to the library directory it sits in, and its names are resolved against that library:

| Written as | Means | |---|---| | hellor.rpgle | A source file in the Rules.mk's own directory | | qrpglesrc/hellor.rpgle | A source file in this library (mylib/qrpglesrc/hellor.rpgle) | | srclib/qrpglesrc/hellor.rpgle | A source file in another library directory, srclib | | hellod.file | An object in this library | | dtalib/custp.file | An object in another library directory, dtalib |

A path is a cross-library path when its first segment names a library directory. Names containing $ or # use makefile escaping: write $$ and \# (d$$pgm.pgm, h\#pgm.pgm). On the command line and in --list-targets output they appear literally ('h#pgm.pgm'). A two-segment object name whose first segment is not a library directory is reported as an invalid library qualifier.

The examples below are written for a library-level Rules.mk (mylib/Rules.mk).

Supported File Types

Output Objects:

  • .pgm - Program
  • .module - Module
  • .srvpgm - Service Program (from modules, SQL procedures with PROGRAM 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
  • .cblle - ILE COBOL source (CRTBNDCBL / CRTCBLMOD)
  • .sqlcblle - ILE COBOL source with embedded SQL (CRTSQLCBLI)
  • .cbl - OPM COBOL source (CRTCBLPGM)
  • .cbl38 - System/38 compatible COBOL source (QSYS38/CRTCBLPGM)
  • .sqlcbl - OPM COBOL source with embedded SQL (CRTSQLCBL)
  • .cpy - COBOL copybook (target of a COPY statement)
  • .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 *SRVPGM with PROGRAM 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 .file target)
  • .msgf - Message file CL commands (when first prerequisite of .msgf target)
  • .bnddir - Binding directory CL commands (when first prerequisite of .bnddir target)
  • .exports or .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: qrpglesrc/mypgm.rpgle

Service Program:

mymod.module: qrpglesrc/mymod.rpgle
mysrvpgm.srvpgm: mymod.module qsrvsrc/myexports.exports

# Without export list (exports all procedures via EXPORT(*ALL)):
mysrvpgm.srvpgm: mymod.module

Program from Modules (with automatic binding):

calc.module: qrpglesrc/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: qrpglesrc/hellor.rpgle hellod.file custr.srvpgm

Note: 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: qddssrc/mydisplay.json

CL Programs:

# ILE CL
hellocl.pgm: qclsrc/hellocl.clle

# OPM CL
simpgm.pgm: qclsrc/simpgm.clp

# CL Module
mathmod.module: qclsrc/mathmod.clle
mathpgm.pgm: mathmod.module

Printer File:

helloprt.file: qddssrc/helloprt.prtf

File Objects That Depend on Other Files:

IBM i *FILE objects frequently depend on other files, and on more than one of them: a logical file is based on one or more physical files, and a file of any type can take its field definitions from a field reference physical file (DDS REF/REFFLD). List the dependency's source as a normal prerequisite so the dependent file is recompiled when that source changes, and the dependency's file object as an order-only prerequisite so it exists on IBM i at compile time:

# Field reference physical file
fldref.file: qddssrc/fldref.pf

# Physical file whose fields come from the field reference file (REF)
custp.file: qddssrc/custp.pf qddssrc/fldref.pf | fldref.file
ordp.file: qddssrc/ordp.pf qddssrc/fldref.pf | fldref.file

# Logical file over a physical file
custl.file: qddssrc/custl.lf qddssrc/custp.pf | custp.file

# Join logical file over two physical files
custjoin.file: qddssrc/custjoin.lf qddssrc/custp.pf qddssrc/ordp.pf | custp.file ordp.file

# Display file with field references to two physical files (REF and REFFLD)
custd.file: qddssrc/custd.dspf qddssrc/fldref.pf qddssrc/ordp.pf | fldref.file ordp.file

# SQL view over a DDS physical file
custvw.file: qsqlsrc/custvw.view.sql qddssrc/custp.pf | custp.file

Because the first prerequisite is the source, custl.file compiles with CRTLF even though the rule also lists a .pf — the physical file's DDS is a dependency, not the source.

System/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: qddssrc/custp38.pf38
custl38.file: qddssrc/custl38.lf38 custp38.file
hello38.file: qddssrc/hello38.dspf38
rpt38.file: qddssrc/rpt38.prtf38

RPG III and System/38 CL Programs:

# RPG/400 (RPG III) — CRTRPGPGM ... REPLACE(*YES)
myrpg.pgm: qrpgsrc/myrpg.rpg

# System/38 compatible RPG III — QSYS38/CRTRPGPGM (delete-then-create)
myrpg38.pgm: qrpgsrc/myrpg38.rpg38

# System/38 compatible OPM CL — QSYS38/CRTCLPGM (delete-then-create)
mycl38.pgm: qclsrc/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) and the member name (base name). The member is created in the program's own library — or, in multi-library output mode, in the library its library directory is mapped to, when that directory has a map entry:

# QCPYSRC,SHARED is staged as a source member (QCPYSRC/SHARED) before compiling
myrpg.pgm: qrpgsrc/myrpg.rpg qcpysrc/shared.rpg

COBOL Programs:

# ILE COBOL — CRTBNDCBL
hellocbl.pgm: qcbllesrc/hellocbl.cblle

# ILE COBOL module bound into a program
cblmod.module: qcbllesrc/cblmod.cblle
cblpgm.pgm: cblmod.module

# ILE COBOL with embedded SQL — CRTSQLCBLI
sqlcbl.pgm: qcbllesrc/sqlcbl.sqlcblle

# OPM COBOL — CRTCBLPGM ... REPLACE(*YES)
opmcbl.pgm: qcblsrc/opmcbl.cbl

# System/38 compatible COBOL — QSYS38/CRTCBLPGM (delete-then-create)
opm38.pgm: qcblsrc/opm38.cbl38

# OPM COBOL with embedded SQL — CRTSQLCBL
sqlopm.pgm: qlblsrc/sqlopm.sqlcbl

COBOL Copy Sources (Compile Time Includes):

List each copybook as a prerequisite: that ships it to IBM i for a remote build and makes a change to it rebuild the program.

# ILE COBOL: the COPY statement may name an IFS path, relative to the project root
#   COPY 'mylib/qcpysrc/cpybook.cpy'.
# or a source member, which codermake rewrites to that path before compiling
#   COPY CPYBOOK OF QCPYSRC.
cblcpy.pgm: qcbllesrc/cblcpy.cblle qcpysrc/cpybook.cpy
sqlcpy.pgm: qcbllesrc/sqlcpy.sqlcblle qcpysrc/cpybook.cpy

# OPM COBOL compiles from a source member, so the COPY statement is left as
# written and QCPYSRC/CPYBOOK is created before compiling
opmcpy.pgm: qcblsrc/opmcpy.cbl qcpysrc/cpybook.cpy

.cbl, .cbl38, and .sqlcbl cannot compile directly from stream files, so codermake creates a temporary source member (QCBLSRC for .cbl/.cbl38, QLBLSRC for .sqlcbl) before compiling, plus a member for every COPY prerequisite. See design/COBOL_COPY_PREPROCESSING.md.

SQL Objects:

employee.file: qsqlsrc/employee.table.sql
empaudit.file: qsqlsrc/empaudit.table.sql
empview.file: qsqlsrc/empview.view.sql employee.file
empname.file: qsqlsrc/empname.index.sql employee.file
getcount.pgm: qsqlsrc/getcount.proc.sql employee.file          # SQL procedure -> *PGM
getcounts.srvpgm: qsqlsrc/getcounts.proc.sql employee.file     # SQL procedure (PROGRAM TYPE SUB) -> *SRVPGM
getname.srvpgm: qsqlsrc/getname.udf.sql employee.file          # SQL UDF (LANGUAGE SQL) -> *SRVPGM
emptrg.pgm: qsqlsrc/emptrg.trg.sql employee.file empaudit.file # SQL trigger -> *PGM

SQL 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: qrpglesrc/mypgm.rpgle | mybnddir.bnddir

Order-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
  • --list-targets[=FORMAT] - Report which targets a build would affect, without building anything. FORMAT is text (default), json or tsv. Remote builds only (not available when running on IBM i)
  • --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:

codermake

Build specific target:

codermake mylib/hellor.pgm   # library-qualified
codermake hellor.pgm         # bare name: the first library with that target

Parallel build:

codermake -j4

Dry run:

codermake -n

Clean:

codermake clean

Debug preprocessing:

codermake --print-rules

Listing Affected Targets

--list-targets reports the objects a build would create or replace — the ones whose source changed, plus everything that depends on them — without compiling anything and without connecting to the IBM i.

codermake --list-targets
3 targets affected (dependency order)

TARGET             LIBRARY  OBJECT    TYPE     SOURCE                     TGTRLS  REASON
LIBB/srvb.module   APPLIBB  srvb      *module  LIBB/qrpglesrc/srvb.rpgle          changed
LIBB/srvb.srvpgm   APPLIBB  srvb      *srvpgm  LIBB/srvb.module                   dependent
LIBA/crossbnd.pgm  APPLIBA  crossbnd  *pgm     LIBA/crossbnd.module               dependent

REASON says why each object is in the list:

  • changed — one of this object's own sources changed: the source it is compiled from, a /COPY member or copybook it includes, its export list.
  • dependent — it only needs building because another object in the build changed: a *module it is bound from, a *file it compiles against, or a file whose DDS it references for field definitions.
  • missing — the object does not exist yet.

The changed rows are the objects you actually edited; the dependent rows are the reach of those edits. So editing one field reference physical file's DDS reports that file as changed and every file that takes its fields from it as dependent, rather than reporting all of them as direct changes:

TARGET             LIBRARY  OBJECT  TYPE   SOURCE                   TGTRLS  REASON
devlib/fdref.file  DEVLIB   fdref   *file  devlib/qddssrc/fdref.pf          changed
devlib/fdcust.file DEVLIB   fdcust  *file  devlib/qddssrc/fdcust.pf         dependent
devlib/fdord.file  DEVLIB   fdord   *file  devlib/qddssrc/fdord.pf          dependent

This works off the prerequisites in Rules.mk: a referenced file's DDS is a normal prerequisite of the files built over it, and codermake knows that member is what fdref.file is compiled from. A shared source that no rule builds into an object — a /COPY member — has no object to attribute the change to, so each program that lists it is changed. Every listed object needs rebuilding whatever its reason.

TGTRLS is blank unless the project sets a target release and the object's compile command accepts one, as above.

All three output formats carry the same seven fields in the same order, always — the columns never move or disappear.

The answer is make's own out-of-date determination, so it is exactly what a build from this checkout would do.

Remote builds only. --list-targets is available when building from a Unix-like host (Linux, macOS), where codermake tracks build state as touch markers under build/. Running codermake locally on IBM i there are no markers — the build targets are the objects themselves, which codermake -t cannot baseline — so the option is refused there with an explanation.

The comparison being against those local markers rather than the IBM i objects is what lets the answer come back immediately with no connection. The flip side is that the markers describe what this checkout has built: if an object is deleted on the IBM i by hand, or a second checkout builds into the same library, the markers do not know, and neither listing nor a build will notice.

Two more consequences worth knowing:

  • List before you build. After a successful build everything is up to date, so the list is empty. Listing again afterwards is a cheap way to confirm that everything intended was built.
  • Listing changes nothing. No object is created, no marker is touched, no compile listing is discarded. Running it twice gives the same answer.

Machine-readable output

--list-targets=tsv prints one record per line with no header, and --list-targets=json prints a JSON document. Both carry the same seven fields in this order:

| Field | Example | Notes | |---|---|---| | target | LIBA/app.pgm | Can be passed straight back to codermake as a target | | library | APPLIBA | The IBM i library this invocation builds it into | | object | app | Bare object name, as written in Rules.mk | | type | *pgm | *pgm, *module, *srvpgm, *file, *menu, *msgf, *bnddir | | source | LIBA/qrpglesrc/app.rpgle | The primary input; for bound objects, the object it is built from | | targetRelease | V7R4M0 | From .codermake/config.json, where the compile command accepts TGTRLS | | reason | dependent | changed (one of its own sources changed), dependent (another object it depends on changed) or missing |

The first field is a name codermake accepts as a target argument, so a list can be narrowed and fed back in — including with a different CODERMAKE_LIBRARY_MAP, to build the same objects into staging libraries:

codermake --list-targets=tsv > affected.tsv
codermake $(cut -f1 affected.tsv | tr '\n' ' ')

The exit status is 0 whether or not anything is affected, so the output can be piped unconditionally. --list-targets cannot be combined with -t, -q or clean.

Compile 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" } }
  }
}
  • targetRelease sets TGTRLS, applied to each compile command that accepts it.
  • compileOptions is 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 command

Project Layout

Every top-level directory of the project is an IBM i library directory. A library directory holds one directory per source physical file, with one file per member (<lib>/<srcpf>/<member>.<ext>), and can also hold source files directly — a binding directory's or message file's CL source, for instance (<lib>/app.bnddir). A library that receives objects has a Rules.mk:

project/
├── srclib/                # Source library: source files, no Rules.mk
│   ├── qrpglesrc/
│   │   └── custr.rpgle
│   ├── qddssrc/
│   │   ├── custp.pf
│   │   └── custd.dspf
│   └── qcpysrc/
│       └── custpr.rpgle
├── dtalib/                # Object library for files
│   └── Rules.mk
├── objlib/                # Object library for programs
│   ├── Rules.mk
│   └── app.bnddir
├── applib/                # A library holding both its source and its objects
│   ├── Rules.mk
│   └── qrpglesrc/
│       └── apppgm.rpgle
├── tmp/                   # Build outputs (gitignored)
│   └── logs/              # Compiler output logs
└── build/                 # Dummy IBM i objects (Unix-type systems only, gitignored)
# dtalib/Rules.mk
custp.file: srclib/qddssrc/custp.pf

# objlib/Rules.mk
custd.file: srclib/qddssrc/custd.dspf
custr.pgm: srclib/qrpglesrc/custr.rpgle srclib/qcpysrc/custpr.rpgle | custd.file dtalib/custp.file
app.bnddir: app.bnddir

# applib/Rules.mk
apppgm.pgm: qrpglesrc/apppgm.rpgle | objlib/app.bnddir

A library's rules live in <lib>/Rules.mk, or in a Rules.mk inside one of its source-file directories (<lib>/qrpglesrc/Rules.mk, where a bare source name such as mypgm.rpgle is a file in qrpglesrc/). A library can use both. A Rules.mk at the project root is an error.

Hidden directories and node_modules/, tmp/, build/ and test/ are not library directories. Other top-level directories — docs/, say — are library directories that simply hold no rules.

By default every library's objects build into a single output library (set via BUILD_LIBRARY / IBMI_BUILD_LIBRARY). To build each library directory into its own IBM i output library, see Multi-library output below.

Multi-library output

CODERMAKE_LIBRARY_MAP enables per-library output:

export CODERMAKE_LIBRARY_MAP="LIBA=APPLIBA LIBB=APPLIBB"

Each library directory builds into the IBM i library named on the right side of =. Every library directory with a Rules.mk needs an entry; source-only library directories need none. The sentinel value auto enables a "natural" mapping where each library directory with a Rules.mk maps to a library of the same name:

export CODERMAKE_LIBRARY_MAP=auto

CODERMAKE_LIBRARY_MAP is mutually exclusive with BUILD_LIBRARY / IBMI_BUILD_LIBRARY. Setting both, or neither, is an error.

Cross-library object references

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.srvpgm

The qualifier works on both prerequisites and targets. Source files in another library use the 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.rpgle

Optional: 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 and COBOL COPY resolution

Member-style RPG /COPY and /INCLUDE directives and COBOL COPY ... OF statements resolve against the library directories:

  • A library-qualified reference (/COPY LIBB/QCPYSRC,MEMBER) is looked up in that library directory.
  • An unqualified reference (/COPY QCPYSRC,MEMBER, COPY MEMBER OF QCPYSRC) is looked up in each library directory in turn; a bare RPG member (/COPY MEMBER) is looked up in each library's qrpglesrc/. When a member exists in more than one library, the first is used and a warning names them all.

Set CODERMAKE_INCLUDE_PATH to choose which library directories are searched for unqualified references, and in what order:

export CODERMAKE_INCLUDE_PATH="LIBA LIBB LIBC"

A /COPY or COPY that names an IFS path is left as written and is relative to the project root (/COPY mylib/qcpysrc/member.rpgle).

How It Works

  1. Platform Detection: Detects IBM i vs Unix-like system
  2. Rules Discovery: Finds the library directories and the Rules.mk files in them
  3. Preprocessing: Transforms simplified rules into full GNU Make syntax
  4. Recipe Inference: Automatically determines build recipes from file extensions
  5. 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.

COBOL COPY (Copy Source) Handling

codermake resolves COBOL copy sources the way each compiler expects. ILE COBOL (.cblle, .sqlcblle) compiles from a stream file: an IFS style COPY resolves against the compile-time include path, and a source member style COPY MBR OF QCPYSRC is rewritten to the copybook's IFS path before compiling. OPM COBOL (.cbl, .cbl38, .sqlcbl) compiles from a source member, so the statement is left as written and codermake creates the member it names. Either way the original source keeps working on a system that compiles from source members.

See design/COBOL_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