H2O

From BlackBox Framework Wiki
Jump to: navigation, search

H2O — is the tool for making Oberon and Component Pascal import modules from C header files.

Contents

Intro and Credits

H2O (Header to Oberon) is a tool for creating Oberon interface modules from C-Header files for foreign libraries. It has been created by Stewart Greenhill around 2001 for use with the Optimizing Oberon Compiler OOC/oo2c, which in turn has been created by Michael van Acken end of the 1990. Ability to create interface modules for Component Pascal has been implemented by Bernhard Treutwein end of 2006 and fed back into the cvs tree at sourceforge by Stewart Greenhill in March, 2007.

Installation

The H2O sources are distributed with oo2c compiler. The oo2c compiler is/must be used for building H2O.

Linux

1. Download binary distribution and install it: https://sourceforge.net/projects/ooc/files/ooc2/2.1.11/

./configure
sudo make install

2. Download last sources from: https://github.com/Spirit-of-Oberon/oo2c

git clone https://github.com/Spirit-of-Oberon/oo2c

3. Compile last version of oo2c from repository:

cd oo2c
make cvsclean
./configure
. ENV
make $OOC_DEV_ROOT/oo2crc-install.xml
/usr/bin/oo2c -M --config oo2crc-install.xml oo2c
make install BOOTSTRAP_COMPILER=bin/oo2c

4. Compilation of H2O

oo2c -M src/TestH2O.Mod
sudo cp bin/TestH2O /usr/bin/TestH2O

Windows

H2O can be run on windows with Cygwin.

The example of built distribution you can find here.

Download

Usage

There are a couple of modes in which H2O can work.

  • With arguments "-cp" it is producing Component Pascal output.
  • With arguments "--preprocess" it just preprocesses source, outputting tokenised symbols (not very useful).
  • With arguments "--preprocess --text" it preprocesses source, outputting text.
  • Without arguments, it translates "C" definitions, producing Oberon-2 output.


Processing C source code

There are few C files ready for trial usage in the folder with oo2c sources.

cd tests/h2o
TestH2O misc/test.c

The results will be two files: mod.Mod and test.Mod.

Name C (sources) Oberon-2 (output)
mod.h
typedef int T;
typedef int * pT;
MODULE mod [ INTERFACE "C" ];

IMPORT SYSTEM;
TYPE
  T* = LONGINT;
  pT* = POINTER TO ARRAY OF LONGINT;
END mod.
test.c
#include "mod.h"

typedef T t1;
pT pt1;

int i1;
short int i2;
long int i3;
unsigned int i4;
unsigned short int i5;
unsigned long int i6;

unsigned u;
short s;
long l;
char c;

int * pi;
int ** ppi;

int f1(int x, int y);
int * f2(int x, int y);
int (* f3)(int x, int y);
void f4 (void);
void f5 (int, int, ...);

int ai [];
int * aai1 [3][3];
int (* aai2) [3][3];

struct A {
  int a:1;
  int b;
} sA1;

struct A sA2;
struct A * psA;

struct A * (* next)(struct A * p);

enum B { a=1, b, c } e1;

typedef unsigned long long I;

I i7;
volatile I i8;
volatile I i9, * const i10;
int a[sizeof(int)];
MODULE test [ INTERFACE "C" ];

IMPORT SYSTEM, mod;
CONST
  (* Constants occurring in enumeration 'B' *)
  a* = 1;
  b* = 2;
  c* = 3;
TYPE
  A_tag* = RECORD 
    a* : LONGINT;
    b* : LONGINT;
  END;
  B_tag* = LONGINT (* enumerated type *);
  AutoPtrA_tag* = POINTER TO A_tag;
  t1* = mod.T;
  I* = HUGEINT;
VAR
  pt1* : mod.pT;
  i1* : LONGINT;
  i2* : INTEGER;
  i3* : LONGINT;
  i4* : LONGINT;
  i5* : INTEGER;
  i6* : LONGINT;
  u* : LONGINT;
  s* : INTEGER;
  l* : LONGINT;
  c* : CHAR;
  pi* : POINTER TO ARRAY OF LONGINT;
  ppi* : POINTER TO ARRAY OF POINTER TO ARRAY OF LONGINT;
  f3* : PROCEDURE(x : LONGINT; y : LONGINT) : LONGINT;
  ai* : ARRAY OF LONGINT;
  aai1* : ARRAY 3 OF ARRAY 3 OF POINTER TO ARRAY OF LONGINT;
  aai2* : POINTER TO ARRAY 3 OF ARRAY 3 OF LONGINT;
  sA1* : A_tag;
  sA2* : A_tag;
  psA* : POINTER TO A_tag;
  next* : PROCEDURE(p : AutoPtrA_tag) : AutoPtrA_tag;
  e1* : B_tag;
  i7* : I;
  i8* : I;
  i9* : I;
  i10* : POINTER TO ARRAY OF I;
  a* : ARRAY 4 OF LONGINT;
  PROCEDURE f1* (x : LONGINT; y : LONGINT) : LONGINT;
  PROCEDURE f2* (x : LONGINT; y : LONGINT) : mod.pT;
  PROCEDURE f4* ();
  PROCEDURE f5* (p0 : LONGINT; p1 : LONGINT);
END test.

Processing with outer H2O directive

Directive file have structure:

H2O {
  ... translation options here ...
}
#include "topLevelFile"

If we want to translate test.c from previous example we would make test.h2o that looks like this:

H2O {
  OPTIONS {
    MapChar = "SHORTCHAR";
    MapShort = "SHORTINT";
    MapLong = "INTEGER";
    MapLongLong = "LONGINT";
    MapFloat = "SHORTREAL";
    MapDouble = "REAL";
    MapPointer = "ANYPTR";
  }

  MODULE "test" {
      LinkLib = "libtest.so";
  }

  MODULE "mod" {
      LinkLib = "libmod.so";
  }
}
#include "test.c"


To initiate the translation you would do:

TestH2O misc/test.h2o

For making of Component Pascal import modules we can use key -cp.

TestH2O -cp misc/test.h2o
Name C (sources) Component Pascal (output)
mod.h
typedef int T;
typedef int * pT;
MODULE mod [ "libmod.so" ];

IMPORT SYSTEM;
TYPE
  T* = INTEGER;
  pT* = POINTER TO ARRAY [untagged] OF INTEGER;
END mod.
test.c
#include "mod.h"

typedef T t1;
pT pt1;

int i1;
short int i2;
long int i3;
unsigned int i4;
unsigned short int i5;
unsigned long int i6;

unsigned u;
short s;
long l;
char c;

int * pi;
int ** ppi;

int f1(int x, int y);
int * f2(int x, int y);
int (* f3)(int x, int y);
void f4 (void);
void f5 (int, int, ...);

int ai [];
int * aai1 [3][3];
int (* aai2) [3][3];

struct A {
  int a:1;
  int b;
} sA1;

struct A sA2;
struct A * psA;

struct A * (* next)(struct A * p);

enum B { a=1, b, c } e1;

typedef unsigned long long I;

I i7;
volatile I i8;
volatile I i9, * const i10;
int a[sizeof(int)];
MODULE test [ "libtest.so" ];

IMPORT SYSTEM, mod;
CONST
  (* Constants occurring in enumeration 'B' *)
  a* = 1;
  b* = 2;
  c* = 3;
TYPE
  A_tag* = RECORD [untagged] 
    a* : INTEGER;
    b* : INTEGER;
  END;
  B_tag* = INTEGER (* enumerated type *);
  AutoPtrA_tag* = POINTER TO A_tag;
  t1* = mod.T;
  I* = HUGEINT;
VAR
  pt1* : mod.pT;
  i1* : INTEGER;
  i2* : SHORTINT;
  i3* : INTEGER;
  i4* : INTEGER;
  i5* : INTEGER;
  i6* : INTEGER;
  u* : INTEGER;
  s* : SHORTINT;
  l* : INTEGER;
  c* : SHORTCHAR;
  pi* : POINTER TO ARRAY [untagged] OF INTEGER;
  ppi* : POINTER TO ARRAY [untagged] OF
     POINTER TO ARRAY [untagged] OF INTEGER;
  f3* : PROCEDURE(x : INTEGER; y : INTEGER) : INTEGER;
  ai* : ARRAY [untagged] OF INTEGER;
  aai1* : ARRAY [untagged] 3 OF ARRAY [untagged] 3 OF
     POINTER TO ARRAY [untagged] OF INTEGER;
  aai2* : POINTER TO ARRAY [untagged] 3 OF
     ARRAY [untagged] 3 OF INTEGER;
  sA1* : A_tag;
  sA2* : A_tag;
  psA* : POINTER TO A_tag;
  next* : PROCEDURE(p : AutoPtrA_tag) : AutoPtrA_tag;
  e1* : B_tag;
  i7* : I;
  i8* : I;
  i9* : I;
  i10* : POINTER TO ARRAY [untagged] OF I;
  a* : ARRAY [untagged] 4 OF INTEGER;
  PROCEDURE f1* (x : INTEGER; y : INTEGER) : INTEGER;
  PROCEDURE f2* (x : INTEGER; y : INTEGER) : mod.pT;
  PROCEDURE f4* ();
  PROCEDURE f5* (p0 : INTEGER; p1 : INTEGER);
END test.


For included modules, the behavior depends on the type of include. If you do:

 #include <mod.h>

it looks in the search path specified by the Include option. If you do:

 #include "mod.h"

it looks in the same directory as the file that does the #include.


The "OPTIONS" directive is global to the translation, and includes things like:

  • OutputDirectory — where to put the generated module files
  • Include — list of paths to get include files
  • Exclude — list of files to not include (#include ignored)
  • AutoPrefix — prefix for auto-generated type names (default "Auto")
  • TagSuffix — suffix to be added to structure tags (default "_tag")
  • RenameProcedures — when set to "1" (the default) renames procedures using module "StripPrefix" specification.
  • RenameVariables — when set to "1" (default is "0", since this is unsupported in OOC V2) renames variables using module "StripPrefix" specification.
  • ModuleSuffix — suffix for module file names (default "Mod").

In this section, you can also specify the type mappings for scalar types:

  OPTION symbol          C type                Default output type
  ----------------------------------------------------------------
  MapChar                "char"                "CHAR"
  MapUnsignedChar        "unsigned char"       "CHAR"
  MapShort               "short"               "INTEGER"
  MapUnsignedShort       "unsigned short"      "INTEGER"
  MapLong                "long"                "LONGINT"
  MapUnsignedLong        "unsigned long"       "LONGINT"
  MapLongLong            "long long"           "HUGEINT"
  MapUnsignedLongLong    "unsigned long long"  "HUGEINT"
  MapFloat               "float"               "REAL"
  MapDouble              "double"              "LONGREAL"
  MapLongDouble          "long double"         "LONGDOUBLE"
  MapPointer             "void *"              "SYSTEM.PTR"
  MapEnum                "enum ..."            "LONGINT"
  MapVoid                "void"                "C_VOID"

Each "MODULE" directive controls how to treat the named module. The base module name comes from the name of the corresponding ".h" file. Options per module include:

  • OutputName — Output name for this module (defaults to header name)
  • StripPrefix — list of prefixes to be removed from symbol names
  • LinkLib, LinkFile, LinkFramework — specifies libraries, files and frameworks to link to this module (OOC-specific directive)
  • Prolog — "C" definitions to be processed at the start of this module
  • Epilog — "C" definitions to be processed at the end of this module
  • Merge — when set to "1", causes all files included by this module to be declared within this module, rather than in separate modules.

Each "VARIANT" directive specifies how to translate particular symbols. As you would know, there are many ways of interpreting C declarations, and the default rules don't always work. For example:

f(char * arg);

could be:

PROCEDURE f (arg : POINTER TO ARRAY OF CHAR);
PROCEDURE f (arg : ARRAY OF CHAR);
PROCEDURE f (VAR arg : ARRAY OF CHAR);
PROCEDURE f (VAR arg : CHAR);

So VARIANT directives allow you to place particular interpretations on the "C" declarations. Normally, you can see some specific patterns, but it varies from API to API.

The format of variants is a form of designator. The designators are composed of:

  • strings — which match the names of globally declared objects
  • [n] — which matches item <n> in a compound type or parameter list
  • ^ — which matches the item referenced by a pointer type
  • .symbol — which matches a named item in a type or paramter list

Strings or symbols use OOC's regexp string format. Basically, ".*" matches an string of characters, "$" matches the end of a string, and "[]" matches one of a set of characters.

Examples:

"gl.*v$".params : ARRAY;

This means any symbol (here, procedure) starting with "gl" and ending with "v" has its "params" parameter interpreted as an array.

"gl.*Matrix[fd]$"[0] : ARRAY;

This means any symbol starting with "gl" and ending with "Matrixf" or "Matrixd" has its first parameter interpreted as an array.

"String$" : CSTRING POINTER;

This means that the type "String" is to be interpreted as a POINTER to a C string (in OOC, it assigns the "CSTRING" attribute to the pointer, which means that you can use a string literal or CHAR array for this type).

"glutInit$".argcp : VAR;

This means that the "argcp" parameter of function "glutInit" is to be treated as a VAR parameter.

"cvRelease[^D].*$"[0] : VAR;

This means that symbols starting "cvRelease" followed by any character EXCEPT "D" has its first parameter interpreted as a VAR parameter.


Complex examples

Media:Interfaces.tar.gz