Mainframe Assembler & Zowe: “Hello World” Example
My starting point is a mainframe, IBM assembler “Hello World” program which takes user input from JCL (described later) and prints it.
I’ll take this program on a quick transition from editing / running the program in a traditional ISPF interface to something a little more streamlined (with all of the necessary z/OS underpinnings): edit, source control, build, and run from a single interface (VS Code).
Editing
My background is heavy in z/OS assembler development side. After years of ISPF editing, I want to use something with more than 16 colors (although in reality, only 4 colors are ever shown 😃). I also want to paste from my clipboard without needing to know UPFRONT how many blank lines I need to insert:
As you can see, I overlay an instruction here by [wrongly] guessing 25 lines were enough for my paste 😢.
Editing 2.0
Ideally, I would rather use one of this guy’s 16 Best Code Editors for Windows (#9 looks good to me).
I have access to z/OSMF (and its REST APIs) available for use on our system. I also have access to the new open source framework for z/OS, Zowe.
I grabbed Zowe CLI and created a “profile” (just a YAML config) to specify connection information for my development system. I pull my assembler program with this command:
$ zowe files download ds “KELDA16.WORK.ASMPGM(TEMPLATE) --extension .asm
Things are starting to look a bit brighter 😉!
Source Control
Usually, if I’m working on an assembler program based in z/OS data sets, it’s because I’m working on a test program, trying out the mechanics of an assembler service documented in one (or more) of the several z/OS MVS Programming: Authorized Assembler Services Reference xxx-xxx. On rough days, I’ll need to reference the Principles of Operation 😨.
Typically, I wouldn’t version control programs like this because it seemed like too much overhead for something that’s not production code. But on my PC, it’s just three steps (assuming you have git installed):
$ git init
$ git add .
$ git commit -m "first commit"
From here, I setup / push to my Enterprise GitHub, Bitbucket, or some other source management server where I can use issue tracking, code review, and other integrations those tools offer.
Building
Generally I build on z/OS (not USS) with JCL (a.k.a. “Just Copy Libraries”, that is copy someone else’s JCL because JCL is confusing if you have < ~15 years development experience on z/OS).
The command:
$ zowe files download ds “KELDA16.WORK.JCL(TEMPLATE)” --extension .jcl
pulls my same-named JCL member (TEMPLATE) into my project:
(I use mediocre highlighters for assembler and JCL).
Now, I have the two, basic core pieces of my project on my PC:
With just this, I can execute my JCL and download output:
$ zowe jobs submit lf ./kelda16/work/jcl/template.jcl --directory ./output
At this point, I’m merely on par with the standard ISPF interface capabilities, but that will change quickly.
Scripting
My assembler source is not embedded in my JCL, so I’m actually building the assembler source that resides in a z/OS data set (not the version on my PC).
Because of that, I want a simple way to upload my source, run it, and get my output. I like task runners (or build tools) like Gulp, Grunt, Gradle, and npm for this purpose.
Since I already have Node.js and npm (prerequisites of the Zowe CLI), I’ll keep my project light and use npm.
I run:
$ npm init
This prompts me for input, but I press enter over and over again to accept all defaults and eventually get a package.json created. Within package.json, I changed the “scripts” to be like this:
"scripts": {
"postinstall": "tsc --pretty",
"genjcl": "node ./scripts/lib/genjcl",
"upload": "zowe files upload ftds \"./kelda16/work/asmpgm/template.asm\" \"kelda16.work.asmpgm(template)\"",
"build": "zowe jobs submit lf \"./build/custom.jcl\" --directory \"./output\"",
"start": "npm run upload && npm run build"
},
What this buys me is that I can run these scripts from the command line in a shorter form and begin to string them together:
$ npm run upload
→ upload my source to a z/OS data set$ npm run build
→ run build JCL and download output$ npm [run] start
→ upload and run build JCL
Here’s a complete example of making a code change and the process to observe my change. I add a new WTO to my source and issue one command to test:
$ npm start
Generalizing
At the time of writing this, there are over 800,000 npm packages available for use in development projects, and now I have a simple way to use them. Node-config and mustache.js are two that I’ll grab right away to help with project configuration and templating.
To add both to my project (i.e. to get these packages downloaded and saved in my package.json), run:
$ npm i config mustache -D
For Node-config, I created a “config” folder with a member “default.json”:
{
“job”: {
“name”: “TEMPLATE”,
“account”: “#ACCT”
},
“program”: {
“name”: “TEMPLATE”
}
}
Also in the “config” folder, I create a separate “local.json”. This file gets added to .gitignore).
The idea here is that team members can clone this project and get the same config/default.json element. They override (via local.json) any settings they want. Some info in local.json may be sensitive (not for sharing), and because it’s in .gitignore; it’ll never be pushed to the repository.
I alter the TEMPLATE JCL with mustache tags {{
and }}
:
//{{job.name}} JOB {{job.account}},’ASM/BIND/RUN’,MSGCLASS=A,CLASS=A,
// MSGLEVEL=(1,1),REGION=0M
/*JOBPARM SYSAFF=*
//*
// SET SRC={{program.name}}
Lastly, I create a small script file (written in Typescript):
#! /bin/env nodeimport * as config from “config”;
import * as mustache from “mustache”;
import * as fs from “fs”;const jcl = fs.readFileSync(“kelda16/work/jcl/template.jcl”).toString();const rendered = mustache.render(jcl, config);if (!fs.existsSync(“./build”)) fs.mkdirSync(“./build”);
fs.writeFileSync(“./build/custom.jcl”, rendered);console.log(“Generated custom JCL to ./build/custom.jcl”);
This script reads my mustached JCL (via Node.js fs), overlays settings in default.json with settings in local.json, and then renders (i.e. substitutes values in {{
}}
with actual values) the JCL to a ./build/custom.jcl file.
With a few adjustments to my project to support Typescript (one being a need to npm install -g typescript
) and creating a tsconfig.json for Typescript compiler options, I create one more new task:
$ npm run genjcl
→ invoke my new script to create custom JCL from my local.json
Now, I can share this project with others in my group. We can work off the same assembler source file and templated JCL while independently having our own JCL job account numbers and job names maintained outside of git.
Team members just need to clone the project, npm install
, npm run genjcl
, and they’re setup to contribute (assuming proper data sets are in place… if not Zowe CLI commands could help automate that as well).
Summary
This shows migrating an example project that was previously based entirely in z/OS data sets for use in VS Code. It was then source controlled with git (and managed in GitHub), and its build logic was encapsulated with npm scripts. Lastly, it was generalized so that others within the team could use it.
The complete project can be found here: https://github.com/dkelosky/assembler-template/tree/v1