Edited. Forgot 'DD'.
Original Message:
Sent: Mon March 04, 2024 01:47 PM
From: Frank Swarbrick
Subject: JCL compiling, linking, running of Cobol source against custom library built on USS
If you want to use the .x Unix file directly, replace this:
//LKED.SYSIN DD DSN=IBMUSER.COBOL.SIDEDECK(MYLIB),DISP=SHR *SIDEDECK
with this:
//LKED.SYSIN DD * INCLUDE /your/Unix/library/MYLIB.x
------------------------------
Frank Swarbrick
Original Message:
Sent: Mon March 04, 2024 12:53 PM
From: Piotr Synowiec
Subject: JCL compiling, linking, running of Cobol source against custom library built on USS
Big Thanks Roy
This was exactly what I was looking for.
Works like charm,
Regards,
Piotr
------------------------------
Piotr Synowiec
Original Message:
Sent: Mon March 04, 2024 11:23 AM
From: Roy Bae
Subject: JCL compiling, linking, running of Cobol source against custom library built on USS
Hello Piotr,
I am not too sure how far you can make it without understanding JCL syntax and usage. Anyway the JCL you are asking is in my previous posting which can be found from
https://www.ibm.com/docs/en/cobol-zos/6.4?topic=application-example-sample-jcl-procedural-dll
If I may try to provide the customization, it is below.
############################################################################
# STEP.1 Pre-allocate a DS for a sidedeck using the following attribute:
############################################################################
Data Set Name . . . . : IBMUSER.COBOL.SIDEDECK
General Data Current Allocation
Management class . . : STANDARD Allocated cylinders : 1
Storage class . . . : OS390 Allocated extents . : 1
Volume serial . . . : LABW80
Device type . . . . : 3390
Data class . . . . . : **None**
Organization . . . : PO Current Utilization
Record format . . . : FB Used cylinders . . : 1
Record length . . . : 80 Used extents . . . : 1
Block size . . . . : 3200
1st extent cylinders: 1
Secondary cylinders : 1 Dates
Data set name type : PDS Creation date . . . : 2024/03/04
Referenced date . . : 2024/03/04
Expiration date . . : ***None***
############################################################################
# STEP.2
############################################################################
Open your sidedeck in USS and edit it in such a way that each line starts with a space and is 80 columns long.
For example, before editing:
> cat MYLIB.x
IMPORT CODE,'MYLIB','DemoDLLSubprogram' IMPORT CODE,'MYLIB','DemoDLLSub2'
After editing:
> cat MYLIB.x
IMPORT CODE,'MYLIB','DemoDLLSubprogram'
IMPORT CODE,'MYLIB','DemoDLLSub2'
############################################################################
# STEP.3 copy the edited MYLIB.x to IBMUSER.COBOL.SIDEDECK in USS
############################################################################
> cp MYLIB.x "//'IBMUSER.COBOL.SIDEDECK(MYLIB)'"
############################################################################
# STEP.4 run the following JCL
# Note: you might need to customize this JCL for your environment
############################################################################
//IBMUSER1 JOB ,
// TIME=(1),MSGLEVEL=(1,1),MSGCLASS=S,CLASS=A,
// NOTIFY=&SYSUID,USER=IBMUSER
//*---------------------------------------------------------------------
//* Compile and bind COBOL main program
//*---------------------------------------------------------------------
//STEP2 EXEC IGYWCL,REGION=800M,GOPGM=MAINPGM,
// PARM.COBOL='RENT,PGMNAME(LM),DLL',
// PARM.LKED='RENT,LIST,XREF,LET,MAP,DYNAM(DLL),CASE(MIXED)'
//COBOL.SYSIN DD DSN=IBMUSER.APP.COBOL(TEST1),DISP=SHR *SOURCE
//LKED.SYSIN DD DSN=IBMUSER.COBOL.SIDEDECK(MYLIB),DISP=SHR *SIDEDECK
//*---------------------------------------------------------------------
//* Execute the main program, calling the subprogram DLL.
//*---------------------------------------------------------------------
//STEP3 EXEC PGM=MAINPGM,REGION=80M
//STEPLIB DD DSN=&&GOSET,DISP=(OLD,DELETE)
// DD DSN=IBMUSER.APP.LIB,DISP=SHR *DLL
//SYSOUT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//CEEDUMP DD SYSOUT=*
#---------------------------
# Note
#---------------------------
Where IBMUSER.APP.COBOL(TEST1) is a COBOL soure code that contains
Identification division.
Program-id. "MainProgram".
Procedure division.
Call "DemoDLLSubprogram"
Call "DemoDLLSub2"
Stop Run.
End program "MainProgram".
Where IBMUSER.COBOL.SIDEDECK(MYLIB) is a sidedeck
Where IBMUSER.APP.LIB(MYLIB) is a DLL
(I happened to use the same name, MYLIB, for both sidedeck and DLL)
------------------------------
Roy Bae
Original Message:
Sent: Mon March 04, 2024 09:21 AM
From: Piotr Synowiec
Subject: JCL compiling, linking, running of Cobol source against custom library built on USS
Hi Roy,
Thank you for so comprehensive response.
steps 1 to 5 I had already working.
Step 6 implemented and work great.
The problem I face is that final user of the library I compiled doesn't know USS and they do everything through TN3270 with JCL.
That's why I'm looking for the way to compile Cobol source code against my custom library in dataset.
I already have DLL library created and USS compiled objects runs through JCL with library specified on dataset.
Could you please provide mean example how to compile and link Cobol source code againts my library ?
source code IBMUSER.APP.COBOL(TEST1)
library IBMUSER.APP.LIB(MYLIB)
Regards,
Piotr
------------------------------
Piotr Synowiec
Original Message:
Sent: Thu February 29, 2024 11:19 AM
From: Roy Bae
Subject: JCL compiling, linking, running of Cobol source against custom library built on USS
Hello Piotr,
You can compile/link/run using a JCL but if you are not familiar with JCL and its syntax, you will spend quite time to figure things out. Alternatively, you can use USS to compile and link, but use a JCL just to run a program. I found this approach is easier to those who have more background on Linux/Unix than MVS/JCL/z/OS. Therefore my suggestion below is a combination of USS and JCL. (I know this is not what you asked). I described the general way using simple programs below. See if this helps answer your questions.
##########################################################
# To help you speed up and catch up the concept of a DLL,
##########################################################
1) Let's say a COBOL program wants to call functions named FUNC1 and FUNC2.
2) FUNC1 and FUNC2 are C functions defined in MYLIB (dll moudle). You need to create MYLIB (dll) and a sidedeck MYLIB.x.
3) sidedeck (MYLIB.x) is just texts telling where FUNC1 and FUNC2 are located.
For example,
> cat MYLIB.x
IMPORT CODE,'MYLIB','FUNC1' IMPORT CODE,'MYLIB','FUNC2'
The sidedeck contents just say:
- MYLIB (DLL) contains FUNC1
- MYLIB (DLL) contains FUNC2
4) When a COBOL source code is compiled/linked to create a runnable program (that is, COBOL program), you need to tell where FUNC1 and FUNC2 are located. Therefore COBOL source code needs to copmiled/linked with the sidedeck as follows:
> cob2 -v -o test1 -qdll test1.cbl MYLIB.x
5) the STEP.4 generates a runnable COBOL program, 'test1'. When test1 is executed, sidedeck is no longer needed but 'MYLIB' should be found.
#------------------------------------------------------------------------
#
# ACTUAL EXAMPLE
#
#------------------------------------------------------------------------
########################################
# > cat src/c.c
########################################
#include<stdio.h>
int FUNC1(){
printf("I am func1\n");
}
int FUNC2(){
printf("I am func2\n");
}
########################################
# > cat test1.cbl
########################################
IDENTIFICATION DIVISION.
PROGRAM-ID. TEST1.
ENVIRONMENT DIVISION.
DATA DIVISION.
WORKING-STORAGE SECTION.
PROCEDURE DIVISION.
BEGIN.
display "TEST1 starts"
call 'func1'
call 'func2'
display "TEST1 end"
stop run.
END PROGRAM TEST1.
##############################################################################
# Current in my current dir (/home/roybae/TEMP/240228_140754), I have these
##############################################################################
./src/c.c
./test1.cbl
##############################################################################
# STEP.1 - Compile a 'c.c' file to generate an object named c.o
# Note that '-c'(compile only) is used in xlc
##############################################################################
> xlc -c -O2 -Wl,DLL -qdll -qexportall -v src/*.c
> ls # note c.o is generated
c.o src test1.cbl
#########################################################################################
# STEP.2 - link a 'c.o' file to generate a dll named 'MYLIB' and a sidedeck named 'MYLIB.x'
#########################################################################################
> xlc -o MYLIB *.o -Wl,DLL -qdll -qexportall -v
> ls # note MYLIB and MYLIB.x are generated
MYLIB MYLIB.x c.o src test1.cbl
#########################################################################################
# What's the sidedeck?
#########################################################################################
> cat MYLIB.x
IMPORT CODE,'MYLIB','FUNC1' IMPORT CODE,'MYLIB','FUNC2'
It tells that
- the C function 'FUNC1' is located in a module named 'MYLIB'
- the C function 'FUNC2' is located in a module named 'MYLIB'.
When COBOL code is compiled/linked, the sidedeck is needed. Otherwise COBOL does not know where to find FUNC1 and FUNC2
#########################################################################################
# STEP.3 - Compile a COBOL code with the sidedeck. It generates a program named test1
#########################################################################################
> cob2 -v -o test1 -qdll test1.cbl MYLIB.x
> ls # note test1 is generated along with .lst and .o
MYLIB MYLIB.x c.o test1 test1.cbl test1.lst test1.o
#########################################################################################
# STEP.4
# Let's run test1. Note that LIBPATH should point to the dir where MYLIB is located.
# In my case, /home/roybae/TEMP/240228_140754 is where MYLIB is created
#########################################################################################
> LIBPATH=/home/roybae/TEMP/240228_140754:$LIBPATH ./test1
TEST1 starts
I am func1
I am func2
TEST1 end
#########################################################################################
# STEP.5
# Let's run test1 using JCL.
# For preparation,
# 1) I copy 'test1' to ROYBAE.COBOL.LOAD
# 2) I copy 'MYLIB' to ROYBAE.APP.LIB
#########################################################################################
> cp test1 "//'ROYBAE.COBOL.LOAD(TEST1)'"
> cp MYLIB "//'ROYBAE.APP.LIB(MYLIB)'"
Both ROYBAE.COBOL.LOAD and ROYBAE.APP.LIB need to be pre-created. I used the following attributes for both.
Data Set Name . . . : ROYBAE.APP.LIB
General Data Current Allocation
Management class . . : STANDARD Allocated tracks . : 3
Storage class . . . : OS390 Allocated extents . : 1
Volume serial . . . : LW002E Maximum dir. blocks : NOLIMIT
Device type . . . . : 3390
Data class . . . . . : **None**
Organization . . . : PO Current Utilization
Record format . . . : U Used pages . . . . : 22
Record length . . . : 0 % Utilized . . . . : 61
Block size . . . . : 4096 Number of members . : 1
1st extent tracks . : 3
Secondary tracks . : 4
Data set name type : LIBRARY Dates
Data set version . : 2 Creation date . . . : 2024/02/29
Referenced date . . : 2024/02/29
Expiration date . . : ***None***
#########################################################################################
# STEP.6
# Let's run test1 using JCL.
#########################################################################################
Submit the following JCL.
- Change 'ROYBAEZ' to yours
- Change 'ROYBAE.COBOL.LOAD' and 'ROYBAE.APP.LIB'.
EXEC PGM=TEST1 tells which program to run. It will look up datasets in STEPLIB.
In this case, the STEPLIB has ROYBAE.COBOL.LOAD and ROYBAE.APP.LIB.
ROYBAE.COBOL.LOAD is where TEST1 is found
ROYBAE.APP.LIB is where MYLIB is found
#----------
# JCL
#----------
//ROYBAEZ JOB (641A,2327),'COBOL TEST',TIME=60,
// REGION=512M,CLASS=B,MSGCLASS=S,
// MSGLEVEL=(1,1),NOTIFY=&SYSUID
/*JOBPARM T=60,L=300
//RUN1 EXEC PGM=TEST1,
// PARM=''
//STEPLIB DD DSN=ROYBAE.COBOL.LOAD,DISP=SHR
// DD DSN=ROYBAE.APP.LIB,DISP=SHR
//SYSOUT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//CEEDUMP DD SYSOUT=*
//SYSMDUMP DD SYSOUT=*
//CEEOPTS DD *
/*
#-----------------------------------------------------------------------------------
# in the joglog, at the very bottom of it, I can find the program output as follows:
#-----------------------------------------------------------------------------------
...
TEST1 starts
TEST1 end
I am func1
I am func2
------------------------------
Roy Bae
Original Message:
Sent: Tue February 27, 2024 11:22 AM
From: Piotr Synowiec
Subject: JCL compiling, linking, running of Cobol source against custom library built on USS
Whole story
All these steps are done in USS
- I have custom C library that contains over 1000 different functions.
- I can compile library and link to DLL by
xlc -O2 -Wl,DLL -qdll -qexportall -v src/*.c
xlc -o MYLIB.o o/*.o -Wl,DLL -qdll -qexportall -v
- In result I have two files MYLIB.o and MYLIB.x
- Now I have several Cobol source programs to test some of the functions from library
- I can compile them dynamic
cob2 -v -o test1 -qdll test1.cbl MYLIB.x
and static
cob2 -v -o test1 test1.cbl MYLIB.o
I can execute them both and they work fine.
Now problem starts (JCL part)
- I have basic Cobol Hello world source that I can compile and run with JCL (thanks to Geoffrey Decker)
- I can't figure it out how I can compile my test1.cbl file and link it with my custom library
- Things I did so far.
I have IBMUSER.APP.COBOL dataset with all my Cobol sources including Hello World
I have IBMUSER.APP.LIB dataset where I copied MYLIB.o of course without extension
I have IBMUSER.APP.OO dataset where compiled programs are sent
I didn't find a way to copy MYLIB.x file to dataset as it is just one line file.
Could anybody help me to sort out this issue ?
I basically need a way to copy my custom library compiled in USS to dataset and compile test programs from IBMUSER.APP.COBOL
against my custom library preferably static and dynamic way
Could anybody help me with that ?
------------------------------
Piotr Synowiec
------------------------------