---
title: "The workstation path: check, compile and run with GnuCOBOL"
---

# The workstation path: check, compile and run with GnuCOBOL

Checking, compiling and running COBOL and JCL on the workstation with GnuCOBOL, subprogram CALLs, PARM, IDCAMS, VSAM and SORT steps, the local catalog, the tools, and how local results differ from Enterprise COBOL's.

The workstation path is optional. Everything on z/OS works without it. It is for continuous integration and for engineers without a host seat.

**Verification state:** **verified on Linux** (Ubuntu 24.04) with GnuCOBOL 3.1.2 (Ubuntu `gnucobol3`, BDB indexed handler), on the payroll sample, fixture projects and AWS CardDemo. **Not checked on Windows or macOS.** The local IDCAMS and SORT steps are the module's own approximations, checked against hand-derived expectations and CardDemo's data, not against IDCAMS, DFSORT or Syncsort.

## Local results are GnuCOBOL's

Everything that runs on the workstation runs GnuCOBOL, not Enterprise COBOL, and the surfaces say so: the `GnuCOBOL` source on markers, the job editor's note on local runs ("Compiled With" and the version and dialect), and the hover over a CBL statement. Differences to keep in mind:

- **Dialect.** A project that names none is checked and compiled in `ibm-strict`, GnuCOBOL's approximation of Enterprise COBOL. It rejects GnuCOBOL extensions Enterprise COBOL lacks, and also some code Enterprise COBOL compiles; `"dialect": "ibm"` in `mainframe.json` accepts them.
- **Compiler options.** CBL and PROCESS statements are honoured (cobc reads a copy with those lines blank, and every message still names the program). TRUNC(STD) and TRUNC(BIN) from `host.compile.options` and CBL are applied with `-fbinary-truncate` and `-fno-binary-truncate` (TRUNC(STD) is the default in IBM dialects). Options are applied in z/OS order with `cobcOptions` between the shop's and the program's. NUMPROC, ARITH, CODEPAGE, TRUNC(OPT) and options with no verified equivalent are not applied; one Information marker per program names them, and a hover explains them.
- **Collating sequence** is ASCII: '1' sorts before 'A', unlike EBCDIC.
- **No Language Environment.** A GnuCOBOL runtime error stands for an abend.
- **Tabs.** GnuCOBOL's IBM dialects expand a tab to the next multiple of 8 columns; the editor counts 4. Use `"cobcOptions": ["-ftab-width=4"]` for sources indented with tabs.

![An AWS CardDemo job run with GnuCOBOL: the job editor names the compiler, dialect and options applied, and notes that results are GnuCOBOL's.](https://neuralinverse.com/docs-images/modules/mainframe/local-job-run.webp)

Never treat a local result as evidence of how a program behaves on z/OS. Use [Check on z/OS](https://neuralinverse.com/docs/modules/mainframe/compile/#check-on-zos) for Enterprise COBOL's verdict. On AWS CardDemo, 11 of 14 batch programs without EXEC check clean under `ibm-strict`; its CICS programs need IBM copybooks CardDemo does not ship and use functions GnuCOBOL 3.1.2's `ibm-strict` does not know.

## Tools

| Tool | Setting | Minimum |
|---|---|---|
| GnuCOBOL (`cobc`) | `neuralInverse.mainframe.paths.cobc` | 3.1 |
| GDB | `neuralInverse.mainframe.paths.gdb` | 8 |
| COBOL Check | `neuralInverse.mainframe.paths.cobolCheck` (the jar, the unpacked release folder or a `cobolcheck` launcher) | 0.2.19 |
| Java, for COBOL Check | `neuralInverse.mainframe.paths.java` (else `JAVA_HOME` or `PATH`) | 8 |

They are found on `PATH`, in their settings, or in the usual install folders, and are never bundled. **Show Mainframe Tools** lists each with its version and path; the status bar shows an entry only when a configured tool is missing, fails or is too old. Without GnuCOBOL you get one notice per workspace that offers Compile on z/OS and Show Mainframe Tools.

![Show Mainframe Tools listing GnuCOBOL, GDB, COBOL Check and Java with their versions and paths.](https://neuralinverse.com/docs-images/modules/mainframe/mainframe-tools.webp)

## Check on open and save

Each program is checked with `cobc -fsyntax-only` in the project's dialect, format and copybook folders when opened or saved; a copybook through the programs that copy it. Every message lands on its line of the program or the copybook, and a copybook message also points to each COPY that brings it in. A COPY whose copybook is not found is marked as you type.

![A planted syntax error reported by GnuCOBOL in Problems, on the line and column it names.](https://neuralinverse.com/docs-images/modules/mainframe/gnucobol-problems.webp)

### EXEC SQL, EXEC CICS and EXEC DLI

Programs with embedded SQL, CICS and DL/I check clean: each EXEC block is replaced in place (CONTINUE in the PROCEDURE DIVISION, blanks elsewhere; EXEC SQL INCLUDE checked as a COPY), names with the SQL, DFH, EIB and DIB prefixes are not reported as undefined, and one Information marker per program names the statements not checked. The EXEC statements themselves are checked only on z/OS. Verified on a Db2 and a CICS program and on AWS CardDemo's CICS programs.

## Compile

The `ni-cobol` tasks **Compile All with GnuCOBOL** and **Compile <PROGRAM> with GnuCOBOL** (Ctrl+Shift+B while the project does not compile on z/OS; also **Compile with GnuCOBOL** in the title bar) run `cobc -x -std=<dialect> -fsource-location`.

- Programs are found by PROGRAM-ID in the project's `sources`; executables go to `<buildDirectory>/bin`.
- Every message lands on its program or copybook line in Problems.
- A program is recompiled only when it or a copybook it expands changed.
- The programs it CALLs are built as modules (below).

## Run a job: Run JCL with GnuCOBOL

**Run JCL with GnuCOBOL** (Ctrl+Alt+J), from the title bar, the Explorer or the Command Palette, runs a job's steps on this machine. It acts on the given file, else the active JCL, else the only job, else a job picked from the application map.

| JCL | Local behaviour |
|---|---|
| Steps | In order, with COND, JOB COND and IF/THEN/ELSE; abend flushing with EVEN, ONLY and IF on ABEND |
| DDs | Mapped to the project's `datasets` or a local catalog in `<buildDirectory>/datasets` (PDS members as folders, GDGs as `G0001V00` files) |
| DISP | NEW, OLD, SHR and MOD, normal and abnormal dispositions, PASS, temporary data sets and backward references |
| In-stream data | 80-byte cards, and for ACCEPT; DUMMY; concatenations |
| Utilities | IEFBR14, IEBGENER (copy), IDCAMS and SORT (below) |
| Procedures | Expanded as the application map expands them: in-stream and cataloged, nested, symbols, PARM and COND overrides, DD overrides and additions, INCLUDE, JCLLIB ORDER. A missing procedure or member is a JCL error naming the libraries searched |
| Errors | A GnuCOBOL runtime error, or a divide by zero without ON SIZE ERROR, is the step's abend at its statement |

The job can be cancelled. Its spool and record go to `<buildDirectory>/jobs/<JOBNAME>.<n>/` and it opens in the [job editor](https://neuralinverse.com/docs/modules/mainframe/jobs/#the-job-editor).

![A local job's step SYSOUT opened read-only beside its JCL.](https://neuralinverse.com/docs-images/modules/mainframe/local-spool-sysout.webp)

Verified on the payroll job end to end (both steps RC 0, the report and the master byte for byte against a hand run) and on fixtures for each row above. Return codes above 255 are read modulo 256; the local catalog keeps data, not DCB.

### Programs that CALL subprograms

Every program a CALL reaches is built as a module (`cobc -m`) into `<buildDirectory>/modules`, rebuilt only when changed, and put first on COB_LIBRARY_PATH. Static CALLs are followed; once any CALL names a data item's value, every program of the project is built. A static CALL that does not compile stops the job before any step runs; a CALL with no module abends its step at the CALL. The job log names any CALL outside the project.

### PARM as z/OS passes it

A program whose PROCEDURE DIVISION has USING gets a halfword binary length (big-endian, as z/OS stores it) and up to 100 characters after symbolic substitution, through `NIPARMD`, a driver generated into `<buildDirectory>/utilities/parm/` whose header says it is yours to use, change and share. The step's RC is the program's RETURN-CODE. A PARM over 100 characters is a JCL error before any step runs. A program without USING gets PARM as its command line. Verified on CardDemo's interest calculation with `PARM='2022071800'`.

### IDCAMS and VSAM on the workstation

IDCAMS steps run with the module's own IDCAMS reader:

- DEFINE CLUSTER (INDEXED, NONINDEXED, NUMBERED, KEYS, RECORDSIZE), ALTERNATEINDEX, PATH and GENERATIONDATAGROUP; DELETE; REPRO between sequential data sets and clusters; PRINT; BLDINDEX; IF LASTCC/MAXCC, DO, SET.
- SYSPRINT goes to the spool and the step's RC is MAXCC.
- A KSDS, ESDS or RRDS is a GnuCOBOL INDEXED, SEQUENTIAL or RELATIVE file, readable by key from any GnuCOBOL program. A GDG with LIMIT rolls off its oldest generations.

Only IDC0001I and IDC0002I are real IDCAMS message numbers; other SYSPRINT lines are marked "(local)". Local clusters open read-only in the [Record Viewer](https://neuralinverse.com/docs/modules/mainframe/records/) through **Open Local Data Set...**, a KSDS in key order. Verified on CardDemo's file definition jobs and KSDS reads by key over 300 transactions.

### SORT steps

PGM=SORT or ICEMAN: SORT FIELDS and COPY, INCLUDE and OMIT, INREC and OUTREC FIELDS or BUILD with EDIT masks, SUM, SYMNAMES, on fixed-length records with CH, ZD, PD, BI and FI fields; messages in SYSOUT. CH fields compare in ASCII, not EBCDIC. A record longer or shorter than SORTOUT's LRECL is cut or padded with RC 4.

## Run records

Every local compile, job, COBOL Check run and debug build writes a JSON run record in the build folder. See [Run records](https://neuralinverse.com/docs/modules/mainframe/reference/#run-records).

Debugging with GDB is on [Debug on the workstation](https://neuralinverse.com/docs/modules/mainframe/workstation-debug/); unit tests on [Unit tests with COBOL Check](https://neuralinverse.com/docs/modules/mainframe/unit-tests/).
