ocp-ocamlres

A tool ocp-ocamlres to embed files and directories inside an OCaml executables, with a companion library ocplib-ocamlres to manipulate them at run-time.


Ressources

ocp-ocamlres on GithubLatest sources in the official GIT repository

Features:

Simple example:

Note: This example is illustrated through command line calls of the tool, but the same could be achieved in OCaml via library calls.

Suppose you have a folder "res" containing the simple following hierarchy:

You want to embed these files in you application, so you run ocp-ocamlres res -o appres.ml to obtain an OCaml value Appres.root of type string OCamlRes.Res.root:

let root = OCamlRes.Res.([
  Dir ("a", [
    Dir ("x", [ File ("test.int", "1234")]) ;
    Dir ("y", [
      File ("tast.int", "9999") ;
      File ("test.int", "5678")])]) ;
  Dir ("b", [
    Dir ("x", [
      File ("bytes.bin", "\x01\x02\x03\x04\x05\x06") ;
      File ("read.txt", "this is\na text\nfile\n")])])
])

Alternatively, you can also run ocp-ocamlres -format ocaml res -o res.ml to select another output format and obtain:

module A = struct
  module X = struct let test_int = "1234" end
  module Y = struct
    let tast_int = "9999"
    let test_int = "5678"
  end
end
module B = struct
  module X = struct
    let bytes_bin = "\x01\x02\x03\x04\x05\x06"
    let read_txt = "this is\na text\nfile\n"
  end
end

A more advanced feature is resource pre-parsing via subformats. While formats handle the generation of the main tree structure, subfornats handle the pretty printing of files. The tool has an option to select files by extension for applying a specific subformat to them. For instance, we can ask to treat the .int files with the int subformat and the .txt files with the lines subformat ocp-ocamlres -format ocaml res -o res.ml -subformat int int -subformat txt lines

module A = struct
  module Y = struct let tast_int = 9999 let test_int = 5678 end
  module X = struct let test_int = 1234 end
end
module B = struct
  module Y = struct
    let bytes_bin = "\x01\x02\x03\x04\x05\x06"
    let read_txt = [ "this is" ; "a text" ; "file" ]
  end
end

Mixing this feature with the ocamlres format is a bit trickier. Since the 'a root type must be homogeneous, just using different subformat for pretty printing different leaves would produce untypable code. To solve this, the ocamlres format has two ways of working:

  1. If all files are treated by the same subformat, the type parameter will simply be the intrinsic one of the subformat. For instance, there is a Lines subformat which turns text files into list of lines, and if applied to all nodes of the tree, results in a tree of type string list root.
  2. Otherwise, the leaves are boxed using constructors of a generated type unifying all occuring subformats (or in option polymorphic variants), as in the following output of the command ocp-ocamlres -format ocamlres res -o res.ml -subformat int int -subformat txt lines -no-variants
type content =
  | Int of int
  | Lines of string list
  | Raw of string
let root = OCamlRes.Res.([
  Dir ("a", [
    Dir ("y", [
      File ("tast.int", Int 9999) ;
      File ("test.int", Int 5678)]) ;
    Dir ("x", [ File ("test.int", Int 1234)])]) ;
  Dir ("b", [
    Dir ("y", [
      File ("bytes.bin", Raw "\x01\x02\x03\x04\x05\x06") ;
      File ("read.txt", Lines [ "this is" ; "a text" ; "file" ])])])
])

Some use-cases:

Help page:

Usage: ocp-ocamlres [ -format <format> ] [ options ] files...
  -plug "plugin.cmxs" load a plug-in
  -list print the list of available formats
  -list-subformats lists available subformats
  -format "format" define the output format (defaults to "static")
  -ext "ext" only scan files ending with ".ext" (can be called more than once)
  -keep-empty-dirs keep empty dirs in scanned files
Available formats:
  * files: reproduces the original files
    -output-dir "dir" set the base output directory (defaults to ".")
  * ocaml: produces static ocaml bindings (modules for dirs, values for files)
    -width set the maximum chars per line of generated code
    -subformat "ext" "subformat" preprocess files ending by "ext" as "suformat"
    -o "file name" print in a file instead of stdout
  * ocamlres: produces the OCaml source representation of the OCamlRes tree
    -width set the maximum chars per line of generated code
    -subformat "ext" "subformat" preprocess files ending by "ext" as "suformat"
    -o "file name" print in a file instead of stdout
    -no-variants use a plain sum type instead of polymorphic variants
Available subformats (for compatible formats):
  * int: for files containing only an integer
  * lines: splits the input into lines
  * raw: raw file contents as a string