JCL

Job Control Language (JCL) is a scripting language used on a mainframe to instruct the system on how to run a batch job. In AWS Transform for mainframe refactor, it is handled in the mainframe group following the statements listed below.

JCL Statements

DD DSN

The call on DD +DSN can imply dependencies on "DATASET or Partitioned DataSet" object, "FILE_DEFINITION" object and a control card (.CTL).

//file-definition-name DD (DISP=SHR,)? DSN(AME)?=dataset-or-PDS-name(controlcard-file-or-generation)

Where 

  • file-definition-name: a “FILE_DEFINITION” object (optional)
  • dataset-name: a “DATASET” object or a Partitioned DataSet (PDS) object.
  • controlcard-file-or-generation: either a control card name in the project OR a GDG generation number (optional)

The parentheses content can be: 
1. Control card file: A control card name. In this case, the dataset-or-PDS-name will create a dependency to a PDS object and the control card dependency will have the property JCL.PartitionedDataSet. This allows reconstruction of the fully qualified DSN to locate the control card in case it was missing. 
2. GDG generation: A generation number for Generation Data Groups: 
  - (0) or (+0): refers to the most current generation 
  - (-1): refers to the previous generation   
  - (+1): specifies a new generation to be added 
  - Other numeric values like (-2), (+2), etc.

Dynamic dataset-or-PDS-name, i.e with a name containing “%%” or “&”, are not handled yet.

When the file-definition-name is equal to STEPLIB, JOBLIB, or JCLLIB, a PDS dependency is created instead of a DataSet dependency. This helps distinguish between DataSets that are files and Partitioned DataSets that are folders.

Examples:

//SYSIN DD DSN=MY.LIB.AWS(MYCTL),DISP=SHR

Creates:

{

"dependencyType": "Control card",
"name": "MYCTL",
"properties": {
"JCL.PartitionedDataSet": "MY.LIB.AWS"
},
"path" : "MY.LIB.AWS/MYCTL",
"type" : "CTL"
}

and 

{

"dependencyType": "PDS use",
"name": "MY.LIB.AWS",
"path": "Global:PDS:MY.LIB.AWS",
"type": "PDS"
}


For library statements such as STEPLIB, JOBLIB, and JCLLIB, only one PDS dependency is created 
 

//STEPLIB DD DISP=SHR,DSN=AWS.M2.CARDDEMO.LOADLIB

Creates:

{

"dependencyType": "PDS use",
"name": "AWS.M2.CARDDEMO.LOADLIB",
"path": "Global:PDS:AWS.M2.CARDDEMO.LOADLIB",
"type": "PDS"
}

PROC DD DSN with symbolic PDS

When a PROC's DD statement references a PDS via a symbolic parameter whose value is supplied (or overridden) by the calling JCL — for example DSN=&CARDLIB(MY_CTL) with &CARDLIB defined on the JCL — the control card dependency is attached to the calling JCL rather than the PROC. The JCL.PartitionedDataSet property on the JCL-side edge is resolved against the caller's symbolic context so the fully qualified dataset name is visible, while the PROC loses the edge because its PDS name is unknown at the PROC level.

When the same PROC is invoked from multiple JCLs with different symbolic values, each caller JCL receives its own resolved copy of the dependency. Standalone PROCs (never called from any JCL) keep their control-card edge untouched with the symbolic property left as-is.

Example: a PROC PROC1.proc with //SYSIN DD DSN=&CARDLIB(MY_CTL),DISP=SHR called from JOB1.jcl where &CARDLIB=LIB.DATA produces on JOB1.jcl: 

{
	"dependencyType": "Control card", 
	"name": "MY_CTL",
	"path": "LIB.DATA/MY_CTL",
	"type": "CTL",
	"properties": { "JCL.PartitionedDataSet": "LIB.DATA" }
} 

and no matching edge on PROC1.proc.

DLBL

The call to DLBL implies a dependency on a Mainframe file.

  • DLBL file_def 'mainframe_file'

Where file_def is the name of the file definition and mainframe_file the name of a mainframe file in the project.

MQN

An MQN (Message Queue Name) represents a message queue configuration.

MQN NAME=<mqn-name>

The MQN statement defines a message queue object. It creates a dependency link of type "Define Queue" between the MQN and the referencing object.

APL

An APL statement defines an application configuration.

APL NAME=<apl-name>,RENAME=<program-name>

The APL statement defines an application object. It creates a dependency link of type "Define Program" between the APL and the specified program name.

EXEC

EXEC statements like NAME1.NAME2.EXEC implies a dependency on a  JCL, PROC, COB, EZT, REXX, PL1, CLIST or RPG file.

  • NAME1.NAME2.EXEC(MY_PROG) implies a dependency on the program MY_PROG where NAME1 and NAME2 can be any valid names

  • NAME1.EXEC(MY_PROG) implies a dependency on the program MY_PROG where NAME1 can be any valid names

EXEC PGM

The call to EXEC PGM implies a dependency on a COB, EZT, REXX, PL1 or RPG file.

  • EXEC PGM=program_file ... PROGRAM=program
  • EXEC PGM=program_file ... PROG=program
  • EXEC PGM=program_file ... PGMNAME=program
  • EXEC PGM=program_file ... PGMNAME='program'
  • EXEC PGM=system_utility PARM=(program_type,program_name,psb_name, ...) ... PROGRAM=program
  • EXEC PGM=system_utility PARM=('program_type',program_name,psb_name, ...) ... PROGRAM=program
  • EXEC PGM=system_utility PARM='program_type,program_name,psb_name, ...' ... PROGRAM=program
  • EXEC PGM=system_utility PARM=(program_type,program_name,psb_name,'PROG(nested_program)', ...) ... PROGRAM=program

Where program_file can be the raw name of a file or a variable (&program_file) containing a filename. PROGRAM, PROG and PGNAME are parameters pass to program_file and program (JCL, PROC, COB, EZT, REXX, PL1, RPG or CCDEF) the name of the program to be executed. PARM=( ... ) or PARM=' ... ' is optional parameter. 
When system_utility is configured with some system utility program (DFSRRC00, DLIBATCH, DBBBATCH, IMSBATCH, IMSFP and DLIBMP) and the program_type as DLI, BMP, DBB, IFP. The system utility program uses PARM parameter to execute a program (COBOL, JCL, PROC, REXX, PL1, RPG, ASM, LNK, EZT, CTL or CCDEF) mentioned in program_name and PSB file mentioned in psb_name
Inside the PARM parameter, we can find a PROG call where nested_program will be the name of an existing file in the project. This nested parameter will create another link to nested_program
We can also call an in-stream procedure thanks to the EXEC PGM statement. In that case, it will create no dependency.

EXEC PROC

The call to EXEC PROC implies a dependency on a JCL, PROC, COB, EZT, REXX, PL1, CLIST, RPG or CCDEF file.

  • EXEC PROC=procedure_file ... PROGRAM=program
  • EXEC PROC=procedure_file ... PROG=program
  • EXEC PROC=procedure_file ... PGMNAME=program
  • EXEC proc=procedure_file ... PGMNAME='program'
  • EXEC procedure_file ... PROGRAM=program
  • EXEC procedure_file ... PROG=program
  • EXEC procedure_file ... PGMNAME=program
  • EXEC procedure_file ... PGMNAME='program'
  • EXEC PROC=system_utility ... NAME=program,PSB=psb_name
  • EXEC system_utility ... NAME=program,PSB=psb_name

Where procedure_file can be the raw name of a file or a variable (&procedure_file) containing a filename. PROGRAM, PROG and PGNAME are parameters pass to procedure_file and program the name of the program to be executed. 
When system_utility is configured with some system utility program (DFSRRC00, DLIBATCH, DBBBATCH, IMSBATCH, IMSFP and DLIBMP) then it executes a program (COBOL, JCL, PROC, REXX, PL1, RPG, ASM, LNK, EZT, CLIST, CTL or CCDEF) mentioned in program_name and PSB file mentioned in psb_name
We can also call an in-stream procedure thanks to the EXEC PROC statement. In that case, it will create no dependency.

ISPSTART CMD

The call to ISPSTART CMD can be used to reference a program (COBOL, JCL, PROC, REXX, PL1, RPG, ASM, LNK, EZT, CLIST or CTL) 

  • ISPSTART CMD(MY_PROG) implies a dependency on a program called MY_PROG

RUN PROG

The call to RUN PROG implies a dependency on a JCL, PROC, COB, EZT, REXX, PL1 or RPG file.

  • RUN PROG(program_file)

Where program_file can be the raw name of a file or a variable (&program_file) containing this name.

RUN PROGRAM

The call to RUN PROGRAM implies a dependency on a JCL, PROC, COB, EZT, REXX, PL1 or RPG file.

  • RUN PROGRAM(program_file)

Where program_file can be the raw name of a file or a variable (&program_file) containing this name.

SQL STATEMENT

In JCL program, SQL statements are getting executed in the in-stream data section. In-stream data will never begin with //. End of the in-stream can be identified by line begins with // or /* or //* and sometimes end of the file.

  • //SYSIN DD * 
    SELECT * FROM TABLE_NAME;
  • //SYSIN DD DATA 
    UPDATE TABLE_NAME SET COLUMN1 = VALUE1 WHERE ...;

Note: All in-stream data are not SQL commands. It may be RUN PROGRAM statement or others.

INCLUDE MEMBER

The INCLUDE MEMBER statement implies a dependency on a Mainframe file. Dependencies with variables starting with “$” are ignored.

  • INCLUDE MEMBER=mainframe_file

Where mainframe_file is the name of a Mainframe file in the project.

ADDRQ

The call to ADDRQ statement implies a dependency on a JCL file, like the following example:

  • ADDRQ,JOB=file_name_1,DEPJOB= file_name_2

Where file_name_1 and file_name_2 can be the names of a JCL files.

DEMAND

The call to DEMAND statement implies a dependency on a JCL file, like the following example:

  • DEMAND,JOB=file_name_1,DEPJOB= file_name_2

Where file_name_1 and file_name_2 can be the names of a JCL files.

DEMANDH

The call to DEMANDH statement implies a dependency on a JCL file, like the following example:

  • DEMANDH,JOB=file_name_1,DEPJOB= file_name_2

Where file_name_1 and file_name_2 can be the names of a JCL files.

Symbolic values

When possible, the dependency analysis will replace the usage of a symbolic parameter by its value. This applies to all dependency targets and property values — not just dataset names.

Defining symbolic parameters

Symbolic parameters can be defined in three ways:

  • SET statements: // SET BNK=G
  • PROC parameter defaults: //MYPROC PROC BASEOS=APP2.BASE.
  • EXEC statement overrides: // EXEC MYPROC,SYSTEM=SUB1

Variable syntax

  • Variables are prefixed with & (e.g., &BNK)
  • A trailing period (.) terminates the variable name and is consumed during resolution: &BNK.TEXT with BNK=G resolves to GTEXT, not G.TEXT
  • Multiple variables can appear in a single value: &VAR1.TEXT&VAR2.

Resolution process

Symbolic resolution happens in two phases:

Phase 1 — Scanning (per-file): Each JCL and PROC file is scanned individually. The analyzer collects symbolic parameter definitions (SET, PROC defaults, EXEC overrides). Dependencies whose target or property values contain & are stored as unresolved at this stage.

Phase 2 — Cross-file resolution (global): After all JCL and PROC files have been scanned, a global post-processor resolves symbolic parameters across file boundaries:

  1. For each JCL file, it walks the dependencies and follows EXEC PROC references into the corresponding PROC files.
  2. It builds a symbolic context from the calling JCL (SET values + EXEC overrides) combined with the PROC's own defaults.
  3. It substitutes & variables in the PROC's dependency targets and property values using this context.
  4. If the PROC calls nested PROCs, the process recurses with the accumulated context.
  5. Chained symbolics (a variable whose value contains another & reference) are resolved iteratively.

Resolution priority (highest to lowest):

  1. EXEC statement overrides from the calling JCL
  2. SET statements in the calling JCL
  3. PROC parameter defaults

Re-parenting to the calling JCL

Once symbolic resolution succeeds, the resolved dependency is moved from the PROC to the calling JCL file. In the dependency graph JSON, the dependency appears under the JCL — not under the PROC.

  • The dependency target name contains the resolved value
  • All property values are resolved
  • Exception: the symbolic property preserves the original unresolved expression. This is the only property that retains the pre-resolution form.

When the same PROC is called from multiple JCLs with different overrides, each JCL gets its own resolved copy of the dependency. Standalone PROCs (never called from any JCL) keep their edges untouched with the symbolic property left as-is.

Examples

Multiple symbolics in a dataset name:

// SET BNK=G
// EXEC MYPROC,BASEOS=APP2.BASE.

In the PROC:

//ISPSLIB  DD DSN=&BASEOS.DB2DATA&BNK.,DISP=SHR

Resolution: &BASEOSAPP2.BASE. (trailing period consumed), &BNKG (trailing period consumed). Resolved name: APP2.BASE.DB2DATAG.

Creates on the calling JCL:

{
    "dependencyType": "DataSet use; Resolved Symbol",
    "name": "APP2.BASE.DB2DATAG",
    "properties": {
        "symbolic": "&BASEOS.DB2DATA&BNK"
    }
}

Symbolic in member name:

//DB2SORTI DD DSN=PGDF.BASE.DATA(DB2SRT&SORT),DISP=SHR

Given SORT=64, the PDS name PGDF.BASE.DATA is literal and the member DB2SRT&SORT resolves to DB2SRT64.

Creates:

{
    "dependencyType": "Control card; Resolved Symbol",
    "name": "DB2SRT64",
    "properties": {
        "symbolic": "DB2SRT&SORT",
        "JCL.PartitionedDataSet": "PGDF.BASE.DATA"
    },
    "path": "PGDF.BASE.DATA/DB2SRT64",
    "type": "CTL"
}

Symbolic in PDS name with literal member:

// SET BNK=G
//SYSIN    DD DSN=PGOS.BASE.DB2DATA&BNK.(UIGTMP01),DISP=SHR

The PDS name resolves to PGOS.BASE.DB2DATAG and the member UIGTMP01 is literal.

Creates:

{
    "dependencyType": "Control card; Resolved Symbol",
    "name": "UIGTMP01",
    "properties": {
        "JCL.PartitionedDataSet": "PGOS.BASE.DB2DATAG"
    },
    "path": "PGOS.BASE.DB2DATAG/UIGTMP01",
    "type": "CTL"
}

Symbolics in both PDS and member:

// SET TLQ=
// EXEC MYPROC,SYSTEM=SUB1

In the PROC:

//SYSTSIN  DD DSN=PGOS.&TLQ.BASE.DB2DATAG(&SYSTEM.DSN),DISP=SHR

The empty &TLQ collapses the double period: PGOS..BASE.DB2DATAGPGOS.BASE.DB2DATAG. The member &SYSTEM.DSN resolves to SUB1DSN.

Creates on the calling JCL:

{
    "dependencyType": "Control card; Resolved Symbol",
    "name": "SUB1DSN",
    "properties": {
        "symbolic": "&SYSTEM.DSN",
        "JCL.PartitionedDataSet": "PGOS.BASE.DB2DATAG"
    },
    "path": "PGOS.BASE.DB2DATAG/SUB1DSN",
    "type": "CTL"
}

Edge cases

  • Empty symbolic values: If a PROC declares parameters with empty default values and the calling JCL does not override them, the symbolic resolution produces an empty value which appears as <<empty symbolic value>> nodes in the dependency graph. This also occurs for blank symbolic parameters concatenation, like &VAR.&VAR2.
  • Dynamic names (%%): Values containing %% (system symbols) are treated as dynamic and are not resolved.
  • Temporary datasets (&&): Double-ampersand names (e.g., &&TEMP) are temporary datasets and are not treated as symbolic parameters.
  • Chained symbolics: A variable whose value itself contains & is resolved iteratively until no more substitutions are possible.