Ocsigen: how to compile an Eliom project. Understand the compilation process in deep.

It’s time to write my first article on Ocsigen and especially on Eliom. I began a week ago my internship in BeSport, a social network centralized on sports and entirely developed in OCaml using the Ocsigen projects so I needed to learn how Ocsigen works in general.

Ocsigen? What’s that? It’s an atom no?

Yes, oxygen is a chemical element but Ocsigen is also an OCaml web framework began in 2004.

Wait, another web framework? There are plenty of existing web frameworks.

Yeah, I agree. But Ocsigen is different in some cases. First, Ocsigen is entirely written in OCaml: no PHP, no JavaScript, no HTML. Second, Ocsigen contains independent «small» projects which, together, form a very elegant framework. Here some of these sub-projects:

  • Ocsigenserver: web server entirely written in OCaml. It supports HTTPS, multiple hosts (virtual hosts), proxy, content compression, access control, authentication, etc. Everything you need is implemented and very easy to configure. Ocsigenserver allows you to develop modules to add functionalities to the server.
  • Js_of_ocaml: a compiler OCaml bytecode to JavaScript. It allows you to write OCaml program and to compile it in JavaScript so you can write web application entirely in OCaml.
  • Eliom: a high level library to build client side and server side applications. In few lines of code and in the same file, you can write complex websites and the server side associated. It uses the strong OCaml type system to create route, html pages, data exchange, session mechanisms, etc. It uses new concepts in web programming which are very interesting and are up-to-date with modern needed.
  • Lwt: cooperative programming in OCaml. It’s popular in the OCaml community even in non-web project.
  • TyXML: write typed XML tree (in particular HTML).
  • and more… You can find all Ocsigen sub-projects here

Oh great! Can you show me an example?

Yes, of course. I will show a basic Eliom project and how to compile it. In other articles, I will give js_of_ocaml, ocsigenserver and lwt examples. The main goal of this article is to show you the entire process to build an Eliom project, from code to compilation by configuring the ocsigenserver.

This article doesn’t explain how Eliom works and all Eliom modules in details. It’s just a step by step explanation of the compilation process. Other articles are coming for these purpose.

How to install Eliom?

First, switch to a stable 4.02.3 OCaml compiler with

opam switch 4.02.3

or if you want to give a name to the compiler, use

opam switch eliom-build-test --alias-of 4.02.3

In this way, dependencies are the same for you and me.

The simplest way to install Eliom is to use OPAM:

opam install eliom

It will install all dependencies such like lwt, js_of_ocaml, ocsigenserver and the last Eliom version (which is currently 5.0). For the dev version (ie by using opam pin to the Github repository), you need to pin the dev version of ocsigenserver.

The build tools

Eliom opam package comes with some tools (binary) you can use to build Eliom projects easier. Most of them are wrappers to common OCaml build tools:

  • eliomc: wrapper to ocamlc for Eliom project
  • eliomopt: wrapper to ocamlopt for Eliom project
  • js_of_eliom: wrapper to js_of_ocaml for Eliom project
  • eliom-distillery: provides templates for Eliom projects

eliomc compiles to bytecode and eliomopt to native code. For the rest of this article, we will use eliomc

Small Eliom code

An Eliom project uses files with extension .eliom and .eliomi (for interface). These filetypes are OCaml common filetypes (ml and mli) and contains OCaml code. It’s just a way to distinguish between common OCaml code and Eliom code (Eliom build tools use these differences for some compilation rules).

This is a code example which defines a website and a page containing a paragraph with «Hello, World!». Paste it in a file hello_world.eliom. We will use this filename for the rest of the article.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[%%shared
    open Eliom_content
    open Html5.D
]

let content =
  fun () () ->
    Lwt.return
      (html
        (head (title (pcdata "Hello, World!")) [])
        (body
          [p [pcdata "Hello, World!"]]
        )
      )

let main_service =
  Eliom_registration.Html5.register_service
    ~path:[]
    ~get_params:Eliom_parameter.unit
    content

Eliom works in term of services and not in term of page or url. The service is defined by

1
2
3
4
5
let main_service =
  Eliom_registration.Html5.register_service
    ~path:[]
    ~get_params:Eliom_parameter.unit
    (* content *)

This service (main_service) returns an HTML5 page (because we use the module Eliom_registration.Html5) which has as a content content. The function content defines a typed HTML5 page: head, body, title, pcdata and p are functions defined in the module Eliom_content.Html5.D.

You can find more information about services and Eliom in the official Ocsigen tutorials.

Extract the client and server types information

Even if we don’t have client side code in this example, as I said, Eliom allows you to write the client and service side codes of your web applications in only one file. More information available here.

The first step to compile an Eliom project is to extract client and server type information. eliomc has an option (-infer) to extract these information.
eliomc copies all compiled files in a directory called _server. This directory can be changed by modifying the variable ELIOM_SERVER_DIR. It’s the
same thing for the client side code which are compiled in _client directory. This directory could be changed by modifying the variable ELIOM_CLIENT_DIR.

In this tutorial, we use the PPX syntax ([%%shared (* code *)] is an example of the PPX extension) so we need to add -ppx to each call to eliomc, eliomopt and js_of_eliom.

Extract type information with

eliomc -ppx -infer hello_world.eliom

Compile server side code

Now we are able to compile the server side code in bytecode. This server side code must be compiled as a library for Ocsigenserver.

eliomc -ppx -c hello_world.eliom # compile in bytecode
eliomc -ppx -a -o hello_world.cma _server/hello_world.cmo # build a library for ocsigenserver

Compile client side code

Client side code are essentially JavaScript code got from OCaml and compiled with js_of_ocaml. We use js_of_eliom to get client side code. We need the client side bytecode and use the resulting bytecode with js_of_eliom (same process with js_of_ocaml) to obtain the corresponding JavaScript. js_of_eliom extracts the client side of the Eliom file and compiles it in JavaScript. The corresponding cmo file is copied in _client directory.

js_of_eliom -ppx -c hello_world.eliom # get bytecode
js_of_eliom -ppx -o hello_world.js _client/hello_world.cmo # compile in JavaScript

Ocsigenserver configuration

Now we have all compiled files we need for ocsigenserver: the hello_world.cma library which will be loaded in ocsigenserver and the client side code hello_world.js. The last thing is to configure the server.

Ocsigenserver has an interesting feature: you can locally run the server on a port > 1024 and the only thing you need to give is a configuration file which is an XML file. All information can be found here. I don’t describe each line because it’s not the goal of this article.

If you have already configured a HTTP server like Apache, you know the main directories are:

  • /var/www/html: contains all sites in a sub-directory
  • /etc: contains the configuration file, often in a sub-directory with the site name.
  • /lib library files.
  • /var/log: contains all log files. We often create a sub-directory for all sites registered in /var/www/html where we put the log files.
  • /var/data: contains all data. Often, a sub-directory for each website.
  • /var/run: contains the run-time variable.

We use the same hierarchy for a local configuration of ocsigenserver except than all these directories will be created in a local directory called local. Our site will be named hello_world so we create a sub-directory in each directory. We add a sub-directory to local/var/www/hello_world called eliom to put in compiled Eliom client file ie hello_world.js: it gives the possibility to distinguish with other JavaScript or CSS files (called static).

mkdir -p local/var/www/html/hello_world # Site files
mkdir -p local/var/www/html/hello_world/eliom # Eliom files
mkdir -p local/etc/hello_world # Configuration files
mkdir -p local/lib/hello_world # Library file ie cma file.
mkdir -p local/var/log/hello_world # Log
mkdir -p local/var/data/hello_world # Data
mkdir -p local/var/run # Pipe to send command to ocsigenserver.

I give a simple ocsigenserver configuration file:

<ocsigen>
  <server>
    <charset>utf-8</charset>

    <port>8080</port>

    <logdir>local/var/log/hello_world</logdir>
    <datadir>local/var/data/hello_world</datadir>

    <extension findlib-package="ocsigenserver.ext.staticmod"/>
    <extension findlib-package="ocsigenserver.ext.ocsipersist-dbm"/>
    <extension findlib-package="eliom.server"/>

    <commandpipe>local/var/run/hello_world-run</commandpipe>
    <host hostfilter="*">
      <static dir="local/var/www/hello_world/eliom" />
      <eliommodule module="local/lib/hello_world/hello_world.cma" />
      <eliom/>
    </host>
  </server>
</ocsigen>

Save this content in a file named hello_world.conf

The tags are explicit so I don’t explain it. You can find all significations in the official documentation..

The last thing to do is to copy all our files in the corresponding local ocsigenserver configuration i.e. in the proper sub-directory of the local directory.

cp hello_world.cma local/lib/hello_world
cp hello_world.js local/var/www/html/hello_world/eliom
cp hello_world.conf local/etc/hello_world

Launch ocsigenserver

Now we have a full ocsigenserver configuration and a Eliom website build from scratch with tools given by the Ocsigen team.
To run the ocsigenserver with the previous configuration, use

ocsigenserver -c local/etc/hello_world/hello_world.conf

and go to http://localhost:8080: you have a «Hello, World!» web application entirely written in OCaml!

Leave a Reply

Your email address will not be published. Required fields are marked *