ILE: Integrated Language Environment

El modelo de compilacion moderno de IBM i: modulos, service programs, binding directories, activation groups, prototipos, binder language y la migracion desde OPM.

Que es ILE

ILE (Integrated Language Environment) es el modelo de compilacion y ejecucion moderno de IBM i, introducido con OS/400 V3R1 en 1994. Reemplaza al modelo OPM (Original Program Model) y permite crear programas a partir de multiples modulos compilados independientemente, escritos potencialmente en distintos lenguajes (RPG, COBOL, C, CL). Es el equivalente conceptual a compilar archivos .o y enlazarlos con un linker en C/C++, o empaquetar clases en un JAR en Java.

OPM (Original Program Model)

  • Un fuente = un programa (*PGM)
  • CRTCLPGM, CRTRPGPGM (un paso)
  • No hay concepto de modulos
  • Codigo compartido solo via CALL externo
  • Cada CALL tiene overhead de activacion
  • Equivale a compilar cada .c en un ejecutable separado

ILE (Integrated Language Environment)

  • Multiples fuentes = modulos -> un programa
  • CRTRPGMOD + CRTPGM (compilar + enlazar)
  • Modulos reutilizables (*MODULE)
  • Service programs para codigo compartido
  • Llamadas bound (rapidas, sin overhead)
  • Equivale a compilar .o + linkar ejecutable / .so

La cadena de compilacion ILE

Fuente

QRPGLESRC

QCLSRC

Modulo

*MODULE

CRTRPGMOD

Programa

*PGM o *SRVPGM

CRTPGM / CRTSRVPGM

Modulos y programas

Un modulo (*MODULE) es el resultado de compilar un fuente individual. No es ejecutable por si solo. Un programa (*PGM) se crea enlazando uno o mas modulos con CRTPGM. Es como compilar archivos .c en .o y luego enlazarlos en un ejecutable con gcc.

CL — Crear modulo RPG (paso 1: compilar)
/* Crear modulo desde fuente RPG */
CRTRPGMOD MODULE(MILIB/UTILSTR)
          SRCFILE(MILIB/QRPGLESRC)
          SRCMBR(UTILSTR)
          DBGVIEW(*SOURCE)
          OPTION(*SRCSTMT *NODEBUGIO)

/* Crear modulo desde fuente CL */
CRTCLMOD MODULE(MILIB/UTILCL)
         SRCFILE(MILIB/QCLSRC)
         SRCMBR(UTILCL)
         DBGVIEW(*SOURCE)

/* Crear modulo desde fuente C */
CRTCMOD MODULE(MILIB/UTILC)
        SRCFILE(MILIB/QCSRC)
        DBGVIEW(*SOURCE)

/* Crear modulo SQLRPGLE */
CRTSQLRPGI OBJ(MILIB/UTILSQL)
           SRCFILE(MILIB/QRPGLESRC)
           OBJTYPE(*MODULE)
           DBGVIEW(*SOURCE)
           COMMIT(*NONE)

/* Listar modulos existentes */
WRKOBJ OBJ(MILIB/*ALL) OBJTYPE(*MODULE)
CL — Crear programa desde modulos (paso 2: enlazar)
/* Programa simple: un solo modulo */
CRTPGM PGM(MILIB/MIPGM)
       MODULE(MILIB/MIPGM)
       ACTGRP(*CALLER)

/* Programa con multiples modulos */
/* El primer modulo contiene el entry point (PEP) */
CRTPGM PGM(MILIB/GESTCLI)
       MODULE(MILIB/GESTCLI   +
              MILIB/UTILSTR    +
              MILIB/UTILFECHA  +
              MILIB/VALIDA)
       ACTGRP(*CALLER)
       DETAIL(*FULL)

/* Programa enlazado a modulos y service programs */
CRTPGM PGM(MILIB/GESTCLI)
       MODULE(MILIB/GESTCLI)
       BNDSRVPGM(MILIB/UTILITARIOS +
                 MILIB/VALIDACION)
       ACTGRP(*CALLER)

/* Shortcut: CRTBNDRPG = CRTRPGMOD + CRTPGM en un paso */
CRTBNDRPG PGM(MILIB/SIMPLE)
          SRCFILE(MILIB/QRPGLESRC)
          DBGVIEW(*SOURCE)
          DFTACTGRP(*NO)
          ACTGRP(*CALLER)
CRTBNDRPG vs CRTRPGMOD + CRTPGM: CRTBNDRPG es un atajo que compila y enlaza en un solo paso, ideal para programas simples. Para programas que comparten modulos o usan service programs, necesitas los dos pasos separados. Piensa en CRTBNDRPG como gcc -o programa fuente.c y los dos pasos como gcc -c fuente.c + gcc -o programa fuente.o.

Service Programs

Un service program (*SRVPGM) es una coleccion de procedimientos reutilizables que multiples programas pueden compartir. Es el equivalente directo de una shared library (.so en Linux, .dll en Windows) o un package/JAR en Java. Los procedimientos se ejecutan dentro del espacio de activacion del programa llamador, sin el overhead de un CALL externo.

RPG — Fuente del service program (UTILSTR)
**free
// ---------------------------------------------------------------
// UTILSTR: Service program de utilidades de string
//          Exporta procedimientos reutilizables
// ---------------------------------------------------------------
ctl-opt nomain;  // Sin mainline: solo exporta procedimientos

// ---------------------------------------------------------------
// toUpper: convierte string a mayusculas
// ---------------------------------------------------------------
dcl-proc toUpper export;
  dcl-pi *n varchar(1000);
    texto varchar(1000) const;
  end-pi;

  dcl-s resultado varchar(1000);

  exec sql
    SET :resultado = UPPER(:texto);

  return resultado;
end-proc;

// ---------------------------------------------------------------
// toLower: convierte string a minusculas
// ---------------------------------------------------------------
dcl-proc toLower export;
  dcl-pi *n varchar(1000);
    texto varchar(1000) const;
  end-pi;

  dcl-s resultado varchar(1000);

  exec sql
    SET :resultado = LOWER(:texto);

  return resultado;
end-proc;

// ---------------------------------------------------------------
// contiene: busca substring (retorna *on/*off)
// ---------------------------------------------------------------
dcl-proc contiene export;
  dcl-pi *n ind;
    texto   varchar(1000) const;
    buscar  varchar(100) const;
  end-pi;

  return %scan(buscar : texto) > 0;
end-proc;

// ---------------------------------------------------------------
// padLeft: rellena a la izquierda con un caracter
// ---------------------------------------------------------------
dcl-proc padLeft export;
  dcl-pi *n varchar(1000);
    texto  varchar(1000) const;
    largo  int(10) const;
    relleno char(1) const options(*nopass);
  end-pi;

  dcl-s car char(1) inz('0');
  dcl-s resultado varchar(1000);
  dcl-s faltan   int(10);

  if %parms >= 3;
    car = relleno;
  endif;

  faltan = largo - %len(%trim(texto));
  if faltan <= 0;
    return texto;
  endif;

  resultado = '';
  for i = 1 to faltan;
    resultado += car;
  endfor;
  resultado += %trim(texto);

  return resultado;
end-proc;
CL — Crear service program
/* Paso 1: Compilar como modulo */
CRTSQLRPGI OBJ(MILIB/UTILSTR)
           SRCFILE(MILIB/QRPGLESRC)
           SRCMBR(UTILSTR)
           OBJTYPE(*MODULE)
           DBGVIEW(*SOURCE)
           COMMIT(*NONE)

/* Paso 2: Crear service program */
CRTSRVPGM SRVPGM(MILIB/UTILSTR)
          MODULE(MILIB/UTILSTR)
          EXPORT(*ALL)
          ACTGRP(*CALLER)

/* Con binder language (mejor control de exports) */
CRTSRVPGM SRVPGM(MILIB/UTILSTR)
          MODULE(MILIB/UTILSTR)
          SRCFILE(MILIB/QSRVSRC)
          SRCMBR(UTILSTR)
          ACTGRP(*CALLER)

/* Service program con multiples modulos */
CRTSRVPGM SRVPGM(MILIB/UTILITARIOS)
          MODULE(MILIB/UTILSTR   +
                 MILIB/UTILFECHA +
                 MILIB/UTILNUM)
          EXPORT(*ALL)
          ACTGRP(*CALLER)
RPG — Usar un service program desde otro programa
**free
// Programa que usa procedimientos del service program UTILSTR
ctl-opt dftactgrp(*no) actgrp(*caller);
ctl-opt bnddir('MILIB/MYBNDDIR');

// Prototipos de los procedimientos exportados
dcl-pr toUpper varchar(1000) extproc('TOUPPER');
  texto varchar(1000) const;
end-pr;

dcl-pr toLower varchar(1000) extproc('TOLOWER');
  texto varchar(1000) const;
end-pr;

dcl-pr contiene ind extproc('CONTIENE');
  texto   varchar(1000) const;
  buscar  varchar(100) const;
end-pr;

dcl-pr padLeft varchar(1000) extproc('PADLEFT');
  texto   varchar(1000) const;
  largo   int(10) const;
  relleno char(1) const options(*nopass);
end-pr;

// Uso directo (llamada bound, sin CALL overhead)
dcl-s nombre varchar(100);
dcl-s codigo varchar(10);

nombre = toUpper('fernando secchi');
// nombre = 'FERNANDO SECCHI'

codigo = padLeft('42' : 7 : '0');
// codigo = '0000042'

if contiene(nombre : 'SECCHI');
  dsply 'Apellido encontrado';
endif;

*inlr = *on;
return;

Binding directories

Un binding directory (*BNDDIR) es una lista de modulos y service programs que el enlazador (binder) busca automaticamente al crear un programa. Es el equivalente a los paths de libreria ( -L en gcc) o las dependencias en un pom.xml de Maven. En lugar de especificar cada modulo y service program manualmente en CRTPGM, los agrupos en un binding directory.

CL — Crear y poblar binding directory
/* Crear binding directory */
CRTBNDDIR BNDDIR(MILIB/MYBNDDIR)
          TEXT('Utilidades de la aplicacion')

/* Agregar service programs al binding directory */
ADDBNDDIRE BNDDIR(MILIB/MYBNDDIR)
           OBJ((MILIB/UTILSTR   *SRVPGM)  +
               (MILIB/UTILFECHA *SRVPGM)   +
               (MILIB/VALIDACION *SRVPGM))

/* Agregar modulos individuales tambien */
ADDBNDDIRE BNDDIR(MILIB/MYBNDDIR)
           OBJ((MILIB/ERRHANDLER *MODULE))

/* Ver contenido de un binding directory */
DSPBNDDIR BNDDIR(MILIB/MYBNDDIR)

/* Remover entrada del binding directory */
RMVBNDDIRE BNDDIR(MILIB/MYBNDDIR)
           OBJ((MILIB/UTILSTR *SRVPGM))
RPG — Usar binding directory en el fuente
**free
// El CTL-OPT referencia el binding directory
// El binder resuelve automaticamente los procedimientos
ctl-opt dftactgrp(*no) actgrp(*caller);
ctl-opt bnddir('MILIB/MYBNDDIR');

// Ahora puedo usar procedimientos de UTILSTR, UTILFECHA,
// VALIDACION sin especificarlos manualmente en CRTPGM

dcl-pr toUpper varchar(1000) extproc('TOUPPER');
  texto varchar(1000) const;
end-pr;

dcl-pr formatFecha varchar(10) extproc('FORMATFECHA');
  fecha date(*iso) const;
  formato char(10) const;
end-pr;

dcl-pr validarCUIT ind extproc('VALIDARCUIT');
  cuit varchar(13) const;
end-pr;

// Uso transparente
dcl-s nombre varchar(100);
nombre = toUpper('test');

dcl-s fechaStr varchar(10);
fechaStr = formatFecha(%date() : 'DD/MM/YYYY');

if validarCUIT('20-12345678-9');
  dsply 'CUIT valido';
endif;
CL — Compilar usando binding directory
/* El BNDDIR en el CTL-OPT hace que CRTBNDRPG busque ahi */
CRTBNDRPG PGM(MILIB/MIPGM)
          SRCFILE(MILIB/QRPGLESRC)
          DBGVIEW(*SOURCE)
          DFTACTGRP(*NO)
          ACTGRP(*CALLER)

/* Alternativa: especificar BNDDIR en el comando */
CRTBNDRPG PGM(MILIB/MIPGM)
          SRCFILE(MILIB/QRPGLESRC)
          BNDDIR(MILIB/MYBNDDIR)
          DFTACTGRP(*NO)

/* Con dos pasos (modulo + programa) */
CRTRPGMOD MODULE(MILIB/MIPGM)
          SRCFILE(MILIB/QRPGLESRC)
          DBGVIEW(*SOURCE)

CRTPGM PGM(MILIB/MIPGM)
       MODULE(MILIB/MIPGM)
       BNDDIR(MILIB/MYBNDDIR)
       ACTGRP(*CALLER)

Activation groups

Un activation group es un contexto de ejecucion aislado dentro de un job. Controla el scope de recursos compartidos: archivos abiertos, commitment control, variables estaticas y manejo de errores. Es conceptualmente similar a un application domain en .NET o un class loader en Java: define los limites de aislamiento entre componentes.

Activation GroupComportamientoCaso de uso
*NEWCrea un AG nuevo cada vez que se activa el programaProgramas totalmente aislados, sin estado compartido
*CALLERUsa el AG del programa que lo llamaRecomendado para la mayoria de programas y SRVPGM
Nombre fijoUsa un AG con el nombre dado (se crea si no existe)Agrupar programas que comparten archivos y commit
QILEAG default del sistema para programas ILEUsado cuando no se especifica (legacy)
*DFTACTGRPAG default para OPM (compatibilidad)Solo para programas OPM, no usar en ILE nuevo
RPG — Especificar activation group
**free
// Recomendado: *CALLER (comparte AG con el llamador)
ctl-opt dftactgrp(*no) actgrp(*caller);

// Programa aislado: *NEW (AG propio cada vez)
ctl-opt dftactgrp(*no) actgrp(*new);

// AG nombrado: agrupa programas relacionados
ctl-opt dftactgrp(*no) actgrp('MIAPP');

// IMPORTANTE: dftactgrp(*no) es OBLIGATORIO para ILE
// Si usas dftactgrp(*yes), el programa corre en el
// default activation group OPM y pierde beneficios ILE
CL — Gestionar activation groups
/* Recuperar AG actual desde RPG via RTVJOBA */
RTVJOBA ACTGRP(&ACTGRP)

/* Reclamar (destruir) un AG por nombre */
RCLACTGRP ACTGRP('MIAPP')

/* Reclamar todos los AG elegibles */
RCLACTGRP ACTGRP(*ELIGIBLE)
Regla practica: Usa ACTGRP(*CALLER) para el 90% de tus programas y service programs. Solo usa *NEW cuando necesitas aislamiento total (ej: un programa que podria fallar y no quieres que afecte al llamador). Usa un AG nombrado cuando necesitas que multiples programas compartan archivos abiertos y commitment control.

Prototipos y procedure interfaces

Los prototipos (DCL-PR) declaran la interfaz de un procedimiento o programa externo: su nombre, parametros y tipo de retorno. La procedure interface (DCL-PI) define los parametros que recibe el procedimiento actual. Son como los header files (.h) en C o las interfaces en Java: permiten al compilador verificar tipos en tiempo de compilacion.

RPG — Prototipos (DCL-PR) para distintos targets
// Prototipo para un PROGRAMA externo (CALL)
dcl-pr enviarEmail extpgm('MILIB/SNDMAIL');
  destinatario varchar(200) const;
  asunto       varchar(100) const;
  cuerpo       varchar(5000) const;
end-pr;

// Prototipo para un PROCEDIMIENTO en service program
dcl-pr toUpper varchar(1000) extproc('TOUPPER');
  texto varchar(1000) const;
end-pr;

// Prototipo para API del sistema (C function)
dcl-pr sleep int(10) extproc('sleep');
  segundos int(10) value;
end-pr;

// Prototipo con parametros opcionales
dcl-pr log extproc('LOG');
  mensaje  varchar(500) const;
  nivel    char(10) const options(*nopass);
  programa char(10) const options(*nopass);
end-pr;

// Prototipo con LIKEDS (estructura como parametro)
dcl-ds clienteT qualified template;
  id      packed(7:0);
  nombre  varchar(100);
  email   varchar(200);
end-ds;

dcl-pr grabarCliente extproc('GRABARCLIENTE');
  cliente likeds(clienteT) const;
end-pr;
RPG — Procedure interface y EXPORT
// En el service program: procedimiento exportado
dcl-proc calcularDescuento export;
  // Procedure interface: define parametros y retorno
  dcl-pi *n packed(11:2);
    monto    packed(11:2) const;  // CONST = por valor
    porcentaje packed(5:2) const;
    aplicarIVA ind const options(*nopass);
  end-pi;

  dcl-s resultado packed(11:2);
  dcl-s conIVA ind inz(*off);

  // Verificar parametro opcional
  if %parms >= 3;
    conIVA = aplicarIVA;
  endif;

  resultado = monto * (porcentaje / 100);

  if conIVA;
    resultado *= 1.21;  // IVA 21%
  endif;

  return resultado;
end-proc;

// Procedimiento interno (NO exportado, privado al modulo)
dcl-proc validarPorcentaje;
  dcl-pi *n ind;
    pct packed(5:2) const;
  end-pi;

  return (pct >= 0 and pct <= 100);
end-proc;
CONST

Parametro por valor (no modificable). Recomendado por defecto.

VALUE

Pasa por valor (copia). Para APIs C y funciones.

OPTIONS(*NOPASS)

Parametro opcional. Verificar con %PARMS.

OPTIONS(*OMIT)

Se puede pasar *OMIT. Verificar con %ADDR.

EXTPGM('nombre')

El target es un programa (*PGM) externo.

EXTPROC('nombre')

El target es un procedimiento (en SRVPGM).

LIKEDS(template)

Parametro con estructura de una DS template.

DIM(n)

Parametro es un array de n elementos.

Import/Export y binder language

El binder language es un fuente que define explicitamente que procedimientos y variables exporta un service program. Sin binder language, se usa EXPORT(*ALL) que exporta todo. Con binder language se tiene control fino y se puede evolucionar la interfaz sin romper programas existentes. Es el equivalente a un archivo .def de exports en Windows o un version script en Linux.

Binder language — QSRVSRC/UTILSTR
/* ================================================== */
/* Binder language para service program UTILSTR        */
/* Fuente: MILIB/QSRVSRC miembro UTILSTR              */
/* ================================================== */

STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('UTILSTR_V2')

  /* Procedimientos exportados (interface publica) */
  EXPORT SYMBOL('TOUPPER')
  EXPORT SYMBOL('TOLOWER')
  EXPORT SYMBOL('CONTIENE')
  EXPORT SYMBOL('PADLEFT')

  /* V2: nuevos procedimientos agregados */
  EXPORT SYMBOL('TRIM_ALL')
  EXPORT SYMBOL('REPLACE')

ENDPGMEXP

/* Signature anterior para compatibilidad */
STRPGMEXP PGMLVL(*PRV) SIGNATURE('UTILSTR_V1')

  EXPORT SYMBOL('TOUPPER')
  EXPORT SYMBOL('TOLOWER')
  EXPORT SYMBOL('CONTIENE')
  EXPORT SYMBOL('PADLEFT')

ENDPGMEXP
CL — Crear SRVPGM con binder language
/* Crear service program usando binder language */
CRTSRVPGM SRVPGM(MILIB/UTILSTR)
          MODULE(MILIB/UTILSTR)
          SRCFILE(MILIB/QSRVSRC)
          SRCMBR(UTILSTR)
          ACTGRP(*CALLER)

/* El binder language controla:
   1. Que procedimientos son visibles externamente
   2. La signature (version) del service program
   3. Compatibilidad con versiones anteriores (PGMLVL(*PRV))

   Cuando agregas nuevos procedimientos:
   1. La CURRENT signature cambia (ej: V1 -> V2)
   2. La V1 se convierte en *PRV (compatible)
   3. Programas viejos siguen funcionando con V1
   4. Programas nuevos compilados usan V2 */
Por que usar binder language: Sin binder language, si cambias la lista de procedimientos del service program y recompilas, TODOS los programas que lo usan se invalidan y necesitan recompilarse. Con binder language y signatures, solo necesitas recompilar los programas que usan los nuevos procedimientos. Los existentes siguen funcionando con la signature anterior.

Depuracion ILE

El depurador de IBM i soporta depuracion de programas ILE compuestos por multiples modulos, incluyendo modulos de distintos lenguajes (RPG, CL, C). Los programas y service programs deben compilarse con DBGVIEW(*SOURCE) o DBGVIEW(*ALL).

CL — Depuracion de programas y service programs ILE
/* Iniciar debug de un programa ILE */
STRDBG PGM(MILIB/GESTCLI)
       UPDPROD(*YES)        /* Permitir cambios en prod */

/* Agregar service program al debug (si no se agrego auto) */
/* Desde la pantalla de debug: */
/*   ADDPGM SRVPGM(MILIB/UTILSTR)   */

/* Debug con multiples programas */
STRDBG PGM(MILIB/GESTCLI MILIB/VALIDA)

/* Teclas del depurador ILE: */
/*   F5  = Reanudar ejecucion              */
/*   F6  = Agregar breakpoint              */
/*   F10 = Step over                        */
/*   F11 = Step into (entrar al proc)       */
/*   F22 = Step out (salir del proc)        */
/*   F17 = Watch (monitorear variable)      */

/* Desde linea de comandos del depurador: */
/*   EVAL wkNombre              Ver variable      */
/*   EVAL clienteDS             Ver data structure */
/*   EVAL clienteDS.saldo       Ver campo de DS   */
/*   EVAL wkNombre = 'TEST'     Modificar valor   */
/*   EVAL *IN50                 Ver indicador      */
/*   BREAK 45                   Breakpoint linea 45*/
/*   BREAK calcular             BP en procedimiento*/
/*   CLEAR 45                   Quitar breakpoint  */
/*   WATCH wkSaldo              Monitorear cambios */

/* Terminar debug */
ENDDBG
CL — Debug opciones avanzadas
/* Opciones de DBGVIEW al compilar: */

/* *SOURCE: ve el fuente original (recomendado) */
CRTRPGMOD MODULE(MILIB/MIPGM) SRCFILE(MILIB/QRPGLESRC) +
          DBGVIEW(*SOURCE)

/* *LIST: ve el listing (fuente expandido) */
CRTRPGMOD MODULE(MILIB/MIPGM) SRCFILE(MILIB/QRPGLESRC) +
          DBGVIEW(*LIST)

/* *ALL: ve fuente original + listing */
CRTRPGMOD MODULE(MILIB/MIPGM) SRCFILE(MILIB/QRPGLESRC) +
          DBGVIEW(*ALL)

/* *STMT: solo breakpoints por numero de sentencia */
CRTRPGMOD MODULE(MILIB/MIPGM) SRCFILE(MILIB/QRPGLESRC) +
          DBGVIEW(*STMT)

/* *NONE: sin debug info (produccion final) */
CRTRPGMOD MODULE(MILIB/MIPGM) SRCFILE(MILIB/QRPGLESRC) +
          DBGVIEW(*NONE)

/* Tip: para SQLRPGLE, usa DBGVIEW(*SOURCE) */
/* y el debug muestra el fuente RPG original, */
/* no el fuente expandido por el precompilador SQL */

Migracion de OPM a ILE

Migrar de OPM a ILE no requiere reescribir codigo. La estrategia recomendada es incremental: primero recompilar los programas existentes como ILE, luego gradualmente extraer logica comun en service programs.

Programa OPM (antes)

  • CRTCLPGM / CRTRPGPGM
  • Corre en *DFTACTGRP
  • CALL externo para cada invocacion
  • No puede compartir procedimientos
  • Sin binding directories
  • Sin signatures ni versionado

Programa ILE (despues)

  • CRTBNDCL / CRTBNDRPG con DFTACTGRP(*NO)
  • Corre en AG nombrado o *CALLER
  • Bound calls para procedimientos compartidos
  • Service programs para logica reutilizable
  • Binding directories organizan dependencias
  • Binder language para versionado de API
Estrategia de migracion paso a paso
/* ================================================ */
/*  PASO 1: Recompilar programas como ILE           */
/*  (cambio minimo, maximo beneficio)               */
/* ================================================ */

/* ANTES (OPM): */
CRTCLPGM PGM(MILIB/MIPGMCL) SRCFILE(MILIB/QCLSRC)

/* AHORA (ILE): agregar DFTACTGRP(*NO) al fuente
   y compilar con CRTBNDCL */
CRTBNDCL PGM(MILIB/MIPGMCL) SRCFILE(MILIB/QCLSRC) +
         DFTACTGRP(*NO) ACTGRP(*CALLER) +
         DBGVIEW(*SOURCE)

/* Para RPG: */
/* ANTES: CRTRPGPGM */
/* AHORA: CRTBNDRPG con DFTACTGRP(*NO) */
/* Agregar **free y ctl-opt dftactgrp(*no) al fuente */

/* ================================================ */
/*  PASO 2: Identificar logica comun                */
/*  Buscar programas que hacen CALL a rutinas        */
/*  de utilidad compartida                           */
/* ================================================ */

/* Candidatos a service program: */
/* - Validaciones compartidas */
/* - Formateo de strings y fechas */
/* - Acceso a tablas comunes */
/* - Calculo de impuestos, descuentos */

/* ================================================ */
/*  PASO 3: Extraer a service programs              */
/* ================================================ */

/* 1. Mover subprocedimientos a fuente separado */
/* 2. Agregar EXPORT a los procedimientos       */
/* 3. Agregar CTL-OPT NOMAIN al fuente          */
/* 4. Compilar como *MODULE: CRTRPGMOD          */
/* 5. Crear *SRVPGM: CRTSRVPGM                 */
/* 6. Crear binding directory con el SRVPGM     */
/* 7. Actualizar programas consumidores:        */
/*    - Agregar prototipos (DCL-PR)             */
/*    - Agregar BNDDIR al CTL-OPT              */
/*    - Reemplazar CALL externo por llamada     */
/*      directa al procedimiento                */

/* ================================================ */
/*  PASO 4: Crear binding directory                 */
/* ================================================ */
CRTBNDDIR BNDDIR(MILIB/APPBNDDIR)
ADDBNDDIRE BNDDIR(MILIB/APPBNDDIR) +
           OBJ((MILIB/UTILSTR   *SRVPGM) +
               (MILIB/UTILFECHA *SRVPGM) +
               (MILIB/VALIDA    *SRVPGM))
Convivencia OPM + ILE: Los programas OPM e ILE pueden coexistir y llamarse entre si sin problemas. Un programa OPM puede hacer CALL a un programa ILE y viceversa. La migracion no tiene que ser todo-o-nada; puede hacerse gradualmente modulo por modulo.

Patrones ILE comunes

Los siguientes son patrones arquitectonicos que se repiten en aplicaciones IBM i bien estructuradas usando el modelo ILE.

Patron: Aplicacion en capas con ILE

Capa de presentacion

Programas *PGM con display files

Reciben input del usuario

Llaman a la capa de negocio

Capa de negocio

Service programs *SRVPGM

Reglas de negocio

Validaciones y calculos

Capa de datos

Service programs *SRVPGM

CRUD sobre tablas Db2

SQL embebido

RPG — Header file (copy book) con prototipos
// ---------------------------------------------------------------
// UTILSTR_H: Copy book con prototipos del SRVPGM UTILSTR
// Fuente: MILIB/QRPGLESRC miembro UTILSTR_H
// Incluir con /COPY en cada programa que use UTILSTR
// ---------------------------------------------------------------

dcl-pr toUpper varchar(1000) extproc('TOUPPER');
  texto varchar(1000) const;
end-pr;

dcl-pr toLower varchar(1000) extproc('TOLOWER');
  texto varchar(1000) const;
end-pr;

dcl-pr contiene ind extproc('CONTIENE');
  texto   varchar(1000) const;
  buscar  varchar(100) const;
end-pr;

dcl-pr padLeft varchar(1000) extproc('PADLEFT');
  texto   varchar(1000) const;
  largo   int(10) const;
  relleno char(1) const options(*nopass);
end-pr;
RPG — Programa consumidor con /COPY
**free
ctl-opt dftactgrp(*no) actgrp(*caller);
ctl-opt bnddir('MILIB/MYBNDDIR');

// Incluir prototipos del service program
/copy milib/qrpglesrc,utilstr_h
/copy milib/qrpglesrc,utilfec_h
/copy milib/qrpglesrc,valida_h

// Templates de data structures compartidas
/copy milib/qrpglesrc,cliente_t
/copy milib/qrpglesrc,factura_t

// Interfaz del programa
dcl-pi *n;
  prmCliente packed(7:0);
end-pi;

// Uso transparente de procedimientos de service programs
dcl-s nombre varchar(100);
dcl-s valido ind;

nombre = toUpper('fernando');           // de UTILSTR
valido = validarCUIT('20-12345678-9');  // de VALIDA
dsply formatFecha(%date() : 'DD/MM');   // de UTILFEC

*inlr = *on;
return;
CL — Script de build completo para una aplicacion ILE
PGM

  MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(ERROR))

  /* ======================================= */
  /*  BUILD: Aplicacion de Gestion           */
  /* ======================================= */

  /* --- Compilar modulos de service programs --- */
  CRTSQLRPGI OBJ(MILIB/UTILSTR) SRCFILE(MILIB/QRPGLESRC) +
             OBJTYPE(*MODULE) DBGVIEW(*SOURCE) COMMIT(*NONE)

  CRTSQLRPGI OBJ(MILIB/UTILFECHA) SRCFILE(MILIB/QRPGLESRC) +
             OBJTYPE(*MODULE) DBGVIEW(*SOURCE) COMMIT(*NONE)

  CRTSQLRPGI OBJ(MILIB/VALIDACION) SRCFILE(MILIB/QRPGLESRC) +
             OBJTYPE(*MODULE) DBGVIEW(*SOURCE) COMMIT(*NONE)

  CRTSQLRPGI OBJ(MILIB/DATOSCLI) SRCFILE(MILIB/QRPGLESRC) +
             OBJTYPE(*MODULE) DBGVIEW(*SOURCE) COMMIT(*NONE)

  /* --- Crear service programs --- */
  CRTSRVPGM SRVPGM(MILIB/UTILSTR) MODULE(MILIB/UTILSTR) +
            SRCFILE(MILIB/QSRVSRC) ACTGRP(*CALLER)

  CRTSRVPGM SRVPGM(MILIB/UTILFECHA) MODULE(MILIB/UTILFECHA) +
            SRCFILE(MILIB/QSRVSRC) ACTGRP(*CALLER)

  CRTSRVPGM SRVPGM(MILIB/VALIDACION) MODULE(MILIB/VALIDACION) +
            SRCFILE(MILIB/QSRVSRC) ACTGRP(*CALLER)

  CRTSRVPGM SRVPGM(MILIB/DATOSCLI) MODULE(MILIB/DATOSCLI) +
            SRCFILE(MILIB/QSRVSRC) ACTGRP(*CALLER)

  /* --- Actualizar binding directory --- */
  CRTBNDDIR BNDDIR(MILIB/APPBNDDIR)
  MONMSG MSGID(CPF9801)  /* Ya existe */
  RMVBNDDIRE BNDDIR(MILIB/APPBNDDIR) OBJ((*ALL))
  MONMSG MSGID(CPF0000)
  ADDBNDDIRE BNDDIR(MILIB/APPBNDDIR) +
             OBJ((MILIB/UTILSTR    *SRVPGM) +
                 (MILIB/UTILFECHA  *SRVPGM) +
                 (MILIB/VALIDACION *SRVPGM) +
                 (MILIB/DATOSCLI   *SRVPGM))

  /* --- Compilar programas principales --- */
  CRTBNDRPG PGM(MILIB/GESTCLI) SRCFILE(MILIB/QRPGLESRC) +
            DBGVIEW(*SOURCE) DFTACTGRP(*NO) ACTGRP(*CALLER)

  CRTBNDRPG PGM(MILIB/RPTVENTAS) SRCFILE(MILIB/QRPGLESRC) +
            DBGVIEW(*SOURCE) DFTACTGRP(*NO) ACTGRP(*CALLER)

  CRTBNDCL PGM(MILIB/BATCHCLI) SRCFILE(MILIB/QCLSRC) +
           DBGVIEW(*SOURCE) DFTACTGRP(*NO) ACTGRP(*CALLER)

  SNDPGMMSG MSG('=== BUILD completado OK ===') TOPGMQ(*EXT)
  GOTO CMDLBL(FIN)

  ERROR:
    DCL VAR(&ERRMSG) TYPE(*CHAR) LEN(256)
    RCVMSG MSGTYPE(*EXCP) MSG(&ERRMSG)
    SNDPGMMSG MSG('BUILD FALLO: ' *CAT &ERRMSG) TOPGMQ(*EXT)

  FIN:

ENDPGM
Organizacion recomendada: Crea un programa CL de build que compile todos los modulos, service programs y programas de tu aplicacion en el orden correcto. Esto es el equivalente a un Makefile o build.gradle en el mundo IBM i. Muchas tiendas usan herramientas como Bob (Better Object Builder) o iProjects de RDi para automatizar esto.