Using DLLs that call each other

An application can use DLLs that call each other. There are two methods for building these applications, as illustrated in the examples that follow:

In both cases, the result is that the side-deck is produced for CCNGA2D3, so that the DLLs that reference CCNGA2D3 can be built.

The CCNGA2 application (Figure 1) imports functions and variables from three DLLs: (Figure 2, Figure 3, and Figure 4). It is an example of an application that uses DLLs that call each other.

Figure 1. Application CCNGA2
#include <stdlib.h>

extern  int   var1_d1;              /*imported from CCNGA2D1     */
extern  int  func1_d1(int);         /*imported from CCNGA2D1     */

extern  int   var1_d2;              /*imported from CCNGA2D2     */
extern  int  func1_d2(int);         /*imported from CCNGA2D2     */

extern  int   var1_d3;              /*imported from CCNGA2D3     */
extern  int  func1_d3(int);         /*imported from CCNGA2D3     */

int main() {
 int rc = 0;

   printf("+-CCNGA2::main() starting  \n");
/* ref DLL1 */
   if (var1_d1 == 100) {
      printf("|     var1_d1=<%d>\n",var1_d1++);
      func1_d1(var1_d1);
   }
/* ref DLL2 */

   if (var1_d2 == 200) {
      printf("|     var1_d2=<%d>\n",var1_d2++);
      func1_d2(var1_d2);
   }
/* ref DLL3 */
   if (var1_d3 == 300) {
      printf("|     var1_d3=<%d>\n",var1_d3++);
      func1_d3(var1_d3);
   }

   printf("+-CCNGA2::main() Ending    \n");
}

Figure 2 shows application CCNGA2D1, which imports functions from Figure 3 and Figure 4.

Figure 2. Application CCNGA2D1
#include <stdio.h>

int func1_d1();            /* A function to be externalized */
int var1_d1 = 100;         /* export this variable          */

extern  int  func1_d2(int);         /*imported from CCNGA2D2     */
extern  int  func1_d3(int);         /*imported from CCNGA2D3     */

int func1_d1 (int input)
{
  int rc2 = 0;
  int rc3 = 0;
  printf("| +-CCNGA2D1() func1_d1() starting.  Input is %d\n", input);
  rc2 = func1_d2(200);
  rc3 = func1_d3(300);
  printf("| | func1_d1() dll1 - rc2=<%d> rc3=<%d>\n", rc2,
rc3);
  printf("| +-CCNGA2D1() func1_d1() ending. \n");
}

Figure 3 shows application CCNGA2D2, which imports a function from Figure 4.

Figure 3. Application CCNGA2D2
#include <stdio.h>

int func1_d2();            /* A function to be externalized */
int  var1_d2 = 200;

extern int func1_d3(int);  /* import this function         */

int func1_d2 (int input)
{
  int rc3 =0;
  printf("| | +-CCNGA2D2() func1_d2() starting.  Input is %d\n",
input);
  rc3 = func1_d3(300);
  printf("| | | func1_d2() dll2 - rc3=<%d>\n",  rc3);
  printf("| | +-CCNGA2D2() func1_d2() ending\n");
}

Application CCNGA2D3 (Figure 4) imports variables from Figure 2 and Figure 3.

Figure 4. Application CCNGA2D3
#include <stdio.h>

int func1_d3();            /* A function to be externalized */
int var1_d3 = 300;

extern int var1_d1;       /* imported variable from CCNGA2D1 */
extern int var1_d2;       /* imported variable from CCNGA2D2 */

int func1_d3 (int input)
{
  printf("| | | +-CCNGA2D3()-func1_d3() starting.  Input is %d\n",
                                        input);
  printf("| | | | value of var1_d1=%d var1_d2=%d\n",
                           var1_d1,   var1_d2);
  printf("| | | +-CCNGA2D3()-func1_d3() ending\n");
}

The first method uses the JCL in Figure 5. The following processing occurs:

  1. CCNGA2D3 is compiled and bound to create a DLL. The binder uses the control cards supplied through SYSIN to import variables from CCNGA2D1 and CCNGA2D2. The binder also generates a side-deck CCNGA2D3 that is used in the following steps.
  2. CCNGA2D2 is compiled and bound to create a DLL. The binder uses the control cards supplied through SYSIN to include the side-deck from CCNGA2D3. The following steps use the binder which generates the side-deck CCNGA2D2.
  3. CCNGA2D1 is compiled and bound to create a DLL. The binder uses the control cards supplied through SYSIN to include the side-decks from CCNGA2D2 and CCNGA2D3. The following steps show the binder generating the side-deck CCNGA2D1.
  4. CCNGA2 is compiled, bound, and run. The binder uses the control statements supplied through SYSIN to include the side-decks from CCNGA2D1, CCNGA2D2, and CCNGA2D3.
Figure 5. CCNGA2M1
//jobcard information...
//PROC JCLLIB ORDER=(CBC.SCCNPRC, CEE.SCEEPROC)
//*
//* CBDLL3: -Compile and bind CCNGA2D3
//* -Explicit import of variables from CCNGA2D1 and CCNGA2D2
//* -Generate the side-deck CCNGA2D3
//*
//CBDLL3 EXEC EDCCB,INFILE='CBC.SCCNSAM(CCNGA2D3)',
// CPARM='SO,LIST,DLL,EXPO,RENT,LONG',
// OUTFILE='myid.LOAD,DISP=SHR'
//BIND.SYSIN DD *
 IMPORT DATA CCNGA2D1 var1_d1
 IMPORT DATA CCNGA2D2 var1_d2
 NAME CCNGA2D3(R)
/*
//BIND.SYSDEFSD DD DSN=myid.IMPORT(CCNGA2D3),DISP=SHR
//*
//*CDDLL2: -Compile and bind CCNGA2D2
//* -Include the side-deck CCNGA2D3
//* -Generate the side-deck CCNGA2D2
//*
//CBDLL2 EXEC EDCCB,INFILE='CBC.SCCNSAM(CCNGA2D2)',
// CPARM='SO,LIST,DLL,EXPO,RENT,LONG',
// OUTFILE='myid.LOAD,DISP=SHR'
//BIND.SYSIN DD *
 INCLUDE DSD(CCNGA2D3)
 NAME CCNGA2D2(R)
/*
//BIND.SYSDEFSD DD DSN=myid.IMPORT(CCNGA2D2),DISP=SHR
//BIND.DSD DD DSN=myid.IMPORT,DISP=SHR
//*
//* CBDLL1: -Compile and bind CCNGA2D1
//* -Include the side-deck CCNGA2D2 and CCNGA2D3
//* -Generate the side-deck CCNGA2D1
//*
//CBDLL1 EXEC EDCCB,INFILE='CBC.SCCNSAM(CCNGA2D1)',
// CPARM='SO,LIST,DLL,EXPO,RENT,LONG',
// OUTFILE='myid.LOAD,DISP=SHR'
//BIND.SYSIN DD *
 INCLUDE DSD(CCNGA2D2)
 INCLUDE DSD(CCNGA2D3)
 NAME CCNGA2D1(R)
/*
//BIND.SYSDEFSD DD DSN=myid.IMPORT(CCNGA2D1),DISP=SHR
//BIND.DSD DD DSN=myid.IMPORT,DISP=SHR
//*
//* CBAPP2: -Compile, bind and run CCNGA2
//* -Include the side-deck CCNGA2D1, CCNGA2D2 and CCNGA2D3
//*
//CBAPP2 EXEC EDCCBG,INFILE='CBC.SCCNSAM(CCNGA2)',
// CPARM='SO,LIST,DLL,RENT,LONG',
// OUTFILE='myid.LOAD(CCNGA2),DISP=SHR'
//BIND.SYSIN DD *
 INCLUDE DSD(CCNGA2D1)
 INCLUDE DSD(CCNGA2D2)
 INCLUDE DSD(CCNGA2D3)
 NAME CCNGA2(R)
/*
//BIND.DSD DD DSN=myid.IMPORT,DISP=SHR
//GO.STEPLIB DD
// DD DSN=myid.LOAD,DISP=SHR

The second method uses the JCL in Figure 6. The following processing occurs:

  1. Once compiled, the object module CCNGA2D2 is saved for the following steps.
  2. CCNGA2D1 is compiled, the object module is saved for the following steps.
  3. CCNGA2D3 is compiled and bound to generate the side-deck and the object module is not used in the following steps. The load module for this step is not saved, as it is not being used. The load module for CCNGA2D3 is generated at a later step.
  4. CCNGA2D2 is bound to create a DLL. The binder takes as input the object module CCNGA2D2 and the side-deck CCNGA2D3. It also generates the side-deck CCNGA2D2 that is used in the following steps.
  5. CCNGA2D1 is bound to create a DLL. The binder takes as input the object module CCNGA2D1 and the side-decks CCNGA2D3 and CCNGA2D2. It also generates the side-deck CCNGA2D1 that is used in the following steps.
  6. CCNGA2D3 is bound to create a DLL. The binder takes as input the object module CCNGA2D3 and the side-decks CCNGA2D1 and CCNGA2D2. It also generates the side-deck CCNGA2D3 that is used in the following step.
  7. CCNGA2 is compiled, bound, and run. The binder takes as input the object module CCNGA2 and the side-decks CCNGA2D1, CCNGA2D2, and CCNGA2D3.
Figure 6. CCNGA2M2
//jobcard information...
//PROC JCLLIB ORDER=(CBC.SCCNPRC, CEE.SCEEPROC)
//* CDLL2: -Compile CCNGA2D2
//*
//CDLL2 EXEC EDCC,INFILE='CBC.SCCNSAM(CCNGA2D2)',
// OUTFILE='myid.OBJ(CCNGA2D2),DISP=SHR',
// CPARM='SO,LIST,DLL,EXPO,RENT,LONG'
//*
//* CDLL1: -Compile CCNGA2D1
//*
//CDLL1 EXEC EDCC,INFILE='CBC.SCCNSAM(CCNGA2D1)',
// OUTFILE='myid.OBJ(CCNGA2D1),DISP=SHR',
// CPARM='SO,LIST,DLL,EXPO,RENT,LONG'
//*
//* CBDLL3: -Compile and bind CCNGA2D3 with NCAL
//* -Generate the side-deck CCNGA2D3
//* -The load module will not be kept, as it will not be
//* used
//*
//CBDLL3 EXEC EDCCB,INFILE='CBC.SCCNSAM(CCNGA2D3)',
// CPARM='SO,LIST,DLL,EXPO,RENT,LONG',
// BPARM='NCAL'
//COMPILE.SYSLIN DD DSN=myid.OBJ(CCNGA2D3),DISP=SHR
//BIND.SYSLIN DD DSN=myid.OBJ(CCNGA2D3),DISP=SHR
//BIND.SYSIN DD *
 INCLUDE OBJ(CCNGA2D2)
 INCLUDE OBJ(CCNGA2D1)
 NAME CCNGA2D3(R)
/*
//BIND.SYSDEFSD DD DSN=myid.IMPORT(CCNGA2D3),DISP=SHR
//BIND.OBJ DD DSN=myid.OBJ,DISP=SHR
//*
//*
//* BDLL2: -Bind CCNGA2D2
//* -Generate the side-deck CCNGA2D2
//*
//*
//BDLL2 EXEC CBCB,INFILE='myid.OBJ(CCNGA2D2)',
// BPARM='CALL',
// OUTFILE='myid.LOAD(CCNGA2D2),DISP=SHR'
//BIND.SYSIN DD DSN=myid.IMPORT(CCNGA2D3),DISP=SHR
//BIND.SYSDEFSD DD DSN=myid.IMPORT(CCNGA2D2),DISP=SHR
//*
//*
//* BDLL1: -Bind CCNGA2D1
//* -Generate the side-deck CCNGA2D1
//*
//BDLL1 EXEC CBCB,INFILE='myid.OBJ(CCNGA2D1)',
// BPARM='CALL',
// OUTFILE='myid.LOAD(CCNGA2D1),DISP=SHR'
//BIND.SYSIN DD *
 INCLUDE DSD(CCNGA2D2)
 INCLUDE DSD(CCNGA2D3)
/*
//BIND.SYSDEFSD DD DSN=myid.IMPORT(CCNGA2D1),DISP=SHR
//BIND.DSD DD DSN=myid.IMPORT,DISP=SHR
//*
//* BDLL3: -Bind CCNGA2D3
//* -Generate the side-deck CCNGA2D3
//*
//BDLL3 EXEC CBCB,INFILE='myid.OBJ(CCNGA2D3)',
// BPARM='CALL',
// OUTFILE='myid.LOAD(CCNGA2D3),DISP=SHR'
//BIND.SYSIN DD *
 INCLUDE DSD(CCNGA2D1)
 INCLUDE DSD(CCNGA2D2)
 NAME CCNGA2D3(R)
/*
//BIND.SYSDEFSD DD DSN=myid.IMPORT(CCNGA2D3),DISP=SHR
//BIND.DSD DD DSN=myid.IMPORT,DISP=SHR
//*
//* CBAPP2: -Compile, bind and run CCNGA2
//* -Input the side-decks CCNGA2D1, CCNGA2D2 and CCNGA2D3
//*
//CBAPP2 EXEC EDCCBG,INFILE='CBC.SCCNSAM(CCNGA2)',
// CPARM='SO,LIST,DLL,RENT,LONG',
// OUTFILE='myid.LOAD(CCNGA2),DISP=SHR'
//BIND.SYSIN DD *
 INCLUDE DSD(CCNGA2D1)
 INCLUDE DSD(CCNGA2D2)
 INCLUDE DSD(CCNGA2D3)
 NAME CCNGA2(R)
/*
//BIND.DSD DD DSN=myid.IMPORT,DISP=SHR
//GO.STEPLIB DD
// DD DSN=myid.LOAD,DISP=SHR