Skip to content

Building Source Code for Multiple Environments

Dennis Behm edited this page Nov 15, 2022 · 1 revision

Building a source file for multi-runtime environments

Overview

Recently, some users of zAppBuild have requested additional guidance on a specific build scenario. The use case is about a single program, which is built to be executed in multiple runtime environments, i.e in BATCH as well in CICS. Their existing build process requires either to compile + link edit or only link edit the source code for each of the different runtime environments. The source code is only maintained once.

The following note outlines the required modifications to integrate this process into the zAppBuild language scripts.

Scenario walk-through: Link-edit for multi-runtime environments

Outline

The scenario assumes, that the executable needs to include particular environment-specific object modules, such as the various language stubs for MQ, which are specified through the INCLUDE control statement of the linkage editor.

In that configuration, the build process must invoke the linkage editor twice to:

  • include the environment specific object module
  • store the executable in a dedicated output load library
  • pass different values for the deployType

Build framework customization

This below sample is based on the zAppBuild codebase at commit 99624f6

Define new output datasets

Add the additional output library to build-conf/Cobol.properties

cobol_onlineLoadPDS = ${hlq}.CICSLOAD

Add the new output library to list of output datasets at build-conf/Cobol.properties to make sure that the dataset is automatically created.

cobol_loadDatasets = ${cobol_loadPDS},${cobol_onlineLoadPDS}

Application configuration to flag files for multi-runtime usage

Application teams need to specify which programs require to be link edited for multi-runtime use. A new file property allows the teams to specify which programs require to be built for multiple environments in file.properties. The deployType will be derived from the default for BATCH and ONLINE modules.

# new property to define multi runtime module flag to source code
cobol_multiruntimeModule = true :: **/cobol/member.cbl

# new properties for configure the link edit include statements depending on multi runtime use
# Using DBB PropertyMappings
cobol_linkEditStream_MRM =    INCLUDE OBJECT(@{member})  \n    INCLUDE SYSLIB(CUSTBAT)  :: BatchModule
cobol_linkEditStream_MRM =    INCLUDE OBJECT(@{member})  \n    INCLUDE SYSLIB(CUSTCICS) :: OnlineModule

Language script customization

The Cobol.groovy language script needs to process the new build properties.

In the mainline section of the Cobol.groovy language script, we need to evaluate the new build property cobol_multiruntimeModule to invoke the link edit step twice for files which have the multiruntimeModule flag set active.

Additionally, we need to ensure the two output libraries are correctly used to store the batch modules in cobol_loadPDS and the CICS modules in cobol_onlineLoadPDS. This is passed to the createLinkEditCommand method.

		String needsLinking = props.getFileProperty('cobol_linkEdit', buildFile)
		if (needsLinking.toBoolean()) {
            // replaces line 62 and will perform the default link edit
            MVSExec linkEdit
            String defaultTargetLibrary

            //evaluate multiuse flag
            String isMultiUseModule = props.getFileProperty('cobol_multiruntimeModule', buildFile)

            if (isMultiUseModule.toBoolean()){ // Multi-Runtime Module: Run link edit for BATCH environment first:

				// Get the deployType for Batch
                cobol_deployType = props.getFileProperty('cobol_deployType', buildFile)
				
				// evaluate the cobol_linkEditStream_MRM PropertyMapping for BatchModule
				PropertyMappings linkEditStream_MRM = new PropertyMappings("cobol_linkEditStream_MRM")
				String cobol_linkEditStream_MRM = linkEditStream_MRM.getValue("BatchModule")

          	    linkEdit = createLinkEditCommand(buildFile, logicalFile, member, props.cobol_loadPDS, cobol_deployType, cobol_linkEditStream_MRM, logFile)
               	rc = linkEdit.execute()
		        maxRC = props.getFileProperty('cobol_linkEditMaxRC', buildFile).toInteger()

   		    	if (rc > maxRC) {
    		    	bindFlag = false
	    		    String errorMsg = "*! The link edit return code ($rc) for $buildFile exceeded the maximum return code allowed ($maxRC) in multi-runtime link edit step BATCH"
			        println(errorMsg)
			        props.error = "true"
			        buildUtils.updateBuildResult(errorMsg:errorMsg,logs:["${member}.log":logFile])
		        } else { // Multi-Runtime Module:  Run link edit for ONLINE environment as second step:

					// Get the deployType for Online
                    cobol_deployTypeCICS = props.getFileProperty('cobol_deployTypeCICS', buildFile)                

					// get the cobol_linkEditStream_MRM for OnlineModules
					cobol_linkEditStream_MRM = linkEditStream_MRM.getValue("OnlineModule")
					
                    linkEdit = createLinkEditCommand(buildFile, logicalFile, member, props.cobol_onlineLoadPDS, cobol_deployTypeCICS, cobol_linkEditStream_MRM, logFile)

                    rc = linkEdit.execute()
                    maxRC = props.getFileProperty('cobol_linkEditMaxRC', buildFile).toInteger()
                    if (rc > maxRC) {
                        bindFlag = false
                        String errorMsg = "*! The link edit return code ($rc) for $buildFile exceeded the maximum return code allowed ($maxRC) in multi-runtime link edit step ONLINE"
                        println(errorMsg)
                        props.error = "true"
                        buildUtils.updateBuildResult(errorMsg:errorMsg,logs:["${member}.log":logFile])
                    }
                }
            } else { // non-multiuse modules: single link module to target library depending on file flag

                if (buildUtils.isCICS(logicalFile)){ // store load module in cics module library
                    defaultTargetLibrary = props.cobol_loadPDS
                    linkEdit = createLinkEditCommand(buildFile, logicalFile, member, defaultTargetLibrary, null, null, logFile)

                } else { // store load module in default module library
                    defaultTargetLibrary = props.cobol_onlineLoadPDS
                    linkEdit = createLinkEditCommand(buildFile, logicalFile, member, defaultTargetLibrary, null, null, logFile)
                }                    
                
                rc = linkEdit.execute()
			    maxRC = props.getFileProperty('cobol_linkEditMaxRC', buildFile).toInteger()

                if (rc > maxRC) {
                    bindFlag = false
                    String errorMsg = "*! The link edit return code ($rc) for $buildFile exceeded the maximum return code allowed ($maxRC)"
                    println(errorMsg)
                    props.error = "true"
                    buildUtils.updateBuildResult(errorMsg:errorMsg,logs:["${member}.log":logFile])
                }
            }             
			
            // scan module from defaultTargetLibrary
			if(!props.userBuild && !isZUnitTestCase){
				// only scan the load module if load module scanning turned on for file
				String scanLoadModule = props.getFileProperty('cobol_scanLoadModule', buildFile)
				if (scanLoadModule && scanLoadModule.toBoolean())
					impactUtils.saveStaticLinkDependencies(buildFile, defaultTargetLibrary, logicalFile)
			}
		}

The signature of createLinkEditCommand method, requires to be updated to take a new set of arguments, which differ from the two environments.

def createLinkEditCommand(String buildFile, LogicalFile logicalFile, String member, String targetLoadPDS, String deployType, String linkEditStream, File logFile) {

and update the appropriate configuration of the link edit step.

	String parms = props.getFileProperty('cobol_linkEditParms', buildFile)
	String linker = props.getFileProperty('cobol_linkEditor', buildFile)
    // EYE-CATCHER: modified next line
	if (linkEditStream == null) linkEditStream = props.getFileProperty('cobol_linkEditStream', buildFile)
	String linkDebugExit = props.getFileProperty('cobol_linkDebugExit', buildFile)

	// obtain githash for buildfile
	String cobol_storeSSI = props.getFileProperty('cobol_storeSSI', buildFile)
	if (cobol_storeSSI && cobol_storeSSI.toBoolean() && (props.mergeBuild || props.impactBuild || props.fullBuild)) {
		String ssi = buildUtils.getShortGitHash(buildFile)
		if (ssi != null) parms = parms + ",SSI=$ssi"
	}
	
	// define the MVSExec command to link edit the program
	MVSExec linkedit = new MVSExec().file(buildFile).pgm(linker).parm(parms)

	// Create a physical link card
	if ( (linkEditStream) || (props.debug && linkDebugExit!= null)) {
		def langQualifier = "linkedit"
		buildUtils.createLanguageDatasets(langQualifier)
		def lnkFile = new File("${props.buildOutDir}/linkCard.lnk")
		if (lnkFile.exists())
			lnkFile.delete()

		if 	(linkEditStream)
			lnkFile << "  " + linkEditStream.replace("\\n","\n").replace('@{member}',member)
		else
			lnkFile << "  " + linkDebugExit.replace("\\n","\n").replace('@{member}',member)

		if (props.verbose)
			println("Copying ${props.buildOutDir}/linkCard.lnk to ${props.linkedit_srcPDS}($member)")
		new CopyToPDS().file(lnkFile).dataset(props.linkedit_srcPDS).member(member).execute()
		// Alloc SYSLIN
		linkedit.dd(new DDStatement().name("SYSLIN").dsn("${props.linkedit_srcPDS}($member)").options("shr"))
		// add the obj DD
		linkedit.dd(new DDStatement().name("OBJECT").dsn("${props.cobol_objPDS}($member)").options('shr'))

	} else { // no debug && no link card
		// Use &&TEMP from Compile
	}

	// add DD statements to the linkedit command

    // EYE-CATCHER: modified next line
	if (deployType == null ) deployType = buildUtils.getDeployType("cobol", buildFile, logicalFile)

	if(isZUnitTestCase){
		linkedit.dd(new DDStatement().name("SYSLMOD").dsn("${props.cobol_testcase_loadPDS}($member)").options('shr').output(true).deployType('ZUNIT-TESTCASE'))
	}
	else {
        // EYE-CATCHER: updated target pds
		linkedit.dd(new DDStatement().name("SYSLMOD").dsn("${targetLoadPDS}($member)").options('shr').output(true).deployType(deployType))
	}