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 "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 "ppc", "i386", and "X86_64". The C back end also recognizes this option. Constants of type "FourCharCode" are inverted when "i386" or "X86_64" is active because ot the different endianess if the Intel machines.
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 are activated.
WARNINGSWarnings are written to standard error output.
VECTOR Support for AltiVec-instructions available on G4/G5 processors.
FLOATOPT Optimization of floating point arithmetic by 3-operand instructions on G4/G5 processors.
XCODE3 Activates using linker conventions for Xcode 3.0 and above.

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. vectors) are not initialized ("VECTOR" is not regarded as a keyword). 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:
-EXTENDED -WARNINGS -NOPROGRESS
-NOERROR -NOLISTING -NOCROSSREFERENCE -COPYREFPARAM
-CHECK NORMAL -REFERENCE -OPTIMIZE FULL
-NOFIXEDSUBRANGESIZE -NOVECTOR -NOCOUTPUT -ARCH I386 -XCODE3

All options may be abbreviated.

Example:
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 (ppc, i386, 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
    Germany
    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 "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 -Wl,-no_pie -L$M2LIB -o MyTool MyTool.o -lm2lib", where "M2LIB" is an environment variable containing the path to the directory where "libm2lib.a" resides. The option "-Wl,-no_pie" is necessary since macOS 10.12 to avoid warnings (32-bit code) or linker errors (64-bit code).

    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). 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, but parts may be written in Modula-2 as well.

    1.7.2.1 Simple Project D3Cocoa

    The project D3Cocoa shows the basic concepts, the more complex examples PlotFun and Dungeon show further details. The project D3Cocoa consists of four targets. "genmake" and "interfacelib" are used to set up the make file for the Modula-2 part resp. to build the Modula-2 part as a library. "D3Cocoa" is the main target that produces the application. "D3CocoaTests" is generated automatically by Xcode and is not needed in the following paragraphs.


    Main Target Settings

    The main target links against three 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 Objective C part in the file "M2Foreign.h". For details please refer to chapter 4.4.2.2

    The module initialization is added to the (automatically created) file main.c:

    //
    //  main.m
    //  D3Cocoa
    //
    //  Created by Albert Wiedemann on 05.08.13.
    //  Copyright (c) 2013 Albert Wiedemann. All rights reserved.
    //
    
    #import 
    #import "M2Foreign.h"
    
    int main(int argc, char *argv[])
    {
        M2InitModule ();
        return NSApplicationMain(argc, (const char **)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 ();
    }
    
    @end
    
    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) *>
    BEGIN
        aktionen := Aktionen {Zeichnen, Dummy, Dummy};
        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. Though in the given example only one interface procedure is needed, two more ("dummy1" and "dummy2") are defined for ilustration.

    <* ASSIGN (Foreign, TRUE) *>
    DEFINITION MODULE AktionsProzeduren;
    
    FROM CGContext IMPORT CGContextRef;
    
    TYPE
        Aktionen = RECORD
        	zeichen: PROCEDURE (CGContextRef, INTEGER);
            dummy1, dummy2: PROC;
        END(*RECORD*);
        
    	<* ASSIGN (Calling, "CCalling") *>
        PROCEDURE Setzen (<* ASSIGN (Reference, TRUE) *> aktionen: Aktionen);
    
    END AktionsProzeduren.
    

    As the interface is only used in the class "ZeichenView", its C counterpart is locally defined in "ZeichenView.m". The definition is marked in green, the use is marked in red.

    //
    //  ZeichenView.m
    //  D3Cocoa
    //
    //  Created by Albert Wiedemann on 06.08.13.
    //  Copyright (c) 2013 Albert Wiedemann. All rights reserved.
    //
    
    #import "ZeichenView.h"
    
    typedef struct Aktionen
    {
        void (* zeichnen) (CGContextRef, long);
        void (* dummy1) ();
        void (* dummy2) ();
    } Aktionen;
    
    static Aktionen* aktionen = NULL;
    
    extern void Setzen (Aktionen* a)
    {
        aktionen = a;
    }
    
    @implementation ZeichenView
    
      long nummer;
    
    - (id)initWithFrame:(NSRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
            //
        }
        
        return self;
    }
    
    - (void) flaechennummerSetzen: (long) n
    {
        nummer = n;
    }
    
    //
    // some ommitted code that is used in case of problem, see example file.
    //
    
    - (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.
        CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
    
        if (aktionen != NULL)
        {
            (*(aktionen ->  zeichnen)) (context, nummer);
        }
        else
        {
            //
            // some ommitted code that is used in case of problem, see example file.
            //
        }
    }
    
    @end
    

    1.7.2.2 Document Based Project PlotFun Written in Objective C

    The project PlotFun is a typical document based application. In contrast to the (still working) 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 newly introduced 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 five targets:

    1.7.2.3 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 eleven 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 Carbon Applications

    For Carbon applications you use the library "libm2libcarbon.a" which contains a different implementation of the Modula-2 runtime kernel. You also have to link against the Carbon framework by specifiying "-framework Carbon" on the linker command line in addition to "-lm2libcarbon". Thus the linker command might look like:
    "gcc -Wl,-no_pie -L$M2LIB -o MyApp MyApp.o -lm2libcarbon -framework Carbon"

    If you want to use the Modula-2 source level debugger, you must specify "-lm2dlibcarbon" instead of "-lm2libcarbon" in the link command. "m2dlibcarbon.a" contains a special version of the runtime kernel which replaces that of "libm2libcarbon.a" (see the "Utilities" part of the manual). In this case the ggc command line might look like this:
    "gcc -Wl,-no_pie -L$M2LIB -o MyApp MyApp.o -lm2dlibcarbon -framework Carbon"

    A native MacOS X Carbon application (or better: an application in contrast to a command line tool) is not specified by a file flag or something similar but by the directory structure it resides in: Every executable is started in a command shell unless it is stored in an application bundle.
    A detailed description of bundles can be found in the documentations of the apple developer tools in "file:///Developer/Documentation/MacOSX/". Here a short overview is given for rapid migration from Carbon Applications linked for MacOS 9 / MacOS X by the MPW linker to native MacOS X Carbon Applications.

    Given you want to set up an application called "MyApp", you need to

    Your application goes into the folder "MacOS". All resources go into the folder "Resources". A resource file named "MyApp.rsrc" is automatically found by the resource manager. All documentations talk about putting the resources into the data fork of this file, but the resource manager does also find them when they are stored in the resource fork of this file. So you can create this file with ResEdit as you do for MacOs 9 applications.

    The folder "Application Examples"contains some examples (PlotFun, Dungeon) with a more complex bundle structure that also show how to associate document files to an applications. A description of the keys in "Info.plist" can be found in "file:///Developer/Documentation/MacOSX/Conceptual/BPRuntimeConfig/Concepts/PListKeys.html".

    RESTRICTION: As Apple ships the Carbon library for ppc and i386 architectures exclusivly, there is no way to generate a Carbon application using 64-bit-code.

    1.7.4 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 libraries m2lib resp. m2libcarbon have to be replaced by m2clib resp. m2clibcarbon. The path to these files has to be specified for ld. Within Xcode, you have to specefiy the path to this file in the target settings ("library search path"). Both files are universal binaries i. e. contain ppc, i386, and x86 versions of the libraries. Starting with version 9.2 the C back end supports 64-bit-code.

    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.5 Building Universal Binaries

    The linker (ld) cannot produce Universal Binaries; it always emits "thin" output files. You generate an output file for each ppc, i386 resp. x86_64 code in the usual way, e.g. MyAppPPC, MyAppI386, and MyAppX86. Then you have to use the command line tool "lipo" to combine the three thin executables to a Universal Binary.
    lipo MyAppPPC MyAppI386 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
    M2SYMS=${M2HOME}/SYMs
    M2LIB=${M2HOME}/lib
    PATH=$PATH:$M2HOME/bin
    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.

    1.9.1.1 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

    1.9.1.2 Setting up a Simple New Project with source subfolders

    Though several steps overlap with the example above, the creation of this project is also 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 OS X -> other->→ Empty (see below)


    Create Project

    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 OS X -> other -> Empty (see below)


    Create Empty Files

    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 Test.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 (the black rectangle is intentional)

    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:

    D./
    M./
    DLibrary/
    MLibrary/
    
    OBin/
    SBin/
    ABin/
    -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


    New Genmake Target

    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.


    Target Name

    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).


    Target Parameters

    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
    M2OptionsI386 = -ref bin/
    M2PragmaVarsI386 =
    M2SymOpsI386 = -sym Bin/
    
    #----- rules
    .phony: all
    all: Hello
    
    Bin/Hello.o:  \
    	./Hello.MOD \
    	Bin/TestImport.MSYM
    	$(Modula2) -output - -arch i386 $(M2OptionsI386) $(M2SymOpsI386) $(M2PragmaVarsI386) ./Hello.MOD | as -arch i386 -o Bin/Hello.o -
    Bin/TestImport.MSYM:  \
    	Library/TestImport.DEF
    	$(Modula2) -output Bin/TestImport.MSYM -arch i386 $(M2OptionsI386) $(M2SymOpsI386) $(M2PragmaVarsI386) Library/TestImport.DEF
    Bin/TestImport.o:  \
    	Library/TestImport.MOD \
    	Bin/TestImport.MSYM
    	$(Modula2) -output - -arch i386 $(M2OptionsI386) $(M2SymOpsI386) $(M2PragmaVarsI386) Library/TestImport.MOD | as -arch i386 -o Bin/TestImport.o -
    ObjFiles = \
    	Bin/Hello.o \
    	Bin/TestImport.o
    Hello:  \
    	$(ObjFiles)
    	gcc -Wl,-no_pie -arch i386 -L$(M2LIB) $(ObjFiles) -lm2lib -o	 \
    		Hello

    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.

    1.9.1.3 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 (ppc, i386, and x86_64) (cf 1.9.2 and Utilities 2.1) as well as the folders for ppc support (cf 1.9.6).


    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.1.4 Setting up a Project for Xcode 3.x (And Earlier)

    In Xcode 3.x and earlier there existed a special kind of build phase, called "Shell Script Build Phase " is used in this versions. All commands that would have gone into "command.sh" in section 1.9.1.1 or section 1.9.1.2 are directly written in the target description.

    The easiest approach is again to start with an empty project. Xcode asks for the project name and a path where the project file is to be stored.

    The second step is to set up all required targets. For simple projects only one target is needed. More complex projects may use more targets. p1 Modula-2 e.g. consists of 21 targets that partially depend on each other. The type for targets that compile and link Modula-2 programs is "Shell Script Target". This type consists of one build phase of type "Shell Script Build Phase ".


    Shell Script Build Phase

    The shell command line is preset by "/bin/sh". You can replace this by a specific shell if your commands depend on it. More important is to add the "-l " option. This causes the shell to act as a login shell. Only in this case the profiles are executed. Otherwise variables set in the profiles are not available.

    Input and output files have to be specified. The shell is not executed unless at least one of the input files is newer than the output file.

    The script itself is nothing than a command file. In general, it consists only of one make command.

    If products are to be run from Xcode an executable has to be set up for each product. It specifies the path to the application/tool, eventual command line arguments, working directory, standard input/output etc. Please note that the pseudo terminal for tools is a very simple one.


    Custom Executable

    A typical project is found in "Application Examples/Dungeon"


    Project Example

    An other typical project can be found in the folder "LibSources".

    1.9.2 Building Universal Binaries

    This description is based on an example named PlotFun (used to plot function graphs). This is an old project that still contains variants for Classic applications. So as a side effect you can trace the migration steps from Classic 7.x to Carbon Universal Binary for MacOS X.

    The first step ist to set up a GenMake target to produce the necessary make file. This makes use of the GenMake description file and also of a file "makefilepost" that defines some additional make targets.
    genmakemakefilepost
    The first line groups define the architecture specific paths (objects, symbol files, assembler files, reference files) for Ppc and Intel. The third group sets the path for the executable and the overall options. The last two lines inform GenMake to generate rules for generation of Ppc and Intel code (together with an appropriate lipo command).

    The shell script for the external build sytem target to generate the make file may look like this:


    Shell Script to Generate Make File

    In the above example the option "-d TARGET_API_MAC_CARBON=TRUE" is used by GenMake and also passed on to the make rules for all targets.

    The application target itself consists of less more than a single make command:


    Shell Script to Make PlotFun

    The next image shows the project view including the targets and the setting for the make target.


    Project Window

    1.9.3 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.4 Setting up a C Application Project for Universal Binaries

    For predefined targets using C or assembler sources, Xcode by default uses the architecture of the current machine as target architecture for new projects and new targets within projects. To produce universal binaries you have to set up the project as well as the targets in question accordingly. The dialog below, activated by "Project -> Edit Project Settings", shows the correct setting for universal binaries containing Ppc and Intel 32 bit code.


    project settings for universal binaries

    You also have to set the target architecture in each target by activating the target and then choosing "Project -> Edit Active Target 'target name'".


    target settings

    target settings result

    1.9.5 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.

    1.9.6 Setting up Xcode 4.6 and later for PowerPC Support

    Starting with newer versions of Xcode Apple entirely dropped the support for generating PPC code. p1 Modula-2 still does, but for assembling an linking the programs the old libraries are needed as well as the old command line tools, especially "gcc", "as", and "ld".

    To install these items an older version of Xcode is needed, recommended is 3.x, especially 3.2.6. These versions are still available for download on the Apple developer pages. As Apple changed the location for Xcode starting with version 4.0, there are no problems if you install the old version in their default location "/Developer". From this installatation, the following is needed

    Other folders may be kept but are not needed.


    Typical Folder Developer for PPC Support

    To use these tools, the path has to be specefied exeplicitly like "/Developer/usr/bin/gcc". Adding this path to the PATH variable would shadow the new versions of these tools. Also the SDK root to the desired SDK version has to be passed to "gcc" with the option "-isysroot". The default file for "GenMake" supports for automatically adding these paths at the appropriate places (cf. chapter 2.1 in the Utilities part of this manual ).

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