In a recent Tuesday Tips video newsletter (www.systeminetwork.com/TuesdayTips), I provided a quick-and-easy way for programmers to insert the creation source file name into ILE *PGM objects. Unlike legacy RPG III *PGM objects, this information is only stored in the *MODULE, not in the resulting *PGM. So it becomes bothersome when using in-house and even some older versions of third-party tools. The CHGPGMSRC CL command I featured in that issue of Tuesday Tips seemed to be well received, but then someone wrote in and asked me this question:
(and I paraphrase) Bob, is there a way to automate the process of assigning the source file, library, and member name to the *PGM object? For example, can I pull this information from the *PGM's first *MODULE object and assign it?
This a great question because I've actually wanted this capability since they shipped ILE. Here's the basic scenario: If you run the DSPOBJD command for a given ILE program and then selection option 8 (Service Information), a display similar to the following appears:
Display Object Description - Service
Object . . . . . . . . . . . . . . . : CHGPGMINF
Library . . . . . . . . . . . . . : RPGCODER
Library ASP device . . . . . . . . . : *SYSBAS
Library ASP group . . . . . . . . . : *SYSBAS
Type . . . . . . . . . . . . . . . . : *PGM
Source file . . . . . . . . . . . . :
Library . . . . . . . . . . . . . :
Member . . . . . . . . . . . . . . . :
Attribute . . . . . . . . . . . . . : RPGLE
User-defined attribute . . . . . . . :
Freed . . . . . . . . . . . . . . . : NO
Size . . . . . . . . . . . . . . . . : 208896
Creation date/time . . . . . . . . . : 06/01/08 05:34:38
Source file date/time . . . . . . . :
System level . . . . . . . . . . . . : V5R4M0
Compiler . . . . . . . . . . . . . . : CRTPGM V5R4M0
Notice the source file, library, and member name items are empty. This is because of the fundamental design of *PGM objects under ILE. They are made up of one or more *MODULE objects. In order to produce a *MODULE, a source member is compiled, and that compiled source member produces a *MODULE object that is bound into a *PGM object by the CRTPGM command or "bind phase" of the compiler.
The CHGPGMSRC command I mentioned earlier allows you to insert something (anything actually) into the source file, library, and member name area of an existing ILE *PGM object. So you could issue the following command:
CHGPGMSRC PGM(AP0150) SRCFILE(VEGGIE/PICKLES) SRCMBR(PEPPERS)
And end up having something that looks like this:
Display Object Description - Service
Object . . . . . . . . . . . . . . . : CHGPGMINF
Library . . . . . . . . . . . . . : RPGCODER
Library ASP device . . . . . . . . . : *SYSBAS
Library ASP group . . . . . . . . . : *SYSBAS
Type . . . . . . . . . . . . . . . . : *PGM
Source file . . . . . . . . . . . . : PICKLES
Library . . . . . . . . . . . . . : VEGGIE
Member . . . . . . . . . . . . . . . : PEPPERS
Attribute . . . . . . . . . . . . . : RPGLE
User-defined attribute . . . . . . . :
Freed . . . . . . . . . . . . . . . : NO
Size . . . . . . . . . . . . . . . . : 208896
Creation date/time . . . . . . . . . : 06/01/08 05:34:38
Source file date/time . . . . . . . :
System level . . . . . . . . . . . . : V5R4M0
Compiler . . . . . . . . . . . . . . : CRTPGM V5R4M0
Which probably isn't as good a result as you might like.
This could make the viewer's request seem much more urgent. So I've spend a few minutes throwing together a new RPG IV program and CL command that does what you really want -- you give it an ILE *PGM name, and it looks at the *MODULE objects contained in that program, and if it finds one that has the same name as that of the *PGM, it extracts its source file, library, and member name and then changes the *PGM's creation information to match.
I've written a simple CL command definition source member to go with it, called Change Program Information (CHGPGMINF). The source code for that command follows:
CHGPGMINF: CMD PROMPT('Change Program Information')
/* Command processing program is: CHGPGMINF */
/* Updates a program's Creation Source File & Mbr */
/* with that of the *MODULE in the *PGM with the */
/* same name as the *PGM. */
PARM KWD(PGM) TYPE(QUAL2) MIN(1) PROMPT('Program +
name')
QUAL2: QUAL TYPE(*NAME) EXPR(*YES)
QUAL TYPE(*NAME) DFT(*LIBL) SPCVAL((*LIBL) +
(*CURLIB)) EXPR(*YES) PROMPT('Library')
To compile this command, just use PDM option 14 (CRTCMD). No additional parameters or options are needed.
| RPG TnT: 101 Dynamic Tips ‘n Techniques with RPG IV by Bob Cozzi is available now. | |
![]() |
The latest book from author Bob Cozzi is 300 pages, containing 101 example RPG IV Tips and Techniques for everyday programming tasks, from date calculations, to regular express searches, to using APIs. Cozzi wrote down every cool technique he's found over the years, updated them, and consolidated them into this compact book that is a great desktop companion -- and it includes full example source code. Order your copy today. |
Of course, we also need the RPG IV source code that will perform this task for us. Rather than go over it line by line, I'm going to comment on specific areas in a generalized sense and leave the analysis to you. Here's to source code:
H BNDDIR('QC2LE')
H DFTACTGRP(*NO) OPTION(*SRCSTMT : *NODEBUGIO)
/INCLUDE QSYSINC/QRPGLESRC,qbnlpgmi
/INCLUDE QSYSINC/QRPGLESRC,qusgen
/INCLUDE QSYSINC/QRPGLESRC,qusec
D apiError DS LikeDS(QUSEC) Inz
D szUserSpace DS Qualified
D name 10A Inz('MODLIST')
D library 10A Inz('QTEMP')
D nPos S 10I 0
D apiFmt C Const('PGML0100')
D QUALOBJ DS Qualified
D object 10A
D obj 10A overlay(object)
D objName 10A overlay(object)
D name 10A overlay(object)
D library 10A
D lib 10A overlay(library)
D libname 10A overlay(library)
D objlib 10A overlay(library)
D module_T DS Qualified
D pgm LikeDS(qualObj)
D module 10A
D modname 10A Overlay(module)
D name 10A Overlay(module)
D library 10A
D modLib 10A Overlay(library)
D srcfile LikeDS(qualObj)
D srcMbr 10A
D seuType 10A
D module DS LikeDS(module_T)
** Retrieve User Space Data
D QusRtvUS PR extPgm('QUSRTVUS')
D szUserSpace 20A Const
D nStart 10I 0 Const
D nLength 10I 0 Const
D szRtnData 65535A Options(*VARSIZE)
D api_error LikeDS(QUSEC)
D OPTIONS(*VARSIZE:*NOPASS)
** List the Modules in an ILE *PGM
D ListModules PR extPgm('QBNLPGMI')
D userSpace 20A Const
D Format 8A Const
D pgmName 20A Const
D apiError LikeDS(QUSEC)
D OPTIONS(*VARSIZE)
** Change Object Description
D QLICOBJD PR EXTPGM('QLICOBJD')
D rtnLib 10A
D object Const LikeDS(QualObj)
D objType 10A Const
D chgData 4096A Const OPTIONS(*VARSIZE)
D apiErrorDS 16A OPTIONS(*VARSIZE)
D GetNextModule PR 10I 0
D module LikeDS(Module_T)
D ref 10I 0
D ChgPgmSrc PR
D pgm LikeDS(QUALOBJ) Const
D srcFile LikeDS(QUALOBJ) Const
D srcMbr 10A Const OPTIONS(*NOPASS)
D chgpgmInf PR EXTPGM('CHGPGMINF')
D pgm LikeDS(qualobj)
D ChgpgmInf PI
D pgm LikeDS(qualobj)
C eval *INLR = *ON
/FREE
ListModules(szUserSpace : apiFmt : pgm: apiError);
dow GetNextModule(module : nPos ) > 0;
if module.pgm.Name = pgm.name and
module.pgm.Library = pgm.library;
chgpgmsrc(pgm : module.srcfile : module.srcMbr);
endif;
enddo;
/END-FREE
P GetNextModule B
D GetNextModule PI 10I 0
D module LikeDS(Module_T)
D nRef 10I 0
D nRtvLen S 10I 0
D nStart S 10I 0
D genHdr DS LikeDS(QUSH0100) Inz
D moduleInfo DS LikeDS(QBNL0100)
/FREE
QusRTVUS(szUserSpace : 1 : %size(genHdr) : genHdr);
if (nRef <= 0);
nRef = 1;
endif;
if (nRef > genHdr.QUSNBRLE);
return 0;
endif;
// Each entry is located at start-of-list, plus length of entry,
// (QUSSEE) multipled by the nRefPos (entry number).
nStart = (genHdr.QUSOLD+1) + ((nRef-1) * genHdr.QUSSEE);
nRtvLen = %size(moduleInfo);
QusRTVUS(szUserSpace : nStart : nRtvLen : moduleInfo);
module.Pgm.Name = moduleInfo.QBNPGMN00;
module.Pgm.Library = moduleInfo.QBNPGMLN;
module.Name = moduleInfo.QBNBMN;
module.library = moduleInfo.QBNBMLN;
module.srcfile.name = moduleInfo.QBNSFILN;
module.srcfile.library = moduleInfo.QBNSFLN;
module.srcMbr = moduleInfo.QBNSFILM;
module.seuType = moduleInfo.QBNMA;
nRef += 1;
return nRef;
/end-free
P GetNextModule E
P ChgPgmSrc B
D ChgPgmSrc PI
D pgm LikeDS(QUALOBJ) Const
D srcFile LikeDS(QUALOBJ) Const
D srcMbr 10A Const OPTIONS(*NOPASS)
D OIR_srcInfo_T DS Qualified
D srcFile 10A
D srcLib 10A
D srcMbr 10A
D keyInfo_T DS Qualified Inz
D keyID 10I 0
D length 10I 0
D data LikeDS(OIR_srcInfo_T)
D objInfo_T DS Qualified
D keyCount 10I 0
D keyData LikeDS(keyInfo_T) Inz(*LIKEDS)
D rtnLib S 10A
D myObjInfo DS LikeDS(objInfo_T) Inz
C eval *INLR = *ON
/free
myObjInfo.keyCount = 1;
myObjInfo.keyData.length = %size(oir_srcInfo_T);
myObjInfo.keyData.keyID = 1; // Change source lib/file/mbr
myObjInfo.keyData.data.srcFile = srcfile.name;
myObjInfo.keyData.data.srcLib = srcfile.lib;
if (%parms < 3 or srcmbr = '*PGM' or srcmbr = ' ');
myObjInfo.keyData.data.srcMbr = pgm.name;
else;
myObjInfo.keyData.data.srcMbr = srcmbr;
endif;
qlicobjd(rtnLib : pgm :'*PGM': myObjInfo : apiError);
return;
/end-free
P chgPgmSrc E
To use this source member, you really need the "System Openness Includes" or QSYSINC library installed. If you don't have it installed (it is free from IBM and already on your installation media), you may be able to locate the necessary include members on the web or simply enter them yourself. But if you don't have QSYSINC installed, you really need to change your installation procedures so that this option is included next time you update the operation system. The three lines of code that depend on QSYSINC are the three /INCLUDE statements, as follows:
/INCLUDE QSYSINC/QRPGLESRC,qbnlpgmi
/INCLUDE QSYSINC/QRPGLESRC,qusgen
/INCLUDE QSYSINC/QRPGLESRC,qusec
These include members copy in several data structures that are used as Data Structure Templates. The data returned from a couple of API calls is stored in this structures. Those APIs are
The QBNLPGMI API produces a list of module names and their corresponding creation information for a given *PGM. This information includes the source file, library, and member names along with things such as SEU type, creation date, compilers and version of compilers used, and so on.
Call QBNLPGMI to generate the list of *MODULE objects and their information. This information is stored in a user space. I've published information on creating a user space in January 2008, and you will need the Creation User Space (CRTUSRSPC) CL command featured in that issue or one of the similar commands from TAATOOLS or RPG xTools. A user space named MODLIST in the QTEMP library must be created before running the CRTPGMINF command. (Wouldn't it be great if the APIs were smart enough to just create the user spaces if they didn't exist? Is that asking too much?)
The QUSRTVUS API allows you to retrieve data from a user space. It's similar to using the RTVDTAARA command, but it works with user spaces. This API is used to extract each entry on the list generated by QBNLPGMI. Each entry is the description of the *MODULE object and is stored in a data structure based on the QBNL0100 data structure from QSYSINC. I named my data structure MODULEINFO on the following statement:
D moduleInfo DS LikeDS(QBNL0100)
Each module entry on the list produced by QBNLPGMI is returned by the GetNextModule subprocedure. This subprocedure simply iterates through the list of modules and returns the "next" one to the caller. I created a more concise data structure to return just the module information I'm interested in. That data structure is based on MODULE_T. It also accepts, modifies, and returns a reference handle that is used to determine which entry index is being requested. This process continues in the DOW loop until the name of the module returned is the same as that of the program.
Once the correct module information is returned, the third API, QLICOBJD, is called. This API allows you to change what's called the "object information repository," or OIR, for the program. The name of the source file, library, and member that were used to create the internal *MODULE is passed to this API, which changes the program's description.
At this point, the ILE *PGM has the name of the source file, library, and member used to create it stored in its description. Using DSPOBJD on the program and selecting option 8 reveals the following:
Display Object Description - Service
Object . . . . . . . . . . . . . . . : CHGPGMINF
Library . . . . . . . . . . . . . : RPGCODER
Library ASP device . . . . . . . . . : *SYSBAS
Library ASP group . . . . . . . . . : *SYSBAS
Type . . . . . . . . . . . . . . . . : *PGM
Source file . . . . . . . . . . . . : QRPGLESRC
Library . . . . . . . . . . . . . : RPGCODER
Member . . . . . . . . . . . . . . . : CHGPGMINF
Attribute . . . . . . . . . . . . . : RPGLE
User-defined attribute . . . . . . . :
Freed . . . . . . . . . . . . . . . : NO
Size . . . . . . . . . . . . . . . . : 208896
Creation date/time . . . . . . . . . : 06/01/08 05:34:38
Source file date/time . . . . . . . :
System level . . . . . . . . . . . . : V5R4M0
Compiler . . . . . . . . . . . . . . : CRTPGM V5R4M0
Note that the source file name is now display as desired.
Bob Cozzi is currently touring the U.S. with his one-day RPG World Academy seminar on RPG Application Modernization entitled Subprocedures Modules, and Service Programs. Visit www.RPGWorld.com for details, dates, and locations.
if module.pgm.Name = pgm.name and module.pgm.Library = pgm.library;The update is posted on the Downloads page for RPG Coder and Tuesday Tips located on www.RPGWorld.com (click the Tuesday Tips Downloads button to get the updated version. The fundamental change is to use the Module name not the module's program name:if module.Name = pgm.name;We also use Java here. One of the source management tools you can use is Concurrent Versioning System (CVS). With CVS, you can include markings when you take a version and/or release of source code that goes into a build.
You can use this common build-release reference as a fine-grained control and a reference that is identifiable in the compiled code, Jars and manifests. The object code will then include byte codes that you can identify so that you know at what version and release the code was and thus what changes or dependencies (e.g., file, file systems, databases, network access) may have been implemented.
How could you do the same thing for RPG, RPG ILE, CL, and CL ILE type programs? How would you be able to identify the version and release in System i-based objects to corroborate with a build system that used version and release (e.g., CVS)?
We have been doing Java and RPG development for a while and use two totally different source management faciltities, like many other shops. The preferred model is to have one. Our i5 solution for source management does not handle SQL-based objects and source code well. Unfortunately, one of our legacy-based databases is dependent on DDS physical and logical files.
Do you have any ideas on best practices?