The GNU Modula-2 front end to GCC

How to produce a Python module

This section descibes how it is possible to produce a Python module from your Modula-2 code. There are a number of advantages to this approach, it ensures your code reaches a wider audience, maybe it is easier to initialize your application in Python, maybe users of your code are familiar with Python and can use it to configure your application.

The examples given here can be found in the source tree gcc-version-GCC/gcc/gm2/examples/gravity/. The example used here is a pedagogical two dimensional gravity next event simulation. The Python module needs to have a clear API, this also needs to be placed in a single definition module. In the gravity simulation example this is found in the file: gcc-version-GCC/gcc/gm2/examples/gravity/twoDsim.def. Furthermore the API should only use fundamental pervasive data types and strings. Below is twoDsim.def:

DEFINITION MODULE twoDsim ;

EXPORT UNQUALIFIED gravity, box, poly3, poly5, poly6, mass,
                   fix, circle, pivot, velocity, accel, fps,
                   replayRate, simulateFor, addDebugging ;
(*
   gravity - turn on gravity at: g m^2
*)

PROCEDURE gravity (g: REAL) ;


(*
   box - place a box in the world at (x0,y0),(x0+i,y0+j)
*)

PROCEDURE box (x0, y0, i, j: REAL) : CARDINAL ;


(*
   poly3 - place a triangle in the world at:
           (x0,y0),(x1,y1),(x2,y2)
*)

PROCEDURE poly3 (x0, y0, x1, y1, x2, y2: REAL) : CARDINAL ;


(*
   poly5 - place a pentagon in the world at:
           (x0,y0),(x1,y1),(x2,y2),(x3,y3),(x4,y4)
*)

PROCEDURE poly5 (x0, y0, x1, y1, x2, y2, x3, y3, x4, y4: REAL) : CARDINAL ;


(*
   poly6 - place a hexagon in the world at:
           (x0,y0),(x1,y1),(x2,y2),(x3,y3),(x4,y4),(x5,y5)
*)

PROCEDURE poly6 (x0, y0, x1, y1, x2, y2, x3, y3, x4, y4, x5, y5: REAL) : CARDINAL ;


(*
   mass - specify the mass of an object and return the, id.
*)

PROCEDURE mass (id: CARDINAL; m: REAL) : CARDINAL ;


(*
   fix - fix the object to the world.
*)

PROCEDURE fix (id: CARDINAL) : CARDINAL ;


(*
   circle - adds a circle to the world.  Center
            defined by: x0, y0 radius, r.
*)

PROCEDURE circle (x0, y0, r: REAL) : CARDINAL ;


(*
   pivot - pivot an object at position, (x0,y0).
*)

PROCEDURE pivot (x0, y0: REAL; id1: CARDINAL) : CARDINAL ;


(*
   velocity - give an object, id, a velocity, vx, vy.
*)

PROCEDURE velocity (id: CARDINAL; vx, vy: REAL) : CARDINAL ;


(*
   accel - give an object, id, an acceleration, ax, ay.
*)

PROCEDURE accel (id: CARDINAL; ax, ay: REAL) : CARDINAL ;


(*
   fps - set frames per second.
*)

PROCEDURE fps (f: REAL) ;


(*
   replayRate - set frames per second during replay.
*)

PROCEDURE replayRate (f: REAL) ;


(*
   simulateFor - render for, t, seconds.
*)

PROCEDURE simulateFor (t: REAL) ;


(*
   addDebugging - add a debugging event at time, t, which colours objects,
                  a, and, b, blue.
*)

PROCEDURE addDebugging (t: REAL; a, b: CARDINAL) ;


END twoDsim.

By using the keyword UNQUALIFIED we ensure that the compiler will provide externally accessible functions gravity, box, poly3, poly5, poly6, mass, fix, circle, pivot, velocity, accel, fps, replayRate, simulateFor, addDebugging rather than name mangled alternatives. Hence in our Python application we could write:

#!/usr/bin/python

from twoDsim import *

b = box(0.0, 0.0, 1.0, 1.0)
b = fix(b)
c1 = circle(0.7, 0.7, 0.05)
c1 = mass(c1, 0.01)
c2 = circle(0.7, 0.1, 0.05)
c2 = mass(c2, 0.01)
c2 = fix(c2)
gravity(-9.81)
fps(24.0*4.0)
replayRate(24.0)
print "creating frames"
try:
    simulateFor(1.0)
    print "all done"
except:
    print "exception raised"

which accesses the various functions defined and implemented by the module twoDsim. The Modula-2 source code is compiled via:

$ gm2 -g -fiso -c -fswig twoDsim.mod
$ gm2 -g -fiso -c -fmakelist twoDsim.mod
$ gm2 -g -fiso -c -fmakeinit twoDsim.mod

Notice that the last command both compiled and produced a swig interface file swig.i. We now use swig and gcc to produce and compile the interface wrappers:

$ libtool --mode=compile g++ -g -c _m2_twoDsim.cpp -o _m2_twoDsim.lo
$ swig -c++ -python twoDsim.i
$ libtool --mode=compile g++ -c -fPIC twoDsim_wrap.cxx -I/usr/include/python3.5 -o twoDsim_wrap.lo
$ libtool --mode=compile gm2 -g -fPIC -fiso -c deviceGnuPic.mod
$ libtool --mode=compile gm2 -g -fPIC -fiso -c roots.mod
$ libtool --mode=compile gm2 -g -fPIC -fiso -c -fswig twoDsim.mod -o twoDsim.lo

Finally the application is linked into a shared library:

$ libtool --mode=link gcc -g _m2_twoDsim.lo twoDsim_wrap.lo \
  roots.lo deviceGnuPic.lo \
   -L$(yourprefix)/lib64 \
   -rpath `pwd` -lgm2 -lstdc++ -lm -o libtwoDsim.la
cp .libs/libtwoDsim.so _twoDsim.so

The library name must start with a _ to comply with the Python naming scheme.