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.
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.
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.
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.
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 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
The call to EXEC PGM implies a dependency on a COB, EZT, REXX, PL1 or RPG file.
EXEC PGM=program_file ... PROGRAM=programEXEC PGM=program_file ... PROG=programEXEC PGM=program_file ... PGMNAME=programEXEC PGM=program_file ... PGMNAME='program'EXEC PGM=system_utility PARM=(program_type,program_name,psb_name, ...) ... PROGRAM=programEXEC PGM=system_utility PARM=('program_type',program_name,psb_name, ...) ... PROGRAM=programEXEC PGM=system_utility PARM='program_type,program_name,psb_name, ...' ... PROGRAM=programEXEC PGM=system_utility PARM=(program_type,program_name,psb_name,'PROG(nested_program)', ...) ... PROGRAM=programWhere 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.
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=programEXEC PROC=procedure_file ... PROG=programEXEC PROC=procedure_file ... PGMNAME=programEXEC proc=procedure_file ... PGMNAME='program'EXEC procedure_file ... PROGRAM=programEXEC procedure_file ... PROG=programEXEC procedure_file ... PGMNAME=programEXEC procedure_file ... PGMNAME='program'EXEC PROC=system_utility ... NAME=program,PSB=psb_nameEXEC system_utility ... NAME=program,PSB=psb_nameWhere 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.
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
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.
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.
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.
The INCLUDE MEMBER statement implies a dependency on a Mainframe file. Dependencies with variables starting with “$” are ignored.
INCLUDE MEMBER=mainframe_fileWhere mainframe_file is the name of a Mainframe file in the project.
The call to ADDRQ statement implies a dependency on a JCL file, like the following example:
ADDRQ,JOB=file_name_1,DEPJOB= file_name_2Where file_name_1 and file_name_2 can be the names of a JCL files.
The call to DEMAND statement implies a dependency on a JCL file, like the following example:
DEMAND,JOB=file_name_1,DEPJOB= file_name_2Where file_name_1 and file_name_2 can be the names of a JCL files.
The call to DEMANDH statement implies a dependency on a JCL file, like the following example:
DEMANDH,JOB=file_name_1,DEPJOB= file_name_2Where file_name_1 and file_name_2 can be the names of a JCL files.
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.
Symbolic parameters can be defined in three ways:
// SET BNK=G//MYPROC PROC BASEOS=APP2.BASE.// EXEC MYPROC,SYSTEM=SUB1& (e.g., &BNK).) terminates the variable name and is consumed during resolution: &BNK.TEXT with BNK=G resolves to GTEXT, not G.TEXT&VAR1.TEXT&VAR2.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:
& variables in the PROC's dependency targets and property values using this context.& reference) are resolved iteratively.Resolution priority (highest to lowest):
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.
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.
Multiple symbolics in a dataset name:
// SET BNK=G
// EXEC MYPROC,BASEOS=APP2.BASE.In the PROC:
//ISPSLIB DD DSN=&BASEOS.DB2DATA&BNK.,DISP=SHRResolution: &BASEOS → APP2.BASE. (trailing period consumed), &BNK → G (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=SHRGiven 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=SHRThe 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=SUB1In the PROC:
//SYSTSIN DD DSN=PGOS.&TLQ.BASE.DB2DATAG(&SYSTEM.DSN),DISP=SHRThe empty &TLQ collapses the double period: PGOS..BASE.DB2DATAG → PGOS.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"
}<<empty symbolic value>> nodes in the dependency graph. This also occurs for blank symbolic parameters concatenation, like &VAR.&VAR2.%%): Values containing %% (system symbols) are treated as dynamic and are not resolved.&&): Double-ampersand names (e.g., &&TEMP) are temporary datasets and are not treated as symbolic parameters.& is resolved iteratively until no more substitutions are possible.