1 Use

This chapter explains how the p1 Modula-2 compiler is used as a tool from a command shell. As Xcode does less more than invoking command shells for various build stages, this information is also important if p1 Modula-2 is used from Xcode exclusively. Xcode integration is described in detail in chapter 1.9.

1.1 General

The Modula-2 compiler is a three-pass compiler. In the first two passes the declarations and statements are analyzed and stored in tree form, ready for the subsequent code generation. The back end generates the assembler code to be processed by "clang", "gcc", "as", or a similar assembler. The exact sequence of the passes is as follows (those in brackets may be omitted):

For definition modules:
1. Syntax and declaration analysis
2. Symbol file generation]
3. Listing and / or error message generation]

For implementation modules:
1. Syntax and declaration analysis
2. Statement analysis]
3. Statement tree optimization]
4. Reference file generation]
5. Machine code generation]
6. Listing and / or error message generation]

The compiler writes any error messages to error output (file descriptor 2) and also includes them in the optional listing output.

Output files (a symbol/-reference file, machine code) are only created if there are no errors in the module being compiled. When particularly serious errors occur (e.g. symbol files for imports are missing) only the first pass of the compilation is completed.

1.2 The Command Line

The command line to compile a Modula-2 source file has the following form (specifications in square brackets "[...]" are optional):
"Modula2 [options] ... source"

source name of the source file in the form [path]name[.ext]
(Default extension is ".MOD".)
options = option [options]
option = -[NO]optionText | -d compileTimeVariable=value | -optionText [optionSpec].

Compile-time variables are described in detail in section 4.3. "optionText" can be any one of the following:
PROGRESS Progress information is sent to error output (file descriptor 2).
ERROREverything the compiler outputs to error output is also appended to file M2ERRORS.LOG.
CROSSREFERENCE Cross reference information is included in the listing file. Unused objects are marked and a warning specifying the number of unused objects is written to standard output. This option can only be used in conjunction with the "LISTING" option.
CHECK[off | none | normal | full | all]
  off | none No runtime checks are generated. These specifications are equal to "-NOCHECK"
  normal |
  no specification
Runtime checks provided for in the standard are generated (exception: overflow checks for whole number arithmetic). The distinction between exceptions from real arithmetic and complex arithmetic is not activated by this option (cf. 2.10, 4.2 and 5.1.1).
  full | all All runtime checks provided for in the standard are generated. The distinction between exceptions from real arithmetic and complex arithmetic is activated by this option.
COPYREFPARAM Copies of reference parameters are created (cf. 5.3.2).
OUTPUT nameOutput is sent to file "name". When compiling a definition module, this is the name of the symbol file, otherwise it is the name of the assembler source file (C source file if option "COUTPUT" is specified). If the "OUTPUT" option is omitted, the name of the output file is formed from the name of source file with appropriately modified extension.
If "-" is specified for "name" output is directed to standard out (file descriptor 1). This is especially used to pipe the assembler output directly to the assembler.
LISTING [name] A listing file is generated. If no name is specified, the name of the source file is used with the extension ".MLST" appended.
REFERENCE [name] A reference file (for the source level debugger) is generated. If no name is specified, the name of the source file is used but with the extension ".MREF".
SYMFILES path Directory "path" is also searched for imported modules' symbol files. The current directory and the directories specified in the environment variable "M2SYMS" are always searched. This environment variable may consist of several search paths separated by commas.
This option may be specified more than once in the command line.
GENFILES path Directory "path" to be searched for files for generic definitions (".GDE", ".GMO"). The current directory is searched first, then the paths defined by the option "GENFILES". If necessary the paths for symbol files are searched too.
This option may be specified more than once in the command line.
FIXEDSUBRANGESIZE Variables of subrange types always occupy the size of the parent type (cf. 5.3.1).
ARCH architectureTarget architecture. Recognized values are "arm64" and "x86_64". The C back end also recognizes this option.
COUTPUTA C / C++ source is generated as output file.
OPTIMIZE[off | none | 0 | 1 | 2 | full | all]
  off | none | 0 All optimizations are deactivated. This is sometimes desirable when using debuggers. Equal to "NOOPTIMIZE".
  1 Only peephole optimizations are activated.
  all | full | 2 All optimizations, especially the 3-operand instructions on ARM processors, are activated.
WARNINGSWarnings are written to standard error output.

The following options are used only if a C source is generated:
INCLUDESOURCE The Modula-2 source is incorporated as comments into the C source.

The prefix "NO..." is not allowed with the following options. In cases of conflicts between these options the more severe restrictions have priority.
EXTENDEDAll extensions specific to p1 Modula-2 are activated.
COMPATIBILITY All uses of extensions with respect to the ISO standard (including the ones " allowed" in module "SYSTEM") are flagged with a warning.
This mode can be used to make a quick check on the compatibility and portability of a program.
WEAKSTANDARD The compiler is switched at first to "standard" mode, but the language extensions are initialized (e.g. "INLINE" is treated as a keyword and is not permitted as an identifier). All extensions to the standard (apart from the "allowed" ones from "SYSTEM") are flagged as errors. Initially the generation of all run-time tests is enabled, but this can be partly or wholly reversed using pragmas. The compiler's operating mode can also be changed by means of pragmas (to "COMPATIBILITY" or "EXTENDED").
This mode is intended for standard compilations in which runtime optimizations (e.g. no tests, no copying of reference parameters) are possible.
STANDARD The compiler is switched to standard mode. The language extensions (e.g. dynamic arrays) are not initialized. Only the extensions to "SYSTEM" allowed by the standard remain in effect. The generation of all the runtime tests required by the standard is enabled and can be disabled neither via the option "NOCHECK" nor by using pragmas.
This is the "conformity" mode required by the standard.

The default settings of the options are:

All options may be abbreviated.

Modula2 -nocheck -p -sym $UserLib -weakstandard test

1.3 Files

The compiler accepts four kinds of source files:
  1. Definition modules
  2. Implementation and program modules
  3. Generic definition modules
  4. Generic implementation modules

File extensions can be chosen freely, since the module's heading already documents what kind of file it is. However, the following conventions are suggested:

  • for definition modules the extension ".DEF"
  • for implementation and program modules the extension ".MOD" (which is also the default for source files)
  • for generic definition modules the extension ".GDE"
  • for generic implementation modules the extension ".GMO"
  • The compiler creates the following kinds of output files:

    1. Code generation (arm64 and x86_64)
      1. Symbol files (".MSYM") from definition modules
      2. Assembler sources (".MOD.s") from implementation and program modules
      3. Reference files (".MREF") for the debugger from implementation and program modules
      4. Listing files (".MOD.MLST" resp. ".DEF.MLST")
    2. C generation
      1. Symbol files (".MSYM") from definition modules
      2. C sources (".MOD.c") from implementation and program modules
      3. Listing files (".MOD.MLST" resp. ".DEF.MLST")

    The compiler options described in the previous section can be used to define the names of all output files freely or to suppress output altogether.

    1.4 Compiler Directives

    Compiler directives can be placed anywhere in the source code in the form of pragmas. The variables used in such pragmas can be assigned values from the command line using the pragma "ENVIRON".

    After the commandline option "-d" the value of a pragma variable must be declared in the form "name=value". Such a value declaration ("name=value") must also conform to the shell conventions for a parameter (e.g. no spaces or if so then indicated using "" or some other means). If the pragma "ENVIRON" is used (in the program source) with an as-yet undefined variable, the compiler looks for the given variable name in the list of value declarations from the command line. Case is significant in such names, and in contrast to compiler options, abbreviations are not permitted. If the name is found in the list the variable is assigned the value declared, otherwise it is given the default value specified in the pragma (cf. sections 4.2 and 4.3).

    1.5 Compiler Errors

    Compiler error messages are output both to the terminal window and, if present, to the source listing.

    Messages with an error number of 900 indicate an internal compiler error. The message "compiler assertion error" is output along with the PC and sourcecode line number if an internal inconsistency occurs in the code generation. If either of these errors occurs, please send the source code which caused it, along with any details given in the error message, to:

    p1 Gesellschaft für Informatik mbH
    Schwanthalerstraße 14
    D-80336 München
    Tel.: +49 / (0)89 / 546131-0
    Fax: +49 / (0)89 / 5802597
    e-mail:m2@p1-gmbh.deElmar Henne
    or:m2@awiedemann.deAlbert Wiedemann

    1.6 Built-In Version-Compatibility Check

    On compilation, a key is calculated for each definition module based on the actual contents of the generated symbol file. This means that the module key remains exactly the same as long as nothing (other than possibly comments, indentation etc.) in the definition module itself is changed. A definition module's key is placed in the object code of its corresponding implementation module and in all modules that import from it.

    The compiler checks for each module whether all directly or indirectly imported versions of the module use the same module key. If mismatching keys are found the compiler issues an error message.

    An additional check on the module keys of the object files of each module is carried out at link time (meaning that inconsistencies involving foreign definition modules cannot be detected). If there are mismatching versions, unresolved externals of the form "<module name>_xxxxxxxx" will result from the link. In such a case, either the referencing or the defining object file was not recompiled after a change was made in the definition file "<module name>.DEF". The currently valid module key can be viewed in the symbol file using the utility "M2SymfileDump" (cf. Utilities, chapter 2.2).

    Since a file's contents are examined (and not just its modification date, which could have been altered due to a backup/-restore procedure for example), this version check is more accurate than that provided by the make utility.

    1.7 Linking Modula-2 Programs

    Linking of Modula-2 modules is done by the standard GNU linker "ld", as a rule invoked by the universal compiler driver "clang" / "gcc". Exactly one module must be a program module, i.e. one with the header "MODULE ..." and no corresponding definition module. Program execution starts with this module. If you try to link more than one program module, the linker will issue an error message because of multiple defined symbols.

    Compiling and linking Modula-2 programs can be simplified by using the utility "make". A "makefile" containing compile and link rules can be generated automatically using the utility "GenMake", which is also distributed with the compiler (see the "Utilities" part of the manual).

    The folder "Tool Examples" contains various different example tools including appropriate makefiles with commands for compiling and linking tools. The folder "Application Examples" contains some example applications including appropriate makefiles with commands for compiling and linking applications.

    1.7.1 Command Line Tools

    You must specify the Modula-2 library "libm2lib.a" by the option "-lm2lib" in the command line for gcc.
    libm2lib.a contains the library modules (see the "Library" part of the manual) and the Modula-2 runtime kernel.
    A typical ggc command line is "gcc -L$M2LIB -o MyTool MyTool.o -lm2lib", where "M2LIB" is an environment variable containing the path to the directory where "libm2lib.a" resides.

    If you want to use the Modula-2 source level debugger for tools, you must specify "-lm2dlib" instead of "-lm2lib" in the link command. "libm2lib.a" contains a special version of the runtime kernel which replaces that of "libm2lib.a" (see the "Utilities" part of the manual).

    1.7.2 Cocoa Applications

    The graphical user interface for Cocoa applications should be constructed using the GUI builder integrated in Xcode. But as, in contrast to the old Carbon interface, not only outlines for the elements of the GUI are designed, but also objects of specified user designed subclasses are instantiated, Xcode uses special syntax elements of Swift / Objective C to bind the GUI description to the given code. This cannot be mapped to Modula-2 code, as this would need a Modula-2 compiler integrated in Xcode.

    To circumvent this problem, it is recommended to devide an application that is structured according to the design pattern MVC (model – view – controller) into a part written in Modula-2 (the model) and a part written in Swift or Objective C (the view and the controller). Many aspects of the controller are handled automatically by the GUI builder or hidden under the surface of the Cocoa classes, so this part is also written in Swift / Objective C. Parts of the view may be written in Modula-2 as well; especially the drawing of specialized view my be done by using the core interfaces from Modula-2.

    This approach also has the advantage that the Modula-2 part may be used for macOS applications as well as for iOS applications in the same project. Simple Project D3Cocoa

    The project D3Cocoa shows the basic integration concepts, the more complex examples PlotFun and Dungeon show further details. A desription of how to set up Xcode projects is given in chapter 1.9.

    The project D3Cocoa consists of six targets. "GenMakeD3Cocoa", "D3Cocoa", and "clean" are used to set up the make file for the Modula-2 part resp. to build the Modula-2 part as a library; "clean" allows to clear all build results. "D3CocoaSwift", "D3CocoaC", and "D3CocoaiOS" are the main targets that produce the applications.

    Main Target Settings Integration in a Swift macOS application

    The main target links against two libraries:

    The initialization and finalization parts of the Modula-2 modules are executed via special entry points in "libm2libforeign.a". These are made known to the Swift part in the file "M2Foreign.h". For details please refer to chapter
    The interface parts for CocoaInterfaceLib are made known in "Interface.h" / "Interface.c".


    //  Interface.h
    //  D3CocoaSwift
    //  Created by Albert Wiedemann on 16.03.22.
    #ifndef Interface_h
    #define Interface_h
    typedef struct Aktionen
        void (* zeichnen) (CGContextRef ref, long num);
        void (* winkelSetzen) (float, float, long);
        float (* phiGeben) (long);
        float (* deltaGeben) (long);
    } Aktionen;
    Aktionen* aktionen;
    extern void Setzen (Aktionen* a);
    #endif /* Interface_h */
    #include "Interface.h"
    Aktionen* aktionen;
    extern void Setzen (Aktionen* a)
        aktionen = a;
    This headers cannot be directly included in Swift files. Therefore a scpecial interface file, the briding file, has to be created, which inludes all header files that should be exposed to Swift. This file can be generated automatically when the interface file pair is created.

    Creation of a C file pair for interface descriptionName is "Interface", header is also created Save in project folderCreate Briding Header

    The briding file should look like this:

    //  Use this file to import your target's public headers that you would like to expose to Swift.
    #ifndef D3CocoaSwift_Bridging_Header_h
    #define D3CocoaSwift_Bridging_Header_h
    #import "M2Foreign.h"
    #import "Interface.h"
    #endif /* D3CocoaSwift_Bridging_Header_h */

    Module initialization and finalization are added to the (automatically created) file AppDelegate.swift:

    //  AppDelegate.swift
    //  D3CocoaSwift
    //  Created by Albert Wiedemann on 15.03.22.
    import Cocoa
    class AppDelegate: NSObject, NSApplicationDelegate {
        @IBOutlet var window: NSWindow!
        override init()
        func applicationDidFinishLaunching(_ aNotification: Notification) {
            // Insert code here to initialize your application
        func applicationWillTerminate(_ aNotification: Notification) {
            // Insert code here to tear down your application
        func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
            return true
    Please notice that the module initialization is not called from within the method "AppDelegate.applicationDidFinishLaunching", because this is too late; the application initialization already needs access to the Modula-2 procedures.

    The start of the initialization chain ist set in the module "CocoaInterface", (marked in red) which itself imports all other modules:

    <* ASSIGN (ForeignEntry, TRUE) *>
        aktionen := Aktionen {Zeichnen, WinkelSetzen, PhiGeben, DeltaGeben};
        Setzen (aktionen);
        myColorSpace := CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB);
        red := CGColorCreate (myColorSpace, RGBAArray {1.0, 0.0, 0.0, 1.0});
        white := CGColorCreate (myColorSpace, RGBAArray {1.0, 1.0, 1.0, 1.0});
        black := CGColorCreate (myColorSpace, RGBAArray {0.0, 0.0, 0.0, 1.0});
    END CocoaInterface.

    This module also makes the interface procedures known to the Objective C part (marked in green). All interface procedures are stored in one record (first line), the address of which is then passed to the C part in the second line.

    The record for the interface procedures as well as the procedure "Setzen" are defined in a foreign definition module.

    <* ASSIGN (Foreign, TRUE) *>
    DEFINITION MODULE AktionsProzeduren;
    FROM CGContext IMPORT CGContextRef;
        Aktionen = RECORD
        	zeichen: PROCEDURE (CGContextRef, INTEGER);
        	winkelSetzen: PROCEDURE (REAL, REAL, INTEGER);
        	phiGeben: PROCEDURE (INTEGER): REAL;
        	deltaGeben: PROCEDURE (INTEGER): REAL;
    	<* ASSIGN (Calling, "CCalling") *>
        PROCEDURE Setzen (<* ASSIGN (Reference, TRUE) *> aktionen: Aktionen);
    END AktionsProzeduren.
    Use of the interface is made in several parts, this example shows the use of the drawing method in the customized restult view.
    //  MyView.swift
    //  D3CocoaSwift
    //  Created by Albert Wiedemann on 16.03.22.
    import Foundation
    import Cocoa
    class MyView: NSView
        var nummer = 0
        var phi: Float = 0.0
        var delta: Float = 0.0
        func FlächendatenSetzen (nummer: Int, phi: Float, delta:Float)
        	self.nummer = nummer
        	self.phi = phi
        	self.delta = delta
        override func draw(_ dirtyRect: NSRect)
            let context = NSGraphicsContext.current!.cgContext
            if (aktionen != nil)
                aktionen.pointee.winkelSetzen (phi, delta, nummer)
                aktionen.pointee.zeichnen(context, nummer)
                let myRect = CGRect(x: 50, y: 50, width: 100, height: 100)
                context.setFillColor(CGColor(red: 0.0, green: 0.0, blue: 1.0, alpha: 1.0))
                context.setStrokeColor(CGColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0))
 Integration in an Objective-C macOS application

    The integration of Modula-2 into an Objective-C application is much the same as the integration into a Swift application.

    The used libraries "libm2libforeign.a" and "libCocoainterfacelib.a" and the interface files "M2Foreign.h" and "Interface.h" / "Interface.c" are identically.

    The initialization part of the Modula-2 modules may be called in the initializer of the NSApplicationDelegate-Object as with Swift, but it may alternatively be called in the main routine in the (automatically created) file main.c itself.

    //  main.m
    //  D3CocoaC
    //  Created by Albert Wiedemann on 15.03.22.
    #import "M2Foreign.h"
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // Setup code that might create autoreleased objects goes here.
        return NSApplicationMain(argc, argv);

    The module finalization is added to the (automatically created) file AppDelegate.m:

    //  AppDelegate.m
    //  D3Cocoa
    //  Created by Albert Wiedemann on 05.08.13.
    //  Copyright (c) 2013 Albert Wiedemann. All rights reserved.
    #import "AppDelegate.h"
    #import "M2Foreign.h"
    @implementation AppDelegate
    - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
        // Insert code here to initialize your application
    - (void)applicationWillTerminate:(NSNotification *)aNotification
        M2Terminate ();

    The call of the proceures defined in the interface description looks a bit different, though it is semantically identically.

    Use in the controller:

    //  Controller.m
    //  D3CocoaC
    //  Created by Albert Wiedemann on 16.03.22.
    #include "Controller.h"
    @implementation Controller
        long num = 0;
    - (IBAction) ZeichnenKnopfGedrückt: (id) sender
        [zeichenView FlächendatenSetzen: num: [phi floatValue]: [delta floatValue]];
        [zeichenView display];
    - (IBAction) FlächeGeändert: (id) sender
        num = [flächenAuswahl indexOfSelectedItem];
        phi.floatValue = (*(aktionen ->  phiGeben)) (num);
        delta.floatValue = (*(aktionen ->  deltaGeben)) (num);
        [zeichenView FlächendatenSetzen: num: [phi floatValue]: [delta floatValue]];
        [zeichenView display];
    Use in MyView.draw
    - (void)drawRect:(NSRect)dirtyRect
        // Obtain the Quartz context from the current NSGraphicsContext at the time the view's
        // drawRect method is called. This context is only appropriate for drawing in this invocation
        // of the drawRect method.
        [super drawRect:dirtyRect];
        CGContextRef context = [[NSGraphicsContext currentContext] CGContext];
        if (aktionen != NULL)
            (*(aktionen ->  winkelSetzen)) (phi, delta, nummer);
            (*(aktionen ->  zeichnen)) (context, nummer);
            // Make a CGRect that has its origin at (40,40)
            // with a width of 130 units and height of 100 units.
            CGRect ourRect = CGRectMake(40, 40, 130, 100);
            // Set the fill color to an opaque blue.
            CGContextSetFillColorWithColor(context, myGetBlueColor());
            // Fill the rect.
            CGContextFillRect(context, ourRect);
            // Set the stroke color to an opaque green.
            CGContextSetStrokeColorWithColor(context, myGetGreenColor());
            // Stroke the rect with a line width of 10 units.
            CGContextStrokeRectWithWidth(context, ourRect, 10);
 Integration in a Swift iOS application

    The integration of Modula-2 into an Swift iOS application is much the same as the integration into a Swift macOS application. Though, there are some subtle differences to be observed.

    The used libraries "libm2libforeign.a" and "libCocoainterfacelib.a" have to be replaced by iOS versions of those libraries: "libm2libforeigniOS.a" and "libCocoainterfacelibiOS.a".
    For Modula-2 generated code there is no difference between macOS and iOS version. Nevertheless, when running the assembler stage ("as") you have to provide for the minimal OS version your application is intended to run on and additionally the path to the SDK used to assemble ("system root"). When you run "as" from a shell script, it assumes the actual macOS as minimal and uses the system given by the command line tools. When assembling for iOS, you have to specify both informations like (See "makefile" for details):

    as -arch arm64 -force_cpusubtype_ALL -miphoneos-version-min=13 -isysroot /Library/Developer/CommandLineTools/SDKs/iPhoneOS14.4.sdk -o myfile.o myfile.s
    Note: Minimal version my be lesser than the SDK you use.

    The C files and header files look the same as when integrating Modula-2 in a macOS application. To show variants, the contents of "M2Foreign.h" has been copied into "Interface.h" so there is only one header file needed.

    The file for the controller class is missing in this project, as there is already a customized view controller generated automatically. This controller may be used to implement the necessary outlets and action methods.
    The inserted code for "D3Cocoa" ist marked red. The green marked code has been added to make the keyboard disappear when no longer needed.

    import UIKit
    class ViewController: UIViewController, UITextFieldDelegate {
    	@IBOutlet var zeichenView: MyView!
    	@IBOutlet var phi: UITextField!
    	@IBOutlet var delta: UITextField!
        var nummer = 0
        @IBAction func ZeichenKnopfGedrückt(sender: Any)
            if let phi = Float(self.phi.text ?? "0.0")
                if let delta = Float(self.delta.text ?? "0.0")
                    zeichenView.FlächendatenSetzen(nummer: nummer,  phi: phi, delta: delta)
    	@IBAction func strahlSelected (sender: Any)
    		nummer = 0
            FlächeGeändert ()
    	@IBAction func kugelSelected (sender: Any)
    		nummer = 1
    		FlächeGeändert ()
    	@IBAction func meridianSelected (sender: Any)
    		nummer = 2
    		FlächeGeändert ()
    	@IBAction func schichtSelected (sender: Any)
    		nummer = 3
    		FlächeGeändert ()
    	@IBAction func kugelZylinderSelected (sender: Any)
    		nummer = 4
    		FlächeGeändert ()
    	@IBAction func fehlerSelected (sender: Any)
    		nummer = 10
    		FlächeGeändert ()
        private func FlächeGeändert ()
        	phi.text = "\(aktionen.pointee.phiGeben(nummer))"
        	delta.text = "\(aktionen.pointee.deltaGeben(nummer))"
        	zeichenView.FlächendatenSetzen(nummer: nummer, phi: aktionen.pointee.phiGeben(nummer), delta: aktionen.pointee.deltaGeben(nummer))
        override func viewDidLoad() {
        	phi.text = "\(aktionen.pointee.phiGeben(0))"
        	delta.text = "\(aktionen.pointee.deltaGeben(0))"
        func textFieldShouldReturn(_ textField: UITextField) -> Bool
            return true
 Document Based Project PlotFun Written in Objective C

    The project PlotFun is a typical document based application. In contrast to the former Carbon implementation, the windows menu and all other stuff for opening and closing documents is implicitly maintained by the Cocoa framework. This project makes use of the library module "ByteArrayFile" to read resp. write the document data to a byte array that can be handled by the Cocoa IO features.

    PlotFun consists of three targets: Comlex GUI Example Project Dungeon Written in Swift

    The project Dungeon shows an application where the GUI is written in Swift. Swift itself does not support external interfaces, but projects written with Swift allow beeing mixed with Objectice C parts; this allows to write glue code to interfce with Modula-2 parts. This seeming disatvantage is equalized by the fact that Swift is much more close to Modula-2 then Objective C.
    The Dungeon expample shows a very extensive use of the core foundation interface regarding access to advanced graphic features as well as basic use of the core text interface and the access of bundle ressources.

    Dungeon consists of eight targets:

    As Modula-2 cannot be interfaced directly from Swift, this projects needs some glue code written in (Objective) C ("M2Interface.h" and "M2Interface.c"). Whereas the Swift headers are implcitly bridged to C, there has to be specified a special C header file ("DungeonCocoa-Bridging-Header.h") that imports all C header files which are meant to be bridged to Swift.

    How the various image files (.png) are copied to the appropriate ressource folders may be viewed in the target definition of DungeonCocoa (manually added Copy Files build phases, to prevent the image files from merely beeing copied flat into the ressource folder).

    1.7.3 Compiling and Linking Applications and Tools when using the C Back End

    The C definitions of the predefined types and procedures are stored in an include file named "p1M2.h", by default located in the folder "Modula2". The path to this file has to be specefied in the command line of the C compiler. Within Xcode, you have to specefiy the path to this file in the target settings ("user header search path").

    When producing code via C back end, the library m2lib has to be replaced by m2clib. The path to this file has to be specified for ld. Within Xcode, you have to specefiy the path to this file in the target settings ("library search path"). The file is universal binaries i. e. contains arm64 and x86 versions of the libraries.

    There are no debugger versions of the C back end libraries. If you need to debug programs compiled via C back end, you can use any available C debugger, e. g. gdb. It is useful to include the Modula-2 source lines in the C files using the Modula-2 compiler command line option "-includesource".

    1.7.4 Building Universal Binaries

    The linker (ld) cannot produce Universal Binaries; it always emits "thin" output files. You generate an output file for each arm64 resp. x86_64 code in the usual way, e.g. MyAppA64 and MyAppX86. Then you have to use the command line tool "lipo" to combine the two thin executables to a Universal Binary.
    lipo MyAppA64 MyAppX86 -create -output MyApp.app/Contents/MacOS/MyApp

    The easiest way to produce a Universal binary is to set up "GenMake" for all desired architectures and just execute the generated make file. For a detailed description please refer to 1.9.2.

    1.8 Environment Variables and Path Settings

    As p1 Modula-2 is designed for use from the command line, you are free to store binaries, libraries, and symbol files where ever it is convenient. All you need to do is to set paths and shell variables so that they point to where you store these items. It is suggested that you have a folder the path of which is stored int the variable M2HOME. This folder contains all the Modula-2 stuff. To configure the command shell, you should add the following lines to either "/etc/.profile" for all users or to "~/.profile" for a single user. Some shells allow even for shell specific declarations:
    M2HOME=<where ever you place the Modula2 folder, e.g. ${HOME}>/Modula2
    export PATH
    export M2HOME
    export M2SYMS
    export M2LIB

    1.9 Xcode Integration

    This manual is not an Xcode manual. It describes only the special aspects of integrating p1 Modula-2 into Xcode.

    All actions needed to produce a product out of source files are done by Xcode via shell commands. The shell output is directed to Xcode and scanned for eventual error messages. So Xcode can stop the build process if one or more steps fail. Furthermore, error messages that contain file and line informations in a given format (as issued by compilers) allow Xcode to show the erroneous code. For procjects written in a language "known" to Xcode, all build phases (compilation, linking, resource generation) are set up automatically. For other languages, like Modula-2, Xcode needs to be told what to do.

    To use p1 Modula-2 from Xcode requires nothing more than to set up compilation and link commands correctly. In general, it is a good idea to use make files in the same way as when working directly from the terminal.

    1.9.1 Setting up Xcode Projects for p1 Modula-2

    For the following sections it is important to know that Xcode calculates all path names relatively to the location of the project file. So it is wise to place this file so that all files which are part of this project reside in the same folder as the project file or in a subfolder. Setting up a Very Simple New Project

    A very simple project is one where all source and binary files reside in one folder and which supports only the current system archtecure. So you need not generate an excplit make file but can use the command "M2Build" to do all the job for you.

    The first step is to choose the correct kind of project. For Modula-2 this is "External Build System".

    Project Kind

    The next step specifies the project options. Please make sure you have chosen "bash" as build system.

    Project Options

    In the third step, the project is created in the chosen folder.

    Project Creation

    Xcode creates automatically a external build system target within the new project where "bash" is already preset as build system. You only need to fill in the argument line with "-l command.sh". The option "-l" tells "bash" to behave like a log in shell which is necessary to set op the environment variables needed by p1 Modula-2. "command.sh" is the name of the command file to be executed; you can use any correct file name for this file. In this example, "command.sh" will be used.

    Target Specification

    Now you can create the source file(s) for your program. For tasks known to Xcode, there are many templates provided for. For a new Modula-2 source file you best select the template "Empty" for an empty document.

    Template for Source File

    As next step give the file an appropriate name and save it (normally within the project folder). The check box near the target "ProducerConsumer" tells Xcode that the given target depends on the file you are about to create and that the build command of Xcode needs to rebuild that target when the file has changed.

    Source File Creation

    The last step is to create the command file "command.sh". The creation is done exactly the same way as the source file creation. You may even want to the target be checked for this file too. The contents of this file is the command sequence you want to be executed. In the simplest case it is just the command "M2Build <program name>", in this case "M2Build ProducerConsumer".

    Project View

    Build Menu Setting up a Simple New Project with source subfolders

    Several steps overlap with the example above, only the different parts are given in detail. The project itself is part of the distribution.

    1. Create a new project
    Create a new project (file -> new -> project). From the dialog choose macOS -> other->→ Empty (see above)

    On the next page give the project an appropriate name (e. g. Modula-2 project). On the last page select a location where the project folder will be created. You get an empty project with no targets and files.

    2. Add files
    a) Create new files
    New files are created by file -> new -> file. From the dialog choose macOS -> other -> Empty (see above)

    On the next page give the file a name and store it in the project folder (as suggested). The group should be the project itself. No targets need to be specified for Modula-2 projects. If you want to use subfolders for new files, you can create the subfolders in the dialog. Make sure to select as group the name of the subfolder—the group structure of the file tree within Xcode should in general reflect the folder structure the files are stored in.

    b) Add existing files
    If you already have existing files to add to the projects, just copy these files into the project folder. They are allowed to reside in subfolders, so you can add any folder structure as is appropriate for your project.
    In this example a file Hello.MOD is copied into the project as well as a folder Library that contains auxiliary modules. To add these files select file -> add files to from the menu bar. Chose all files and folders you want to add. Make sure the button “Create groups for any added folders” is selected (see below)

    Add Files

    Below the project icon you now find the files and folders (as groups) that you have added. Files may be edited in the main window by selecting them; double click opens a new editor window.

    Edit Files

    3. Add targets
    A target is something Xcode can execute to perform a specific task. The targets for task well known to Xcode include also a program framework setting up default behaviour etc. As Modula-2 is completely unknown to Xcode, the only usable offer Xcode gives is the so called "external build system" target, which just executes the given command.
    As the Modula-2 tools need several environment variables to be set correctly the most convenient way to use the external build system is to start a new shell (e. g. bash) as login shell, so all environment variables set. This shell gets as parameter a command file which is responsible for the desired action. The current directory for this shell is always the project folder, so all paths may be declared relative to the project folder.
    To compile and link a Modula-2 program, you typically have to create two targets. The first one is responsible for the generation of a make file, the second one executes make (with this make file) to build your program. (Both steps could be combined into only one targets, but that would mean to run genmake every time though the file structure did not change.)

    a) Setting up the target for genmake
    Setting up this targets needs the same preparations as when using genmake from the command line. You first have to arrange all auxilliary folders (in this case: Bin) and then to write Genmake.DAT. In the given example, the sources reside in the project folder and in the folder Library, all intermediate files (.SYM, .MREF etc.) go into Bin. Thus Genmake.DAT looks like this:

    -ref bin/

    The command file for this target gets the name genmake.sh, the contents is

    genmake -default Genmake.DAT -tool Hello > makefile

    After this preparation the target genmake is created by file -> new -> target. From the given dialog an external build system target is chosen (see above)

    On the next page, the target is given its name (Product Name), the next two line should be filled in though they are not used later. For targets known to Xcode, Company identifier and product name form together the bundle identifier for an applications, which has to be unique world wide. Therefore Apple requires the each company identifies itself by the company domain name in reverse order. The build tool is bash (see above).

    After pressing "Finish" you come back to the project main view where the target parameters may be edited. You need to fill in the arguments for bash, which should be "-l" for login behavior and the the name of the command file (genmake.sh) (see above).

    Executing the newly defined target by product -> build results in the file makefile, which may be also added to the project. It should look like this:

    #----- options
    Modula2 = Modula2
    M2OptionsX86 = -ref bin/
    M2PragmaVarsX86 =
    M2SymOpsX86 = -sym Bin/
    #----- rules
    .phony: all
    all: Hello
    Bin/Hello.o:  \
    	./Hello.MOD \
    	$(Modula2) -output - -arch x86_64 $(M2OptionsX86) $(M2SymOpsX86) $(M2PragmaVarsX86) ./Hello.MOD | as -arch x86_64 -o Bin/Hello.o -
    Bin/TestImport.MSYM:  \
    	$(Modula2) -output Bin/TestImport.MSYM -arch x86_64 $(M2OptionsX86) $(M2SymOpsX86) $(M2PragmaVarsX86) Library/TestImport.DEF
    Bin/TestImport.o:  \
    	Library/TestImport.MOD \
    	$(Modula2) -output - -arch x86_64 $(M2OptionsX86) $(M2SymOpsX86) $(M2PragmaVarsX86) Library/TestImport.MOD | as -arch x86_64 -o Bin/TestImport.o -
    ObjFiles = \
    	Bin/Hello.o \
    Hello:  \
    	gcc -arch x86_64 -L$(M2LIB) $(ObjFiles) -lm2lib -o	 \

    b) Setting up the target for make
    Last step is to set up a target for the make utility. This is done the same way as the genmake target. The target name is make for example, the command file for this target might be make.sh, the contents is just

    make Hello

    The Project with all Targets Defined

    Choosing the new target from the pop up menu in the titlebar and executing it should result in the command line tool "Hello" in the project folder. Setting up a Typical Project

    For more complex projects source files, binary files (eventually for multiple targets) etc are placed in various subfolders. Also you may have to set up more than one target. Also it may be desrirable to generate the make file(s) seperatly from using them to make the targets, especially, when the make files need subtle modification. In this case, you cannot use "M2Build", but you have either to write the make file manually (not recomended) or to set up a seperate target that produces the make file via "GenMake" in combination with an appropriate "GenMake.DAT" file. The use of "GenMake" is described in detail in chapter 2.1 in the Utilities part of this manual. Additional information on how to build fat binares is provided for in section 1.9.2.

    A project with two command line tools might look like this:

    Typical Project

    The three targets "genmake", "Server", and "Client" build the make file resp. the tools. The target "SimpleChat" is an Aggregate Target which simply depends on the three previous targets and so build the complete package in the correct order.

    The file "genmake.dat" speciefies fat binaries (arm64 and x86_64) (cf 1.9.2 and Utilities 2.1).

    Typical Default File for GenMake

    The file above is used for both tools; "GenMake" is run just twice. To procduce just one make file with two targets, both files have to be copied into one, the duplicate definition of the options has to be dropped, and the object list variable at lest for one target has to be renamend. This may be done manually, but also automatically by the tool "awk". This is shown in the next picture. The output of "GenMake" is piped through "AWK" to perform the necessary changes.

    GenMake And AWK

    "makeserver.sh" resp. "makeclient.sh" just use make with the specific target to build the targets.

    Make Server

    1.9.2 Error Messages and Path Names

    As mentioned above, Xcode calculates all paths relatively to the projects file. This is important if file names issued by the compiler in an error message need to be interpreted correctly. Automatical positioning to lines with errors does work if and only if the compiler runs with the working directory set to the directory of the project file.

    The easiest way to obtain correct path is to never change the working directory. That means especially that "GenMake" and "make" are run from this directory. So "genmake.dat" and as a result the produced make file use paths relative to the project file. That does not imply that those files need to be placed in this directory. Especially in a project with multiple targets these are often located in subfolders.

    1.9.3 Making Modula-2 source files known to Xcode

    At the moment Apple does not support any API to introduce foreign languages to Xcode. As soon as such an API is provided for, this chapter will be rewritten to show how this API is to be used for the integration of Modula-2.

    table of contents (compiler) start page chapter 2 (compiler)