use oasis to build files, splitted into libraries, including

a Container module that packs everything;
ToWeb module (depends on CamlGI) to expoert values on the web
This commit is contained in:
Simon Cruanes 2013-10-18 22:27:30 +02:00
parent 9dc91cec65
commit 7313a258b4
16 changed files with 7637 additions and 44 deletions

1
.gitignore vendored
View file

@ -5,3 +5,4 @@ _build
.session
TAGS
*.docdir
setup.log

44
META Normal file
View file

@ -0,0 +1,44 @@
# OASIS_START
# DO NOT EDIT (digest: 228a31c6369b1dd4a6ea763cff7e020e)
version = "0.1"
description = "A bunch of modules, including polymorphic containers."
requires = "unix"
archive(byte) = "containers.cma"
archive(byte, plugin) = "containers.cma"
archive(native) = "containers.cmxa"
archive(native, plugin) = "containers.cmxs"
exists_if = "containers.cma"
package "thread" (
version = "0.1"
description = "A bunch of modules, including polymorphic containers."
requires = "containers lwt"
archive(byte) = "containers_thread.cma"
archive(byte, plugin) = "containers_thread.cma"
archive(native) = "containers_thread.cmxa"
archive(native, plugin) = "containers_thread.cmxs"
exists_if = "containers_thread.cma"
)
package "lwt" (
version = "0.1"
description = "A bunch of modules, including polymorphic containers."
requires = "containers lwt"
archive(byte) = "containers_lwt.cma"
archive(byte, plugin) = "containers_lwt.cma"
archive(native) = "containers_lwt.cmxa"
archive(native, plugin) = "containers_lwt.cmxs"
exists_if = "containers_lwt.cma"
)
package "cgi" (
version = "0.1"
description = "A bunch of modules, including polymorphic containers."
requires = "containers CamlGI"
archive(byte) = "containers_cgi.cma"
archive(byte, plugin) = "containers_cgi.cma"
archive(native) = "containers_cgi.cmxa"
archive(native, plugin) = "containers_cgi.cmxs"
exists_if = "containers_cgi.cma"
)
# OASIS_STOP

View file

@ -1,53 +1,52 @@
# OASIS_START
# DO NOT EDIT (digest: bc1e05bfc8b39b664f29dae8dbd3ebbb)
INTERFACE_FILES = $(shell find -name '*.mli')
IMPLEMENTATION_FILES = $(shell find -name '*.ml')
SETUP = ocaml setup.ml
build: setup.data
$(SETUP) -build $(BUILDFLAGS)
doc: setup.data build
$(SETUP) -doc $(DOCFLAGS)
test: setup.data build
$(SETUP) -test $(TESTFLAGS)
all:
$(SETUP) -all $(ALLFLAGS)
install: setup.data
$(SETUP) -install $(INSTALLFLAGS)
uninstall: setup.data
$(SETUP) -uninstall $(UNINSTALLFLAGS)
reinstall: setup.data
$(SETUP) -reinstall $(REINSTALLFLAGS)
clean:
$(SETUP) -clean $(CLEANFLAGS)
distclean:
$(SETUP) -distclean $(DISTCLEANFLAGS)
setup.data:
$(SETUP) -configure $(CONFIGUREFLAGS)
.PHONY: build doc test all install uninstall reinstall clean distclean configure
# OASIS_STOP
TARGETS_LIB = containers.cmxa containers.cma
TARGETS_DOC = containers.docdir/index.html
EXAMPLES = examples/mem_size.native examples/collatz.native \
examples/bencode_write.native # examples/crawl.native
OPTIONS = -use-ocamlfind
ENABLE_THREAD ?= yes
ENABLE_LWT ?= yes
ifeq ($(ENABLE_THREAD), yes)
OPTIONS += -tag thread
TARGETS_LIB += thread_containers.cmxa thread_containers.cma
TARGETS_DOC += thread_containers.docdir/index.html
endif
ifeq ($(ENABLE_LWT), yes)
OPTIONS += -package lwt -package lwt.unix
TARGETS_LIB += lwt_containers.cmxa lwt_containers.cma
TARGETS_DOC += lwt_containers.docdir/index.html
endif
all: lib
lib:
ocamlbuild $(OPTIONS) $(TARGETS_LIB) $(TARGETS_DOC)
doc:
ocamlbuild $(OPTIONS) $(TARGETS_DOC)
examples: all
ocamlbuild $(OPTIONS) -package unix -I . $(EXAMPLES)
tests: lib
ocamlbuild $(OPTIONS) -package oUnit -I . tests/run_tests.native
bench:
ocamlbuild $(OPTIONS) -package bench -package unix -I . tests/benchs.native
push_doc: doc
scp -r containers.docdir/* cedeela.fr:~/simon/root/software/containers/
clean:
ocamlbuild -clean
tags:
otags *.ml *.mli
.PHONY: all clean tests tags examples
.PHONY: examples push_doc tags

106
_oasis Normal file
View file

@ -0,0 +1,106 @@
OASISFormat: 0.3
Name: containers
Version: 0.1
Homepage: https://github.com/c-cube/ocaml-containers
Authors: Simon Cruanes
License: BSD3
LicenseFile: LICENSE
Plugins: META (0.3), DevFiles (0.3)
OCamlVersion: >= 4.00.1
BuildTools: ocamlbuild
Synopsis: A bunch of modules, including polymorphic containers.
Description:
A bunch of useful modules, including polymorphic containers, graph
abstractions, serialization systems, testing systems and various
experiments.
Flag "cgi"
Description: Build modules related to FastCGI, depending on CamlGI
Default: false
Flag "lwt"
Description: Build modules which depend on Lwt
Default: false
Flag "thread"
Description: Build modules that depend on threads
Default: true
Flag "bench"
Description: Build and run benchmarks
Default: false
Library "containers"
Path: .
Pack: true
Modules: Cache, Deque, Gen, FHashtbl, FQueue, FlatHashtbl, Hashset,
Heap, LazyGraph, PersistentGraph, PersistentHashtbl,
PHashtbl, Sequence, SkipList, SplayTree, SplayMap, Univ,
Vector, Bij, PiCalculus, Bencode, Sexp, RAL, MultiSet,
UnionFind, SmallSet, Leftistheap, AbsSet, CSM, MultiMap,
ActionMan, BV, QCheck, BencodeOnDisk, Show
BuildDepends: unix
Library "containers_thread"
Path: .
Modules: Future
FindlibName: thread
FindlibParent: containers
Build$: flag(thread)
Install$: flag(thread)
BuildDepends: containers,threads
XMETARequires: containers,lwt
Library "containers_lwt"
Path: .
Modules: Behavior
FindlibName: lwt
FindlibParent: containers
Build$: flag(lwt)
Install$: flag(lwt)
BuildDepends: containers,lwt
XMETARequires: containers,lwt
Library "containers_cgi"
Path: .
Modules: ToWeb
FindlibName: cgi
FindlibParent: containers
Build$: flag(cgi)
Install$: flag(cgi)
BuildDepends: containers,CamlGI
XMETARequires: containers,CamlGI
Document containers
Title: Containers docs
Type: ocamlbuild (0.3)
BuildTools+: ocamldoc
Install: true
XOCamlbuildPath: .
XOCamlbuildLibraries: containers,containers.lwt
Executable benchs
Path: tests/
Install: false
CompiledObject: native
MainIs: benchs.ml
BuildDepends: containers,bench
Test all
Command: $run_tests
TestTools: run_tests
Run$: flag(tests)
Executable run_tests
Path: tests/
Install: false
CompiledObject: native
MainIs: run_tests.ml
Build$: flag(tests) && flag(lwt)
BuildDepends: containers, oUnit, lwt
SourceRepository head
Type: git
Location: https://github.com/c-cube/ocaml-containers
Browser: https://github.com/c-cube/ocaml-containers/tree/master/src

82
_tags
View file

@ -1,4 +1,78 @@
<**/*future.*>: thread
<examples/crawl.*>: package(batteries), thread, package(unix)
<behavior.*>: package(lwt), package(unix)
<tests/*.native>: thread
# OASIS_START
# DO NOT EDIT (digest: 2ccc5fef0936d36155ec2a8d19a2c516)
# Ignore VCS directories, you can use the same kind of rule outside
# OASIS_START/STOP if you want to exclude directories that contains
# useless stuff for the build process
<**/.svn>: -traverse
<**/.svn>: not_hygienic
".bzr": -traverse
".bzr": not_hygienic
".hg": -traverse
".hg": not_hygienic
".git": -traverse
".git": not_hygienic
"_darcs": -traverse
"_darcs": not_hygienic
# Library containers
"containers.cmxs": use_containers
"cache.cmx": for-pack(Containers)
"deque.cmx": for-pack(Containers)
"gen.cmx": for-pack(Containers)
"fHashtbl.cmx": for-pack(Containers)
"fQueue.cmx": for-pack(Containers)
"flatHashtbl.cmx": for-pack(Containers)
"hashset.cmx": for-pack(Containers)
"heap.cmx": for-pack(Containers)
"lazyGraph.cmx": for-pack(Containers)
"persistentGraph.cmx": for-pack(Containers)
"persistentHashtbl.cmx": for-pack(Containers)
"pHashtbl.cmx": for-pack(Containers)
"sequence.cmx": for-pack(Containers)
"skipList.cmx": for-pack(Containers)
"splayTree.cmx": for-pack(Containers)
"splayMap.cmx": for-pack(Containers)
"univ.cmx": for-pack(Containers)
"vector.cmx": for-pack(Containers)
"bij.cmx": for-pack(Containers)
"piCalculus.cmx": for-pack(Containers)
"bencode.cmx": for-pack(Containers)
"sexp.cmx": for-pack(Containers)
"RAL.cmx": for-pack(Containers)
"multiSet.cmx": for-pack(Containers)
"unionFind.cmx": for-pack(Containers)
"smallSet.cmx": for-pack(Containers)
"leftistheap.cmx": for-pack(Containers)
"absSet.cmx": for-pack(Containers)
"CSM.cmx": for-pack(Containers)
"multiMap.cmx": for-pack(Containers)
"actionMan.cmx": for-pack(Containers)
"BV.cmx": for-pack(Containers)
"qCheck.cmx": for-pack(Containers)
"bencodeOnDisk.cmx": for-pack(Containers)
"show.cmx": for-pack(Containers)
# Library containers_thread
"containers_thread.cmxs": use_containers_thread
<*.ml{,i}>: pkg_threads
# Library containers_lwt
"containers_lwt.cmxs": use_containers_lwt
<*.ml{,i}>: pkg_lwt
# Library containers_cgi
"containers_cgi.cmxs": use_containers_cgi
<*.ml{,i}>: use_containers
<*.ml{,i}>: pkg_CamlGI
<*.ml{,i}>: pkg_unix
# Executable benchs
"tests/benchs.native": use_containers
"tests/benchs.native": pkg_bench
"tests/benchs.native": pkg_unix
<tests/*.ml{,i}>: pkg_bench
# Executable run_tests
"tests/run_tests.native": use_containers
"tests/run_tests.native": pkg_oUnit
"tests/run_tests.native": pkg_lwt
"tests/run_tests.native": pkg_unix
<tests/*.ml{,i}>: use_containers
<tests/*.ml{,i}>: pkg_oUnit
<tests/*.ml{,i}>: pkg_lwt
<tests/*.ml{,i}>: pkg_unix
# OASIS_STOP

27
configure vendored Executable file
View file

@ -0,0 +1,27 @@
#!/bin/sh
# OASIS_START
# DO NOT EDIT (digest: 425187ed8bfdbdd207fd76392dd243a7)
set -e
FST=true
for i in "$@"; do
if $FST; then
set --
FST=false
fi
case $i in
--*=*)
ARG=${i%%=*}
VAL=${i##*=}
set -- "$@" "$ARG" "$VAL"
;;
*)
set -- "$@" "$i"
;;
esac
done
ocaml setup.ml -configure "$@"
# OASIS_STOP

View file

@ -1,3 +1,5 @@
# OASIS_START
# DO NOT EDIT (digest: c71ccfdbd010237adfeae06ed09c46ad)
Cache
Deque
Gen
@ -33,3 +35,4 @@ BV
QCheck
BencodeOnDisk
Show
# OASIS_STOP

View file

@ -1 +0,0 @@
containers.mllib

39
containers.odocl Normal file
View file

@ -0,0 +1,39 @@
# OASIS_START
# DO NOT EDIT (digest: 3b0c2006d9fcd3955eb8118014479a19)
Cache
Deque
Gen
FHashtbl
FQueue
FlatHashtbl
Hashset
Heap
LazyGraph
PersistentGraph
PersistentHashtbl
PHashtbl
Sequence
SkipList
SplayTree
SplayMap
Univ
Vector
Bij
PiCalculus
Bencode
Sexp
RAL
MultiSet
UnionFind
SmallSet
Leftistheap
AbsSet
CSM
MultiMap
ActionMan
BV
QCheck
BencodeOnDisk
Show
Behavior
# OASIS_STOP

4
containers_cgi.mllib Normal file
View file

@ -0,0 +1,4 @@
# OASIS_START
# DO NOT EDIT (digest: f5192440033c9e32b425a31804bbddaa)
ToWeb
# OASIS_STOP

4
containers_lwt.mllib Normal file
View file

@ -0,0 +1,4 @@
# OASIS_START
# DO NOT EDIT (digest: b39a035a995fc6597c8eb942210d1527)
Behavior
# OASIS_STOP

4
containers_thread.mllib Normal file
View file

@ -0,0 +1,4 @@
# OASIS_START
# DO NOT EDIT (digest: adff855173c9b92b5478129af7d39e03)
Future
# OASIS_STOP

497
myocamlbuild.ml Normal file
View file

@ -0,0 +1,497 @@
(* OASIS_START *)
(* DO NOT EDIT (digest: 240e1f430a880e29ecae75f577790609) *)
module OASISGettext = struct
(* # 21 "src/oasis/OASISGettext.ml" *)
let ns_ str =
str
let s_ str =
str
let f_ (str : ('a, 'b, 'c, 'd) format4) =
str
let fn_ fmt1 fmt2 n =
if n = 1 then
fmt1^^""
else
fmt2^^""
let init =
[]
end
module OASISExpr = struct
(* # 21 "src/oasis/OASISExpr.ml" *)
open OASISGettext
type test = string
type flag = string
type t =
| EBool of bool
| ENot of t
| EAnd of t * t
| EOr of t * t
| EFlag of flag
| ETest of test * string
type 'a choices = (t * 'a) list
let eval var_get t =
let rec eval' =
function
| EBool b ->
b
| ENot e ->
not (eval' e)
| EAnd (e1, e2) ->
(eval' e1) && (eval' e2)
| EOr (e1, e2) ->
(eval' e1) || (eval' e2)
| EFlag nm ->
let v =
var_get nm
in
assert(v = "true" || v = "false");
(v = "true")
| ETest (nm, vl) ->
let v =
var_get nm
in
(v = vl)
in
eval' t
let choose ?printer ?name var_get lst =
let rec choose_aux =
function
| (cond, vl) :: tl ->
if eval var_get cond then
vl
else
choose_aux tl
| [] ->
let str_lst =
if lst = [] then
s_ "<empty>"
else
String.concat
(s_ ", ")
(List.map
(fun (cond, vl) ->
match printer with
| Some p -> p vl
| None -> s_ "<no printer>")
lst)
in
match name with
| Some nm ->
failwith
(Printf.sprintf
(f_ "No result for the choice list '%s': %s")
nm str_lst)
| None ->
failwith
(Printf.sprintf
(f_ "No result for a choice list: %s")
str_lst)
in
choose_aux (List.rev lst)
end
# 117 "myocamlbuild.ml"
module BaseEnvLight = struct
(* # 21 "src/base/BaseEnvLight.ml" *)
module MapString = Map.Make(String)
type t = string MapString.t
let default_filename =
Filename.concat
(Sys.getcwd ())
"setup.data"
let load ?(allow_empty=false) ?(filename=default_filename) () =
if Sys.file_exists filename then
begin
let chn =
open_in_bin filename
in
let st =
Stream.of_channel chn
in
let line =
ref 1
in
let st_line =
Stream.from
(fun _ ->
try
match Stream.next st with
| '\n' -> incr line; Some '\n'
| c -> Some c
with Stream.Failure -> None)
in
let lexer =
Genlex.make_lexer ["="] st_line
in
let rec read_file mp =
match Stream.npeek 3 lexer with
| [Genlex.Ident nm; Genlex.Kwd "="; Genlex.String value] ->
Stream.junk lexer;
Stream.junk lexer;
Stream.junk lexer;
read_file (MapString.add nm value mp)
| [] ->
mp
| _ ->
failwith
(Printf.sprintf
"Malformed data file '%s' line %d"
filename !line)
in
let mp =
read_file MapString.empty
in
close_in chn;
mp
end
else if allow_empty then
begin
MapString.empty
end
else
begin
failwith
(Printf.sprintf
"Unable to load environment, the file '%s' doesn't exist."
filename)
end
let var_get name env =
let rec var_expand str =
let buff =
Buffer.create ((String.length str) * 2)
in
Buffer.add_substitute
buff
(fun var ->
try
var_expand (MapString.find var env)
with Not_found ->
failwith
(Printf.sprintf
"No variable %s defined when trying to expand %S."
var
str))
str;
Buffer.contents buff
in
var_expand (MapString.find name env)
let var_choose lst env =
OASISExpr.choose
(fun nm -> var_get nm env)
lst
end
# 215 "myocamlbuild.ml"
module MyOCamlbuildFindlib = struct
(* # 21 "src/plugins/ocamlbuild/MyOCamlbuildFindlib.ml" *)
(** OCamlbuild extension, copied from
* http://brion.inria.fr/gallium/index.php/Using_ocamlfind_with_ocamlbuild
* by N. Pouillard and others
*
* Updated on 2009/02/28
*
* Modified by Sylvain Le Gall
*)
open Ocamlbuild_plugin
(* these functions are not really officially exported *)
let run_and_read =
Ocamlbuild_pack.My_unix.run_and_read
let blank_sep_strings =
Ocamlbuild_pack.Lexers.blank_sep_strings
let split s ch =
let x =
ref []
in
let rec go s =
let pos =
String.index s ch
in
x := (String.before s pos)::!x;
go (String.after s (pos + 1))
in
try
go s
with Not_found -> !x
let split_nl s = split s '\n'
let before_space s =
try
String.before s (String.index s ' ')
with Not_found -> s
(* this lists all supported packages *)
let find_packages () =
List.map before_space (split_nl & run_and_read "ocamlfind list")
(* this is supposed to list available syntaxes, but I don't know how to do it. *)
let find_syntaxes () = ["camlp4o"; "camlp4r"]
(* ocamlfind command *)
let ocamlfind x = S[A"ocamlfind"; x]
let dispatch =
function
| Before_options ->
(* by using Before_options one let command line options have an higher priority *)
(* on the contrary using After_options will guarantee to have the higher priority *)
(* override default commands by ocamlfind ones *)
Options.ocamlc := ocamlfind & A"ocamlc";
Options.ocamlopt := ocamlfind & A"ocamlopt";
Options.ocamldep := ocamlfind & A"ocamldep";
Options.ocamldoc := ocamlfind & A"ocamldoc";
Options.ocamlmktop := ocamlfind & A"ocamlmktop"
| After_rules ->
(* When one link an OCaml library/binary/package, one should use -linkpkg *)
flag ["ocaml"; "link"; "program"] & A"-linkpkg";
(* For each ocamlfind package one inject the -package option when
* compiling, computing dependencies, generating documentation and
* linking. *)
List.iter
begin fun pkg ->
flag ["ocaml"; "compile"; "pkg_"^pkg] & S[A"-package"; A pkg];
flag ["ocaml"; "ocamldep"; "pkg_"^pkg] & S[A"-package"; A pkg];
flag ["ocaml"; "doc"; "pkg_"^pkg] & S[A"-package"; A pkg];
flag ["ocaml"; "link"; "pkg_"^pkg] & S[A"-package"; A pkg];
flag ["ocaml"; "infer_interface"; "pkg_"^pkg] & S[A"-package"; A pkg];
end
(find_packages ());
(* Like -package but for extensions syntax. Morover -syntax is useless
* when linking. *)
List.iter begin fun syntax ->
flag ["ocaml"; "compile"; "syntax_"^syntax] & S[A"-syntax"; A syntax];
flag ["ocaml"; "ocamldep"; "syntax_"^syntax] & S[A"-syntax"; A syntax];
flag ["ocaml"; "doc"; "syntax_"^syntax] & S[A"-syntax"; A syntax];
flag ["ocaml"; "infer_interface"; "syntax_"^syntax] & S[A"-syntax"; A syntax];
end (find_syntaxes ());
(* The default "thread" tag is not compatible with ocamlfind.
* Indeed, the default rules add the "threads.cma" or "threads.cmxa"
* options when using this tag. When using the "-linkpkg" option with
* ocamlfind, this module will then be added twice on the command line.
*
* To solve this, one approach is to add the "-thread" option when using
* the "threads" package using the previous plugin.
*)
flag ["ocaml"; "pkg_threads"; "compile"] (S[A "-thread"]);
flag ["ocaml"; "pkg_threads"; "doc"] (S[A "-I"; A "+threads"]);
flag ["ocaml"; "pkg_threads"; "link"] (S[A "-thread"]);
flag ["ocaml"; "pkg_threads"; "infer_interface"] (S[A "-thread"])
| _ ->
()
end
module MyOCamlbuildBase = struct
(* # 21 "src/plugins/ocamlbuild/MyOCamlbuildBase.ml" *)
(** Base functions for writing myocamlbuild.ml
@author Sylvain Le Gall
*)
open Ocamlbuild_plugin
module OC = Ocamlbuild_pack.Ocaml_compiler
type dir = string
type file = string
type name = string
type tag = string
(* # 56 "src/plugins/ocamlbuild/MyOCamlbuildBase.ml" *)
type t =
{
lib_ocaml: (name * dir list) list;
lib_c: (name * dir * file list) list;
flags: (tag list * (spec OASISExpr.choices)) list;
(* Replace the 'dir: include' from _tags by a precise interdepends in
* directory.
*)
includes: (dir * dir list) list;
}
let env_filename =
Pathname.basename
BaseEnvLight.default_filename
let dispatch_combine lst =
fun e ->
List.iter
(fun dispatch -> dispatch e)
lst
let tag_libstubs nm =
"use_lib"^nm^"_stubs"
let nm_libstubs nm =
nm^"_stubs"
let dispatch t e =
let env =
BaseEnvLight.load
~filename:env_filename
~allow_empty:true
()
in
match e with
| Before_options ->
let no_trailing_dot s =
if String.length s >= 1 && s.[0] = '.' then
String.sub s 1 ((String.length s) - 1)
else
s
in
List.iter
(fun (opt, var) ->
try
opt := no_trailing_dot (BaseEnvLight.var_get var env)
with Not_found ->
Printf.eprintf "W: Cannot get variable %s" var)
[
Options.ext_obj, "ext_obj";
Options.ext_lib, "ext_lib";
Options.ext_dll, "ext_dll";
]
| After_rules ->
(* Declare OCaml libraries *)
List.iter
(function
| nm, [] ->
ocaml_lib nm
| nm, dir :: tl ->
ocaml_lib ~dir:dir (dir^"/"^nm);
List.iter
(fun dir ->
List.iter
(fun str ->
flag ["ocaml"; "use_"^nm; str] (S[A"-I"; P dir]))
["compile"; "infer_interface"; "doc"])
tl)
t.lib_ocaml;
(* Declare directories dependencies, replace "include" in _tags. *)
List.iter
(fun (dir, include_dirs) ->
Pathname.define_context dir include_dirs)
t.includes;
(* Declare C libraries *)
List.iter
(fun (lib, dir, headers) ->
(* Handle C part of library *)
flag ["link"; "library"; "ocaml"; "byte"; tag_libstubs lib]
(S[A"-dllib"; A("-l"^(nm_libstubs lib)); A"-cclib";
A("-l"^(nm_libstubs lib))]);
flag ["link"; "library"; "ocaml"; "native"; tag_libstubs lib]
(S[A"-cclib"; A("-l"^(nm_libstubs lib))]);
flag ["link"; "program"; "ocaml"; "byte"; tag_libstubs lib]
(S[A"-dllib"; A("dll"^(nm_libstubs lib))]);
(* When ocaml link something that use the C library, then one
need that file to be up to date.
*)
dep ["link"; "ocaml"; "program"; tag_libstubs lib]
[dir/"lib"^(nm_libstubs lib)^"."^(!Options.ext_lib)];
dep ["compile"; "ocaml"; "program"; tag_libstubs lib]
[dir/"lib"^(nm_libstubs lib)^"."^(!Options.ext_lib)];
(* TODO: be more specific about what depends on headers *)
(* Depends on .h files *)
dep ["compile"; "c"]
headers;
(* Setup search path for lib *)
flag ["link"; "ocaml"; "use_"^lib]
(S[A"-I"; P(dir)]);
)
t.lib_c;
(* Add flags *)
List.iter
(fun (tags, cond_specs) ->
let spec =
BaseEnvLight.var_choose cond_specs env
in
flag tags & spec)
t.flags
| _ ->
()
let dispatch_default t =
dispatch_combine
[
dispatch t;
MyOCamlbuildFindlib.dispatch;
]
end
# 476 "myocamlbuild.ml"
open Ocamlbuild_plugin;;
let package_default =
{
MyOCamlbuildBase.lib_ocaml =
[
("containers", []);
("containers_thread", []);
("containers_lwt", []);
("containers_cgi", [])
];
lib_c = [];
flags = [];
includes = [];
}
;;
let dispatch_default = MyOCamlbuildBase.dispatch_default package_default;;
# 496 "myocamlbuild.ml"
(* OASIS_STOP *)
Ocamlbuild_plugin.dispatch dispatch_default;;

63
setup.data Normal file
View file

@ -0,0 +1,63 @@
ocamlfind="/home/simon/.opam/system/bin/ocamlfind"
ocamlc="/usr/bin/ocamlc.opt"
ocamlopt="/usr/bin/ocamlopt.opt"
ocamlbuild="/usr/bin/ocamlbuild"
pkg_name="containers"
pkg_version="0.1"
os_type="Unix"
system="linux"
architecture="amd64"
ccomp_type="cc"
ocaml_version="4.00.1"
standard_library_default="/usr/lib/ocaml"
standard_library="/usr/lib/ocaml"
standard_runtime="/usr/bin/ocamlrun"
bytecomp_c_compiler="gcc -fno-defer-pop -Wall -D_FILE_OFFSET_BITS=64 -D_REENTRANT -fPIC"
native_c_compiler="gcc -Wall -D_FILE_OFFSET_BITS=64 -D_REENTRANT"
model="default"
ext_obj=".o"
ext_asm=".s"
ext_lib=".a"
ext_dll=".so"
default_executable_name="a.out"
systhread_supported="true"
prefix="/usr/local"
exec_prefix="$prefix"
bindir="$exec_prefix/bin"
sbindir="$exec_prefix/sbin"
libexecdir="$exec_prefix/libexec"
sysconfdir="$prefix/etc"
sharedstatedir="$prefix/com"
localstatedir="$prefix/var"
libdir="$exec_prefix/lib"
datarootdir="$prefix/share"
datadir="$datarootdir"
infodir="$datarootdir/info"
localedir="$datarootdir/locale"
mandir="$datarootdir/man"
docdir="$datarootdir/doc/$pkg_name"
htmldir="$docdir"
dvidir="$docdir"
pdfdir="$docdir"
psdir="$docdir"
findlib_version="1.4"
is_native="true"
suffix_program=""
rm="rm -f"
rmdir="rm -rf"
debug="true"
profile="false"
native_dynlink="true"
ocamlbuildflags=""
cgi="false"
lwt="false"
thread="true"
bench="false"
docs="true"
tests="false"
ocaml_version_ge_4_00_1="4.00.1"
pkg_unix="/usr/lib/ocaml"
pkg_threads="/usr/lib/ocaml"
ocamldoc="/usr/bin/ocamldoc"
pkg_bench="/home/simon/.opam/system/lib/bench"
pkg_ounit="/home/simon/.opam/system/lib/oUnit"

6196
setup.ml Normal file

File diff suppressed because it is too large Load diff

341
toWeb.ml Normal file
View file

@ -0,0 +1,341 @@
(*
copyright (c) 2013, simon cruanes
all rights reserved.
redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. redistributions in binary
form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with
the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*)
(** {1 Expose the State of a Program to the Web}
We export some values (and associated functions for converting them to
html, and update them) as a FastCGI interface.
This module depends on CamlGI.
*)
(** {2 Some combinators to build HTML documents} *)
module HTML = struct
type t =
| Str of string (* content *)
| List of t list
| Url of url
| Img of image
| Concat of t list
| H of int * t
| Link of link
| Tag of string * t
and url = {
url_alt : string option;
url_url : string;
url_descr : string;
}
and image = {
img_alt : string option;
img_url : string;
}
and link = {
link_rel : string;
link_url : string;
}
let str s = Str s
let bprintf format =
let buffer = Buffer.create 64 in
Printf.kbprintf
(fun fmt -> str (Buffer.contents buffer))
buffer
format
let bprintf format =
let buffer = Buffer.create 64 in
Printf.ksprintf
str
format
let list l = List l
let url ?alt ~url ~descr = Url {
url_alt = alt;
url_url = url;
url_descr = descr;
}
let img ?alt url = Img {
img_alt = alt;
img_url = url;
}
let append a b = Concat [a; b]
let concat l = Concat l
let h1 x = H (1, x)
let h2 x = H (2, x)
let h3 x = H (3, x)
let h n x = H (n, x)
let link ~rel ~url = Link {
link_rel = rel;
link_url = url;
}
let head x = Tag ("head", x)
let body x = Tag ("body", x)
let html x = Tag ("html", x)
(* XXX UGLY: I copied code from CamlGI because it doesn't provide a way
to encode strings with HTML conventions. Bad bad bad. *)
module Encode = struct
(* Use a table lookup for speed. *)
let char_of_hex =
let hex = [| '0'; '1'; '2'; '3'; '4'; '5'; '6'; '7'; '8'; '9';
'A'; 'B'; 'C'; 'D'; 'E'; 'F' |] in
fun i -> Array.unsafe_get hex i
let encode_wrt is_special s0 =
let len = String.length s0 in
let encoded_length = ref len in
for i = 0 to len - 1 do
if is_special(String.unsafe_get s0 i) then
encoded_length := !encoded_length + 2
done;
let s = String.create !encoded_length in
let rec do_enc i0 i = (* copy the encoded string in s *)
if i0 < len then begin
let s0i0 = String.unsafe_get s0 i0 in
if is_special s0i0 then begin
let c = Char.code s0i0 in
let i1 = succ i in
let i2 = succ i1 in
String.unsafe_set s i '%';
String.unsafe_set s i1 (char_of_hex (c lsr 4));
String.unsafe_set s i2 (char_of_hex (c land 0x0F));
do_enc (succ i0) (succ i2)
end
else if s0i0 = ' ' then begin
String.unsafe_set s i '+';
do_enc (succ i0) (succ i)
end
else begin
String.unsafe_set s i s0i0;
do_enc (succ i0) (succ i)
end
end in
do_enc 0 0;
s
(* Unreserved characters consist of all alphanumeric chars and the
following limited set of punctuation marks and symbols: '-' | '_' |
'.' | '!' | '~' | '*' | '\'' | '(' | ')'. According to RFC 2396,
they should not be escaped unless the context requires it. *)
let special_rfc2396 = function
| ';' | '/' | '?' | ':' | '@' | '&' | '=' | '+' | '$' | ',' (* Reserved *)
| '\000' .. '\031' | '\127' .. '\255' (* Control chars and non-ASCII *)
| '<' | '>' | '#' | '%' | '"' (* delimiters *)
| '{' | '}' | '|' | '\\' | '^' | '[' | ']' | '`' (* unwise *)
-> true
| _ -> false
(* ' ' must also be encoded but its encoding '+' takes a single char. *)
let encode = encode_wrt special_rfc2396
end
(* real rendering is always into a buffer (for now) *)
let rec to_buf buf x =
match x with
| Str s -> Buffer.add_string buf (Encode.encode s)
| List l ->
Buffer.add_string buf "<ul>";
List.iter
(fun y -> Printf.bprintf buf "<li>%a</li>" to_buf y)
l;
Buffer.add_string buf "</ul>"
| Url url ->
begin match url.url_alt with
| None ->
Printf.bprintf buf "<a href=\"%s\">%s</a>" url.url_url
(Encode.encode url.url_descr)
| Some alt ->
Printf.bprintf buf "<a href=\"%s\" alt=\"%s\">%s</a>"
url.url_url (Encode.encode alt) (Encode.encode url.url_descr)
end
| Img i -> failwith "img: not implemented"
| Concat l -> List.iter (to_buf buf) l
| H (n, y) ->
Printf.bprintf buf "<h%i> %a </h%i>" n to_buf y n
| Link _ -> failwith "link: not implemented"
| Tag (str, y) -> Printf.bprintf buf "<%s> %a </%s>" str to_buf y str
let render x =
let buf = Buffer.create 256 in
to_buf buf x;
Buffer.contents buf
let to_chan oc x =
let buf = Buffer.create 256 in
to_buf buf x;
Buffer.output_buffer oc buf
end
(** {2 Stateful Object on the Web} *)
module State = struct
type 'a t = {
mutable content : 'a;
mutable callbacks : ('a -> unit) list;
id : ('a -> string) option;
export : 'a -> HTML.t;
update : (string * string) list -> 'a -> 'a;
} (** A value that can be exposed to the Web.
The [export] function is used to print the current state of
the object into HTML (when requested).
The [update] optional function can be used to update
the value, given a query with parameters. *)
type wrap = Wrap : 'a t -> wrap
(** Hides the type parameter in a GADT. *)
let create ?(update=fun _ x -> x) ?id ~export content = {
content;
export;
id;
callbacks = [];
update;
}
let on_change st f =
st.callbacks <- f :: st.callbacks
let handle_request st req =
let cgi = new CamlGI.Cgi.cgi req in
(* update value? *)
try
let x = st.content in
let params = cgi#params in
(* update [x] using the parameters? *)
let y = st.update params x in
let changed = match st.id with
| None -> x != y
| Some id -> id x <> id y
in
(* notify callbacks that we have a new object *)
if changed then
List.iter (fun f -> f y) st.callbacks;
(* now print [y] *)
(* TODO: add a head, declaration, etc. *)
let html = st.export y in
let final_output = HTML.render html in
(* render output *)
let template = object
method output f = f final_output
end in
cgi#template template
with e ->
let msg = Printf.sprintf "error: %s" (Printexc.to_string e) in
cgi#log msg
end
(** {2 Routing} *)
module Router = struct
type t = {
mutable default : State.wrap;
log : out_channel option;
tbl : (string, State.wrap) Hashtbl.t;
}
let __default =
State.Wrap (State.create ~export:HTML.str "<no default handler>")
let _log router fmt = match router.log with
| None ->
Printf.ifprintf stdout fmt
| Some oc ->
Printf.kfprintf
(fun oc ->
output_char oc '\n';
flush oc)
oc
fmt
let create ?(default=__default) ?log () =
let router = {
default;
log;
tbl = Hashtbl.create 15;
} in
_log router "new router created";
router
let default router default =
router.default <- default
let unregister router name =
Hashtbl.remove router.tbl name
let register ?(weak=false) router name state =
if Hashtbl.mem router.tbl name
then failwith "Router: name already registered"
else begin
Hashtbl.add router.tbl name state;
if weak then match state with
| State.Wrap st ->
Gc.finalise (fun _ -> unregister router name) st.State.content
end
let add_list router l =
List.iter
(fun (name, state) -> register router name state)
l
let to_list router =
Hashtbl.fold
(fun name state acc -> (name,state) :: acc)
router.tbl []
let random_id () =
CamlGI.Cgi.random_sessionid ()
let handle_request router req =
let cgi = new CamlGI.Cgi.cgi req in
let url = cgi#url () in
let st =
try
let last_part_i = String.rindex url '/' in
let last_part = String.sub url (last_part_i+1) (String.length url -last_part_i-1) in
_log router "received request for url /%s" last_part;
Hashtbl.find router.tbl last_part
with Not_found ->
router.default
in
match st with
| State.Wrap st -> State.handle_request st req
end

192
toWeb.mli Normal file
View file

@ -0,0 +1,192 @@
(*
copyright (c) 2013, simon cruanes
all rights reserved.
redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. redistributions in binary
form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with
the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*)
(** {1 Expose the State of a Program to the Web}
We export some values (and associated functions for converting them to
html, and update them) as a FastCGI interface.
This module depends on CamlGI.
*)
(** {2 Some combinators to build HTML documents} *)
module HTML : sig
type t
(** A html document. Encoding is assumed to be UTF8 for now. *)
val str : string -> t
(** Simple string *)
val bprintf : ('a, Buffer.t, unit, string) format4 -> t
(** Use a buffer printer to render a string. Shortcut for {!str} *)
val sprintf : ('a, unit, string) format -> t
(** Use a string printer to render into a string. Shortcut for {!str} *)
val list : t list -> t
(** Build a list of items *)
val url : ?alt:string -> url:string -> descr:string -> t
(** build an URL tag. *)
val img : ?alt:string -> string -> t
(** Link to an image *)
val append : t -> t -> t
(** Concatenation of two documents *)
val concat : t list -> t
(** Concatenation of html documents *)
val h1 : t -> t
val h2 : t -> t
val h3 : t -> t
val h : int -> t -> t
(** Title of level parametrized by the integer *)
val link : rel:string -> url:string -> t
(** Link (for head) *)
val head : t -> t
(** Head part of a document *)
val body : t -> t
(** Body part of a document *)
val html : t -> t
(** The whole document *)
val render : t -> string
(** Print into a string *)
val to_buf : Buffer.t -> t -> unit
(** Print in the buffer *)
val to_chan : out_channel -> t -> unit
(** Print on the channel *)
end
(** {2 Stateful Object on the Web} *)
(** This module defines how to bundle an OCaml value (possibly
stateful) with functions that export it to HTML,
and possibly update it from a CGI request.
*)
module State : sig
type 'a t
(** A value that can be exposed to the web. *)
type wrap = Wrap : 'a t -> wrap
(** Hides the type parameter in a GADT. Useful for {!Router}. *)
val create : ?update:((string*string) list -> 'a -> 'a) ->
?id:('a -> string) ->
export:('a -> HTML.t) ->
'a ->
'a t
(** Create a value that can be exposed to the Web.
@param export function used to print the current state of
the object into HTML (when requested).
@param update optional function that can be used to update
the value, given a query with parameters.
@param id optional function that maps a value to a (unique)
string. Can be used to obtain a unique URL for this value. *)
val on_change : 'a t -> ('a -> unit) -> unit
(** Register a callback that will be called everytime the value
is updated. Physical equality is used to determine whether
the value changed if no [id] function was provided;
otherwise, [id] is used to check whether the old and the new
strings are equal. *)
val handle_request : 'a t -> CamlGI.Cgi.Request.t -> unit
(** Handle the incoming request. It replies to the request by
possibly updating the local state, and
object. *)
end
(** {2 Routing} *)
module Router : sig
type t
(** An URL router. It dispatches incoming requests to registered
{!State.t} values depending on the request's URL. *)
val create : ?default:State.wrap -> ?log:out_channel -> unit -> t
(** New router.
@param log a channel on which to log events (incoming requests)
@param default a default object to expose, for incorrect routes
*)
val default : t -> State.wrap -> unit
(** Set the default handler, for incorrect routes (for which no
object is registered) or for routing the root url *)
val register : ?weak:bool -> t -> string -> State.wrap -> unit
(** Register a state object (see {!State}) under a given path.
Right now routing only dispatches at one level, there is no
tree-like structure, only a flat "directory" of objects.
@param weak (default false) if true, the object will unregister itself
when it's garbage collected. Only works if the type of the wrapped
object is heap allocated.
@raise Failure if the name is already taken
@raise Invalid_argument if [weak] is true and no finalizer can be
registered. *)
val unregister : t -> string -> unit
(** Remove a stateful value *)
val add_list : t -> (string * State.wrap) list -> unit
(** Register several handlers.
@raise Failure if it meets an already registered handler *)
val to_list : t -> (string * State.wrap) list
(** Currently registered objects *)
val random_id : unit -> string
(** Fresh, random ID that can be used for registering temporary objects *)
val handle_request : t -> CamlGI.Cgi.Request.t -> unit
(** Handle the incoming request, by routing to an appropriate
object. *)
end
(** {2 Main interface} *)
val serve_state : ?sockaddr:Unix.sockaddr -> 'a State.t -> unit
(** Serve incoming requests using a single object. *)
val serve_router : ?sockaddr:Unix.sockaddr -> Router.t -> unit
(** Shortcut. It calls {!CamlGI.Cgi.register_script} with a callback
that forwards requests to the given Router. *)