Guru: The Binding Directory Is The Key
October 1, 2018 Ted Holt
Hey, Ted:
We license a software package to handle email on our IBM i system. When I write an RPG program that uses those routines, I can’t compile with the usual Create Bound RPG Program (CRTBNDRPG) command. Instead I have to use a two-step process — Create RPG Module (CRTRPGMOD) followed by Create Program (CRTPGM). Can you explain why this is? Even better, can you tell me if there is a way that I’m not aware of to compile with CRTBNDRPG?
— David
David continued, “I use CRTBNDCL and CRTBNDRPG, as they get me what I need, otherwise I am like the ostrich with his head in the sand, until I am attacked by the lion of need.” Today is David’s lucky day. He asked me to write an article, and I am granting his wish.
Why does David have to go through two steps? Because his program does not contain all the routines it uses. One of the beauties of ILE is that a program can use an external routine as if it were an internal routine. This is not a new concept. Linkage editors have been around for a long time.
In David’s case, the software vendor has delivered the email routines in the form of a service program. David must link his program to those routines.
I think I can best explain David’s situation with an example.
Let’s say that you and I work in a shop that stores dates as seven-digit packed-decimal numbers in CYYMMDD format (e.g. 1181225), and we often have to print or display such dates as day, month-name, and year (25 December 2018). We have licensed a DATERTNS (date routines) service program that contains a DisplayDate subprocedure to make such a conversion. Here’s the source code for that service program:
ctl-opt nomain option(*srcstmt: *nodebugio); /include prototypes,DateRtns dcl-ds Months; *n varchar(11) inz(' January ' ); *n varchar(11) inz(' February ' ); *n varchar(11) inz(' March ' ); *n varchar(11) inz(' April ' ); *n varchar(11) inz(' May ' ); *n varchar(11) inz(' June ' ); *n varchar(11) inz(' July ' ); *n varchar(11) inz(' August ' ); *n varchar(11) inz(' September '); *n varchar(11) inz(' October ' ); *n varchar(11) inz(' November ' ); *n varchar(11) inz(' December ' ); Month varchar(11) dim(12) pos(1); end-ds Months; dcl-proc DisplayDate export; dcl-pi DisplayDate varchar(17); inDate packed (7) const; end-pi DisplayDate; dcl-s CharDate char(8); CharDate = %char(%date(inDate: *cymd): *iso0); return %triml(%subst(CharDate: 7: 2): '0') + Month(%uns(%subst(CharDate: 5: 2))) + %subst(CharDate: 1: 4); end-proc DisplayDate;
Here’s the prototype, which we copy into our programs using the /INCLUDE compiler directive.
dcl-pr DisplayDate varchar(17); inDate packed (7) const; end-pr DisplayDate;
Here’s our program, which calls the DisplayDate routine.
ctl-opt option(*srcstmt: *nodebugio); /include prototypes,DateRtns dcl-s PrintDate varchar(17); dcl-s FromDate packed(7) inz(1181225); *inlr = *on; PrintDate = DisplayDate (FromDate); return;
Note that our code includes the prototype and calls the DisplayDate subprocedure, which is defined in the service program.
When we need to compile a program, CRTBNDRPG doesn’t work because the compiler doesn’t know where the DisplayDate routine is. At this point, we have been doing the only thing we knew to do — drop back and punt.
CRTRPGMOD MODULE(QTEMP/SALES02R) SRCFILE(MYLIB/QRPGLESRC) SRCMBR(SALES02R)
Now we have a module object. CRTPGM lets us link that module to the vendor’s service program.
CRTPGM PGM(MYLIB/SALES02R) MODULE(QTEMP/SALES02R) BNDSRVPGM((DATERTNS))
Now we have a program object that uses a subprocedure in a vendor-supplied service program. Mission accomplished.
But it would be so much simpler if we could use CRTBNDRPG, and we can! Here’s how.
First, we create a binding directory. I recommend keeping the number of binding directories low. In many shops, one is sufficient. Let’s call our binding directory PRODUCTION.
CRTBNDDIR BNDDIR(MYLIB/PRODUCTION)
A binding directory is just a list of service programs and/or modules that programs might need.
Second, we use the Add Binding Directory Entry (ADDBNDDIRE) command to add the DATERTNS service program to the binding directory.
ADDBNDDIRE BNDDIR(MYLIB/PRODUCTION) OBJ((DATERTNS *SRVPGM))
Last, we refer to the binding directory in the control (header) spec of our RPG programs.
ctl-opt dftactgrp(*no) option(*srcstmt: *nodebugio) bnddir('PRODUCTION');
Now we can compile using CRTBNDRPG. When the compiler looks for external routines, it will search the service programs and modules that are listed in the binding directory.
I hope this helps David and others who have been “attacked by the lion of need.”
This reminds me of a recent conversation with a fellow IBM i professional. He reminded me that one of the System/38 features that IBM widely promoted was that the dynamic calls were so fast, there was no need for a linkage editor on that system. I had forgotten.
And that bit of trivia, in turn, reminds me of a question that I have been asked several times — why bother with service programs? Why not use dynamic calls? I will honor that request, as I have honored David’s, but it will have to wait for another day.
In your last instruction, you meant to say “Now we can compile using CRTBNDRPG.”
Great article Ted. Btw, I think you have a typo here: Now we can compile using CRTBNDDIR.
Now we can compile using CRTBNDDIR ? Typo I believe.
Saddest thing really is that the vendor apparently didn’t include a binding directory and/or instructions on how to create one.