Developer ToolsFree generatorPDF, Excel, Word exportIBM iAll IBM i (AS/400) ERPs tools

RPGLE Service Program Generator: NOMAIN Module, Binder Source and CRTSRVPGM

This generator produces everything needed for an ILE RPG service program on IBM i: a NOMAIN module with two exported date procedures, a prototype copybook, binder source with a signature, a binding directory, a caller and the CL to build it all. Change the names and it becomes the skeleton for your own utility service programs.

Fill in

RPG exports procedure names in uppercase unless EXTPROC gives another name.

*GEN changes the signature whenever exports change; a fixed literal stays stable if you only append exports.

Generated code
Download:

1. Prototypes copybook: /home/dev/dateutil/DATEUTIL_p.rpgleinc

**FREE
// Prototypes for service program DATEUTIL
dcl-pr toIsoDate date extproc('TOISODATE');
  cyymmdd packed(7:0) const;
end-pr;

dcl-pr daysBetween int(10) extproc('DAYSBETWEEN');
  fromDate date const;
  toDate date const;
end-pr;

2. NOMAIN module: /home/dev/dateutil/DATEUTIL.rpgle

**FREE
ctl-opt nomain option(*srcstmt) datfmt(*iso);

/copy '/home/dev/dateutil/DATEUTIL_p.rpgleinc'

// Convert an ERP CYYMMDD number (e.g. 1261022) to a date
dcl-proc toIsoDate export;
  dcl-pi *n date;
    cyymmdd packed(7:0) const;
  end-pi;

  return %date(cyymmdd : *cymd);
end-proc;

// Days from fromDate to toDate (negative if toDate is earlier)
dcl-proc daysBetween export;
  dcl-pi *n int(10);
    fromDate date const;
    toDate date const;
  end-pi;

  return %diff(toDate : fromDate : *days);
end-proc;

3. Binder source: member DATEUTIL in MYLIB/QSRVSRC

STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('V1.0')
  EXPORT SYMBOL('TOISODATE')
  EXPORT SYMBOL('DAYSBETWEEN')
ENDPGMEXP

4. Build commands (CL)

CRTSRCPF FILE(MYLIB/QSRVSRC) RCDLEN(112)
/* add member DATEUTIL type BND with block 3, e.g. with ADDPFM and SEU or your editor */

CRTRPGMOD MODULE(MYLIB/DATEUTIL) SRCSTMF('/home/dev/dateutil/DATEUTIL.rpgle') DBGVIEW(*SOURCE)

CRTSRVPGM SRVPGM(MYLIB/DATEUTIL) MODULE(MYLIB/DATEUTIL) EXPORT(*SRCFILE) SRCFILE(MYLIB/QSRVSRC) SRCMBR(DATEUTIL) ACTGRP(*CALLER)

CRTBNDDIR BNDDIR(MYLIB/UTILS)
ADDBNDDIRE BNDDIR(MYLIB/UTILS) OBJ((MYLIB/DATEUTIL *SRVPGM))

/* check exports and signature */
DSPSRVPGM SRVPGM(MYLIB/DATEUTIL) DETAIL(*SIGNATURE)
DSPSRVPGM SRVPGM(MYLIB/DATEUTIL) DETAIL(*PROCEXP)

5. A caller program

**FREE
ctl-opt dftactgrp(*no) actgrp(*new) bnddir('MYLIB/UTILS');

/copy '/home/dev/dateutil/DATEUTIL_p.rpgleinc'

dcl-s orderDate date;
dcl-s ageDays int(10);

orderDate = toIsoDate(1261022);
ageDays = daysBetween(orderDate : %date());
dsply ('Order age in days: ' + %char(ageDays));

*inlr = *on;
return;

// Compile: CRTBNDRPG PGM(MYLIB/TESTDATE) SRCSTMF('/home/dev/dateutil/testdate.rpgle') DBGVIEW(*SOURCE)

6. Adding a procedure later without breaking callers

/* Fixed signature: add the new export at the END, keep the old list as *PRV */
STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('V1.1')
  EXPORT SYMBOL('TOISODATE')
  EXPORT SYMBOL('DAYSBETWEEN')
  EXPORT SYMBOL('NEWPROC')
ENDPGMEXP
STRPGMEXP PGMLVL(*PRV) SIGNATURE('V1.0')
  EXPORT SYMBOL('TOISODATE')
  EXPORT SYMBOL('DAYSBETWEEN')
ENDPGMEXP

/* Replace the module in the existing service program */
UPDSRVPGM SRVPGM(MYLIB/DATEUTIL) MODULE(MYLIB/DATEUTIL) EXPORT(*SRCFILE) SRCFILE(MYLIB/QSRVSRC) SRCMBR(DATEUTIL)

Export symbols in binder source are case sensitive and must match the procedure's external name, which RPG uppercases by default. Never reorder or remove exports in the *CURRENT list: callers bind to export positions, and a signature mismatch at call time fails with MCH4431 (program signature violation). CRTSRVPGM and UPDSRVPGM also accept SRCSTMF for binder source on recent releases; check the command prompt on your system. Use DATFMT consistently between module and callers.

Get your binder source checked

Send your current binder source and the list of procedures you want to add and we will return a signature-safe version with *PRV blocks.

Optional. The tool and every export work without this. No spam.

Was this tool useful?

How to use it

Set the service program, module and library names, the two procedure names and their uppercase export symbols, and choose a fixed signature or *GEN. Save block 1 and block 2 as stream files in the source directory, put block 3 in a source member of type BND in QSRVSRC, then run block 4 in order: CRTRPGMOD compiles the module, CRTSRVPGM binds it using the binder source, and the binding directory lets callers find it by name. Compile the caller in block 5 with CRTBNDRPG and run it. DSPSRVPGM with DETAIL(*SIGNATURE) and DETAIL(*PROCEXP) confirms the signature and the exported procedures.

  • One copybook of prototypes shared by the module and every caller
  • Binding directory in ctl-opt bnddir, so callers never list the service program by hand
  • ACTGRP(*CALLER) runs the service program in the caller's activation group

Binder language and signatures

STRPGMEXP and ENDPGMEXP enclose the list of exported symbols. When a program binds to the service program it records the service program's current signature and the position of each procedure it uses. At call time the system checks the signature; if it does not match any signature the service program carries, the call fails with MCH4431. With SIGNATURE(*GEN) the signature is computed from the export list, so adding an export changes it and every caller needs rebinding, unless you keep the old list as a PGMLVL(*PRV) block. With a fixed literal such as 'V1.0' the signature only changes when you change it. Either way, append new exports at the end and never reorder or delete existing ones.

Worked example

Many IBM i ERPs store dates as 7-digit CYYMMDD numbers where C = 1 means 2000 to 2099. The caller passes 1261022 to toIsoDate, which uses %DATE with the *CYMD format to return 2026-10-22, then daysBetween with %DIFF and *DAYS returns the age of that order against today's date. DSPSRVPGM DETAIL(*PROCEXP) shows TOISODATE and DAYSBETWEEN as exports. To add a third procedure later, follow block 6: append the export, bump the literal to 'V1.1', keep 'V1.0' as a *PRV block, and UPDSRVPGM. Existing callers keep working without a recompile.

Best practices and gotchas

Keep one prototype copybook per service program and include it in the module itself, so the compiler checks that prototypes and procedure interfaces agree. Use CONST or VALUE parameters wherever possible so callers can pass literals and expressions. EXPORT SYMBOL names are case sensitive: an RPG procedure named toIsoDate exports as TOISODATE unless EXTPROC says otherwise. Do not use DFTACTGRP(*YES) in callers; it cannot bind to service programs. Avoid ACTGRP(*NEW) on the service program itself; *CALLER is the usual choice. Group procedures by subject (dates, strings, API wrappers) rather than one giant utility service program.

Frequently Asked Questions

What is a service program in AS400 RPG?

A service program (*SRVPGM) is a bound collection of exported procedures, the IBM i equivalent of a shared library or DLL. Programs bind to it by reference, so one copy of the code serves every caller, and you can update the service program without recompiling callers as long as the signature stays compatible.

Should I use SIGNATURE(*GEN) or a fixed signature?

*GEN is safe but strict: any export change creates a new signature, so you must keep old export lists as PGMLVL(*PRV) blocks or rebind callers. A fixed literal is simpler to manage when you follow the rule of only appending exports. Many shops use fixed literals with a version string and keep *PRV blocks for history.

What causes MCH4431 signature violation?

MCH4431 means a program was bound to a signature that the service program no longer carries, usually because exports changed with *GEN and no *PRV block was kept, or a fixed signature was edited. Fix by restoring the old signature as *PRV in the binder source and running UPDSRVPGM, or by rebinding the callers with UPDPGM.

What are RPG programming best practices for modular code?

Put reusable logic in NOMAIN modules exposed through service programs, share prototypes through copybooks, find service programs through binding directories, pass parameters CONST or VALUE, and control exports with binder source. Use fully free syntax, ACTGRP(*CALLER) on service programs and named or *NEW groups on entry programs, and compile with DBGVIEW(*SOURCE).

If your developers want an assistant that maps service programs, their exports and who calls them across your IBM i libraries, we can set that up privately on your own servers.