Migrating from RPG III to RPG IV

Log in to save

Converting an RPG III program to RPG IV is less dramatic than the word "migration" suggests. There is a command that does most of the work, and the result compiles. What the command cannot do is reorganise the program: and if you stop at the automatic conversion, what you get is an RPG IV that is still RPG III with different syntax.

The command

CVTRPGSRC FROMFILE(MYLIB/QRPGSRC)
          FROMMBR(PGMORD)
          TOFILE(MYLIB/QRPGLESRC)
          TOMBR(*FROMMBR)
          EXPCPY(*YES)

EXPCPY(*YES) expands the /COPY members into the converted source. It is worth thinking about: on one hand you see all the code in one place, on the other you lose the sharing. For a program using /COPY for layouts shared by dozens of programs, *NO is better, converting the copy members separately.

The command also produces a conversion report. Do not file it without reading it: it lists what could not be translated, and that is your work list.

What almost always gets stuck

Calls with CALL and PARM. The conversion leaves them as they are and they keep working, but they remain dynamic calls with no checking on parameters: if the called program changes the number or type of parameters, the error only shows up at run time. Moving to CALLP with a prototype shifts the check to compile time, and is probably the single improvement with the best benefit-to-effort ratio.

Data areas and long numeric fields. RPG IV allows packed fields longer than RPG III did. A field extended "because now we can" changes the record length: if other programs read the same file with the old layout, they break. Field lengths should only be changed with a list in hand of who uses that file, which you get from DSPPGMREF and DSPDBR.

Dates. RPG III kept them in six- or seven-digit numeric fields. RPG IV has a real date type, with arithmetic and conversions. It is one of the most worthwhile things to adopt — but not during the conversion: convert first, leaving everything as it is, then tackle one field at a time on a working program.

Indicators. They survive the conversion and keep working. Replacing them with %found, %eof and %error can be done gradually, and it is the step that makes the code readable to whoever comes next.

DFTACTGRP(*NO): the choice that opens everything up

A program converted and compiled like this:

CRTBNDRPG PGM(MYLIB/PGMORD) DFTACTGRP(*YES)

behaves exactly as before, and is a perfectly good first step. But with DFTACTGRP(*YES) you cannot bind service programs or use ILE procedures: you are in RPG IV with RPG III's possibilities.

Moving to:

CRTBNDRPG PGM(MYLIB/PGMORD)
          DFTACTGRP(*NO)
          ACTGRP(*CALLER)
          DBGVIEW(*SOURCE)

is what unlocks the ILE environment. It does have a consequence to be aware of: it changes the behaviour on unhandled errors and the way files stay open between calls. On programs that were called many times in a loop, you may notice a performance difference — better or worse depending on how they are written. That is why you convert one program at a time and test.

DBGVIEW(*SOURCE) does nothing for the program but a lot for whoever maintains it: it lets you debug against the source instead of generated code.

An order that reduces the risk

  1. Convert everything, changing nothing, and compile with DFTACTGRP(*YES). The result must behave identically to before.
  2. Put this stage into production. It is the most delicate moment: if something breaks, it breaks here, and the cause is the conversion and nothing else.
  3. Only then, program by program, move to DFTACTGRP(*NO).
  4. One program at a time, and only when you are already touching it for other reasons: prototypes instead of CALL, built-in functions instead of indicators, free-form instead of columns.

Step 2 is the one people tend to skip, merging conversion and rewrite into a single pass. That is a bad idea: when something does not add up, you no longer know whether to blame the conversion or the rewrite.

What is not worth converting

A program that has run for fifteen years, that nobody touches and nobody will touch, has no reason to be converted. RPG III still compiles and still runs. Conversion makes sense where there is maintenance: if a program has already cost hours this year, those hours will shrink in RPG IV. If nobody ever opens it, conversion is just risk with no return.


Which versions this works on

CVTRPGSRC — the conversion command is part of the ILE RPG compiler and has been available since RPG IV existed, that is since V3R1. Any machine on 7.x has it.

CRTBNDRPG with DFTACTGRP(*NO) — available since V3R1 with ILE. This is not a recent addition: it has been the standard compilation model for a very long time.

The next step, free-form, does need recent releases: free-form declarations and **FREE exist from IBM i 7.2 (or 7.1 with TR7 and the compiler PTF SI51094). If the system is on an earlier release, the conversion to RPG IV can still be done: you stay in the column format, which is exactly what the command produces.

Prototypes and CALLP in place of CALL/PARM have existed since V3R2 and require nothing recent.

The order recommended in the article holds on every release: what changes on older machines is only point 4 — free-form will not be available, but prototypes and built-in functions will.

Sources

← Back to blog

Comments

No comments yet. Be the first to comment!

You need an account to comment. Log in · Sign up