The rpgc compiler transforms RPG source files into JVM .class files. Triton RPG runs on a workstation rather than inside the system that holds your files, tables, and library lists. Anything the compiler needs at compile time must be explicitly provided via command-line flags or a network connection.

For running compiled programs after compilation, see Running Compiled Programs. For display file and screen I/O topics, see Display Files and Screen I/O. For calling compiled programs from Java and type mappings, see Java Interoperability.

Basic Usage

rpgc source.rpgle
rpgc --output out --classpath vendor/triton-rpg.jar source.sqlrpgle
rpgc --lib module.rpgle         # Library mode (no main(), no screen handler)
rpgc --bnddir MYBNDDIR=lib/ caller.rpgle   # Resolve BNDDIR('MYBNDDIR') from lib/

One command handles both plain RPG and RPG with embedded SQL - rpgc recognizes EXEC SQL blocks directly, with no separate precompilation step.

The compiled .class file is written to the output directory (default ./out). All compiled programs require triton-rpg.jar on the classpath at runtime.

Class Name Derivation

By default, the compiler derives the output class name from the source filename following the TOBi (IBM's Object Builder for i) naming convention:

  1. Strip the source extension (.rpgle, .sqlrpgle, .rpg, etc.)
  2. Strip any object-type suffix (.pgm, .srvpgm)
  3. Upper-case the result
  4. Replace any remaining character that is not legal in a Java class name - most commonly an interior dot - with an underscore
Source filename Class name
depts.pgm.sqlrpgle DEPTS
empdet.sqlrpgle EMPDET
mylib.srvpgm.rpgle MYLIB
invoice.rpgle INVOICE
empdet.test.sqlrpgle EMPDET_TEST
billing.v2.rpgle BILLING_V2

Step 4 matters for filenames that carry an extra qualifier the TOBi convention does not recognize as an object type - for example the NAME.test.sqlrpgle naming used by RPGUnit test members, or a NAME.v2.rpgle version tag. The leftover dot is kept as an underscore rather than dropped, so two members whose names differ only by such a qualifier (empdet.sqlrpgle and empdet.test.sqlrpgle) derive distinct class names and never overwrite one another when built into the same output directory.

To override the derived name, use --class-name:

rpgc --class-name MYPROGRAM source.rpgle

A class name is a single Java identifier; it cannot contain a dot (use --package to place the class in a Java package). A dot or other illegal character in a --class-name value is sanitized to an underscore the same way a derived name is.

Target Package

By default a compiled class carries no Java package - its name stands alone. To make it referenceable from packaged Java code, emit it into a named package with --package:

rpgc --package com.acme.payroll source.rpgle

The class is then generated as com.acme.payroll.SOURCE and its .class file is written to the matching subdirectory of the output directory (out/com/acme/payroll/SOURCE.class). The value must be a valid Java package name (dot-separated identifiers); an invalid name is rejected. When several programs are compiled into the same package, bound (CALLB/prototype) calls between them resolve correctly.

Include Paths and /COPY Resolution

The RPG /COPY and /INCLUDE directives resolve member paths in this order:

  1. The source file's directory
  2. The working directory (the directory from which rpgc is invoked)
  3. Each --include-path directory, in the order specified

When source files use quoted relative paths (e.g., /COPY 'qrpgleref/myinclude.rpgleinc'), the path is resolved relative to each search location in order. For projects where include files live in a subdirectory and sources reference them with the subdirectory prefix, pass the project root as an include path:

rpgc --include-path . --include-path qrpgleref source.rpgle

Diagnostics report the coordinates you wrote: an error in the main source past a /COPY names the main file and its own line, and an error inside a copy member names that member's file and line (resolving to the innermost member when copies are nested). Editor jump-to-line therefore lands on the correct source, whether it lives in the program or in a copybook.

For the full specification of /COPY and /INCLUDE - including nesting limits, cross-format inclusion, and all supported path forms - see /COPY and /INCLUDE.

Externally Described Data Structures (ExtName)

A DCL-DS ... ExtName('TABLENAME') declaration instructs the compiler to read the table's column definitions from the database catalog. For rpgc, you must provide a connection to a Db2 for i system where the table exists:

JDBC_USER=MYUSER JDBC_PASSWORD=MYPASS \
rpgc --verify-jdbc-url jdbc:as400://hostname \
     --verify-library MYLIB \
     source.rpgle

The catalog user and password are read from environment variables - by default JDBC_USER and JDBC_PASSWORD - so the plaintext credentials never appear on the command line (where they would be visible in the process table). Use --verify-jdbc-user-env <name> / --verify-jdbc-password-env <name> to name different variables.

The --verify-library flag specifies which library (schema) to search for tables referenced by ExtName. Multiple libraries can be specified by repeating the flag.

Alternatively, if a .pf (physical file DDS) source file for the table exists locally, place it in the --dds-path directory. The compiler searches for .pf files before attempting JDBC resolution.

When resolving columns from the Db2 for i catalog, each column's data type is mapped to the corresponding RPG field type: CHAR/VARCHAR (and BINARY/VARBINARY) to character, DECIMAL to packed, NUMERIC to zoned, SMALLINT to a 4-digit binary field and INTEGER to a 9-digit binary field (the way native ILE RPG externally-describes them), BIGINT to integer, FLOAT to floating point (4- or 8-byte, following the column's byte length), DATE/TIME/TIMESTMP to the date, time, and timestamp types, and the DBCS graphic types GRAPHIC and VARG (vargraphic) to fixed-length and varying graphic fields (each of the column's double-byte character length; %SIZE is twice that). The same graphic mapping applies to a .pf DDS field of data type G. A column whose type has no faithful externally-described RPG field type - the large-object types (BLOB, CLOB, DBCLOB), XML, ROWID, DATALINK, and DECFLOAT - is rejected with a diagnostic naming the column and its Db2 type, rather than being silently resolved to character.

Verify mode is not limited to Db2 for i. Against Db2 for i the native catalog is used, because it carries detail no other catalog has - the 10-character system column name (which becomes the field name, with the SQL long name kept as its ALIAS) and the column CCSID (which tells a UCS-2 graphic column from a DBCS one). Against any other database the layout is read from standard JDBC metadata instead, so a program using ExtName compiles there too.

Equivalent column definitions resolve to the same RPG field on either path - a DECIMAL(9,2) is packed with 2 decimal positions, a NUMERIC(5,0) is zoned with 5 digits, wherever it is read from. Two differences follow from what JDBC metadata does not carry: the column name is used as given, with no separate ALIAS, and a national-character column (NCHAR/NVARCHAR) is treated as UCS-2 graphic. A keyed file takes its key from the table's primary key, or from its first unique index if it declares no primary key. Columns with no faithful RPG type are rejected by name on this path too.

In fixed-format, an externally described data structure is declared with an E in position 22 of the D-spec:

     D INVMAST       E DS

When no EXTNAME keyword is given, the external file name defaults to the data structure name (positions 7-21) - above, the file INVMAST. Supply EXTNAME('FILE') in the keyword area (positions 44-80) to import from a file whose name differs from the data structure name. Field resolution uses the same --dds-path / JDBC mechanism described above.

EXTNAME Arguments

EXTNAME accepts up to three colon-separated arguments, in both fixed- and free-format declarations:

     D custDs        E DS                  EXTNAME('INVMAST':'INVMASTR':*INPUT)
DCL-DS custDs ExtName('INVMAST':'INVMASTR':*INPUT) Qualified;
END-DS;
Argument Meaning
file name The external file whose fields become subfields. May be a quoted literal ('INVMAST') or an unquoted name. Required.
record-format name The record format within the file to import. Optional - when omitted, the file's first/only record format is used. The named format must match the format in the resolved .pf.
field-set selector One of *ALL, *INPUT, *OUTPUT, *KEY, or *NULL. Optional - when omitted, the input-buffer field set is imported (this is the IBM i default, not *ALL).

Field-set selector behavior: for a physical file every field is both input- and output-capable, so *ALL, *INPUT, *OUTPUT, and the default (omitted) all import the full field list. *KEY imports only the file's key fields, in key order. *NULL is accepted and imports the full field set, as the default does.

A field-set selector outside the set above is rejected with TRN1012.

The record-format name and field-set selector are both optional and positional, so a two-argument EXTNAME('FILE':*KEY) is ambiguous by position alone. Because a record-format name can never begin with * while every selector does, the compiler treats a second argument that begins with * as the selector (not a format name). Thus EXTNAME('INVMAST':*KEY) imports the key fields of INVMAST's default record format - there is no need to spell out the format name just to supply a selector.

When the named external file cannot be resolved (no .pf on the --dds-path and no JDBC catalog connection), the compiler reports TRN1014 identifying the unresolved file; an empty EXTNAME('') file name is reported as TRN1013.

EXTFLD (Subfield Rename)

EXTFLD('externalName') on a subfield inside an externally described data structure maps that subfield to a differently named field in the external record format. All other external fields are still imported under their original names. In fixed-format, the subfield line carries an E in position 22:

     D CustDs        E DS                  EXTNAME(CUSTMAST)
     D  CustomerName E                     EXTFLD(CUSNAM)
     D  Address      E                     EXTFLD(CUSADR)

In free-format:

DCL-DS CustDs EXTNAME('CUSTMAST');
  CustomerName EXTFLD('CUSNAM');
  Address EXTFLD('CUSADR');
END-DS;

In both cases, the external field CUSNAM is accessible as CustomerName and CUSADR as Address in the program. Fields without an EXTFLD mapping (e.g. CUSNO, CUSCTY) keep their original names, with PREFIX applied if set on the data structure.

A PREFIX('str') keyword renames every imported subfield by prepending the prefix string (so WHSE becomes X_WHSE under PREFIX('X_')). The optional replace-count form PREFIX('str':n) replaces the first n characters of each name instead of prepending. PREFIX is honored on both fixed-format and free-format externally described data structures. Fields renamed by EXTFLD are not affected by PREFIX.

JDBC Driver Requirement

Catalog resolution against Db2 for i requires the jt400 JDBC driver. Download jt400.jar from the JTOpen project or obtain it from your IBM i system at /QIBM/ProdData/HTTP/Public/jt400/lib/jt400.jar.

Tell the compiler where the driver is with --jdbc-driver-path:

rpgc --jdbc-driver-path jt400.jar [options] source.rpgle

This is the only way to supply it. rpgc is a self-executing jar, and a Java program launched from a jar ignores -cp and $CLASSPATH - so a driver placed on the classpath is invisible to the compiler. (--classpath is for resolving service-module exports, not JDBC drivers.) rpgc loads the driver from the path you give and never registers it process-wide.

When catalog resolution is not needed (no ExtName declarations referencing remote tables), no driver is required at all:

rpgc [options] source.rpgle

Build Order

Programs that use ExtName to reference SQL tables require those tables to exist in the database before compilation. A typical build sequence for a project containing both SQL table definitions and RPG programs:

  1. Create tables on the target database (via JDBC, RUNSQLSTM, or equivalent)
  2. Compile RPG programs with --verify-jdbc-url pointing to that database

This mirrors the IBM i workflow where CRTPF/RUNSQL precedes CRTBNDRPG.

Makefile Integration

A representative Makefile for a project with include files, display files, and externally described tables:

RPGC       = bin/rpgc
RUNTIME    = vendor/triton-rpg.jar
DRIVER     = vendor/jt400.jar
OUTDIR     = out

# JDBC_USER / JDBC_PASSWORD are read from the environment (exported by the make
# invocation), so the catalog credentials never appear on the rpgc command line.
RPGC_FLAGS = --include-path . \
             --include-path qrpgleref \
             --dds-path qddssrc \
             --output $(OUTDIR) \
             --classpath $(RUNTIME):$(OUTDIR) \
             --jdbc-driver-path $(DRIVER) \
             --verify-jdbc-url $(JDBC_URL) \
             --verify-library $(JDBC_LIBRARY)

out/%.class: qrpglesrc/%.sqlrpgle | $(OUTDIR)
	$(RPGC) $(RPGC_FLAGS) $<

When no externally described data structures are used, omit the --verify-* and --jdbc-driver-path flags - no driver is needed to compile.