diff options
author | Stéphane Glondu <glondu@debian.org> | 2015-05-04 13:44:55 +0200 |
---|---|---|
committer | Stéphane Glondu <glondu@debian.org> | 2015-05-04 13:44:55 +0200 |
commit | 1907d926ca753f5ce8a238708d419fc716f0d60b (patch) | |
tree | 2ad301f45f71d5bf3438386eb4fd0bcfdf5db066 |
Import ocaml-benchmark_1.3.orig.tar.gz
[dgit import orig ocaml-benchmark_1.3.orig.tar.gz]
-rw-r--r-- | API.odocl | 4 | ||||
-rw-r--r-- | INSTALL.txt | 41 | ||||
-rw-r--r-- | LICENSE.txt | 184 | ||||
-rw-r--r-- | META | 12 | ||||
-rw-r--r-- | Makefile | 55 | ||||
-rw-r--r-- | README.md | 28 | ||||
-rw-r--r-- | _oasis | 136 | ||||
-rw-r--r-- | _tags | 65 | ||||
-rw-r--r-- | benchmark.ml | 615 | ||||
-rw-r--r-- | benchmark.mli | 230 | ||||
-rw-r--r-- | benchmark.mllib | 4 | ||||
-rw-r--r-- | examples/ar_ba.ml | 110 | ||||
-rw-r--r-- | examples/composition.ml | 49 | ||||
-rw-r--r-- | examples/func_record.ml | 39 | ||||
-rw-r--r-- | examples/iter.ml | 45 | ||||
-rw-r--r-- | examples/let_try.ml | 30 | ||||
-rw-r--r-- | examples/loops.ml | 34 | ||||
-rw-r--r-- | examples/match_array.ml | 40 | ||||
-rw-r--r-- | examples/numbers.ml | 51 | ||||
-rw-r--r-- | examples/regexps.ml | 49 | ||||
-rw-r--r-- | examples/try_if.ml | 34 | ||||
-rw-r--r-- | myocamlbuild.ml | 491 | ||||
-rw-r--r-- | setup.ml | 6066 | ||||
-rw-r--r-- | tests/long_run.ml | 16 |
24 files changed, 8428 insertions, 0 deletions
diff --git a/API.odocl b/API.odocl new file mode 100644 index 0000000..271912b --- /dev/null +++ b/API.odocl @@ -0,0 +1,4 @@ +# OASIS_START +# DO NOT EDIT (digest: 74575b23d5305310e904f87eb02ff980) +Benchmark +# OASIS_STOP diff --git a/INSTALL.txt b/INSTALL.txt new file mode 100644 index 0000000..a1decca --- /dev/null +++ b/INSTALL.txt @@ -0,0 +1,41 @@ + + INSTALLATION INSTRUCTIONS +---------------------------------------------------------------------- + +This package uses OASIS to generate its build system. See section OASIS for +full information. + +Dependencies +============ + +In order to compile this package, you will need: +* ocaml for all, doc API +* findlib + +Installing +========== + +1. Run 'ocaml setup.ml -configure' +2. Run 'ocaml setup.ml -build' +3. Run 'ocaml setup.ml -install' + +If the libeary is already present on your computer, replace step 3 +with 'ocaml setup.ml -reinstall'. + +Some examples are compiled at the same time as the library. The +executables are in _build/examples/. + + +Uninstalling +============ + +1. Go to the root of the package +2. Run 'ocaml setup.ml -uninstall' + + +OASIS +===== + +OASIS is a software that helps to write setup.ml using a simple '_oasis' +configuration file. The generated setup only depends on standard OCaml +installation, no additional library is required. diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..087b7d0 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,184 @@ +This Library is distributed under the terms of the GNU Lesser General +Public License version 3 (included below) or, at your option, any +later version. + +As a special exception to the GNU Lesser General Public License, you +may link, statically or dynamically, a "work that uses the Library" +with a publicly distributed version of the Library to produce an +executable file containing portions of the Library, and distribute +that executable file under terms of your choice, without any of the +additional requirements listed in clause 6 of the GNU Lesser General +Public License. By "a publicly distributed version of the Library", +we mean either the unmodified Library as distributed, or a modified +version of the Library that is distributed under the conditions +defined in clause 3 of the GNU Library General Public License. This +exception does not however invalidate any other reasons why the +executable file might be covered by the GNU Lesser General Public +License. + + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. @@ -0,0 +1,12 @@ +# OASIS_START +# DO NOT EDIT (digest: 74c7101d85ba97c754cd5b80df8b4dd1) +version = "1.3" +description = "Benchmark running times of code." +requires = "unix" +archive(byte) = "benchmark.cma" +archive(byte, plugin) = "benchmark.cma" +archive(native) = "benchmark.cmxa" +archive(native, plugin) = "benchmark.cmxs" +exists_if = "benchmark.cma" +# OASIS_STOP + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d040ace --- /dev/null +++ b/Makefile @@ -0,0 +1,55 @@ +PKGNAME = $(shell oasis query name) +PKGVERSION = $(shell oasis query version) +PKG_TARBALL = $(PKGNAME)-$(PKGVERSION).tar.gz + +DISTFILES = INSTALL.txt LICENSE.txt META Makefile README.md \ + benchmark.mllib _tags _oasis API.odocl \ + $(wildcard *.ml) $(wildcard *.mli) $(wildcard examples/*.ml) \ + $(wildcard tests/*.ml) + +WEB = ocaml-benchmark.forge.ocamlcore.org:/home/groups/ocaml-benchmark/htdocs/ + + +.PHONY: all byte native configure doc install uninstall reinstall upload-doc + +all byte native setup.log: configure + ocaml setup.ml -build + +configure: setup.data +setup.data: setup.ml + ocaml $< -configure --enable-examples --enable-tests + +setup.ml: _oasis + oasis setup + +doc install uninstall reinstall: setup.log + ocaml setup.ml -$@ + +upload-doc: doc + scp -C -p -r _build/API.docdir $(WEB) + +# Make a tarball +.PHONY: dist tar +dist tar: $(DISTFILES) + mkdir $(PKGNAME)-$(PKGVERSION) ; \ + cp -a --parents $(DISTFILES) $(PKGNAME)-$(PKGVERSION)/; \ + tar -zcvf $(PKG_TARBALL) $(PKGNAME)-$(PKGVERSION); \ + rm -rf $(PKGNAME)-$(PKGVERSION) + +# Release a Sourceforge tarball and publish the HTML doc +.PHONY: web upload +web: upload-doc + @ if [ -d web ] ; then \ + $(SCP) web/*.html web/*.css web/*.jpg LICENSE.txt $(WEB) \ + && echo "*** Published web site." ; \ + fi + + +.PHONY: clean distclean +clean:: + ocaml setup.ml -clean + $(RM) $(PKG_TARBALL) + +distclean: + ocaml setup.ml -distclean + $(RM) $(wildcard *.ba[0-9] *.bak *~ *.odocl) diff --git a/README.md b/README.md new file mode 100644 index 0000000..3a173e9 --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ + +Benchmark - measure/compare run-time of OCaml functions +======================================================= + + Copyright 2004-present, Christophe Troestler + + Copyright 2002-2003, Doug Bagley + http://www.bagley.org/~doug/ocaml/ + + +Benchmark provides functions to measure and compare the run-time of +functions. It is inspired by the Perl module of the same name. + +See the file INSTALL.txt for compiling and (un)installing. + + +License +------- + +This library is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License version 3 +or later as published by the Free Software Foundation, with the +special exception on linking described in file LICENSE. + +This library is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file +LICENSE.txt for more details. @@ -0,0 +1,136 @@ +# -*-conf-*- +OASISFormat: 0.3 +Name: benchmark +Version: 1.3 +Synopsis: Benchmark running times of code. +Authors: Christophe Troestler <Christophe.Troestler@umons.ac.be> +License: LGPL-3.0 with OCaml linking exception +Description: This module provides a set of tools to measure the running times + of your functions and to easily compare the results. A statistical + test is used to determine whether the results truly differ. +Plugins: META (0.2) +Homepage: http://ocaml-benchmark.forge.ocamlcore.org/ + +Flag examples + Description: Whether to compile the examples. + Default: false + +Library benchmark + Path: . + BuildTools: ocamlbuild + Modules: Benchmark + BuildDepends: unix + +Document API + Title: API reference for Benchmark + Type: OCamlbuild (0.3) + InstallDir: $docdir/api + BuildTools: ocamldoc, ocamlbuild + XOCamlbuildPath: . + XOCamlbuildLibraries: benchmark + +# Examples +Executable ar_ba + Build$: flag(examples) + Path: examples/ + MainIs: ar_ba.ml + BuildTools: ocamlbuild + BuildDepends: benchmark, bigarray + CompiledObject: best + Install: false + +Executable composition + Build$: flag(examples) + Path: examples/ + MainIs: composition.ml + BuildTools: ocamlbuild + BuildDepends: benchmark + CompiledObject: best + Install: false + +Executable iter + Build$: flag(examples) + Path: examples/ + MainIs: iter.ml + BuildTools: ocamlbuild + BuildDepends: benchmark, bigarray + CompiledObject: best + Install: false + +Executable let_try + Build$: flag(examples) + Path: examples/ + MainIs: let_try.ml + BuildTools: ocamlbuild + BuildDepends: benchmark, bigarray + CompiledObject: best + Install: false + +Executable loops + Build$: flag(examples) + Path: examples/ + MainIs: loops.ml + BuildTools: ocamlbuild + BuildDepends: benchmark + CompiledObject: best + Install: false + +Executable match_array + Build$: flag(examples) + Path: examples/ + MainIs: match_array.ml + BuildTools: ocamlbuild + BuildDepends: benchmark + CompiledObject: best + Install: false + +Executable numbers + Build$: flag(examples) + Path: examples/ + MainIs: numbers.ml + BuildTools: ocamlbuild + BuildDepends: benchmark + CompiledObject: best + Install: false + +Executable regexps + Build$: flag(examples) + Path: examples/ + MainIs: regexps.ml + BuildTools: ocamlbuild + BuildDepends: benchmark, str, pcre + CompiledObject: best + Install: false + +Executable try_if + Build$: flag(examples) + Path: examples/ + MainIs: try_if.ml + BuildTools: ocamlbuild + BuildDepends: benchmark, bigarray + CompiledObject: best + Install: false + +Executable func_record + Build$: flag(examples) + Path: examples/ + MainIs: func_record.ml + BuildTools: ocamlbuild + BuildDepends: benchmark + CompiledObject: best + Install: false + +# Tests +Executable long_run + Build$: flag(tests) + Path: tests + MainIs: long_run.ml + BuildTools: ocamlbuild + BuildDepends: benchmark, unix + CompiledObject: best + Install: false + +SourceRepository trunk + Type: svn + Location: svn://scm.ocamlcore.org/svn/ocaml-benchmark/trunk + Browser: https://forge.ocamlcore.org/scm/browser.php?group_id=197 @@ -0,0 +1,65 @@ +# OASIS_START +# DO NOT EDIT (digest: afd393a5850380fa4cf24d59c39177a8) +# 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 benchmark +"benchmark.cmxs": use_benchmark +<*.ml{,i}>: pkg_unix +# Executable ar_ba +<examples/ar_ba.{native,byte}>: use_benchmark +<examples/ar_ba.{native,byte}>: pkg_unix +<examples/ar_ba.{native,byte}>: pkg_bigarray +# Executable composition +<examples/composition.{native,byte}>: use_benchmark +<examples/composition.{native,byte}>: pkg_unix +# Executable iter +<examples/iter.{native,byte}>: use_benchmark +<examples/iter.{native,byte}>: pkg_unix +<examples/iter.{native,byte}>: pkg_bigarray +# Executable let_try +<examples/let_try.{native,byte}>: use_benchmark +<examples/let_try.{native,byte}>: pkg_unix +<examples/let_try.{native,byte}>: pkg_bigarray +# Executable loops +<examples/loops.{native,byte}>: use_benchmark +<examples/loops.{native,byte}>: pkg_unix +# Executable match_array +<examples/match_array.{native,byte}>: use_benchmark +<examples/match_array.{native,byte}>: pkg_unix +# Executable numbers +<examples/numbers.{native,byte}>: use_benchmark +<examples/numbers.{native,byte}>: pkg_unix +# Executable regexps +<examples/regexps.{native,byte}>: use_benchmark +<examples/regexps.{native,byte}>: pkg_unix +<examples/regexps.{native,byte}>: pkg_str +<examples/regexps.{native,byte}>: pkg_pcre +<examples/*.ml{,i}>: pkg_str +<examples/*.ml{,i}>: pkg_pcre +# Executable try_if +<examples/try_if.{native,byte}>: use_benchmark +<examples/try_if.{native,byte}>: pkg_unix +<examples/try_if.{native,byte}>: pkg_bigarray +<examples/*.ml{,i}>: pkg_bigarray +# Executable func_record +<examples/func_record.{native,byte}>: use_benchmark +<examples/func_record.{native,byte}>: pkg_unix +<examples/*.ml{,i}>: use_benchmark +<examples/*.ml{,i}>: pkg_unix +# Executable long_run +<tests/long_run.{native,byte}>: use_benchmark +<tests/long_run.{native,byte}>: pkg_unix +<tests/*.ml{,i}>: use_benchmark +<tests/*.ml{,i}>: pkg_unix +# OASIS_STOP diff --git a/benchmark.ml b/benchmark.ml new file mode 100644 index 0000000..c8c956f --- /dev/null +++ b/benchmark.ml @@ -0,0 +1,615 @@ +(* File: benchmark.ml + For comparing runtime of functions + ********************************************************************* + + Copyright 2004-present, Troestler Christophe + Christophe.Troestler(at)umh.ac.be + + Copyright 2002-2003, Doug Bagley + http://www.bagley.org/~doug/ocaml/ + Initially based on the Perl module Benchmark.pm by Jarkko Hietaniemi + and Tim Bunce + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + version 3 as published by the Free Software Foundation, with the + special exception on linking described in file LICENSE. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file + LICENSE.txt for more details. +*) + +open Printf + +type t = { + wall : float; + utime : float; + stime : float; + cutime : float; + cstime : float; + iters : Int64.t; + (* As of version 0.8, one had to change [iter] from [int] because, + as machines run faster, a number of iterations ~ 2^29 is no + longer enough (2^29 is the largest > 0 power of 2 that [int] can + hold on a 32 bits platform. *) +} + +type style = No_child | No_parent | All | Auto | Nil + +let null_t = + { wall = 0.; utime = 0.; stime = 0.; cutime = 0.; cstime = 0.; iters = 0L } + +let make n = + let tms = Unix.times() in + { wall = Unix.gettimeofday(); + utime = tms.Unix.tms_utime; stime = tms.Unix.tms_stime; + cutime = tms.Unix.tms_cutime; cstime = tms.Unix.tms_cstime; + iters = n } + +let add a b = + { wall = a.wall +. b.wall; utime = a.utime +. b.utime; + stime = a.stime +. b.stime; cutime = a.cutime +. b.cutime; + cstime = a.cstime +. b.cstime; iters = Int64.add a.iters b.iters } + +let sub a b = + { wall = a.wall -. b.wall; utime = a.utime -. b.utime; + stime = a.stime -. b.stime; cutime = a.cutime -. b.cutime; + cstime = a.cstime -. b.cstime; iters = Int64.sub a.iters b.iters } + +(* It may happen that, because of slight variations, the running time + of a fast running test is less than the running time of the null + loop. Returning a negative result is obviously ridiculous, thus + one returns 0. *) +let ( -- ) a b = if (a:float) > b then a -. b else 0. +let pos_sub a b = + { wall = a.wall -- b.wall; utime = a.utime -- b.utime; + stime = a.stime -- b.stime; cutime = a.cutime -- b.cutime; + cstime = a.cstime -- b.cstime; iters = Int64.sub a.iters b.iters } + + +let cpu_process b = b.utime +. b.stime +let cpu_childs b = b.cutime +. b.cstime +let cpu_all b = b.utime +. b.stime +. b.cutime +. b.cstime + +(* Return a formatted representation of benchmark structure according + to [style]. Default values for presentation parameters are set + here. *) +let to_string ?(style=Auto) ?(fwidth=5) ?(fdigits=2) b = + let pt = cpu_process b + and ct = cpu_childs b in + let style = + if style = Auto then if ct > 1e-10 then All else No_child else style in + let iter_info t = + if b.iters > 0L && t > 0.0 then + sprintf " @ %*.*f/s (n=%Ld)" fwidth fdigits + (Int64.to_float b.iters /. t) b.iters + else "" in + let f x = sprintf "%*.*f" fwidth fdigits x in + match style with + | All -> + sprintf "%s WALL (%s usr %s sys + %s cusr %s csys = %s CPU)%s" + (f b.wall) (f b.utime) (f b.stime) (f b.cutime) (f b.cstime) + (f(pt +. ct)) (iter_info pt) + | No_child -> + sprintf "%s WALL (%s usr + %s sys = %s CPU)%s" + (f b.wall) (f b.utime) (f b.stime) (f pt) (iter_info pt) + | No_parent -> + sprintf "%s WALL (%s cusr + %s csys = %s CPU)%s" + (f b.wall) (f b.cutime) (f b.cstime) (f ct) (iter_info ct) + | Nil -> "" + | Auto -> assert false + + +(* Returns a string in minutes-seconds of a time [t >= 0] given in + seconds. *) +let rec string_of_time t = + if t = 0 || t = 1 then string_of_int t ^ "s" + else if t < 60 then string_of_int t ^ "s" + else if t < 120 then "1m " ^ string_of_time(t - 60) + else string_of_int(t / 60) ^ "m " ^ string_of_time(t mod 60) + +(* The time [t >= 0] is rounded to the nearest integer: *) +let string_of_time t = string_of_time(truncate(t +. 0.5)) + + +type samples = (string * t list) list + +let by_name (s1, _) (s2, _) = compare (s1:string) s2 + +let merge (l1:samples) (l2:samples) = + (* [do_merge] assumes [l1] and [l2] are sorted. *) + let rec do_merge acc l1 l2 = + match l1, l2 with + | _, [] -> acc @ l1 + | [], _ -> acc @ l2 + | ((n1, t1) as d1) :: tl1, ((n2, t2) as d2) :: tl2 -> + let sgn = compare n1 n2 in + if sgn = 0 then do_merge ((n1, t1 @ t2) :: acc) tl1 tl2 + else if sgn < 0 then do_merge (d1 :: acc) tl1 l2 + else do_merge (d2 :: acc) l1 tl2 in + do_merge [] (List.sort by_name l1) (List.sort by_name l2) + + +let max_iter = Int64.add (Int64.of_int max_int) 1L + (* even if [int] is 63 bits, [(max_iter:Int64.t) > 0] *) + +(* [runloop n_iters n f x] returns the elapsed time of running [n >= + 0L] times [f] with the argument [x]. The structure returned + declare [n_iter] iterations. *) +let runloop n_iters n f x = + let n' = Int64.div n max_iter in + if n' >= max_iter then + invalid_arg "Benchmark.runloop: number of iterations too large"; + let n1 = Int64.to_int n' + and n0 = Int64.to_int(Int64.rem n max_iter) in + let t0 = ref (make 0L) in + let tbase = !t0.utime in + (* Wait for user timer to tick. This makes the error range more + like -0.01, +0. If we don't wait, then it's more like -0.01, + +0.01. *) + while tbase = (!t0).utime do t0 := make 0L done; + (* Loop over function we are timing [n] times (looping on int64 + quantities takes too long, this is why we use composite loops). *) + for i = 1 to n1 do + for j = 0 to max_int do ignore(f x) done; (* [max_iter] runs *) + done; + for i = 1 to n0 do ignore(f x) done; + let t1 = make n_iters in + pos_sub t1 !t0 + +(* time a null-loop; no iter count *) +let null_loop n = runloop 0L n ignore () + +(* Run function [f] count times, return time taken (all times + garanteed to be [>= 0.]) *) +let timeit n f x = + let bn = null_loop n in + let bm = runloop n n f x in + pos_sub bm bn (* time of function minus null-loop *), + bn.wall +. bm.wall (* how much the used had to wait *) + + +type printer = { + print_indent : string -> unit; (* prefix, flushes *) + print : string -> unit; (* No prefix but flushes *) +} + +(* [print_run ff bm] prints the list of timings [bm] according to the + style defined by the optional parameters. *) +let print_run out ?(min_count=4L) ?(min_cpu=0.4) ~style ?fwidth ?fdigits b = + out.print_indent(to_string ~style ?fwidth ?fdigits b ^ "\n"); + if b.iters < min_count || cpu_all b < min_cpu + || (b.wall < 1. && b.iters < 1000L) + then out.print_indent "(warning: too few iterations for a reliable count)\n" + + +let latency n out ?min_count ?min_cpu ~style ?fwidth ?fdigits + ~repeat name f x = + let rec loop nrep acc = + if nrep < 1 then acc + else ( + Gc.compact(); (* Reclaim memory to avoid undue GC during the test. *) + let bm, _ = timeit n f x in + print_run out ?min_count ?min_cpu ~style ?fwidth ?fdigits bm; + loop (nrep - 1) (bm :: acc) + ) in + loop repeat [] + + +(* Read the code from bottom to top: [min_iter] determines the minimal + number of iterations to have a significant timing, then + [estimate_niter] estimate by linear interpolation the number of + iter to run [> tmin] and then the test is performed. *) +let throughput tmin out ?min_count ?min_cpu ~style ?fwidth ?fdigits + ~repeat name f x = + (* Run [f] for [niter] times and complete with >= [nmin] iterations + (estimated by linear interpolation) to run >= [tmin]. *) + let rec run_test nmin niter bm_init total_wall = + let bm, wall = timeit niter f x in + let bm = add bm_init bm in + let tn = cpu_process bm in + let total_wall = total_wall +. wall in + if tn >= tmin then ( + print_run out ?min_count ?min_cpu ~style ?fwidth ?fdigits bm; + bm, total_wall + ) + else + (* FIXME *) + let n = Int64.of_float((tmin /. tn -. 1.) *. Int64.to_float bm.iters) in + run_test nmin (max nmin n) bm total_wall in + (* Repeat the test [nrep] times and return the list of results. *) + let rec repeat_test nrep acc nmin niter wall_estim = + if nrep < 1 then acc else ( + Gc.compact(); (* Reclaim memory to avoid undue GC during the test. *) + let bm, wall = run_test nmin niter null_t 0. in + let wall_estim = + if wall > wall_estim +. 60. then ( + out.print_indent("(Estimated time for subsequent runs: " + ^ (string_of_time wall) ^ ")\n"); + wall + ) + else wall_estim in + repeat_test (nrep - 1) (bm :: acc) nmin niter wall_estim + ) in + (* Estimate number of iter > [nmin] to have a running time >= + [tmin]. The initial estimate is [n] running [tn] secs. Linear + estimates bear a 5% fudge to improve the overall responsiveness. *) + let tpra = 0.1 *. tmin (* Target/time practice *) in + let rec estimate_niter nmin n tn wall = + if tn >= tpra then + (* FIXME: *) + (* lin estim *) + let niter = Int64.of_float(Int64.to_float n *. (1.05 *. tmin /. tn)) in + let wall_estim = wall *. (1.05 *. tmin /. tn) in + if wall_estim >= 60. then + out.print_indent("(Estimated time for each run: " + ^ (string_of_time wall_estim) ^ ")\n"); + repeat_test repeat [] nmin (max nmin niter) wall_estim + else + (* lin estim *) + let new_n = Int64.of_float(Int64.to_float n *. 1.05 *. tpra /. tn) in + let new_bn, new_wall = timeit new_n f x in + let new_tn = cpu_process new_bn in + let n = (* make sure we make progress *) + if new_tn > 1.2 *. tn then new_n + else Int64.of_float(1.1 *. Int64.to_float n +. 1.) (* FIXME *) in + estimate_niter nmin n new_tn new_wall in + (* Determine the minimum number of iterations to run >= 0.1 sec + (whatever [tmin]). Inform the user if it takes too long. *) + let rec min_iter n ~takes_long:previous_took_long total_wall = + if n <= 0L then + failwith "throughput: number of iterations too large for Int64.t storage"; + let bm, wall = timeit n f x in + let tn = cpu_process bm in + let total_wall = total_wall +. wall in + if tn < 0.1 then ( + let takes_long = total_wall >= 30. in + if takes_long then ( + if total_wall >= 120. then ( + out.print " canceled)\n"; + failwith(sprintf "Benchmark.throughputN: wall time is %g while \ + CPU time is %g. Do you use \"sleep\"?" total_wall tn) + ) + else if previous_took_long then out.print "." else + out.print_indent("(Determining how many runs to perform, \ + please be patient..."); + ); + let twice_n = Int64.shift_left n 1 in + min_iter twice_n ~takes_long total_wall + ) + else ( + if previous_took_long then out.print ")\n"; + if tn < tmin then estimate_niter n n tn wall (* tn > 0.1 *) + else ( (* minimal [n] good for [tmin], use the above measurement + for the first run. *) + print_run out ?min_count ?min_cpu ~style ?fwidth ?fdigits bm; + repeat_test (repeat - 1) [bm] n n wall + ) + ) in + min_iter 1L ~takes_long:false 0. + + +(* Make a print function that prefixes each output except the first + one by [nspace] spaces. *) +let make_printer nspace = + let first = ref true in + let prefix = String.make nspace ' ' in + let print s = print_string s; flush stdout in + let print_indent s = + if !first then first := false else print_string prefix; + print s in + { print_indent = print_indent; print = print; } + +let null_printer = { print_indent = (fun _ -> ()); print = (fun _ -> ()) } + + +(* Generic interface for performing measurments on a list of functions *) +let testN ~test default_f_name ?min_count ?min_cpu ~style + ?fwidth ?fdigits ~repeat funs = + let length_name = + List.fold_left (fun m (n,_,_) -> max m (String.length n)) 0 funs in + let result_of (name, f, x) = + printf "%*s: %!" length_name (if name = "" then default_f_name else name); + let out = if style = Nil then null_printer + else make_printer (length_name + 2) in + let bm = test out ?min_count ?min_cpu ~style ?fwidth ?fdigits + ~repeat name f x in + (name, bm) in + List.map result_of funs + +let rec string_of_names funs = + String.concat ", " (List.map (fun (a,_,_) -> sprintf "%S" a) funs) + + +let latencyN ?min_cpu ?(style=Auto) ?fwidth ?fdigits ?(repeat=1) n funs = + if n < 4L then invalid_arg "Benchmark.latencyN: n < 4"; + if style <> Nil then ( + printf "Latencies for %Ld iterations of %s%s:\n%!" n + (string_of_names funs) + (if repeat > 1 then sprintf " (%i runs)" repeat else ""); + ); + testN ~test:(latency n) (sprintf "[run %Ld times]" n) + ?min_cpu ~style ?fwidth ?fdigits ~repeat funs + +let latency1 ?min_cpu ?style ?fwidth ?fdigits ?repeat n ?(name="") f x = + if n < 4L then invalid_arg "Benchmark.latency1"; + latencyN ?min_cpu ?style ?fwidth ?fdigits ?repeat n [(name, f, x)] + + +let throughputN ?min_count ?(style=Auto) ?fwidth ?fdigits ?(repeat=1) n funs = + if n <= 0 then invalid_arg "Benchmark.throughputN: n <= 0"; + let tmin = float n in + if style <> Nil then ( + printf "Throughputs for %s%s running%s for at least %g CPU second%s:\n%!" + (string_of_names funs) + (if List.length funs > 1 then " each" else "") + (if repeat > 1 then sprintf " %i times" repeat else "") + tmin (if n > 1 then "s" else ""); + ); + testN ~test:(throughput tmin) (sprintf "[run > %3.1g secs]" tmin) + ?min_count ~style ?fwidth ?fdigits ~repeat funs + +let throughput1 ?min_count ?style ?fwidth ?fdigits ?repeat n ?(name="") f x = + if n <= 0 then invalid_arg "Benchmark.throughput1: n <= 0"; + throughputN ?min_count ?style ?fwidth ?fdigits ?repeat n [(name, f, x)] + + +(* Statistical tests and comparison table + ***********************************************************************) + +(* Utility functions *) +let list_mapi f = + let rec loop i = function + | [] -> [] + | a::l -> let r = f i a in r :: loop (i + 1) l in + loop 0 + +let list_iteri f = + let rec loop i = function + | [] -> () + | a::l -> let () = f i a in loop (i + 1) l in + loop 0 + +let is_nan x = (classify_float x = FP_nan) + + +(* [log_gamma x] computes the logarithm of the Gamma function at [x] + using Lanczos method. It is assumed [x > 0.]. + + See e.g. http://home.att.net/~numericana/answer/info/godfrey.htm *) +let log_gamma = + let c = [| 1.000000000000000174663; + 5716.400188274341379136; + -14815.30426768413909044; + 14291.49277657478554025; + -6348.160217641458813289; + 1301.608286058321874105; + -108.1767053514369634679; + 2.605696505611755827729; + -0.7423452510201416151527e-2; + 0.5384136432509564062961e-7; + -0.4023533141268236372067e-8 |] in + let c_last = Array.length c - 1 in + let g = float(c_last - 1) in + let sqrt2pi = sqrt(8. *. atan 1.) in + let rec sum i den s = + if i > 0 then sum (i - 1) (den -. 1.) (s +. c.(i) /. den) + else c.(0) +. s in + fun x -> + assert(x > 0.); + let xg = x +. g in + let xg_5 = xg -. 0.5 in + log(sqrt2pi *. sum c_last xg 0.) +. (x -. 0.5) *. log xg_5 -. xg_5 + +(* Beta function. It is assumed [a > 0. && b > 0.]. *) +let beta a b = + assert(a > 0. && b > 0.); + exp(log_gamma a +. log_gamma b -. log_gamma(a +. b)) + +(* [betai x a b] returns the value of the incomplete Beta function + I_x(a,b). It is evaluated through the continued fraction expansion + (see e.g. Numerical Recipies, 6.4): + + x^a (1-x)^b [ 1 d1 d2 ] + I_x(a,b) = ----------- [ -- -- -- ... ] + a B(a,b) [ 1+ 1+ 1+ ] + + where B(a,b) is the beta function and + + m (b-m) x - (a + m)(a + b + m) x + d_2m = -------------------- d_(2m+1) = ---------------------- + (a + 2m - 1)(a + 2m) (a + 2m)(a + 2m + 1) + + The modified Lentz's method is used for the continued fraction (see + NR, section 5.2) in routine [betai_cf]. +*) +let max_tiny x = max 1e-30 x (* to avoid null divisors *) + +let betai_cf_eps = epsilon_float + +let betai_cf x a b = + let apb = a +. b + and ap1 = a +. 1. + and am1 = a -. 1. in + let rec lentz m c d f = + let m2 = 2. *. m in + (* Even rec step d_2m *) + let cf_d2m = m *. (b -. m) *. x /. ((am1 +. m2) *. (a +. m2)) in + let d = 1. /. max_tiny(1. +. cf_d2m *. d) + and c = max_tiny(1. +. cf_d2m /. c) in + let f = f *. d *. c in + (* Odd rec step d_2m+1 *) + let cf_d2m1 = -. (a +. m) *. (apb +. m) *. x + /. ((a +. m2) *. (ap1 +. m2)) in + let d = 1. /. max_tiny(1. +. cf_d2m1 *. d) + and c = max_tiny(1. +. cf_d2m1 /. c) in + let delta = c *. d in + let f = f *. delta in + if abs_float(delta -. 1.) < betai_cf_eps then f + else lentz (m +. 1.) c d f in + (* Initialize Lentz's method with C2=1, D2 (step 2) *) + let d2 = 1. /. max_tiny(1. -. apb *. x /. ap1) in + lentz 1. 1. d2 d2 + +let betai x a b = + assert(a > 0. && b > 0.); + if x < 0. || x > 1. then invalid_arg "betai"; + if x = 0. then 0. + else if x = 1. then 1. + else + let m = exp(log_gamma(a +. b) -. log_gamma a -. log_gamma b + +. a *. log x +. b *. log(1. -. x)) in + if x < (a +. 1.) /. (a +. b +. 2.) + then m *. betai_cf x a b /. a + else 1. -. m *. betai_cf (1. -. x) b a /. b + +(* [cpl_student_t t nu] compute the "complement" of the Student's + distribution: 1 - A(t|nu). It is used to compute the significance + of probabilistic tests. *) +let cpl_student_t t nu = + betai (nu /. (nu +. t *. t)) (0.5 *. nu) 0.5 + + +(* [comp_rates (name, bm)] computes the number, average and standard + deviation of rates from the list of timings [bm]. If bm = [x(1); + x(2);...; x(n)], the algorithm is + + m(1) = x(1) m(k) = m(k-1) + (x(k) - m(k-1))/k + s(1) = 0 s(k) = s(k-1) + (x(k) - m(k-1))(x(k) - m(k)) + + One proves by recurrence that + + m(k) = sum(x(i) : 1 <= i <= k) / k + s(k) = sum(x(i)**2 : 1 <= i <= k) - k m(k)**2 + = sum( (x(i) - m(k))**2 : 1 <= i <= k) + + Cf. Knuth, Seminumerical algorithms. *) +let comp_rates cpu (name, bm) = + let rec loop n m s = function + | [] -> (name, n, m, s) + | b :: tl -> + let rate = Int64.to_float b.iters /. cpu b in + let n' = n + 1 in + let m' = m +. (rate -. m) /. (float n') in + let s' = s +. (rate -. m) *. (rate -. m') in + loop n' m' s' tl in + match bm with + | [] -> (name, 0, nan, 0.) (* NaN used for no-data *) + | b :: tl -> loop 1 (Int64.to_float b.iters /. (cpu b +. 1e-15)) 0. tl + +(* Compare rates *) +let by_rates (_,_,r1,_) (_,_,r2,_) = compare (r1:float) r2 + +(* Check whether two rates are significantly different. With a small + [significance], a [true] returned value means that the rates are + significantly different. [n1] is the number of repetitions of the + test1, [r1] is its mean rate and [s1] its standard deviation. + [n2], [r2] and [s2] are similar for the test2. *) +let different_rates significance n1 r1 s1 n2 r2 s2 = + assert(n1 > 0 && n2 > 0); + if n1 = 1 && n2 = 1 then true (* no info about distribution, assume + they really are. *) + else + let df = float(n1 + n2 - 2) (* >= 1. *) + and n1 = float n1 + and n2 = float n2 in + let sD = sqrt((s1 +. s2) /. df *. (1. /. n1 +. 1. /. n2)) in + let t = (r1 -. r2) /. sD in + cpl_student_t t df <= significance + + +(* [string_of_rate display_as_rate confidence n r s] *) +let string_of_rate display_as_rate = + let per_sec = if display_as_rate then "/s" else "" in + fun confidence n r s -> + (* Assume Gaussian distribution *) + let sigma = sqrt(s/. float n) in + let err = confidence *. sigma (* FIXME *) in + let a, err = + if display_as_rate then r, err else + let n = 1. /. r in (n, n *. n *. err) (* Taylor of order 1 of 1/r *) in + let p prec = + if sigma < 1e-15 then (sprintf " %0.*f%s" prec a per_sec, "") + else (sprintf " %0.*f+-" prec a, sprintf "%.*f%s" prec err per_sec) in + if a >= 100. then p 0 + else if a >= 10. then p 1 + else if a >= 1. then p 2 + else if a >= 0.1 then p 3 + else if sigma < 1e-15 then (sprintf " %g%s" a per_sec, "") + else (sprintf " %g+-" a, sprintf "%g%s" err per_sec) + + +(* print results of a bench_many run *) +(* results = [(name, bm); (name, bm); (name, bm); ...] *) +let tabulate ?(no_parent=false) ?(confidence=0.95) results = + if confidence < 0. || confidence > 1. then + invalid_arg "Benchmark.tabulate: confidence < 0. or > 1."; + let len = List.length results in + if len = 0 then invalid_arg "Benchmark.tabulate: empty list of results"; + (* Compute (name, rate, sigma) for all results and sort them by rates *) + let cpu = if no_parent then cpu_childs else cpu_process in + let rates = List.sort by_rates (List.map (comp_rates cpu) results) in + (* Decide whether to display by rates or seconds *) + let display_as_rate = + let (_,_,r,_) = List.nth rates (len / 2) in r > 1. in + (* + * Compute rows + *) + let top_row = "" :: (if display_as_rate then " Rate" else " s/iter") + :: "" :: (List.map (fun (s,_,_,_) -> " " ^ s) rates) in + (* Initialize the widths of the columns from the top row *) + let col_width = Array.of_list (List.map String.length top_row) in + (* Build all the data [rows], each starting with separation space *) + let string_of_rate = string_of_rate display_as_rate in + let make_row i (row_name, row_n, row_rate, row_s) = + (* Column 0: test name *) + col_width.(0) <- max (String.length row_name) col_width.(0); + (* Column 1 & 2: performance *) + let ra, ra_err = string_of_rate confidence row_n row_rate row_s in + col_width.(1) <- max (String.length ra) col_width.(1); + col_width.(2) <- max (String.length ra_err) col_width.(2); + (* Columns 3..(len + 2): performance ratios *) + let make_col j (col_name, col_n, col_rate, col_s) = + let ratio = + if i = j || is_nan row_rate || is_nan col_rate then "--" else + let p = 100. *. row_rate /. col_rate -. 100. in + if p = 0. || different_rates (1. -. confidence) + row_n row_rate row_s col_n col_rate col_s + then sprintf " %.0f%%" p + else sprintf " [%.0f%%]" p in + col_width.(j + 3) <- max (String.length ratio) col_width.(j + 3); + ratio in + row_name :: ra :: ra_err :: (list_mapi make_col rates) in + let rows = list_mapi make_row rates in + (* + * Equalize column widths in the chart as much as possible without + * exceeding 80 characters. This does not use or affect cols 0, 1 and 2. + *) + (* Build an array of indexes [nth.(0..(len-1))] to access + [col_width.(3..(len+2))] in nondecreasing order. *) + let nth = Array.init len (fun i -> i + 3) in + let by_width i1 i2 = compare col_width.(i1) col_width.(i2) in + Array.sort by_width nth; + let max_width = col_width.(nth.(len - 1)) in + let rec stretcher min_width total = + if min_width < max_width then stretch_min 0 min_width total + and stretch_min i min_width total = (* try to stretch col [i] *) + if total < 80 then begin + if i < len && col_width.(nth.(i)) = min_width then begin + col_width.(nth.(i)) <- col_width.(nth.(i)) + 1; + stretch_min (i + 1) min_width (total + 1) (* stretch next col? *) + end + else stretcher (min_width + 1) total (* try again to stretch *) + end in + stretcher col_width.(nth.(0)) (Array.fold_left ( + ) 0 col_width); + (* + * Display the table + *) + let row_formatter row = + list_iteri (fun i d -> printf "%*s" col_width.(i) d) row; + print_string "\n" in + row_formatter top_row; + List.iter row_formatter rows; + flush stdout diff --git a/benchmark.mli b/benchmark.mli new file mode 100644 index 0000000..514ea20 --- /dev/null +++ b/benchmark.mli @@ -0,0 +1,230 @@ +(* File: benchmark.mli + + Copyright Aug. 2004-present by Troestler Christophe + Christophe.Troestler(at)umons.ac.be + + Copyright 2002-2003, Doug Bagley + http://www.bagley.org/~doug/ocaml/ + Based on the Perl module Benchmark.pm by Jarkko Hietaniemi and Tim Bunce + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + version 3 as published by the Free Software Foundation, with the + special exception on linking described in file LICENSE.txt. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file + LICENSE.txt for more details. +*) + +(** Benchmark running times of code. + + This module implements benchmarking functions for measuring the + run-time of one or many functions using latency (multiple + repetitions) or throughput (repeat until some time period has + passed) tests. + + {b Examples:} + Run the function [f] with input [5000] for [10] iterations and + print the CPU times: + {[ + Benchmark.latency1 10 f 5000 ]} + + Run the tests [foo], [bar] and [baz] three times for at least [8] + seconds each, printing the results of each test, and then print a + cross tabulation of the results: + {[ + open Benchmark + let res = throughputN ~repeat:3 8 [("foo", foo, 1000000); + ("bar", bar, 2000000); + ("baz", baz, 3000000); ] in + print_newline(); + tabulate res ]} + + Time how long it takes to some piece of code: + {[ + let t0 = Benchmark.make 0L in + (* do something here *) + let b = Benchmark.sub (Benchmark.make 0L) t0 in + print_endline "Benchmark results:"; + print_endline (Benchmark.to_string b) ]} + *) + + +(** {2 Timing and samples structures} *) + +(** The information returned by timing tests. *) +type t = { + wall : float; (** Wallclock time (in seconds) *) + utime : float; (** This process User CPU time (in seconds) *) + stime : float; (** This process System CPU time (in seconds) *) + cutime : float; (** Child process User CPU time (in seconds) *) + cstime : float; (** Child process System CPU time (in seconds) *) + iters : Int64.t; (** Number of iterations. *) +} + +(** Style of the output. *) +type style = + | No_child (** Do not print child CPU times *) + | No_parent (** Do not print parent CPU times *) + | All (** Print parent and child CPU times *) + | Auto (** Same as [No_child] unless there is child CPU used *) + | Nil (** Print nothing *) + +val make : Int64.t -> t + (** [Benchmark.make n] create a new {!Benchmark.t} structure with + current time values and [n] iterations. Only the integer part of + [n] is used, the fractional part is ignored. *) + +val add : t -> t -> t + (** [Benchmark.add b1 b2] add {!Benchmark.t} structure [b1] to [b2]. *) + +val sub : t -> t -> t + (** [Benchmark.sub b1 b2] subtract {!Benchmark.t} structure [b2] + from [b1]. *) + +val to_string : ?style:style -> ?fwidth:int -> ?fdigits:int -> t -> string + (** [Benchmark.to_string ?style ?fwidth ?fdigits b] converts the + {!Benchmark.t} structure to a formatted string. + + @param style printing style (default: [Auto]) + @param fwidth number of chars reserved for the numbers (default: [5]) + @param fdigits number of fractional digits of the numbers (default: [2]) + *) + +type samples = (string * t list) list + (** Association list that links the names of the tests to the list + of their timings. *) + +val merge : samples -> samples -> samples + (** [merge l1 l2] merges the two association lists of timings [l1] + and [l2] into a single one, concatenating the timings for the same + names of [l1] and [l2]. *) + + +(** {2 Timing functions} *) + +val throughputN : + ?min_count:Int64.t -> + ?style:style -> + ?fwidth:int -> + ?fdigits:int -> + ?repeat:int -> + int -> (string * ('a -> 'b) * 'a) list -> samples + (** [Benchmark.throughputN ?min_count ?style ?fwidth ?fdigits t + funs] runs each function in list [funs] for at least [t > 0] + seconds. The list [funs] has the structure: [[(name1, f1, x1); + (name2, f2, x2); ...]], where [name1] is the name to label the + first test, [f1] is the function to run, and [x1] is its + input,... If [~style] is not [Nil], then the results are + printed. Returns the resulting list which can be passed to + {!Benchmark.tabulate} if you want a comparison table. + + REMARK that [t] is the running time of the functions, not of the + repetition loop. Thus a very fast running function will need + lots of repetitions to make a difference of [t] seconds to the + empty loop. In this case, the running time of the loop will + dominate the whole process which can therefore take much longer + than [t] seconds. If you are only interested in the {i + relative} times of fast functions and not in their real running + times, we recommend you wrap each of them in a loop. + + @param min_count a warning will be printed if the number of runs is + less than [min_count]. This is a first defense against + meaningless results. (default: [4L]) + @param style printing style (default: [Auto]) + @param fwidth number of chars reserved for the numbers (default: [5]) + @param fdigits number of fractional digits of the numbers (default: [2]) + + @param repeat number of times each function running time is measured. + The default is [1] to be compatible with the former + version of this library but it is highly recommended + to set it to a higher number to enable confidence + statistics to be performed by {!Benchmark.tabulate}. + *) + +val throughput1 : + ?min_count:Int64.t -> + ?style:style -> + ?fwidth:int -> + ?fdigits:int -> + ?repeat:int -> + int -> ?name:string -> ('a -> 'b) -> 'a -> samples + (** [Benchmark.throughput1 ?min_count ?style ?fwidth ?fdigits t ?name f x] + runs one function [f] with input [x] for at least [t] seconds, and + returns the result, which is also printed unless [~style] is + [Nil]. See {!Benchmark.throughputN} for more information. *) + +val latencyN : + ?min_cpu:float -> + ?style:style -> + ?fwidth:int -> + ?fdigits:int -> + ?repeat:int -> + Int64.t -> (string * ('a -> 'b) * 'a) list -> samples + (** [Benchmark.latencyN ?min_cpu ?style ?fwidth ?fdigits n funs] + runs each function in list [funs] for [n] iterations. [n] must be + at least 4. The list [funs] has the structure: [[(name1, f1, x1); + (name2, f2, x2); ...]], where [name1] is the name to label the + first test, [f1] is the function to run, and [x1] is its input,... + If style is not [Nil], then the results are printed. Returns the + results list, which can be passed to {!Benchmark.tabulate} if you + want to print a comparison table. + + @raise Invalid_argument if [n < 4L]. + @param min_cpu a warning will be printed if the total CPU time is + less than [min_cpu]. This is a first defense against + meaningless results (default: [0.4]). + @param style printing style (default: [Auto]). + @param fwidth number of chars reserved for the numbers (default: [5]). + @param fdigits number of fractional digits of the numbers (default: [2]). + + @param repeat number of times each function running time is measured. + The default is [1] to be compatible with the former + version of this library but it is highly recommended + to set it to a higher number to enable confidence + statistics to be performed by {!Benchmark.tabulate}. + *) + +val latency1 : + ?min_cpu:float -> + ?style:style -> + ?fwidth:int -> + ?fdigits:int -> + ?repeat:int -> + Int64.t -> ?name:string -> ('a -> 'b) -> 'a -> samples + (** [Benchmark.latency1 ?min_cpu ?style ?fwidth ?fdigits n ?name f x] + runs the function [f] with input [x] for [n] iterations, and + returns the results, which are also printed unless [~style] is + [Nil]. See {!Benchmark.latencyN} for more information. *) + + +val tabulate : ?no_parent:bool -> ?confidence:float -> samples -> unit + (** [Benchmark.tablulate results] prints a comparison table for a + list of [results] obtained by {!Benchmark.latencyN} or + {!Benchmark.throughputN} with each function compared to all the + others. The table is of the type + +{[ Rate name1 name2 ... OR s/iter name1 name2 ... + name1 #/s -- r12 name1 # -- r12 + name2 #/s r21 -- name2 # r21 -- + ... ... ]} + + where name1, name2,... are the labels of the tests sorted from + slowest to fastest and rij says how much namei is faster (or + slower if < 0) than namej (technically it is equal to (ri - rj) + expressed in percents of rj where ri and rj are the rates of namei + and namej respectively). + + If several results are associated to a given name, they are used + to compute a Student's statistic to check whether the rates are + significantly different. If ri and rj are not believed to be + different, rij will be printed between brackets. + + @param no_parent if [true], only take in account the times of the + children (default: [false]). + + @param confidence is used to determine the confidence interval for + the Student's test. (default: [0.95]). *) + diff --git a/benchmark.mllib b/benchmark.mllib new file mode 100644 index 0000000..271912b --- /dev/null +++ b/benchmark.mllib @@ -0,0 +1,4 @@ +# OASIS_START +# DO NOT EDIT (digest: 74575b23d5305310e904f87eb02ff980) +Benchmark +# OASIS_STOP diff --git a/examples/ar_ba.ml b/examples/ar_ba.ml new file mode 100644 index 0000000..e992827 --- /dev/null +++ b/examples/ar_ba.ml @@ -0,0 +1,110 @@ +(* Compare bigarray and standard float array access times. *) + +open Bigarray + +let n = 10_000 +let m = 1000 + +(* Bigarrays + ***********************************************************************) +type vec = (float, float64_elt, c_layout) Array1.t + +let a = Array1.create float64 c_layout n +let () = Array1.fill a 1. + +let ba (a: vec) = + let s = ref 0. in + for i = 0 to n-1 do s := !s +. a.{i} done + +let ba_u (a: vec) = + let s = ref 0. in + for i = 0 to n-1 do s := !s +. Array1.unsafe_get a i done + +let ba_cl () = + let s = ref 0. in + for i = 0 to n-1 do s := !s +. a.{i} done + +let ba_gen a = + let s = ref 0. in + for i = 0 to n-1 do s := !s +. a.{i} done + +let ba_alloc () = + let a = Array1.create float64 c_layout n in + Array1.fill a 1.; + let s = ref 0. in + for j = 1 to m do + for i = 0 to n-1 do s := !s +. a.{i} done + done + +let set_ba (a: vec) = for i = 0 to n-1 do a.{i} <- 1. done + +let set_ba_alloc () = + let a = Array1.create float64 c_layout n in + for j = 1 to m do + for i = 0 to n-1 do a.{i} <- 3. done + done + +(* Arrays + ***********************************************************************) +let b = Array.make n 1. + +let arr (b: float array) = + let s = ref 0. in + for i = 0 to n-1 do s := !s +. b.(i) done + +let arr_u (b: float array) = + let s = ref 0. in + for i = 0 to n - 1 do s := !s +. Array.unsafe_get b (i) done + +let arr_cl () = + let s = ref 0. in + for i = 0 to n-1 do s := !s +. b.(i) done + +let arr_alloc () = + let b = Array.make n 1. in + let s = ref 0. in + for j = 1 to m do + for i = 0 to n-1 do s := !s +. b.(i) done + done + +let set_arr (b: float array) = for i = 0 to n-1 do b.(i) <- 1. done + +let set_arr_alloc () = + let a = Array.create n 0. in + for j = 1 to m do + for i = 0 to n-1 do a.(i) <- 3. done + done + +(* Lists + ***********************************************************************) +let c = Array.to_list b + +let list c = ignore(List.fold_left ( +. ) 0. c) + + +open Benchmark + +let () = + let res = throughputN ~repeat:3 3 + [("ba", (fun () -> ba a), ()); + ("ba_u", (fun () -> ba_u a), ()); + ("ba_cl", ba_cl, ()); + ("ba_gen", (fun () -> ba_gen a), ()); + ("set_ba", (fun () -> set_ba a), ()); + ("arr", (fun () -> arr b), ()); + ("arr_u", (fun () -> arr_u b), ()); + ("arr_cl", arr_cl, ()); + ("list", (fun () -> list c), ()); + ("set_arr", (fun () -> set_arr b), ()); + ] in + print_endline "Sum of elements or set all elements to 3.:"; + tabulate res; + + let res = throughputN ~repeat:3 3 + [("ba", ba_alloc, ()); + ("set_ba", set_ba_alloc, ()); + ("arr", arr_alloc, ()); + ("set_arr", set_arr_alloc, ()); + ] in + print_endline "With allocation:"; + tabulate res; diff --git a/examples/composition.ml b/examples/composition.ml new file mode 100644 index 0000000..bd1fdcd --- /dev/null +++ b/examples/composition.ml @@ -0,0 +1,49 @@ +(* Tries to show the profile cost of composing small functions. *) + +(* Small functions: permutations of [0 .. n-1] *) + +let n = 100000 +let rotate r = fun k -> (k + r) mod n +let reverse i j = fun k -> if i <= k && k <= j then j + i - k else k +let splice l i j = fun k -> + if k < j then if k < i then k else k + l + 1 + else if k <= j + l then k - j + i + else let k' = k - l - 1 in if k' < i then k' else k + + +open Benchmark +open Printf + +let ncomp = 400 (* Number of compositions *) + +let make_perms = + (* Create a random list of transformations *) + Random.self_init(); + let rec random_perm ((p_f, p_v) as acc) i = + if i <= 0 then acc else + let c = Random.int 3 in + (* New function *) + let p = + if c = 0 then rotate (Random.int n) + else if c = 1 then reverse (Random.int n) (Random.int n) + else (* c = 2 *) splice (Random.int n) (Random.int n) (Random.int n) in + (* Corresponding array transformer *) + let p_vec w v = + for i = 0 to Array.length v - 1 do w.(i) <- p v.(i) done in + random_perm (p :: p_f, p_vec :: p_v) (i - 1) in + random_perm ([], []) + +let () = + let ncomp = 300 in + let p_f, p_v = make_perms ncomp in + let v = Array.init n (fun k -> k) in + let do_f () = + let f = List.fold_left (fun f f0 -> (fun k -> f0(f k))) (fun k -> k) p_f in + Array.map f v + and do_v () = + snd(List.fold_left (fun (w,v) f -> f w v; (v,w)) (Array.make n 0, v) p_v) + in + + let res = throughputN ~repeat:3 5 [("fun", do_f, ()); + ("vec", do_v, ()) ] in + tabulate res diff --git a/examples/func_record.ml b/examples/func_record.ml new file mode 100644 index 0000000..11c1ab3 --- /dev/null +++ b/examples/func_record.ml @@ -0,0 +1,39 @@ +module F(E : sig + val f : float -> float + val g : float -> float +end) = +struct + let h x = 1. +. E.f x +. E.g x +end + +module A = F(struct let f x = x +. 1. let g x = 2. *. x end) + + +type env = { f : float -> float; g : float -> float } + +let h_rec e x = 1. +. e.f x +. e.g x + + +let h_fun f g x = 1. +. f x +. g x + +let f x = x +. 1. +let g x = 2. *. x + +let h x = 1. +. f x +. g x + + +open Benchmark + +let () = + let res = throughputN ~repeat:3 3 + [("functor", (fun () -> A.h 1.), ()); + ("record", (fun () -> h_rec { f = f; g = g } 1.), ()); + ("fun arg", (fun () -> h_fun f g 1.), ()); + ("no arg", (fun () -> h 1.), ()); + ] in + print_endline "Functor vesus records vesus passing as arg:"; + tabulate res + +(* Local Variables: *) +(* compile-command: "make -k -C .." *) +(* End: *) diff --git a/examples/iter.ml b/examples/iter.ml new file mode 100644 index 0000000..769671c --- /dev/null +++ b/examples/iter.ml @@ -0,0 +1,45 @@ +open Bigarray + +let n = 1_000 + +(* Bigarrays *) +type vec = (float, float64_elt, c_layout) Array1.t +let a = Array1.create float64 c_layout n +let () = Array1.fill a 1. + +let ba f (x: vec) = + for i = 0 to n - 1 do f x.{i} done + +let ba_unsafe f (x: vec) = + for i = 0 to n - 1 do f (Array1.unsafe_get x i) done + +(* Arrays *) +let b = Array.make n 1. + +let arr f (x: float array) = + for i = 0 to n-1 do f x.(i) done + +let arr_unsafe f (x: float array) = + for i = 0 to n-1 do f (Array.unsafe_get x i) done + +(* Lists *) +let c = Array.to_list b + + +open Benchmark + + +let () = + (* Simulate a simple side effect *) + let z = ref 0. in + let f x = z := x in + + let res = throughputN ~repeat:3 3 + [("ba", (fun () -> ba f a), ()); + ("ba_unsafe", (fun () -> ba_unsafe f a), ()); + ("arr", (fun () -> arr f b), ()); + ("arr_unsafe", (fun () -> arr_unsafe f b), ()); + ("list", (fun () -> List.iter f c), ()) + ] in + print_endline "Iterating a function with a simple side effect:"; + tabulate res diff --git a/examples/let_try.ml b/examples/let_try.ml new file mode 100644 index 0000000..11100d9 --- /dev/null +++ b/examples/let_try.ml @@ -0,0 +1,30 @@ +(* Compare two possible implementations of let try x = ... with ... *) + +let k x = if x >= 0 then x else failwith "x < 0" + +let f a = + let sgn s x = + let y = (try Some(k x) with _ -> None) in + match y with + | None -> s + | Some y -> s + y in + ignore(Array.fold_left sgn 0 a) + +let g a = + let sgn s x = + (try + let y = k x in + (fun () -> s + y) + with _ -> + (fun () -> s) + )() in + ignore(Array.fold_left sgn 0 a) + + +open Benchmark + +let () = + let a = Array.init 1000 (fun i -> Random.int 2 - 1) in + let res = throughputN ~repeat:5 1 [("Some", f, a); + ("()->", g, a); ] in + tabulate res diff --git a/examples/loops.ml b/examples/loops.ml new file mode 100644 index 0000000..c0710e7 --- /dev/null +++ b/examples/loops.ml @@ -0,0 +1,34 @@ +open Benchmark + +(* Test for the speed of recusion w.r.t. imperative loops to access + arrays fo floats. *) + +let rec_loop (a : float array) = + let rec loop i = + if i < Array.length a then begin + a.(i) <- a.(i) +. 1.; + loop (i + 1) + end in + loop 0 + +let rec_loop2 (a : float array) = + let len = Array.length a in + let rec loop i = + if i < len then begin + a.(i) <- a.(i) +. 1.; + loop (i + 1) + end in + loop 0 + +let for_loop (a : float array) = + for i = 0 to Array.length a - 1 do + a.(i) <- a.(i) +. 1. + done + +let () = + let a = Array.make 100 1. in + let res = throughputN ~repeat:5 1 + [("rec", rec_loop, a); + ("rec2", rec_loop2, a); + ("for", for_loop, a); ] in + tabulate res diff --git a/examples/match_array.ml b/examples/match_array.ml new file mode 100644 index 0000000..e13a7b4 --- /dev/null +++ b/examples/match_array.ml @@ -0,0 +1,40 @@ +(* This is a typical problem where the functions are so fast (on a + 2Ghz machine) that it takes way too long to get results. Thus a + wrapping in a loop is done. *) + +let n = 100 + +let string_of_month1 = + let month = [| "Jan"; "Feb"; "Mar"; "Apr"; "May"; "Jun"; "Jul"; "Aug"; + "Sep"; "Oct"; "Nov"; "Dec" |] in + fun i -> Array.unsafe_get month i + +let f1 () = + for i = 1 to n do ignore(string_of_month1 7) done + +let string_of_month2 = function + | 0 -> "Jan" + | 1 -> "Feb" + | 2 -> "Mar" + | 3 -> "Apr" + | 4 -> "May" + | 5 -> "Jun" + | 6 -> "Jul" + | 7 -> "Aug" + | 8 -> "Sep" + | 9 -> "Oct" + | 10 -> "Nov" + | 11 -> "Dec" + | _ -> failwith "h" + +let f2 () = + for i = 1 to n do ignore(string_of_month2 7) done + + +open Benchmark + +let () = + let res = throughputN 3 ~repeat:5 [ ("arr", f1, ()); + ("pat", f2, ()); ] in + tabulate res + diff --git a/examples/numbers.ml b/examples/numbers.ml new file mode 100644 index 0000000..38d22b9 --- /dev/null +++ b/examples/numbers.ml @@ -0,0 +1,51 @@ +open Printf +open Benchmark + +(* Test the speed of addition for native ints (unboxed), and + Int32/Int64 (which are both boxed). + + The output looks something like numbers.out +*) + +let f_int n = + let rec loop i sum = + if i < n then loop (i + 1) (sum + 1) else sum in + loop 0 0 + +let f_int32 n = + let rec loop i sum = + if i < n then loop (i + 1) (Int32.add sum Int32.one) else sum in + Int32.to_int (loop 0 Int32.zero) + +let f_int64 n = + let rec loop i sum = + if i < n then loop (i + 1) (Int64.add sum Int64.one) else sum in + Int64.to_int (loop 0 Int64.zero) + +let () = + (* print out the results of the f_* functions to doublecheck that + they work as we intend. *) + printf "f_int 666 = %d\n" (f_int 666); + printf "f_int32 666 = %d\n" (f_int32 666); + printf "f_int64 666 = %d\n" (f_int64 666); + print_newline (); + + (* let's exercise the *1 functions: *) + let _ = latency1 ~name:"int-1-lat" 1000L f_int 10000 in + let _ = throughput1 ~name:"int-1-thru" 5 f_int 10000 in + print_newline (); + + (* now let's exercise the *N functions: *) + let res = throughputN ~repeat:5 10 + [("int", f_int, 10000); + ("int32", f_int32, 10000); + ("int64", f_int64, 10000); ] in + print_newline (); + tabulate res; + + print_newline (); + let res = latencyN 2000L [("int", f_int, 10000); + ("int32", f_int32, 10000); + ("int64", f_int64, 10000); ] in + print_newline (); + tabulate res diff --git a/examples/regexps.ml b/examples/regexps.ml new file mode 100644 index 0000000..8773daf --- /dev/null +++ b/examples/regexps.ml @@ -0,0 +1,49 @@ +open Printf +open Benchmark + +(* Test the speed of standard regular expressions vs. Pcre using a + simple regexp with captures. + + The output looks something like regexps.out +*) + +(* Create a chunk of data to search. + It's full of "near hits", strings of "012345678" + with a string on the end we are searching for: "0123456789" *) +let bigdata = + let size = 500000 in + let buf = Buffer.create size in + for i = 1 to size/10 - 1 do Buffer.add_string buf "012345678 " done; + Buffer.add_string buf "0123456789"; + Buffer.contents buf + +let pcre_re = Pcre.regexp "(012345678) (0123456789)" +let str_re = Str.regexp "\\(012345678\\) \\(0123456789\\)" + +let pcre_match dat = + let group = Pcre.extract ~rex:pcre_re dat in + (group.(1), group.(2)) + +let str_match dat = + let _pos = Str.search_forward str_re dat 0 in + (Str.matched_group 1 dat, Str.matched_group 2 dat) + +let () = + (* Print out the results of the functions to doublecheck that they + work as we intend. *) + let (a, b) = pcre_match bigdata in printf "Pcre matches: %s %s\n" a b; + let (a, b) = str_match bigdata in printf "Str matches: %s %s\n" a b; + print_newline (); + + let res = throughputN ~repeat:5 5 + [("pcre match", pcre_match, bigdata); + ("str match", str_match, bigdata)] in + print_newline(); + tabulate res + +(* print_newline(); *) +(* let res = latencyN ~repeat:5 100 *) +(* [("pcre match", pcre_match, bigdata); *) +(* ("str match", str_match, bigdata)] in *) +(* print_newline(); *) +(* tabulate res *) diff --git a/examples/try_if.ml b/examples/try_if.ml new file mode 100644 index 0000000..5ea18b3 --- /dev/null +++ b/examples/try_if.ml @@ -0,0 +1,34 @@ +open Bigarray + +let n = 100 + +let a = Array1.create float64 fortran_layout n + +(* Base case: no test *) +let f0 () = + for i = 1 to n do + let x = a.{i} in + ignore(x) + done; + ignore(0.) + +let f1 () = + for i = 1 to n+1 do + let x = try a.{i} with _ -> 0. in + ignore(x) + done + +let f2 () = + for i = 1 to n+1 do + let x = if i <= n then a.{i} else 0. in + ignore(x) + done + +open Benchmark + +let () = + let res = throughputN ~repeat:5 3 [("no test", f0, ()); + ("try", f1, ()); + ("if", f2, ()) ] in + print_endline "Bigarray bound checking:"; + tabulate res diff --git a/myocamlbuild.ml b/myocamlbuild.ml new file mode 100644 index 0000000..a646114 --- /dev/null +++ b/myocamlbuild.ml @@ -0,0 +1,491 @@ +(* OASIS_START *) +(* DO NOT EDIT (digest: 5eab8ed07dfb4303dc64c4c33706fe5e) *) +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 = [("benchmark", [])]; + lib_c = []; + flags = []; + includes = []; + } + ;; + +let dispatch_default = MyOCamlbuildBase.dispatch_default package_default;; + +# 490 "myocamlbuild.ml" +(* OASIS_STOP *) +Ocamlbuild_plugin.dispatch dispatch_default;; diff --git a/setup.ml b/setup.ml new file mode 100644 index 0000000..e18726f --- /dev/null +++ b/setup.ml @@ -0,0 +1,6066 @@ +let () = + try Topdirs.dir_directory (Sys.getenv "OCAML_TOPLEVEL_PATH") + with Not_found -> ();; + +(* OASIS_START *) +(* DO NOT EDIT (digest: 1cc62ef077a105a2b3cdd44d970685cc) *) +(* + Regenerated by OASIS v0.3.0 + Visit http://oasis.forge.ocamlcore.org for more information and + documentation about functions used in this file. +*) +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 OASISContext = struct +(* # 21 "src/oasis/OASISContext.ml" *) + + open OASISGettext + + type level = + [ `Debug + | `Info + | `Warning + | `Error] + + type t = + { + quiet: bool; + info: bool; + debug: bool; + ignore_plugins: bool; + ignore_unknown_fields: bool; + printf: level -> string -> unit; + } + + let printf lvl str = + let beg = + match lvl with + | `Error -> s_ "E: " + | `Warning -> s_ "W: " + | `Info -> s_ "I: " + | `Debug -> s_ "D: " + in + prerr_endline (beg^str) + + let default = + ref + { + quiet = false; + info = false; + debug = false; + ignore_plugins = false; + ignore_unknown_fields = false; + printf = printf; + } + + let quiet = + {!default with quiet = true} + + + let args () = + ["-quiet", + Arg.Unit (fun () -> default := {!default with quiet = true}), + (s_ " Run quietly"); + + "-info", + Arg.Unit (fun () -> default := {!default with info = true}), + (s_ " Display information message"); + + + "-debug", + Arg.Unit (fun () -> default := {!default with debug = true}), + (s_ " Output debug message")] +end + +module OASISString = struct +(* # 1 "src/oasis/OASISString.ml" *) + + + + (** Various string utilities. + + Mostly inspired by extlib and batteries ExtString and BatString libraries. + + @author Sylvain Le Gall + *) + + let nsplitf str f = + if str = "" then + [] + else + let buf = Buffer.create 13 in + let lst = ref [] in + let push () = + lst := Buffer.contents buf :: !lst; + Buffer.clear buf + in + let str_len = String.length str in + for i = 0 to str_len - 1 do + if f str.[i] then + push () + else + Buffer.add_char buf str.[i] + done; + push (); + List.rev !lst + + (** [nsplit c s] Split the string [s] at char [c]. It doesn't include the + separator. + *) + let nsplit str c = + nsplitf str ((=) c) + + let find ~what ?(offset=0) str = + let what_idx = ref 0 in + let str_idx = ref offset in + while !str_idx < String.length str && + !what_idx < String.length what do + if str.[!str_idx] = what.[!what_idx] then + incr what_idx + else + what_idx := 0; + incr str_idx + done; + if !what_idx <> String.length what then + raise Not_found + else + !str_idx - !what_idx + + let sub_start str len = + let str_len = String.length str in + if len >= str_len then + "" + else + String.sub str len (str_len - len) + + let sub_end ?(offset=0) str len = + let str_len = String.length str in + if len >= str_len then + "" + else + String.sub str 0 (str_len - len) + + let starts_with ~what ?(offset=0) str = + let what_idx = ref 0 in + let str_idx = ref offset in + let ok = ref true in + while !ok && + !str_idx < String.length str && + !what_idx < String.length what do + if str.[!str_idx] = what.[!what_idx] then + incr what_idx + else + ok := false; + incr str_idx + done; + if !what_idx = String.length what then + true + else + false + + let strip_starts_with ~what str = + if starts_with ~what str then + sub_start str (String.length what) + else + raise Not_found + + let ends_with ~what ?(offset=0) str = + let what_idx = ref ((String.length what) - 1) in + let str_idx = ref ((String.length str) - 1) in + let ok = ref true in + while !ok && + offset <= !str_idx && + 0 <= !what_idx do + if str.[!str_idx] = what.[!what_idx] then + decr what_idx + else + ok := false; + decr str_idx + done; + if !what_idx = -1 then + true + else + false + + let strip_ends_with ~what str = + if ends_with ~what str then + sub_end str (String.length what) + else + raise Not_found + + let replace_chars f s = + let buf = String.make (String.length s) 'X' in + for i = 0 to String.length s - 1 do + buf.[i] <- f s.[i] + done; + buf + +end + +module OASISUtils = struct +(* # 21 "src/oasis/OASISUtils.ml" *) + + open OASISGettext + + module MapString = Map.Make(String) + + let map_string_of_assoc assoc = + List.fold_left + (fun acc (k, v) -> MapString.add k v acc) + MapString.empty + assoc + + module SetString = Set.Make(String) + + let set_string_add_list st lst = + List.fold_left + (fun acc e -> SetString.add e acc) + st + lst + + let set_string_of_list = + set_string_add_list + SetString.empty + + + let compare_csl s1 s2 = + String.compare (String.lowercase s1) (String.lowercase s2) + + module HashStringCsl = + Hashtbl.Make + (struct + type t = string + + let equal s1 s2 = + (String.lowercase s1) = (String.lowercase s2) + + let hash s = + Hashtbl.hash (String.lowercase s) + end) + + let varname_of_string ?(hyphen='_') s = + if String.length s = 0 then + begin + invalid_arg "varname_of_string" + end + else + begin + let buf = + OASISString.replace_chars + (fun c -> + if ('a' <= c && c <= 'z') + || + ('A' <= c && c <= 'Z') + || + ('0' <= c && c <= '9') then + c + else + hyphen) + s; + in + let buf = + (* Start with a _ if digit *) + if '0' <= s.[0] && s.[0] <= '9' then + "_"^buf + else + buf + in + String.lowercase buf + end + + let varname_concat ?(hyphen='_') p s = + let what = String.make 1 hyphen in + let p = + try + OASISString.strip_ends_with ~what p + with Not_found -> + p + in + let s = + try + OASISString.strip_starts_with ~what s + with Not_found -> + s + in + p^what^s + + + let is_varname str = + str = varname_of_string str + + let failwithf fmt = Printf.ksprintf failwith fmt + +end + +module PropList = struct +(* # 21 "src/oasis/PropList.ml" *) + + open OASISGettext + + type name = string + + exception Not_set of name * string option + exception No_printer of name + exception Unknown_field of name * name + + let () = + Printexc.register_printer + (function + | Not_set (nm, Some rsn) -> + Some + (Printf.sprintf (f_ "Field '%s' is not set: %s") nm rsn) + | Not_set (nm, None) -> + Some + (Printf.sprintf (f_ "Field '%s' is not set") nm) + | No_printer nm -> + Some + (Printf.sprintf (f_ "No default printer for value %s") nm) + | Unknown_field (nm, schm) -> + Some + (Printf.sprintf (f_ "Field %s is not defined in schema %s") nm schm) + | _ -> + None) + + module Data = + struct + + type t = + (name, unit -> unit) Hashtbl.t + + let create () = + Hashtbl.create 13 + + let clear t = + Hashtbl.clear t + +(* # 71 "src/oasis/PropList.ml" *) + end + + module Schema = + struct + + type ('ctxt, 'extra) value = + { + get: Data.t -> string; + set: Data.t -> ?context:'ctxt -> string -> unit; + help: (unit -> string) option; + extra: 'extra; + } + + type ('ctxt, 'extra) t = + { + name: name; + fields: (name, ('ctxt, 'extra) value) Hashtbl.t; + order: name Queue.t; + name_norm: string -> string; + } + + let create ?(case_insensitive=false) nm = + { + name = nm; + fields = Hashtbl.create 13; + order = Queue.create (); + name_norm = + (if case_insensitive then + String.lowercase + else + fun s -> s); + } + + let add t nm set get extra help = + let key = + t.name_norm nm + in + + if Hashtbl.mem t.fields key then + failwith + (Printf.sprintf + (f_ "Field '%s' is already defined in schema '%s'") + nm t.name); + Hashtbl.add + t.fields + key + { + set = set; + get = get; + help = help; + extra = extra; + }; + Queue.add nm t.order + + let mem t nm = + Hashtbl.mem t.fields nm + + let find t nm = + try + Hashtbl.find t.fields (t.name_norm nm) + with Not_found -> + raise (Unknown_field (nm, t.name)) + + let get t data nm = + (find t nm).get data + + let set t data nm ?context x = + (find t nm).set + data + ?context + x + + let fold f acc t = + Queue.fold + (fun acc k -> + let v = + find t k + in + f acc k v.extra v.help) + acc + t.order + + let iter f t = + fold + (fun () -> f) + () + t + + let name t = + t.name + end + + module Field = + struct + + type ('ctxt, 'value, 'extra) t = + { + set: Data.t -> ?context:'ctxt -> 'value -> unit; + get: Data.t -> 'value; + sets: Data.t -> ?context:'ctxt -> string -> unit; + gets: Data.t -> string; + help: (unit -> string) option; + extra: 'extra; + } + + let new_id = + let last_id = + ref 0 + in + fun () -> incr last_id; !last_id + + let create ?schema ?name ?parse ?print ?default ?update ?help extra = + (* Default value container *) + let v = + ref None + in + + (* If name is not given, create unique one *) + let nm = + match name with + | Some s -> s + | None -> Printf.sprintf "_anon_%d" (new_id ()) + in + + (* Last chance to get a value: the default *) + let default () = + match default with + | Some d -> d + | None -> raise (Not_set (nm, Some (s_ "no default value"))) + in + + (* Get data *) + let get data = + (* Get value *) + try + (Hashtbl.find data nm) (); + match !v with + | Some x -> x + | None -> default () + with Not_found -> + default () + in + + (* Set data *) + let set data ?context x = + let x = + match update with + | Some f -> + begin + try + f ?context (get data) x + with Not_set _ -> + x + end + | None -> + x + in + Hashtbl.replace + data + nm + (fun () -> v := Some x) + in + + (* Parse string value, if possible *) + let parse = + match parse with + | Some f -> + f + | None -> + fun ?context s -> + failwith + (Printf.sprintf + (f_ "Cannot parse field '%s' when setting value %S") + nm + s) + in + + (* Set data, from string *) + let sets data ?context s = + set ?context data (parse ?context s) + in + + (* Output value as string, if possible *) + let print = + match print with + | Some f -> + f + | None -> + fun _ -> raise (No_printer nm) + in + + (* Get data, as a string *) + let gets data = + print (get data) + in + + begin + match schema with + | Some t -> + Schema.add t nm sets gets extra help + | None -> + () + end; + + { + set = set; + get = get; + sets = sets; + gets = gets; + help = help; + extra = extra; + } + + let fset data t ?context x = + t.set data ?context x + + let fget data t = + t.get data + + let fsets data t ?context s = + t.sets data ?context s + + let fgets data t = + t.gets data + + end + + module FieldRO = + struct + + let create ?schema ?name ?parse ?print ?default ?update ?help extra = + let fld = + Field.create ?schema ?name ?parse ?print ?default ?update ?help extra + in + fun data -> Field.fget data fld + + end +end + +module OASISMessage = struct +(* # 21 "src/oasis/OASISMessage.ml" *) + + + open OASISGettext + open OASISContext + + let generic_message ~ctxt lvl fmt = + let cond = + if ctxt.quiet then + false + else + match lvl with + | `Debug -> ctxt.debug + | `Info -> ctxt.info + | _ -> true + in + Printf.ksprintf + (fun str -> + if cond then + begin + ctxt.printf lvl str + end) + fmt + + let debug ~ctxt fmt = + generic_message ~ctxt `Debug fmt + + let info ~ctxt fmt = + generic_message ~ctxt `Info fmt + + let warning ~ctxt fmt = + generic_message ~ctxt `Warning fmt + + let error ~ctxt fmt = + generic_message ~ctxt `Error fmt + +end + +module OASISVersion = struct +(* # 21 "src/oasis/OASISVersion.ml" *) + + open OASISGettext + + + + type s = string + + type t = string + + type comparator = + | VGreater of t + | VGreaterEqual of t + | VEqual of t + | VLesser of t + | VLesserEqual of t + | VOr of comparator * comparator + | VAnd of comparator * comparator + + + (* Range of allowed characters *) + let is_digit c = + '0' <= c && c <= '9' + + let is_alpha c = + ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') + + let is_special = + function + | '.' | '+' | '-' | '~' -> true + | _ -> false + + let rec version_compare v1 v2 = + if v1 <> "" || v2 <> "" then + begin + (* Compare ascii string, using special meaning for version + * related char + *) + let val_ascii c = + if c = '~' then -1 + else if is_digit c then 0 + else if c = '\000' then 0 + else if is_alpha c then Char.code c + else (Char.code c) + 256 + in + + let len1 = String.length v1 in + let len2 = String.length v2 in + + let p = ref 0 in + + (** Compare ascii part *) + let compare_vascii () = + let cmp = ref 0 in + while !cmp = 0 && + !p < len1 && !p < len2 && + not (is_digit v1.[!p] && is_digit v2.[!p]) do + cmp := (val_ascii v1.[!p]) - (val_ascii v2.[!p]); + incr p + done; + if !cmp = 0 && !p < len1 && !p = len2 then + val_ascii v1.[!p] + else if !cmp = 0 && !p = len1 && !p < len2 then + - (val_ascii v2.[!p]) + else + !cmp + in + + (** Compare digit part *) + let compare_digit () = + let extract_int v p = + let start_p = !p in + while !p < String.length v && is_digit v.[!p] do + incr p + done; + let substr = + String.sub v !p ((String.length v) - !p) + in + let res = + match String.sub v start_p (!p - start_p) with + | "" -> 0 + | s -> int_of_string s + in + res, substr + in + let i1, tl1 = extract_int v1 (ref !p) in + let i2, tl2 = extract_int v2 (ref !p) in + i1 - i2, tl1, tl2 + in + + match compare_vascii () with + | 0 -> + begin + match compare_digit () with + | 0, tl1, tl2 -> + if tl1 <> "" && is_digit tl1.[0] then + 1 + else if tl2 <> "" && is_digit tl2.[0] then + -1 + else + version_compare tl1 tl2 + | n, _, _ -> + n + end + | n -> + n + end + else + begin + 0 + end + + + let version_of_string str = str + + let string_of_version t = t + + let chop t = + try + let pos = + String.rindex t '.' + in + String.sub t 0 pos + with Not_found -> + t + + let rec comparator_apply v op = + match op with + | VGreater cv -> + (version_compare v cv) > 0 + | VGreaterEqual cv -> + (version_compare v cv) >= 0 + | VLesser cv -> + (version_compare v cv) < 0 + | VLesserEqual cv -> + (version_compare v cv) <= 0 + | VEqual cv -> + (version_compare v cv) = 0 + | VOr (op1, op2) -> + (comparator_apply v op1) || (comparator_apply v op2) + | VAnd (op1, op2) -> + (comparator_apply v op1) && (comparator_apply v op2) + + let rec string_of_comparator = + function + | VGreater v -> "> "^(string_of_version v) + | VEqual v -> "= "^(string_of_version v) + | VLesser v -> "< "^(string_of_version v) + | VGreaterEqual v -> ">= "^(string_of_version v) + | VLesserEqual v -> "<= "^(string_of_version v) + | VOr (c1, c2) -> + (string_of_comparator c1)^" || "^(string_of_comparator c2) + | VAnd (c1, c2) -> + (string_of_comparator c1)^" && "^(string_of_comparator c2) + + let rec varname_of_comparator = + let concat p v = + OASISUtils.varname_concat + p + (OASISUtils.varname_of_string + (string_of_version v)) + in + function + | VGreater v -> concat "gt" v + | VLesser v -> concat "lt" v + | VEqual v -> concat "eq" v + | VGreaterEqual v -> concat "ge" v + | VLesserEqual v -> concat "le" v + | VOr (c1, c2) -> + (varname_of_comparator c1)^"_or_"^(varname_of_comparator c2) + | VAnd (c1, c2) -> + (varname_of_comparator c1)^"_and_"^(varname_of_comparator c2) + + let version_0_3_or_after t = + comparator_apply t (VGreaterEqual (string_of_version "0.3")) + +end + +module OASISLicense = struct +(* # 21 "src/oasis/OASISLicense.ml" *) + + (** License for _oasis fields + @author Sylvain Le Gall + *) + + + + type license = string + + type license_exception = string + + type license_version = + | Version of OASISVersion.t + | VersionOrLater of OASISVersion.t + | NoVersion + + + type license_dep_5_unit = + { + license: license; + excption: license_exception option; + version: license_version; + } + + + type license_dep_5 = + | DEP5Unit of license_dep_5_unit + | DEP5Or of license_dep_5 list + | DEP5And of license_dep_5 list + + + type t = + | DEP5License of license_dep_5 + | OtherLicense of string (* URL *) + + +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 + +module OASISTypes = struct +(* # 21 "src/oasis/OASISTypes.ml" *) + + + + + type name = string + type package_name = string + type url = string + type unix_dirname = string + type unix_filename = string + type host_dirname = string + type host_filename = string + type prog = string + type arg = string + type args = string list + type command_line = (prog * arg list) + + type findlib_name = string + type findlib_full = string + + type compiled_object = + | Byte + | Native + | Best + + + type dependency = + | FindlibPackage of findlib_full * OASISVersion.comparator option + | InternalLibrary of name + + + type tool = + | ExternalTool of name + | InternalExecutable of name + + + type vcs = + | Darcs + | Git + | Svn + | Cvs + | Hg + | Bzr + | Arch + | Monotone + | OtherVCS of url + + + type plugin_kind = + [ `Configure + | `Build + | `Doc + | `Test + | `Install + | `Extra + ] + + type plugin_data_purpose = + [ `Configure + | `Build + | `Install + | `Clean + | `Distclean + | `Install + | `Uninstall + | `Test + | `Doc + | `Extra + | `Other of string + ] + + type 'a plugin = 'a * name * OASISVersion.t option + + type all_plugin = plugin_kind plugin + + type plugin_data = (all_plugin * plugin_data_purpose * (unit -> unit)) list + +(* # 102 "src/oasis/OASISTypes.ml" *) + + type 'a conditional = 'a OASISExpr.choices + + type custom = + { + pre_command: (command_line option) conditional; + post_command: (command_line option) conditional; + } + + + type common_section = + { + cs_name: name; + cs_data: PropList.Data.t; + cs_plugin_data: plugin_data; + } + + + type build_section = + { + bs_build: bool conditional; + bs_install: bool conditional; + bs_path: unix_dirname; + bs_compiled_object: compiled_object; + bs_build_depends: dependency list; + bs_build_tools: tool list; + bs_c_sources: unix_filename list; + bs_data_files: (unix_filename * unix_filename option) list; + bs_ccopt: args conditional; + bs_cclib: args conditional; + bs_dlllib: args conditional; + bs_dllpath: args conditional; + bs_byteopt: args conditional; + bs_nativeopt: args conditional; + } + + + type library = + { + lib_modules: string list; + lib_pack: bool; + lib_internal_modules: string list; + lib_findlib_parent: findlib_name option; + lib_findlib_name: findlib_name option; + lib_findlib_containers: findlib_name list; + } + + type executable = + { + exec_custom: bool; + exec_main_is: unix_filename; + } + + type flag = + { + flag_description: string option; + flag_default: bool conditional; + } + + type source_repository = + { + src_repo_type: vcs; + src_repo_location: url; + src_repo_browser: url option; + src_repo_module: string option; + src_repo_branch: string option; + src_repo_tag: string option; + src_repo_subdir: unix_filename option; + } + + type test = + { + test_type: [`Test] plugin; + test_command: command_line conditional; + test_custom: custom; + test_working_directory: unix_filename option; + test_run: bool conditional; + test_tools: tool list; + } + + type doc_format = + | HTML of unix_filename + | DocText + | PDF + | PostScript + | Info of unix_filename + | DVI + | OtherDoc + + + type doc = + { + doc_type: [`Doc] plugin; + doc_custom: custom; + doc_build: bool conditional; + doc_install: bool conditional; + doc_install_dir: unix_filename; + doc_title: string; + doc_authors: string list; + doc_abstract: string option; + doc_format: doc_format; + doc_data_files: (unix_filename * unix_filename option) list; + doc_build_tools: tool list; + } + + type section = + | Library of common_section * build_section * library + | Executable of common_section * build_section * executable + | Flag of common_section * flag + | SrcRepo of common_section * source_repository + | Test of common_section * test + | Doc of common_section * doc + + + type section_kind = + [ `Library | `Executable | `Flag | `SrcRepo | `Test | `Doc ] + + type package = + { + oasis_version: OASISVersion.t; + ocaml_version: OASISVersion.comparator option; + findlib_version: OASISVersion.comparator option; + name: package_name; + version: OASISVersion.t; + license: OASISLicense.t; + license_file: unix_filename option; + copyrights: string list; + maintainers: string list; + authors: string list; + homepage: url option; + synopsis: string; + description: string option; + categories: url list; + + conf_type: [`Configure] plugin; + conf_custom: custom; + + build_type: [`Build] plugin; + build_custom: custom; + + install_type: [`Install] plugin; + install_custom: custom; + uninstall_custom: custom; + + clean_custom: custom; + distclean_custom: custom; + + files_ab: unix_filename list; + sections: section list; + plugins: [`Extra] plugin list; + schema_data: PropList.Data.t; + plugin_data: plugin_data; + } + +end + +module OASISUnixPath = struct +(* # 21 "src/oasis/OASISUnixPath.ml" *) + + type unix_filename = string + type unix_dirname = string + + type host_filename = string + type host_dirname = string + + let current_dir_name = "." + + let parent_dir_name = ".." + + let is_current_dir fn = + fn = current_dir_name || fn = "" + + let concat f1 f2 = + if is_current_dir f1 then + f2 + else + let f1' = + try OASISString.strip_ends_with ~what:"/" f1 with Not_found -> f1 + in + f1'^"/"^f2 + + let make = + function + | hd :: tl -> + List.fold_left + (fun f p -> concat f p) + hd + tl + | [] -> + invalid_arg "OASISUnixPath.make" + + let dirname f = + try + String.sub f 0 (String.rindex f '/') + with Not_found -> + current_dir_name + + let basename f = + try + let pos_start = + (String.rindex f '/') + 1 + in + String.sub f pos_start ((String.length f) - pos_start) + with Not_found -> + f + + let chop_extension f = + try + let last_dot = + String.rindex f '.' + in + let sub = + String.sub f 0 last_dot + in + try + let last_slash = + String.rindex f '/' + in + if last_slash < last_dot then + sub + else + f + with Not_found -> + sub + + with Not_found -> + f + + let capitalize_file f = + let dir = dirname f in + let base = basename f in + concat dir (String.capitalize base) + + let uncapitalize_file f = + let dir = dirname f in + let base = basename f in + concat dir (String.uncapitalize base) + +end + +module OASISHostPath = struct +(* # 21 "src/oasis/OASISHostPath.ml" *) + + + open Filename + + module Unix = OASISUnixPath + + let make = + function + | [] -> + invalid_arg "OASISHostPath.make" + | hd :: tl -> + List.fold_left Filename.concat hd tl + + let of_unix ufn = + if Sys.os_type = "Unix" then + ufn + else + make + (List.map + (fun p -> + if p = Unix.current_dir_name then + current_dir_name + else if p = Unix.parent_dir_name then + parent_dir_name + else + p) + (OASISString.nsplit ufn '/')) + + +end + +module OASISSection = struct +(* # 21 "src/oasis/OASISSection.ml" *) + + open OASISTypes + + let section_kind_common = + function + | Library (cs, _, _) -> + `Library, cs + | Executable (cs, _, _) -> + `Executable, cs + | Flag (cs, _) -> + `Flag, cs + | SrcRepo (cs, _) -> + `SrcRepo, cs + | Test (cs, _) -> + `Test, cs + | Doc (cs, _) -> + `Doc, cs + + let section_common sct = + snd (section_kind_common sct) + + let section_common_set cs = + function + | Library (_, bs, lib) -> Library (cs, bs, lib) + | Executable (_, bs, exec) -> Executable (cs, bs, exec) + | Flag (_, flg) -> Flag (cs, flg) + | SrcRepo (_, src_repo) -> SrcRepo (cs, src_repo) + | Test (_, tst) -> Test (cs, tst) + | Doc (_, doc) -> Doc (cs, doc) + + (** Key used to identify section + *) + let section_id sct = + let k, cs = + section_kind_common sct + in + k, cs.cs_name + + let string_of_section sct = + let k, nm = + section_id sct + in + (match k with + | `Library -> "library" + | `Executable -> "executable" + | `Flag -> "flag" + | `SrcRepo -> "src repository" + | `Test -> "test" + | `Doc -> "doc") + ^" "^nm + + let section_find id scts = + List.find + (fun sct -> id = section_id sct) + scts + + module CSection = + struct + type t = section + + let id = section_id + + let compare t1 t2 = + compare (id t1) (id t2) + + let equal t1 t2 = + (id t1) = (id t2) + + let hash t = + Hashtbl.hash (id t) + end + + module MapSection = Map.Make(CSection) + module SetSection = Set.Make(CSection) + +end + +module OASISBuildSection = struct +(* # 21 "src/oasis/OASISBuildSection.ml" *) + +end + +module OASISExecutable = struct +(* # 21 "src/oasis/OASISExecutable.ml" *) + + open OASISTypes + + let unix_exec_is (cs, bs, exec) is_native ext_dll suffix_program = + let dir = + OASISUnixPath.concat + bs.bs_path + (OASISUnixPath.dirname exec.exec_main_is) + in + let is_native_exec = + match bs.bs_compiled_object with + | Native -> true + | Best -> is_native () + | Byte -> false + in + + OASISUnixPath.concat + dir + (cs.cs_name^(suffix_program ())), + + if not is_native_exec && + not exec.exec_custom && + bs.bs_c_sources <> [] then + Some (dir^"/dll"^cs.cs_name^"_stubs"^(ext_dll ())) + else + None + +end + +module OASISLibrary = struct +(* # 21 "src/oasis/OASISLibrary.ml" *) + + open OASISTypes + open OASISUtils + open OASISGettext + open OASISSection + + type library_name = name + type findlib_part_name = name + type 'a map_of_findlib_part_name = 'a OASISUtils.MapString.t + + exception InternalLibraryNotFound of library_name + exception FindlibPackageNotFound of findlib_name + + type group_t = + | Container of findlib_name * group_t list + | Package of (findlib_name * + common_section * + build_section * + library * + group_t list) + + (* Look for a module file, considering capitalization or not. *) + let find_module source_file_exists (cs, bs, lib) modul = + let possible_base_fn = + List.map + (OASISUnixPath.concat bs.bs_path) + [modul; + OASISUnixPath.uncapitalize_file modul; + OASISUnixPath.capitalize_file modul] + in + (* TODO: we should be able to be able to determine the source for every + * files. Hence we should introduce a Module(source: fn) for the fields + * Modules and InternalModules + *) + List.fold_left + (fun acc base_fn -> + match acc with + | `No_sources _ -> + begin + let file_found = + List.fold_left + (fun acc ext -> + if source_file_exists (base_fn^ext) then + (base_fn^ext) :: acc + else + acc) + [] + [".ml"; ".mli"; ".mll"; ".mly"] + in + match file_found with + | [] -> + acc + | lst -> + `Sources (base_fn, lst) + end + | `Sources _ -> + acc) + (`No_sources possible_base_fn) + possible_base_fn + + let source_unix_files ~ctxt (cs, bs, lib) source_file_exists = + List.fold_left + (fun acc modul -> + match find_module source_file_exists (cs, bs, lib) modul with + | `Sources (base_fn, lst) -> + (base_fn, lst) :: acc + | `No_sources _ -> + OASISMessage.warning + ~ctxt + (f_ "Cannot find source file matching \ + module '%s' in library %s") + modul cs.cs_name; + acc) + [] + (lib.lib_modules @ lib.lib_internal_modules) + + let generated_unix_files + ~ctxt + ~is_native + ~has_native_dynlink + ~ext_lib + ~ext_dll + ~source_file_exists + (cs, bs, lib) = + + let find_modules lst ext = + let find_module modul = + match find_module source_file_exists (cs, bs, lib) modul with + | `Sources (base_fn, _) -> + [base_fn] + | `No_sources lst -> + OASISMessage.warning + ~ctxt + (f_ "Cannot find source file matching \ + module '%s' in library %s") + modul cs.cs_name; + lst + in + List.map + (fun nm -> + List.map + (fun base_fn -> base_fn ^"."^ext) + (find_module nm)) + lst + in + + (* The headers that should be compiled along *) + let headers = + if lib.lib_pack then + [] + else + find_modules + lib.lib_modules + "cmi" + in + + (* The .cmx that be compiled along *) + let cmxs = + let should_be_built = + (not lib.lib_pack) && (* Do not install .cmx packed submodules *) + match bs.bs_compiled_object with + | Native -> true + | Best -> is_native + | Byte -> false + in + if should_be_built then + find_modules + (lib.lib_modules @ lib.lib_internal_modules) + "cmx" + else + [] + in + + let acc_nopath = + [] + in + + (* Compute what libraries should be built *) + let acc_nopath = + (* Add the packed header file if required *) + let add_pack_header acc = + if lib.lib_pack then + [cs.cs_name^".cmi"] :: acc + else + acc + in + let byte acc = + add_pack_header ([cs.cs_name^".cma"] :: acc) + in + let native acc = + let acc = + add_pack_header + (if has_native_dynlink then + [cs.cs_name^".cmxs"] :: acc + else acc) + in + [cs.cs_name^".cmxa"] :: [cs.cs_name^ext_lib] :: acc + in + match bs.bs_compiled_object with + | Native -> + byte (native acc_nopath) + | Best when is_native -> + byte (native acc_nopath) + | Byte | Best -> + byte acc_nopath + in + + (* Add C library to be built *) + let acc_nopath = + if bs.bs_c_sources <> [] then + begin + ["lib"^cs.cs_name^"_stubs"^ext_lib] + :: + ["dll"^cs.cs_name^"_stubs"^ext_dll] + :: + acc_nopath + end + else + acc_nopath + in + + (* All the files generated *) + List.rev_append + (List.rev_map + (List.rev_map + (OASISUnixPath.concat bs.bs_path)) + acc_nopath) + (headers @ cmxs) + + type data = common_section * build_section * library + type tree = + | Node of (data option) * (tree MapString.t) + | Leaf of data + + let findlib_mapping pkg = + (* Map from library name to either full findlib name or parts + parent. *) + let fndlb_parts_of_lib_name = + let fndlb_parts cs lib = + let name = + match lib.lib_findlib_name with + | Some nm -> nm + | None -> cs.cs_name + in + let name = + String.concat "." (lib.lib_findlib_containers @ [name]) + in + name + in + List.fold_left + (fun mp -> + function + | Library (cs, _, lib) -> + begin + let lib_name = cs.cs_name in + let fndlb_parts = fndlb_parts cs lib in + if MapString.mem lib_name mp then + failwithf + (f_ "The library name '%s' is used more than once.") + lib_name; + match lib.lib_findlib_parent with + | Some lib_name_parent -> + MapString.add + lib_name + (`Unsolved (lib_name_parent, fndlb_parts)) + mp + | None -> + MapString.add + lib_name + (`Solved fndlb_parts) + mp + end + + | Executable _ | Test _ | Flag _ | SrcRepo _ | Doc _ -> + mp) + MapString.empty + pkg.sections + in + + (* Solve the above graph to be only library name to full findlib name. *) + let fndlb_name_of_lib_name = + let rec solve visited mp lib_name lib_name_child = + if SetString.mem lib_name visited then + failwithf + (f_ "Library '%s' is involved in a cycle \ + with regard to findlib naming.") + lib_name; + let visited = SetString.add lib_name visited in + try + match MapString.find lib_name mp with + | `Solved fndlb_nm -> + fndlb_nm, mp + | `Unsolved (lib_nm_parent, post_fndlb_nm) -> + let pre_fndlb_nm, mp = + solve visited mp lib_nm_parent lib_name + in + let fndlb_nm = pre_fndlb_nm^"."^post_fndlb_nm in + fndlb_nm, MapString.add lib_name (`Solved fndlb_nm) mp + with Not_found -> + failwithf + (f_ "Library '%s', which is defined as the findlib parent of \ + library '%s', doesn't exist.") + lib_name lib_name_child + in + let mp = + MapString.fold + (fun lib_name status mp -> + match status with + | `Solved _ -> + (* Solved initialy, no need to go further *) + mp + | `Unsolved _ -> + let _, mp = solve SetString.empty mp lib_name "<none>" in + mp) + fndlb_parts_of_lib_name + fndlb_parts_of_lib_name + in + MapString.map + (function + | `Solved fndlb_nm -> fndlb_nm + | `Unsolved _ -> assert false) + mp + in + + (* Convert an internal library name to a findlib name. *) + let findlib_name_of_library_name lib_nm = + try + MapString.find lib_nm fndlb_name_of_lib_name + with Not_found -> + raise (InternalLibraryNotFound lib_nm) + in + + (* Add a library to the tree. + *) + let add sct mp = + let fndlb_fullname = + let cs, _, _ = sct in + let lib_name = cs.cs_name in + findlib_name_of_library_name lib_name + in + let rec add_children nm_lst (children : tree MapString.t) = + match nm_lst with + | (hd :: tl) -> + begin + let node = + try + add_node tl (MapString.find hd children) + with Not_found -> + (* New node *) + new_node tl + in + MapString.add hd node children + end + | [] -> + (* Should not have a nameless library. *) + assert false + and add_node tl node = + if tl = [] then + begin + match node with + | Node (None, children) -> + Node (Some sct, children) + | Leaf (cs', _, _) | Node (Some (cs', _, _), _) -> + (* TODO: allow to merge Package, i.e. + * archive(byte) = "foo.cma foo_init.cmo" + *) + let cs, _, _ = sct in + failwithf + (f_ "Library '%s' and '%s' have the same findlib name '%s'") + cs.cs_name cs'.cs_name fndlb_fullname + end + else + begin + match node with + | Leaf data -> + Node (Some data, add_children tl MapString.empty) + | Node (data_opt, children) -> + Node (data_opt, add_children tl children) + end + and new_node = + function + | [] -> + Leaf sct + | hd :: tl -> + Node (None, MapString.add hd (new_node tl) MapString.empty) + in + add_children (OASISString.nsplit fndlb_fullname '.') mp + in + + let rec group_of_tree mp = + MapString.fold + (fun nm node acc -> + let cur = + match node with + | Node (Some (cs, bs, lib), children) -> + Package (nm, cs, bs, lib, group_of_tree children) + | Node (None, children) -> + Container (nm, group_of_tree children) + | Leaf (cs, bs, lib) -> + Package (nm, cs, bs, lib, []) + in + cur :: acc) + mp [] + in + + let group_mp = + List.fold_left + (fun mp -> + function + | Library (cs, bs, lib) -> + add (cs, bs, lib) mp + | _ -> + mp) + MapString.empty + pkg.sections + in + + let groups = + group_of_tree group_mp + in + + let library_name_of_findlib_name = + Lazy.lazy_from_fun + (fun () -> + (* Revert findlib_name_of_library_name. *) + MapString.fold + (fun k v mp -> MapString.add v k mp) + fndlb_name_of_lib_name + MapString.empty) + in + let library_name_of_findlib_name fndlb_nm = + try + MapString.find fndlb_nm (Lazy.force library_name_of_findlib_name) + with Not_found -> + raise (FindlibPackageNotFound fndlb_nm) + in + + groups, + findlib_name_of_library_name, + library_name_of_findlib_name + + let findlib_of_group = + function + | Container (fndlb_nm, _) + | Package (fndlb_nm, _, _, _, _) -> fndlb_nm + + let root_of_group grp = + let rec root_lib_aux = + (* We do a DFS in the group. *) + function + | Container (_, children) -> + List.fold_left + (fun res grp -> + if res = None then + root_lib_aux grp + else + res) + None + children + | Package (_, cs, bs, lib, _) -> + Some (cs, bs, lib) + in + match root_lib_aux grp with + | Some res -> + res + | None -> + failwithf + (f_ "Unable to determine root library of findlib library '%s'") + (findlib_of_group grp) + +end + +module OASISFlag = struct +(* # 21 "src/oasis/OASISFlag.ml" *) + +end + +module OASISPackage = struct +(* # 21 "src/oasis/OASISPackage.ml" *) + +end + +module OASISSourceRepository = struct +(* # 21 "src/oasis/OASISSourceRepository.ml" *) + +end + +module OASISTest = struct +(* # 21 "src/oasis/OASISTest.ml" *) + +end + +module OASISDocument = struct +(* # 21 "src/oasis/OASISDocument.ml" *) + +end + +module OASISExec = struct +(* # 21 "src/oasis/OASISExec.ml" *) + + open OASISGettext + open OASISUtils + open OASISMessage + + (* TODO: I don't like this quote, it is there because $(rm) foo expands to + * 'rm -f' foo... + *) + let run ~ctxt ?f_exit_code ?(quote=true) cmd args = + let cmd = + if quote then + if Sys.os_type = "Win32" then + if String.contains cmd ' ' then + (* Double the 1st double quote... win32... sigh *) + "\""^(Filename.quote cmd) + else + cmd + else + Filename.quote cmd + else + cmd + in + let cmdline = + String.concat " " (cmd :: args) + in + info ~ctxt (f_ "Running command '%s'") cmdline; + match f_exit_code, Sys.command cmdline with + | None, 0 -> () + | None, i -> + failwithf + (f_ "Command '%s' terminated with error code %d") + cmdline i + | Some f, i -> + f i + + let run_read_output ~ctxt ?f_exit_code cmd args = + let fn = + Filename.temp_file "oasis-" ".txt" + in + try + begin + let () = + run ~ctxt ?f_exit_code cmd (args @ [">"; Filename.quote fn]) + in + let chn = + open_in fn + in + let routput = + ref [] + in + begin + try + while true do + routput := (input_line chn) :: !routput + done + with End_of_file -> + () + end; + close_in chn; + Sys.remove fn; + List.rev !routput + end + with e -> + (try Sys.remove fn with _ -> ()); + raise e + + let run_read_one_line ~ctxt ?f_exit_code cmd args = + match run_read_output ~ctxt ?f_exit_code cmd args with + | [fst] -> + fst + | lst -> + failwithf + (f_ "Command return unexpected output %S") + (String.concat "\n" lst) +end + +module OASISFileUtil = struct +(* # 21 "src/oasis/OASISFileUtil.ml" *) + + open OASISGettext + + let file_exists_case fn = + let dirname = Filename.dirname fn in + let basename = Filename.basename fn in + if Sys.file_exists dirname then + if basename = Filename.current_dir_name then + true + else + List.mem + basename + (Array.to_list (Sys.readdir dirname)) + else + false + + let find_file ?(case_sensitive=true) paths exts = + + (* Cardinal product of two list *) + let ( * ) lst1 lst2 = + List.flatten + (List.map + (fun a -> + List.map + (fun b -> a,b) + lst2) + lst1) + in + + let rec combined_paths lst = + match lst with + | p1 :: p2 :: tl -> + let acc = + (List.map + (fun (a,b) -> Filename.concat a b) + (p1 * p2)) + in + combined_paths (acc :: tl) + | [e] -> + e + | [] -> + [] + in + + let alternatives = + List.map + (fun (p,e) -> + if String.length e > 0 && e.[0] <> '.' then + p ^ "." ^ e + else + p ^ e) + ((combined_paths paths) * exts) + in + List.find + (if case_sensitive then + file_exists_case + else + Sys.file_exists) + alternatives + + let which ~ctxt prg = + let path_sep = + match Sys.os_type with + | "Win32" -> + ';' + | _ -> + ':' + in + let path_lst = OASISString.nsplit (Sys.getenv "PATH") path_sep in + let exec_ext = + match Sys.os_type with + | "Win32" -> + "" :: (OASISString.nsplit (Sys.getenv "PATHEXT") path_sep) + | _ -> + [""] + in + find_file ~case_sensitive:false [path_lst; [prg]] exec_ext + + (**/**) + let rec fix_dir dn = + (* Windows hack because Sys.file_exists "src\\" = false when + * Sys.file_exists "src" = true + *) + let ln = + String.length dn + in + if Sys.os_type = "Win32" && ln > 0 && dn.[ln - 1] = '\\' then + fix_dir (String.sub dn 0 (ln - 1)) + else + dn + + let q = Filename.quote + (**/**) + + let cp ~ctxt ?(recurse=false) src tgt = + if recurse then + match Sys.os_type with + | "Win32" -> + OASISExec.run ~ctxt + "xcopy" [q src; q tgt; "/E"] + | _ -> + OASISExec.run ~ctxt + "cp" ["-r"; q src; q tgt] + else + OASISExec.run ~ctxt + (match Sys.os_type with + | "Win32" -> "copy" + | _ -> "cp") + [q src; q tgt] + + let mkdir ~ctxt tgt = + OASISExec.run ~ctxt + (match Sys.os_type with + | "Win32" -> "md" + | _ -> "mkdir") + [q tgt] + + let rec mkdir_parent ~ctxt f tgt = + let tgt = + fix_dir tgt + in + if Sys.file_exists tgt then + begin + if not (Sys.is_directory tgt) then + OASISUtils.failwithf + (f_ "Cannot create directory '%s', a file of the same name already \ + exists") + tgt + end + else + begin + mkdir_parent ~ctxt f (Filename.dirname tgt); + if not (Sys.file_exists tgt) then + begin + f tgt; + mkdir ~ctxt tgt + end + end + + let rmdir ~ctxt tgt = + if Sys.readdir tgt = [||] then + begin + match Sys.os_type with + | "Win32" -> + OASISExec.run ~ctxt "rd" [q tgt] + | _ -> + OASISExec.run ~ctxt "rm" ["-r"; q tgt] + end + + let glob ~ctxt fn = + let basename = + Filename.basename fn + in + if String.length basename >= 2 && + basename.[0] = '*' && + basename.[1] = '.' then + begin + let ext_len = + (String.length basename) - 2 + in + let ext = + String.sub basename 2 ext_len + in + let dirname = + Filename.dirname fn + in + Array.fold_left + (fun acc fn -> + try + let fn_ext = + String.sub + fn + ((String.length fn) - ext_len) + ext_len + in + if fn_ext = ext then + (Filename.concat dirname fn) :: acc + else + acc + with Invalid_argument _ -> + acc) + [] + (Sys.readdir dirname) + end + else + begin + if file_exists_case fn then + [fn] + else + [] + end +end + + +# 2142 "setup.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 + + +# 2240 "setup.ml" +module BaseContext = struct +(* # 21 "src/base/BaseContext.ml" *) + + open OASISContext + + let args = args + + let default = default + +end + +module BaseMessage = struct +(* # 21 "src/base/BaseMessage.ml" *) + + (** Message to user, overrid for Base + @author Sylvain Le Gall + *) + open OASISMessage + open BaseContext + + let debug fmt = debug ~ctxt:!default fmt + + let info fmt = info ~ctxt:!default fmt + + let warning fmt = warning ~ctxt:!default fmt + + let error fmt = error ~ctxt:!default fmt + +end + +module BaseEnv = struct +(* # 21 "src/base/BaseEnv.ml" *) + + open OASISGettext + open OASISUtils + open PropList + + module MapString = BaseEnvLight.MapString + + type origin_t = + | ODefault + | OGetEnv + | OFileLoad + | OCommandLine + + type cli_handle_t = + | CLINone + | CLIAuto + | CLIWith + | CLIEnable + | CLIUser of (Arg.key * Arg.spec * Arg.doc) list + + type definition_t = + { + hide: bool; + dump: bool; + cli: cli_handle_t; + arg_help: string option; + group: string option; + } + + let schema = + Schema.create "environment" + + (* Environment data *) + let env = + Data.create () + + (* Environment data from file *) + let env_from_file = + ref MapString.empty + + (* Lexer for var *) + let var_lxr = + Genlex.make_lexer [] + + let rec var_expand str = + let buff = + Buffer.create ((String.length str) * 2) + in + Buffer.add_substitute + buff + (fun var -> + try + (* TODO: this is a quick hack to allow calling Test.Command + * without defining executable name really. I.e. if there is + * an exec Executable toto, then $(toto) should be replace + * by its real name. It is however useful to have this function + * for other variable that depend on the host and should be + * written better than that. + *) + let st = + var_lxr (Stream.of_string var) + in + match Stream.npeek 3 st with + | [Genlex.Ident "utoh"; Genlex.Ident nm] -> + OASISHostPath.of_unix (var_get nm) + | [Genlex.Ident "utoh"; Genlex.String s] -> + OASISHostPath.of_unix s + | [Genlex.Ident "ocaml_escaped"; Genlex.Ident nm] -> + String.escaped (var_get nm) + | [Genlex.Ident "ocaml_escaped"; Genlex.String s] -> + String.escaped s + | [Genlex.Ident nm] -> + var_get nm + | _ -> + failwithf + (f_ "Unknown expression '%s' in variable expansion of %s.") + var + str + with + | Unknown_field (_, _) -> + failwithf + (f_ "No variable %s defined when trying to expand %S.") + var + str + | Stream.Error e -> + failwithf + (f_ "Syntax error when parsing '%s' when trying to \ + expand %S: %s") + var + str + e) + str; + Buffer.contents buff + + and var_get name = + let vl = + try + Schema.get schema env name + with Unknown_field _ as e -> + begin + try + MapString.find name !env_from_file + with Not_found -> + raise e + end + in + var_expand vl + + let var_choose ?printer ?name lst = + OASISExpr.choose + ?printer + ?name + var_get + lst + + let var_protect vl = + let buff = + Buffer.create (String.length vl) + in + String.iter + (function + | '$' -> Buffer.add_string buff "\\$" + | c -> Buffer.add_char buff c) + vl; + Buffer.contents buff + + let var_define + ?(hide=false) + ?(dump=true) + ?short_desc + ?(cli=CLINone) + ?arg_help + ?group + name (* TODO: type constraint on the fact that name must be a valid OCaml + id *) + dflt = + + let default = + [ + OFileLoad, (fun () -> MapString.find name !env_from_file); + ODefault, dflt; + OGetEnv, (fun () -> Sys.getenv name); + ] + in + + let extra = + { + hide = hide; + dump = dump; + cli = cli; + arg_help = arg_help; + group = group; + } + in + + (* Try to find a value that can be defined + *) + let var_get_low lst = + let errors, res = + List.fold_left + (fun (errors, res) (o, v) -> + if res = None then + begin + try + errors, Some (v ()) + with + | Not_found -> + errors, res + | Failure rsn -> + (rsn :: errors), res + | e -> + (Printexc.to_string e) :: errors, res + end + else + errors, res) + ([], None) + (List.sort + (fun (o1, _) (o2, _) -> + Pervasives.compare o2 o1) + lst) + in + match res, errors with + | Some v, _ -> + v + | None, [] -> + raise (Not_set (name, None)) + | None, lst -> + raise (Not_set (name, Some (String.concat (s_ ", ") lst))) + in + + let help = + match short_desc with + | Some fs -> Some fs + | None -> None + in + + let var_get_lst = + FieldRO.create + ~schema + ~name + ~parse:(fun ?(context=ODefault) s -> [context, fun () -> s]) + ~print:var_get_low + ~default + ~update:(fun ?context x old_x -> x @ old_x) + ?help + extra + in + + fun () -> + var_expand (var_get_low (var_get_lst env)) + + let var_redefine + ?hide + ?dump + ?short_desc + ?cli + ?arg_help + ?group + name + dflt = + if Schema.mem schema name then + begin + (* TODO: look suspsicious, we want to memorize dflt not dflt () *) + Schema.set schema env ~context:ODefault name (dflt ()); + fun () -> var_get name + end + else + begin + var_define + ?hide + ?dump + ?short_desc + ?cli + ?arg_help + ?group + name + dflt + end + + let var_ignore (e : unit -> string) = + () + + let print_hidden = + var_define + ~hide:true + ~dump:false + ~cli:CLIAuto + ~arg_help:"Print even non-printable variable. (debug)" + "print_hidden" + (fun () -> "false") + + let var_all () = + List.rev + (Schema.fold + (fun acc nm def _ -> + if not def.hide || bool_of_string (print_hidden ()) then + nm :: acc + else + acc) + [] + schema) + + let default_filename = + BaseEnvLight.default_filename + + let load ?allow_empty ?filename () = + env_from_file := BaseEnvLight.load ?allow_empty ?filename () + + let unload () = + env_from_file := MapString.empty; + Data.clear env + + let dump ?(filename=default_filename) () = + let chn = + open_out_bin filename + in + let output nm value = + Printf.fprintf chn "%s=%S\n" nm value + in + let mp_todo = + (* Dump data from schema *) + Schema.fold + (fun mp_todo nm def _ -> + if def.dump then + begin + try + let value = + Schema.get + schema + env + nm + in + output nm value + with Not_set _ -> + () + end; + MapString.remove nm mp_todo) + !env_from_file + schema + in + (* Dump data defined outside of schema *) + MapString.iter output mp_todo; + + (* End of the dump *) + close_out chn + + let print () = + let printable_vars = + Schema.fold + (fun acc nm def short_descr_opt -> + if not def.hide || bool_of_string (print_hidden ()) then + begin + try + let value = + Schema.get + schema + env + nm + in + let txt = + match short_descr_opt with + | Some s -> s () + | None -> nm + in + (txt, value) :: acc + with Not_set _ -> + acc + end + else + acc) + [] + schema + in + let max_length = + List.fold_left max 0 + (List.rev_map String.length + (List.rev_map fst printable_vars)) + in + let dot_pad str = + String.make ((max_length - (String.length str)) + 3) '.' + in + + Printf.printf "\nConfiguration: \n"; + List.iter + (fun (name,value) -> + Printf.printf "%s: %s %s\n" name (dot_pad name) value) + (List.rev printable_vars); + Printf.printf "\n%!" + + let args () = + let arg_concat = + OASISUtils.varname_concat ~hyphen:'-' + in + [ + "--override", + Arg.Tuple + ( + let rvr = ref "" + in + let rvl = ref "" + in + [ + Arg.Set_string rvr; + Arg.Set_string rvl; + Arg.Unit + (fun () -> + Schema.set + schema + env + ~context:OCommandLine + !rvr + !rvl) + ] + ), + "var+val Override any configuration variable."; + + ] + @ + List.flatten + (Schema.fold + (fun acc name def short_descr_opt -> + let var_set s = + Schema.set + schema + env + ~context:OCommandLine + name + s + in + + let arg_name = + OASISUtils.varname_of_string ~hyphen:'-' name + in + + let hlp = + match short_descr_opt with + | Some txt -> txt () + | None -> "" + in + + let arg_hlp = + match def.arg_help with + | Some s -> s + | None -> "str" + in + + let default_value = + try + Printf.sprintf + (f_ " [%s]") + (Schema.get + schema + env + name) + with Not_set _ -> + "" + in + + let args = + match def.cli with + | CLINone -> + [] + | CLIAuto -> + [ + arg_concat "--" arg_name, + Arg.String var_set, + Printf.sprintf (f_ "%s %s%s") arg_hlp hlp default_value + ] + | CLIWith -> + [ + arg_concat "--with-" arg_name, + Arg.String var_set, + Printf.sprintf (f_ "%s %s%s") arg_hlp hlp default_value + ] + | CLIEnable -> + let dflt = + if default_value = " [true]" then + s_ " [default: enabled]" + else + s_ " [default: disabled]" + in + [ + arg_concat "--enable-" arg_name, + Arg.Unit (fun () -> var_set "true"), + Printf.sprintf (f_ " %s%s") hlp dflt; + + arg_concat "--disable-" arg_name, + Arg.Unit (fun () -> var_set "false"), + Printf.sprintf (f_ " %s%s") hlp dflt + ] + | CLIUser lst -> + lst + in + args :: acc) + [] + schema) +end + +module BaseArgExt = struct +(* # 21 "src/base/BaseArgExt.ml" *) + + open OASISUtils + open OASISGettext + + let parse argv args = + (* Simulate command line for Arg *) + let current = + ref 0 + in + + try + Arg.parse_argv + ~current:current + (Array.concat [[|"none"|]; argv]) + (Arg.align args) + (failwithf (f_ "Don't know what to do with arguments: '%s'")) + (s_ "configure options:") + with + | Arg.Help txt -> + print_endline txt; + exit 0 + | Arg.Bad txt -> + prerr_endline txt; + exit 1 +end + +module BaseCheck = struct +(* # 21 "src/base/BaseCheck.ml" *) + + open BaseEnv + open BaseMessage + open OASISUtils + open OASISGettext + + let prog_best prg prg_lst = + var_redefine + prg + (fun () -> + let alternate = + List.fold_left + (fun res e -> + match res with + | Some _ -> + res + | None -> + try + Some (OASISFileUtil.which ~ctxt:!BaseContext.default e) + with Not_found -> + None) + None + prg_lst + in + match alternate with + | Some prg -> prg + | None -> raise Not_found) + + let prog prg = + prog_best prg [prg] + + let prog_opt prg = + prog_best prg [prg^".opt"; prg] + + let ocamlfind = + prog "ocamlfind" + + let version + var_prefix + cmp + fversion + () = + (* Really compare version provided *) + let var = + var_prefix^"_version_"^(OASISVersion.varname_of_comparator cmp) + in + var_redefine + ~hide:true + var + (fun () -> + let version_str = + match fversion () with + | "[Distributed with OCaml]" -> + begin + try + (var_get "ocaml_version") + with Not_found -> + warning + (f_ "Variable ocaml_version not defined, fallback \ + to default"); + Sys.ocaml_version + end + | res -> + res + in + let version = + OASISVersion.version_of_string version_str + in + if OASISVersion.comparator_apply version cmp then + version_str + else + failwithf + (f_ "Cannot satisfy version constraint on %s: %s (version: %s)") + var_prefix + (OASISVersion.string_of_comparator cmp) + version_str) + () + + let package_version pkg = + OASISExec.run_read_one_line ~ctxt:!BaseContext.default + (ocamlfind ()) + ["query"; "-format"; "%v"; pkg] + + let package ?version_comparator pkg () = + let var = + OASISUtils.varname_concat + "pkg_" + (OASISUtils.varname_of_string pkg) + in + let findlib_dir pkg = + let dir = + OASISExec.run_read_one_line ~ctxt:!BaseContext.default + (ocamlfind ()) + ["query"; "-format"; "%d"; pkg] + in + if Sys.file_exists dir && Sys.is_directory dir then + dir + else + failwithf + (f_ "When looking for findlib package %s, \ + directory %s return doesn't exist") + pkg dir + in + let vl = + var_redefine + var + (fun () -> findlib_dir pkg) + () + in + ( + match version_comparator with + | Some ver_cmp -> + ignore + (version + var + ver_cmp + (fun _ -> package_version pkg) + ()) + | None -> + () + ); + vl +end + +module BaseOCamlcConfig = struct +(* # 21 "src/base/BaseOCamlcConfig.ml" *) + + + open BaseEnv + open OASISUtils + open OASISGettext + + module SMap = Map.Make(String) + + let ocamlc = + BaseCheck.prog_opt "ocamlc" + + let ocamlc_config_map = + (* Map name to value for ocamlc -config output + (name ^": "^value) + *) + let rec split_field mp lst = + match lst with + | line :: tl -> + let mp = + try + let pos_semicolon = + String.index line ':' + in + if pos_semicolon > 1 then + ( + let name = + String.sub line 0 pos_semicolon + in + let linelen = + String.length line + in + let value = + if linelen > pos_semicolon + 2 then + String.sub + line + (pos_semicolon + 2) + (linelen - pos_semicolon - 2) + else + "" + in + SMap.add name value mp + ) + else + ( + mp + ) + with Not_found -> + ( + mp + ) + in + split_field mp tl + | [] -> + mp + in + + let cache = + lazy + (var_protect + (Marshal.to_string + (split_field + SMap.empty + (OASISExec.run_read_output + ~ctxt:!BaseContext.default + (ocamlc ()) ["-config"])) + [])) + in + var_redefine + "ocamlc_config_map" + ~hide:true + ~dump:false + (fun () -> + (* TODO: update if ocamlc change !!! *) + Lazy.force cache) + + let var_define nm = + (* Extract data from ocamlc -config *) + let avlbl_config_get () = + Marshal.from_string + (ocamlc_config_map ()) + 0 + in + let chop_version_suffix s = + try + String.sub s 0 (String.index s '+') + with _ -> + s + in + + let nm_config, value_config = + match nm with + | "ocaml_version" -> + "version", chop_version_suffix + | _ -> nm, (fun x -> x) + in + var_redefine + nm + (fun () -> + try + let map = + avlbl_config_get () + in + let value = + SMap.find nm_config map + in + value_config value + with Not_found -> + failwithf + (f_ "Cannot find field '%s' in '%s -config' output") + nm + (ocamlc ())) + +end + +module BaseStandardVar = struct +(* # 21 "src/base/BaseStandardVar.ml" *) + + + open OASISGettext + open OASISTypes + open OASISExpr + open BaseCheck + open BaseEnv + + let ocamlfind = BaseCheck.ocamlfind + let ocamlc = BaseOCamlcConfig.ocamlc + let ocamlopt = prog_opt "ocamlopt" + let ocamlbuild = prog "ocamlbuild" + + + (**/**) + let rpkg = + ref None + + let pkg_get () = + match !rpkg with + | Some pkg -> pkg + | None -> failwith (s_ "OASIS Package is not set") + + let var_cond = ref [] + + let var_define_cond ~since_version f dflt = + let holder = ref (fun () -> dflt) in + let since_version = + OASISVersion.VGreaterEqual (OASISVersion.version_of_string since_version) + in + var_cond := + (fun ver -> + if OASISVersion.comparator_apply ver since_version then + holder := f ()) :: !var_cond; + fun () -> !holder () + + (**/**) + + let pkg_name = + var_define + ~short_desc:(fun () -> s_ "Package name") + "pkg_name" + (fun () -> (pkg_get ()).name) + + let pkg_version = + var_define + ~short_desc:(fun () -> s_ "Package version") + "pkg_version" + (fun () -> + (OASISVersion.string_of_version (pkg_get ()).version)) + + let c = BaseOCamlcConfig.var_define + + let os_type = c "os_type" + let system = c "system" + let architecture = c "architecture" + let ccomp_type = c "ccomp_type" + let ocaml_version = c "ocaml_version" + + (* TODO: Check standard variable presence at runtime *) + + let standard_library_default = c "standard_library_default" + let standard_library = c "standard_library" + let standard_runtime = c "standard_runtime" + let bytecomp_c_compiler = c "bytecomp_c_compiler" + let native_c_compiler = c "native_c_compiler" + let model = c "model" + let ext_obj = c "ext_obj" + let ext_asm = c "ext_asm" + let ext_lib = c "ext_lib" + let ext_dll = c "ext_dll" + let default_executable_name = c "default_executable_name" + let systhread_supported = c "systhread_supported" + + let flexlink = + BaseCheck.prog "flexlink" + + let flexdll_version = + var_define + ~short_desc:(fun () -> "FlexDLL version (Win32)") + "flexdll_version" + (fun () -> + let lst = + OASISExec.run_read_output ~ctxt:!BaseContext.default + (flexlink ()) ["-help"] + in + match lst with + | line :: _ -> + Scanf.sscanf line "FlexDLL version %s" (fun ver -> ver) + | [] -> + raise Not_found) + + (**/**) + let p name hlp dflt = + var_define + ~short_desc:hlp + ~cli:CLIAuto + ~arg_help:"dir" + name + dflt + + let (/) a b = + if os_type () = Sys.os_type then + Filename.concat a b + else if os_type () = "Unix" then + OASISUnixPath.concat a b + else + OASISUtils.failwithf (f_ "Cannot handle os_type %s filename concat") + (os_type ()) + (**/**) + + let prefix = + p "prefix" + (fun () -> s_ "Install architecture-independent files dir") + (fun () -> + match os_type () with + | "Win32" -> + let program_files = + Sys.getenv "PROGRAMFILES" + in + program_files/(pkg_name ()) + | _ -> + "/usr/local") + + let exec_prefix = + p "exec_prefix" + (fun () -> s_ "Install architecture-dependent files in dir") + (fun () -> "$prefix") + + let bindir = + p "bindir" + (fun () -> s_ "User executables") + (fun () -> "$exec_prefix"/"bin") + + let sbindir = + p "sbindir" + (fun () -> s_ "System admin executables") + (fun () -> "$exec_prefix"/"sbin") + + let libexecdir = + p "libexecdir" + (fun () -> s_ "Program executables") + (fun () -> "$exec_prefix"/"libexec") + + let sysconfdir = + p "sysconfdir" + (fun () -> s_ "Read-only single-machine data") + (fun () -> "$prefix"/"etc") + + let sharedstatedir = + p "sharedstatedir" + (fun () -> s_ "Modifiable architecture-independent data") + (fun () -> "$prefix"/"com") + + let localstatedir = + p "localstatedir" + (fun () -> s_ "Modifiable single-machine data") + (fun () -> "$prefix"/"var") + + let libdir = + p "libdir" + (fun () -> s_ "Object code libraries") + (fun () -> "$exec_prefix"/"lib") + + let datarootdir = + p "datarootdir" + (fun () -> s_ "Read-only arch-independent data root") + (fun () -> "$prefix"/"share") + + let datadir = + p "datadir" + (fun () -> s_ "Read-only architecture-independent data") + (fun () -> "$datarootdir") + + let infodir = + p "infodir" + (fun () -> s_ "Info documentation") + (fun () -> "$datarootdir"/"info") + + let localedir = + p "localedir" + (fun () -> s_ "Locale-dependent data") + (fun () -> "$datarootdir"/"locale") + + let mandir = + p "mandir" + (fun () -> s_ "Man documentation") + (fun () -> "$datarootdir"/"man") + + let docdir = + p "docdir" + (fun () -> s_ "Documentation root") + (fun () -> "$datarootdir"/"doc"/"$pkg_name") + + let htmldir = + p "htmldir" + (fun () -> s_ "HTML documentation") + (fun () -> "$docdir") + + let dvidir = + p "dvidir" + (fun () -> s_ "DVI documentation") + (fun () -> "$docdir") + + let pdfdir = + p "pdfdir" + (fun () -> s_ "PDF documentation") + (fun () -> "$docdir") + + let psdir = + p "psdir" + (fun () -> s_ "PS documentation") + (fun () -> "$docdir") + + let destdir = + p "destdir" + (fun () -> s_ "Prepend a path when installing package") + (fun () -> + raise + (PropList.Not_set + ("destdir", + Some (s_ "undefined by construct")))) + + let findlib_version = + var_define + "findlib_version" + (fun () -> + BaseCheck.package_version "findlib") + + let is_native = + var_define + "is_native" + (fun () -> + try + let _s : string = + ocamlopt () + in + "true" + with PropList.Not_set _ -> + let _s : string = + ocamlc () + in + "false") + + let ext_program = + var_define + "suffix_program" + (fun () -> + match os_type () with + | "Win32" -> ".exe" + | _ -> "") + + let rm = + var_define + ~short_desc:(fun () -> s_ "Remove a file.") + "rm" + (fun () -> + match os_type () with + | "Win32" -> "del" + | _ -> "rm -f") + + let rmdir = + var_define + ~short_desc:(fun () -> s_ "Remove a directory.") + "rmdir" + (fun () -> + match os_type () with + | "Win32" -> "rd" + | _ -> "rm -rf") + + let debug = + var_define + ~short_desc:(fun () -> s_ "Turn ocaml debug flag on") + ~cli:CLIEnable + "debug" + (fun () -> "true") + + let profile = + var_define + ~short_desc:(fun () -> s_ "Turn ocaml profile flag on") + ~cli:CLIEnable + "profile" + (fun () -> "false") + + let tests = + var_define_cond ~since_version:"0.3" + (fun () -> + var_define + ~short_desc:(fun () -> + s_ "Compile tests executable and library and run them") + ~cli:CLIEnable + "tests" + (fun () -> "false")) + "true" + + let docs = + var_define_cond ~since_version:"0.3" + (fun () -> + var_define + ~short_desc:(fun () -> s_ "Create documentations") + ~cli:CLIEnable + "docs" + (fun () -> "true")) + "true" + + let native_dynlink = + var_define + ~short_desc:(fun () -> s_ "Compiler support generation of .cmxs.") + ~cli:CLINone + "native_dynlink" + (fun () -> + let res = + let ocaml_lt_312 () = + OASISVersion.comparator_apply + (OASISVersion.version_of_string (ocaml_version ())) + (OASISVersion.VLesser + (OASISVersion.version_of_string "3.12.0")) + in + let flexdll_lt_030 () = + OASISVersion.comparator_apply + (OASISVersion.version_of_string (flexdll_version ())) + (OASISVersion.VLesser + (OASISVersion.version_of_string "0.30")) + in + let has_native_dynlink = + let ocamlfind = ocamlfind () in + try + let fn = + OASISExec.run_read_one_line + ~ctxt:!BaseContext.default + ocamlfind + ["query"; "-predicates"; "native"; "dynlink"; + "-format"; "%d/%a"] + in + Sys.file_exists fn + with _ -> + false + in + if not has_native_dynlink then + false + else if ocaml_lt_312 () then + false + else if (os_type () = "Win32" || os_type () = "Cygwin") + && flexdll_lt_030 () then + begin + BaseMessage.warning + (f_ ".cmxs generation disabled because FlexDLL needs to be \ + at least 0.30. Please upgrade FlexDLL from %s to 0.30.") + (flexdll_version ()); + false + end + else + true + in + string_of_bool res) + + let init pkg = + rpkg := Some pkg; + List.iter (fun f -> f pkg.oasis_version) !var_cond + +end + +module BaseFileAB = struct +(* # 21 "src/base/BaseFileAB.ml" *) + + open BaseEnv + open OASISGettext + open BaseMessage + + let to_filename fn = + let fn = + OASISHostPath.of_unix fn + in + if not (Filename.check_suffix fn ".ab") then + warning + (f_ "File '%s' doesn't have '.ab' extension") + fn; + Filename.chop_extension fn + + let replace fn_lst = + let buff = + Buffer.create 13 + in + List.iter + (fun fn -> + let fn = + OASISHostPath.of_unix fn + in + let chn_in = + open_in fn + in + let chn_out = + open_out (to_filename fn) + in + ( + try + while true do + Buffer.add_string buff (var_expand (input_line chn_in)); + Buffer.add_char buff '\n' + done + with End_of_file -> + () + ); + Buffer.output_buffer chn_out buff; + Buffer.clear buff; + close_in chn_in; + close_out chn_out) + fn_lst +end + +module BaseLog = struct +(* # 21 "src/base/BaseLog.ml" *) + + open OASISUtils + + let default_filename = + Filename.concat + (Filename.dirname BaseEnv.default_filename) + "setup.log" + + module SetTupleString = + Set.Make + (struct + type t = string * string + let compare (s11, s12) (s21, s22) = + match String.compare s11 s21 with + | 0 -> String.compare s12 s22 + | n -> n + end) + + let load () = + if Sys.file_exists default_filename then + begin + let chn = + open_in default_filename + in + let scbuf = + Scanf.Scanning.from_file default_filename + in + let rec read_aux (st, lst) = + if not (Scanf.Scanning.end_of_input scbuf) then + begin + let acc = + try + Scanf.bscanf scbuf "%S %S\n" + (fun e d -> + let t = + e, d + in + if SetTupleString.mem t st then + st, lst + else + SetTupleString.add t st, + t :: lst) + with Scanf.Scan_failure _ -> + failwith + (Scanf.bscanf scbuf + "%l" + (fun line -> + Printf.sprintf + "Malformed log file '%s' at line %d" + default_filename + line)) + in + read_aux acc + end + else + begin + close_in chn; + List.rev lst + end + in + read_aux (SetTupleString.empty, []) + end + else + begin + [] + end + + let register event data = + let chn_out = + open_out_gen [Open_append; Open_creat; Open_text] 0o644 default_filename + in + Printf.fprintf chn_out "%S %S\n" event data; + close_out chn_out + + let unregister event data = + if Sys.file_exists default_filename then + begin + let lst = + load () + in + let chn_out = + open_out default_filename + in + let write_something = + ref false + in + List.iter + (fun (e, d) -> + if e <> event || d <> data then + begin + write_something := true; + Printf.fprintf chn_out "%S %S\n" e d + end) + lst; + close_out chn_out; + if not !write_something then + Sys.remove default_filename + end + + let filter events = + let st_events = + List.fold_left + (fun st e -> + SetString.add e st) + SetString.empty + events + in + List.filter + (fun (e, _) -> SetString.mem e st_events) + (load ()) + + let exists event data = + List.exists + (fun v -> (event, data) = v) + (load ()) +end + +module BaseBuilt = struct +(* # 21 "src/base/BaseBuilt.ml" *) + + open OASISTypes + open OASISGettext + open BaseStandardVar + open BaseMessage + + type t = + | BExec (* Executable *) + | BExecLib (* Library coming with executable *) + | BLib (* Library *) + | BDoc (* Document *) + + let to_log_event_file t nm = + "built_"^ + (match t with + | BExec -> "exec" + | BExecLib -> "exec_lib" + | BLib -> "lib" + | BDoc -> "doc")^ + "_"^nm + + let to_log_event_done t nm = + "is_"^(to_log_event_file t nm) + + let register t nm lst = + BaseLog.register + (to_log_event_done t nm) + "true"; + List.iter + (fun alt -> + let registered = + List.fold_left + (fun registered fn -> + if OASISFileUtil.file_exists_case fn then + begin + BaseLog.register + (to_log_event_file t nm) + (if Filename.is_relative fn then + Filename.concat (Sys.getcwd ()) fn + else + fn); + true + end + else + registered) + false + alt + in + if not registered then + warning + (f_ "Cannot find an existing alternative files among: %s") + (String.concat (s_ ", ") alt)) + lst + + let unregister t nm = + List.iter + (fun (e, d) -> + BaseLog.unregister e d) + (BaseLog.filter + [to_log_event_file t nm; + to_log_event_done t nm]) + + let fold t nm f acc = + List.fold_left + (fun acc (_, fn) -> + if OASISFileUtil.file_exists_case fn then + begin + f acc fn + end + else + begin + warning + (f_ "File '%s' has been marked as built \ + for %s but doesn't exist") + fn + (Printf.sprintf + (match t with + | BExec | BExecLib -> + (f_ "executable %s") + | BLib -> + (f_ "library %s") + | BDoc -> + (f_ "documentation %s")) + nm); + acc + end) + acc + (BaseLog.filter + [to_log_event_file t nm]) + + let is_built t nm = + List.fold_left + (fun is_built (_, d) -> + (try + bool_of_string d + with _ -> + false)) + false + (BaseLog.filter + [to_log_event_done t nm]) + + let of_executable ffn (cs, bs, exec) = + let unix_exec_is, unix_dll_opt = + OASISExecutable.unix_exec_is + (cs, bs, exec) + (fun () -> + bool_of_string + (is_native ())) + ext_dll + ext_program + in + let evs = + (BExec, cs.cs_name, [[ffn unix_exec_is]]) + :: + (match unix_dll_opt with + | Some fn -> + [BExecLib, cs.cs_name, [[ffn fn]]] + | None -> + []) + in + evs, + unix_exec_is, + unix_dll_opt + + let of_library ffn (cs, bs, lib) = + let unix_lst = + OASISLibrary.generated_unix_files + ~ctxt:!BaseContext.default + ~source_file_exists:(fun fn -> + OASISFileUtil.file_exists_case (OASISHostPath.of_unix fn)) + ~is_native:(bool_of_string (is_native ())) + ~has_native_dynlink:(bool_of_string (native_dynlink ())) + ~ext_lib:(ext_lib ()) + ~ext_dll:(ext_dll ()) + (cs, bs, lib) + in + let evs = + [BLib, + cs.cs_name, + List.map (List.map ffn) unix_lst] + in + evs, unix_lst + +end + +module BaseCustom = struct +(* # 21 "src/base/BaseCustom.ml" *) + + open BaseEnv + open BaseMessage + open OASISTypes + open OASISGettext + + let run cmd args extra_args = + OASISExec.run ~ctxt:!BaseContext.default ~quote:false + (var_expand cmd) + (List.map + var_expand + (args @ (Array.to_list extra_args))) + + let hook ?(failsafe=false) cstm f e = + let optional_command lst = + let printer = + function + | Some (cmd, args) -> String.concat " " (cmd :: args) + | None -> s_ "No command" + in + match + var_choose + ~name:(s_ "Pre/Post Command") + ~printer + lst with + | Some (cmd, args) -> + begin + try + run cmd args [||] + with e when failsafe -> + warning + (f_ "Command '%s' fail with error: %s") + (String.concat " " (cmd :: args)) + (match e with + | Failure msg -> msg + | e -> Printexc.to_string e) + end + | None -> + () + in + let res = + optional_command cstm.pre_command; + f e + in + optional_command cstm.post_command; + res +end + +module BaseDynVar = struct +(* # 21 "src/base/BaseDynVar.ml" *) + + + open OASISTypes + open OASISGettext + open BaseEnv + open BaseBuilt + + let init pkg = + (* TODO: disambiguate exec vs other variable by adding exec_VARNAME. *) + (* TODO: provide compile option for library libary_byte_args_VARNAME... *) + List.iter + (function + | Executable (cs, bs, exec) -> + if var_choose bs.bs_build then + var_ignore + (var_redefine + (* We don't save this variable *) + ~dump:false + ~short_desc:(fun () -> + Printf.sprintf + (f_ "Filename of executable '%s'") + cs.cs_name) + (OASISUtils.varname_of_string cs.cs_name) + (fun () -> + let fn_opt = + fold + BExec cs.cs_name + (fun _ fn -> Some fn) + None + in + match fn_opt with + | Some fn -> fn + | None -> + raise + (PropList.Not_set + (cs.cs_name, + Some (Printf.sprintf + (f_ "Executable '%s' not yet built.") + cs.cs_name))))) + + | Library _ | Flag _ | Test _ | SrcRepo _ | Doc _ -> + ()) + pkg.sections +end + +module BaseTest = struct +(* # 21 "src/base/BaseTest.ml" *) + + open BaseEnv + open BaseMessage + open OASISTypes + open OASISExpr + open OASISGettext + + let test lst pkg extra_args = + + let one_test (failure, n) (test_plugin, cs, test) = + if var_choose + ~name:(Printf.sprintf + (f_ "test %s run") + cs.cs_name) + ~printer:string_of_bool + test.test_run then + begin + let () = + info (f_ "Running test '%s'") cs.cs_name + in + let back_cwd = + match test.test_working_directory with + | Some dir -> + let cwd = + Sys.getcwd () + in + let chdir d = + info (f_ "Changing directory to '%s'") d; + Sys.chdir d + in + chdir dir; + fun () -> chdir cwd + + | None -> + fun () -> () + in + try + let failure_percent = + BaseCustom.hook + test.test_custom + (test_plugin pkg (cs, test)) + extra_args + in + back_cwd (); + (failure_percent +. failure, n + 1) + with e -> + begin + back_cwd (); + raise e + end + end + else + begin + info (f_ "Skipping test '%s'") cs.cs_name; + (failure, n) + end + in + let (failed, n) = + List.fold_left + one_test + (0.0, 0) + lst + in + let failure_percent = + if n = 0 then + 0.0 + else + failed /. (float_of_int n) + in + let msg = + Printf.sprintf + (f_ "Tests had a %.2f%% failure rate") + (100. *. failure_percent) + in + if failure_percent > 0.0 then + failwith msg + else + info "%s" msg; + + (* Possible explanation why the tests where not run. *) + if OASISVersion.version_0_3_or_after pkg.oasis_version && + not (bool_of_string (BaseStandardVar.tests ())) && + lst <> [] then + BaseMessage.warning + "Tests are turned off, consider enabling with \ + 'ocaml setup.ml -configure --enable-tests'" +end + +module BaseDoc = struct +(* # 21 "src/base/BaseDoc.ml" *) + + open BaseEnv + open BaseMessage + open OASISTypes + open OASISGettext + + let doc lst pkg extra_args = + + let one_doc (doc_plugin, cs, doc) = + if var_choose + ~name:(Printf.sprintf + (f_ "documentation %s build") + cs.cs_name) + ~printer:string_of_bool + doc.doc_build then + begin + info (f_ "Building documentation '%s'") cs.cs_name; + BaseCustom.hook + doc.doc_custom + (doc_plugin pkg (cs, doc)) + extra_args + end + in + List.iter one_doc lst; + + if OASISVersion.version_0_3_or_after pkg.oasis_version && + not (bool_of_string (BaseStandardVar.docs ())) && + lst <> [] then + BaseMessage.warning + "Docs are turned off, consider enabling with \ + 'ocaml setup.ml -configure --enable-docs'" +end + +module BaseSetup = struct +(* # 21 "src/base/BaseSetup.ml" *) + + open BaseEnv + open BaseMessage + open OASISTypes + open OASISSection + open OASISGettext + open OASISUtils + + type std_args_fun = + package -> string array -> unit + + type ('a, 'b) section_args_fun = + name * (package -> (common_section * 'a) -> string array -> 'b) + + type t = + { + configure: std_args_fun; + build: std_args_fun; + doc: ((doc, unit) section_args_fun) list; + test: ((test, float) section_args_fun) list; + install: std_args_fun; + uninstall: std_args_fun; + clean: std_args_fun list; + clean_doc: (doc, unit) section_args_fun list; + clean_test: (test, unit) section_args_fun list; + distclean: std_args_fun list; + distclean_doc: (doc, unit) section_args_fun list; + distclean_test: (test, unit) section_args_fun list; + package: package; + oasis_fn: string option; + oasis_version: string; + oasis_digest: Digest.t option; + oasis_exec: string option; + oasis_setup_args: string list; + setup_update: bool; + } + + (* Associate a plugin function with data from package *) + let join_plugin_sections filter_map lst = + List.rev + (List.fold_left + (fun acc sct -> + match filter_map sct with + | Some e -> + e :: acc + | None -> + acc) + [] + lst) + + (* Search for plugin data associated with a section name *) + let lookup_plugin_section plugin action nm lst = + try + List.assoc nm lst + with Not_found -> + failwithf + (f_ "Cannot find plugin %s matching section %s for %s action") + plugin + nm + action + + let configure t args = + (* Run configure *) + BaseCustom.hook + t.package.conf_custom + (fun () -> + (* Reload if preconf has changed it *) + begin + try + unload (); + load (); + with _ -> + () + end; + + (* Run plugin's configure *) + t.configure t.package args; + + (* Dump to allow postconf to change it *) + dump ()) + (); + + (* Reload environment *) + unload (); + load (); + + (* Save environment *) + print (); + + (* Replace data in file *) + BaseFileAB.replace t.package.files_ab + + let build t args = + BaseCustom.hook + t.package.build_custom + (t.build t.package) + args + + let doc t args = + BaseDoc.doc + (join_plugin_sections + (function + | Doc (cs, e) -> + Some + (lookup_plugin_section + "documentation" + (s_ "build") + cs.cs_name + t.doc, + cs, + e) + | _ -> + None) + t.package.sections) + t.package + args + + let test t args = + BaseTest.test + (join_plugin_sections + (function + | Test (cs, e) -> + Some + (lookup_plugin_section + "test" + (s_ "run") + cs.cs_name + t.test, + cs, + e) + | _ -> + None) + t.package.sections) + t.package + args + + let all t args = + let rno_doc = + ref false + in + let rno_test = + ref false + in + Arg.parse_argv + ~current:(ref 0) + (Array.of_list + ((Sys.executable_name^" all") :: + (Array.to_list args))) + [ + "-no-doc", + Arg.Set rno_doc, + s_ "Don't run doc target"; + + "-no-test", + Arg.Set rno_test, + s_ "Don't run test target"; + ] + (failwithf (f_ "Don't know what to do with '%s'")) + ""; + + info "Running configure step"; + configure t [||]; + + info "Running build step"; + build t [||]; + + (* Load setup.log dynamic variables *) + BaseDynVar.init t.package; + + if not !rno_doc then + begin + info "Running doc step"; + doc t [||]; + end + else + begin + info "Skipping doc step" + end; + + if not !rno_test then + begin + info "Running test step"; + test t [||] + end + else + begin + info "Skipping test step" + end + + let install t args = + BaseCustom.hook + t.package.install_custom + (t.install t.package) + args + + let uninstall t args = + BaseCustom.hook + t.package.uninstall_custom + (t.uninstall t.package) + args + + let reinstall t args = + uninstall t args; + install t args + + let clean, distclean = + let failsafe f a = + try + f a + with e -> + warning + (f_ "Action fail with error: %s") + (match e with + | Failure msg -> msg + | e -> Printexc.to_string e) + in + + let generic_clean t cstm mains docs tests args = + BaseCustom.hook + ~failsafe:true + cstm + (fun () -> + (* Clean section *) + List.iter + (function + | Test (cs, test) -> + let f = + try + List.assoc cs.cs_name tests + with Not_found -> + fun _ _ _ -> () + in + failsafe + (f t.package (cs, test)) + args + | Doc (cs, doc) -> + let f = + try + List.assoc cs.cs_name docs + with Not_found -> + fun _ _ _ -> () + in + failsafe + (f t.package (cs, doc)) + args + | Library _ + | Executable _ + | Flag _ + | SrcRepo _ -> + ()) + t.package.sections; + (* Clean whole package *) + List.iter + (fun f -> + failsafe + (f t.package) + args) + mains) + () + in + + let clean t args = + generic_clean + t + t.package.clean_custom + t.clean + t.clean_doc + t.clean_test + args + in + + let distclean t args = + (* Call clean *) + clean t args; + + (* Call distclean code *) + generic_clean + t + t.package.distclean_custom + t.distclean + t.distclean_doc + t.distclean_test + args; + + (* Remove generated file *) + List.iter + (fun fn -> + if Sys.file_exists fn then + begin + info (f_ "Remove '%s'") fn; + Sys.remove fn + end) + (BaseEnv.default_filename + :: + BaseLog.default_filename + :: + (List.rev_map BaseFileAB.to_filename t.package.files_ab)) + in + + clean, distclean + + let version t _ = + print_endline t.oasis_version + + let update_setup_ml, no_update_setup_ml_cli = + let b = ref true in + b, + ("-no-update-setup-ml", + Arg.Clear b, + s_ " Don't try to update setup.ml, even if _oasis has changed.") + + let update_setup_ml t = + let oasis_fn = + match t.oasis_fn with + | Some fn -> fn + | None -> "_oasis" + in + let oasis_exec = + match t.oasis_exec with + | Some fn -> fn + | None -> "oasis" + in + let ocaml = + Sys.executable_name + in + let setup_ml, args = + match Array.to_list Sys.argv with + | setup_ml :: args -> + setup_ml, args + | [] -> + failwith + (s_ "Expecting non-empty command line arguments.") + in + let ocaml, setup_ml = + if Sys.executable_name = Sys.argv.(0) then + (* We are not running in standard mode, probably the script + * is precompiled. + *) + "ocaml", "setup.ml" + else + ocaml, setup_ml + in + let no_update_setup_ml_cli, _, _ = no_update_setup_ml_cli in + let do_update () = + let oasis_exec_version = + OASISExec.run_read_one_line + ~ctxt:!BaseContext.default + ~f_exit_code: + (function + | 0 -> + () + | 1 -> + failwithf + (f_ "Executable '%s' is probably an old version \ + of oasis (< 0.3.0), please update to version \ + v%s.") + oasis_exec t.oasis_version + | 127 -> + failwithf + (f_ "Cannot find executable '%s', please install \ + oasis v%s.") + oasis_exec t.oasis_version + | n -> + failwithf + (f_ "Command '%s version' exited with code %d.") + oasis_exec n) + oasis_exec ["version"] + in + if OASISVersion.comparator_apply + (OASISVersion.version_of_string oasis_exec_version) + (OASISVersion.VGreaterEqual + (OASISVersion.version_of_string t.oasis_version)) then + begin + (* We have a version >= for the executable oasis, proceed with + * update. + *) + (* TODO: delegate this check to 'oasis setup'. *) + if Sys.os_type = "Win32" then + failwithf + (f_ "It is not possible to update the running script \ + setup.ml on Windows. Please update setup.ml by \ + running '%s'.") + (String.concat " " (oasis_exec :: "setup" :: t.oasis_setup_args)) + else + begin + OASISExec.run + ~ctxt:!BaseContext.default + ~f_exit_code: + (function + | 0 -> + () + | n -> + failwithf + (f_ "Unable to update setup.ml using '%s', \ + please fix the problem and retry.") + oasis_exec) + oasis_exec ("setup" :: t.oasis_setup_args); + OASISExec.run ~ctxt:!BaseContext.default ocaml (setup_ml :: args) + end + end + else + failwithf + (f_ "The version of '%s' (v%s) doesn't match the version of \ + oasis used to generate the %s file. Please install at \ + least oasis v%s.") + oasis_exec oasis_exec_version setup_ml t.oasis_version + in + + if !update_setup_ml then + begin + try + match t.oasis_digest with + | Some dgst -> + if Sys.file_exists oasis_fn && dgst <> Digest.file "_oasis" then + begin + do_update (); + true + end + else + false + | None -> + false + with e -> + error + (f_ "Error when updating setup.ml. If you want to avoid this error, \ + you can bypass the update of %s by running '%s %s %s %s'") + setup_ml ocaml setup_ml no_update_setup_ml_cli + (String.concat " " args); + raise e + end + else + false + + let setup t = + let catch_exn = + ref true + in + try + let act_ref = + ref (fun _ -> + failwithf + (f_ "No action defined, run '%s %s -help'") + Sys.executable_name + Sys.argv.(0)) + + in + let extra_args_ref = + ref [] + in + let allow_empty_env_ref = + ref false + in + let arg_handle ?(allow_empty_env=false) act = + Arg.Tuple + [ + Arg.Rest (fun str -> extra_args_ref := str :: !extra_args_ref); + + Arg.Unit + (fun () -> + allow_empty_env_ref := allow_empty_env; + act_ref := act); + ] + in + + Arg.parse + (Arg.align + ([ + "-configure", + arg_handle ~allow_empty_env:true configure, + s_ "[options*] Configure the whole build process."; + + "-build", + arg_handle build, + s_ "[options*] Build executables and libraries."; + + "-doc", + arg_handle doc, + s_ "[options*] Build documents."; + + "-test", + arg_handle test, + s_ "[options*] Run tests."; + + "-all", + arg_handle ~allow_empty_env:true all, + s_ "[options*] Run configure, build, doc and test targets."; + + "-install", + arg_handle install, + s_ "[options*] Install libraries, data, executables \ + and documents."; + + "-uninstall", + arg_handle uninstall, + s_ "[options*] Uninstall libraries, data, executables \ + and documents."; + + "-reinstall", + arg_handle reinstall, + s_ "[options*] Uninstall and install libraries, data, \ + executables and documents."; + + "-clean", + arg_handle ~allow_empty_env:true clean, + s_ "[options*] Clean files generated by a build."; + + "-distclean", + arg_handle ~allow_empty_env:true distclean, + s_ "[options*] Clean files generated by a build and configure."; + + "-version", + arg_handle ~allow_empty_env:true version, + s_ " Display version of OASIS used to generate this setup.ml."; + + "-no-catch-exn", + Arg.Clear catch_exn, + s_ " Don't catch exception, useful for debugging."; + ] + @ + (if t.setup_update then + [no_update_setup_ml_cli] + else + []) + @ (BaseContext.args ()))) + (failwithf (f_ "Don't know what to do with '%s'")) + (s_ "Setup and run build process current package\n"); + + (* Build initial environment *) + load ~allow_empty:!allow_empty_env_ref (); + + (** Initialize flags *) + List.iter + (function + | Flag (cs, {flag_description = hlp; + flag_default = choices}) -> + begin + let apply ?short_desc () = + var_ignore + (var_define + ~cli:CLIEnable + ?short_desc + (OASISUtils.varname_of_string cs.cs_name) + (fun () -> + string_of_bool + (var_choose + ~name:(Printf.sprintf + (f_ "default value of flag %s") + cs.cs_name) + ~printer:string_of_bool + choices))) + in + match hlp with + | Some hlp -> + apply ~short_desc:(fun () -> hlp) () + | None -> + apply () + end + | _ -> + ()) + t.package.sections; + + BaseStandardVar.init t.package; + + BaseDynVar.init t.package; + + if t.setup_update && update_setup_ml t then + () + else + !act_ref t (Array.of_list (List.rev !extra_args_ref)) + + with e when !catch_exn -> + error "%s" (Printexc.to_string e); + exit 1 + +end + + +# 4480 "setup.ml" +module InternalConfigurePlugin = struct +(* # 21 "src/plugins/internal/InternalConfigurePlugin.ml" *) + + (** Configure using internal scheme + @author Sylvain Le Gall + *) + + open BaseEnv + open OASISTypes + open OASISUtils + open OASISGettext + open BaseMessage + + (** Configure build using provided series of check to be done + * and then output corresponding file. + *) + let configure pkg argv = + let var_ignore_eval var = + let _s : string = + var () + in + () + in + + let errors = + ref SetString.empty + in + + let buff = + Buffer.create 13 + in + + let add_errors fmt = + Printf.kbprintf + (fun b -> + errors := SetString.add (Buffer.contents b) !errors; + Buffer.clear b) + buff + fmt + in + + let warn_exception e = + warning "%s" (Printexc.to_string e) + in + + (* Check tools *) + let check_tools lst = + List.iter + (function + | ExternalTool tool -> + begin + try + var_ignore_eval (BaseCheck.prog tool) + with e -> + warn_exception e; + add_errors (f_ "Cannot find external tool '%s'") tool + end + | InternalExecutable nm1 -> + (* Check that matching tool is built *) + List.iter + (function + | Executable ({cs_name = nm2}, + {bs_build = build}, + _) when nm1 = nm2 -> + if not (var_choose build) then + add_errors + (f_ "Cannot find buildable internal executable \ + '%s' when checking build depends") + nm1 + | _ -> + ()) + pkg.sections) + lst + in + + let build_checks sct bs = + if var_choose bs.bs_build then + begin + if bs.bs_compiled_object = Native then + begin + try + var_ignore_eval BaseStandardVar.ocamlopt + with e -> + warn_exception e; + add_errors + (f_ "Section %s requires native compilation") + (OASISSection.string_of_section sct) + end; + + (* Check tools *) + check_tools bs.bs_build_tools; + + (* Check depends *) + List.iter + (function + | FindlibPackage (findlib_pkg, version_comparator) -> + begin + try + var_ignore_eval + (BaseCheck.package ?version_comparator findlib_pkg) + with e -> + warn_exception e; + match version_comparator with + | None -> + add_errors + (f_ "Cannot find findlib package %s") + findlib_pkg + | Some ver_cmp -> + add_errors + (f_ "Cannot find findlib package %s (%s)") + findlib_pkg + (OASISVersion.string_of_comparator ver_cmp) + end + | InternalLibrary nm1 -> + (* Check that matching library is built *) + List.iter + (function + | Library ({cs_name = nm2}, + {bs_build = build}, + _) when nm1 = nm2 -> + if not (var_choose build) then + add_errors + (f_ "Cannot find buildable internal library \ + '%s' when checking build depends") + nm1 + | _ -> + ()) + pkg.sections) + bs.bs_build_depends + end + in + + (* Parse command line *) + BaseArgExt.parse argv (BaseEnv.args ()); + + (* OCaml version *) + begin + match pkg.ocaml_version with + | Some ver_cmp -> + begin + try + var_ignore_eval + (BaseCheck.version + "ocaml" + ver_cmp + BaseStandardVar.ocaml_version) + with e -> + warn_exception e; + add_errors + (f_ "OCaml version %s doesn't match version constraint %s") + (BaseStandardVar.ocaml_version ()) + (OASISVersion.string_of_comparator ver_cmp) + end + | None -> + () + end; + + (* Findlib version *) + begin + match pkg.findlib_version with + | Some ver_cmp -> + begin + try + var_ignore_eval + (BaseCheck.version + "findlib" + ver_cmp + BaseStandardVar.findlib_version) + with e -> + warn_exception e; + add_errors + (f_ "Findlib version %s doesn't match version constraint %s") + (BaseStandardVar.findlib_version ()) + (OASISVersion.string_of_comparator ver_cmp) + end + | None -> + () + end; + + (* FlexDLL *) + if BaseStandardVar.os_type () = "Win32" || + BaseStandardVar.os_type () = "Cygwin" then + begin + try + var_ignore_eval BaseStandardVar.flexlink + with e -> + warn_exception e; + add_errors (f_ "Cannot find 'flexlink'") + end; + + (* Check build depends *) + List.iter + (function + | Executable (_, bs, _) + | Library (_, bs, _) as sct -> + build_checks sct bs + | Doc (_, doc) -> + if var_choose doc.doc_build then + check_tools doc.doc_build_tools + | Test (_, test) -> + if var_choose test.test_run then + check_tools test.test_tools + | _ -> + ()) + pkg.sections; + + (* Check if we need native dynlink (presence of libraries that compile to + * native) + *) + begin + let has_cmxa = + List.exists + (function + | Library (_, bs, _) -> + var_choose bs.bs_build && + (bs.bs_compiled_object = Native || + (bs.bs_compiled_object = Best && + bool_of_string (BaseStandardVar.is_native ()))) + | _ -> + false) + pkg.sections + in + if has_cmxa then + var_ignore_eval BaseStandardVar.native_dynlink + end; + + (* Check errors *) + if SetString.empty != !errors then + begin + List.iter + (fun e -> error "%s" e) + (SetString.elements !errors); + failwithf + (fn_ + "%d configuration error" + "%d configuration errors" + (SetString.cardinal !errors)) + (SetString.cardinal !errors) + end + +end + +module InternalInstallPlugin = struct +(* # 21 "src/plugins/internal/InternalInstallPlugin.ml" *) + + (** Install using internal scheme + @author Sylvain Le Gall + *) + + open BaseEnv + open BaseStandardVar + open BaseMessage + open OASISTypes + open OASISLibrary + open OASISGettext + open OASISUtils + + let exec_hook = + ref (fun (cs, bs, exec) -> cs, bs, exec) + + let lib_hook = + ref (fun (cs, bs, lib) -> cs, bs, lib, []) + + let doc_hook = + ref (fun (cs, doc) -> cs, doc) + + let install_file_ev = + "install-file" + + let install_dir_ev = + "install-dir" + + let install_findlib_ev = + "install-findlib" + + let win32_max_command_line_length = 8000 + + let split_install_command ocamlfind findlib_name meta files = + if Sys.os_type = "Win32" then + (* Arguments for the first command: *) + let first_args = ["install"; findlib_name; meta] in + (* Arguments for remaining commands: *) + let other_args = ["install"; findlib_name; "-add"] in + (* Extract as much files as possible from [files], [len] is + the current command line length: *) + let rec get_files len acc files = + match files with + | [] -> + (List.rev acc, []) + | file :: rest -> + let len = len + 1 + String.length file in + if len > win32_max_command_line_length then + (List.rev acc, files) + else + get_files len (file :: acc) rest + in + (* Split the command into several commands. *) + let rec split args files = + match files with + | [] -> + [] + | _ -> + (* Length of "ocamlfind install <lib> [META|-add]" *) + let len = + List.fold_left + (fun len arg -> + len + 1 (* for the space *) + String.length arg) + (String.length ocamlfind) + args + in + match get_files len [] files with + | ([], _) -> + failwith (s_ "Command line too long.") + | (firsts, others) -> + let cmd = args @ firsts in + (* Use -add for remaining commands: *) + let () = + let findlib_ge_132 = + OASISVersion.comparator_apply + (OASISVersion.version_of_string + (BaseStandardVar.findlib_version ())) + (OASISVersion.VGreaterEqual + (OASISVersion.version_of_string "1.3.2")) + in + if not findlib_ge_132 then + failwithf + (f_ "Installing the library %s require to use the flag \ + '-add' of ocamlfind because the command line is too \ + long. This flag is only available for findlib 1.3.2. \ + Please upgrade findlib from %s to 1.3.2") + findlib_name (BaseStandardVar.findlib_version ()) + in + let cmds = split other_args others in + cmd :: cmds + in + (* The first command does not use -add: *) + split first_args files + else + ["install" :: findlib_name :: meta :: files] + + let install pkg argv = + + let in_destdir = + try + let destdir = + destdir () + in + (* Practically speaking destdir is prepended + * at the beginning of the target filename + *) + fun fn -> destdir^fn + with PropList.Not_set _ -> + fun fn -> fn + in + + let install_file ?tgt_fn src_file envdir = + let tgt_dir = + in_destdir (envdir ()) + in + let tgt_file = + Filename.concat + tgt_dir + (match tgt_fn with + | Some fn -> + fn + | None -> + Filename.basename src_file) + in + (* Create target directory if needed *) + OASISFileUtil.mkdir_parent + ~ctxt:!BaseContext.default + (fun dn -> + info (f_ "Creating directory '%s'") dn; + BaseLog.register install_dir_ev dn) + tgt_dir; + + (* Really install files *) + info (f_ "Copying file '%s' to '%s'") src_file tgt_file; + OASISFileUtil.cp ~ctxt:!BaseContext.default src_file tgt_file; + BaseLog.register install_file_ev tgt_file + in + + (* Install data into defined directory *) + let install_data srcdir lst tgtdir = + let tgtdir = + OASISHostPath.of_unix (var_expand tgtdir) + in + List.iter + (fun (src, tgt_opt) -> + let real_srcs = + OASISFileUtil.glob + ~ctxt:!BaseContext.default + (Filename.concat srcdir src) + in + if real_srcs = [] then + failwithf + (f_ "Wildcard '%s' doesn't match any files") + src; + List.iter + (fun fn -> + install_file + fn + (fun () -> + match tgt_opt with + | Some s -> + OASISHostPath.of_unix (var_expand s) + | None -> + tgtdir)) + real_srcs) + lst + in + + (** Install all libraries *) + let install_libs pkg = + + let files_of_library (f_data, acc) data_lib = + let cs, bs, lib, lib_extra = + !lib_hook data_lib + in + if var_choose bs.bs_install && + BaseBuilt.is_built BaseBuilt.BLib cs.cs_name then + begin + let acc = + (* Start with acc + lib_extra *) + List.rev_append lib_extra acc + in + let acc = + (* Add uncompiled header from the source tree *) + let path = + OASISHostPath.of_unix bs.bs_path + in + List.fold_left + (fun acc modul -> + try + List.find + OASISFileUtil.file_exists_case + (List.map + (Filename.concat path) + [modul^".mli"; + modul^".ml"; + String.uncapitalize modul^".mli"; + String.capitalize modul^".mli"; + String.uncapitalize modul^".ml"; + String.capitalize modul^".ml"]) + :: acc + with Not_found -> + begin + warning + (f_ "Cannot find source header for module %s \ + in library %s") + modul cs.cs_name; + acc + end) + acc + lib.lib_modules + in + + let acc = + (* Get generated files *) + BaseBuilt.fold + BaseBuilt.BLib + cs.cs_name + (fun acc fn -> fn :: acc) + acc + in + + let f_data () = + (* Install data associated with the library *) + install_data + bs.bs_path + bs.bs_data_files + (Filename.concat + (datarootdir ()) + pkg.name); + f_data () + in + + (f_data, acc) + end + else + begin + (f_data, acc) + end + in + + (* Install one group of library *) + let install_group_lib grp = + (* Iterate through all group nodes *) + let rec install_group_lib_aux data_and_files grp = + let data_and_files, children = + match grp with + | Container (_, children) -> + data_and_files, children + | Package (_, cs, bs, lib, children) -> + files_of_library data_and_files (cs, bs, lib), children + in + List.fold_left + install_group_lib_aux + data_and_files + children + in + + (* Findlib name of the root library *) + let findlib_name = + findlib_of_group grp + in + + (* Determine root library *) + let root_lib = + root_of_group grp + in + + (* All files to install for this library *) + let f_data, files = + install_group_lib_aux (ignore, []) grp + in + + (* Really install, if there is something to install *) + if files = [] then + begin + warning + (f_ "Nothing to install for findlib library '%s'") + findlib_name + end + else + begin + let meta = + (* Search META file *) + let (_, bs, _) = + root_lib + in + let res = + Filename.concat bs.bs_path "META" + in + if not (OASISFileUtil.file_exists_case res) then + failwithf + (f_ "Cannot find file '%s' for findlib library %s") + res + findlib_name; + res + in + let files = + (* Make filename shorter to avoid hitting command max line length + * too early, esp. on Windows. + *) + let remove_prefix p n = + let plen = String.length p in + let nlen = String.length n in + if plen <= nlen && String.sub n 0 plen = p then + begin + let fn_sep = + if Sys.os_type = "Win32" then + '\\' + else + '/' + in + let cutpoint = plen + + (if plen < nlen && n.[plen] = fn_sep then + 1 + else + 0) + in + String.sub n cutpoint (nlen - cutpoint) + end + else + n + in + List.map (remove_prefix (Sys.getcwd ())) files + in + info + (f_ "Installing findlib library '%s'") + findlib_name; + let ocamlfind = ocamlfind () in + let commands = + split_install_command + ocamlfind + findlib_name + meta + files + in + List.iter + (OASISExec.run ~ctxt:!BaseContext.default ocamlfind) + commands; + BaseLog.register install_findlib_ev findlib_name + end; + + (* Install data files *) + f_data (); + + in + + let group_libs, _, _ = + findlib_mapping pkg + in + + (* We install libraries in groups *) + List.iter install_group_lib group_libs + in + + let install_execs pkg = + let install_exec data_exec = + let (cs, bs, exec) = + !exec_hook data_exec + in + if var_choose bs.bs_install && + BaseBuilt.is_built BaseBuilt.BExec cs.cs_name then + begin + let exec_libdir () = + Filename.concat + (libdir ()) + pkg.name + in + BaseBuilt.fold + BaseBuilt.BExec + cs.cs_name + (fun () fn -> + install_file + ~tgt_fn:(cs.cs_name ^ ext_program ()) + fn + bindir) + (); + BaseBuilt.fold + BaseBuilt.BExecLib + cs.cs_name + (fun () fn -> + install_file + fn + exec_libdir) + (); + install_data + bs.bs_path + bs.bs_data_files + (Filename.concat + (datarootdir ()) + pkg.name) + end + in + List.iter + (function + | Executable (cs, bs, exec)-> + install_exec (cs, bs, exec) + | _ -> + ()) + pkg.sections + in + + let install_docs pkg = + let install_doc data = + let (cs, doc) = + !doc_hook data + in + if var_choose doc.doc_install && + BaseBuilt.is_built BaseBuilt.BDoc cs.cs_name then + begin + let tgt_dir = + OASISHostPath.of_unix (var_expand doc.doc_install_dir) + in + BaseBuilt.fold + BaseBuilt.BDoc + cs.cs_name + (fun () fn -> + install_file + fn + (fun () -> tgt_dir)) + (); + install_data + Filename.current_dir_name + doc.doc_data_files + doc.doc_install_dir + end + in + List.iter + (function + | Doc (cs, doc) -> + install_doc (cs, doc) + | _ -> + ()) + pkg.sections + in + + install_libs pkg; + install_execs pkg; + install_docs pkg + + (* Uninstall already installed data *) + let uninstall _ argv = + List.iter + (fun (ev, data) -> + if ev = install_file_ev then + begin + if OASISFileUtil.file_exists_case data then + begin + info + (f_ "Removing file '%s'") + data; + Sys.remove data + end + else + begin + warning + (f_ "File '%s' doesn't exist anymore") + data + end + end + else if ev = install_dir_ev then + begin + if Sys.file_exists data && Sys.is_directory data then + begin + if Sys.readdir data = [||] then + begin + info + (f_ "Removing directory '%s'") + data; + OASISFileUtil.rmdir ~ctxt:!BaseContext.default data + end + else + begin + warning + (f_ "Directory '%s' is not empty (%s)") + data + (String.concat + ", " + (Array.to_list + (Sys.readdir data))) + end + end + else + begin + warning + (f_ "Directory '%s' doesn't exist anymore") + data + end + end + else if ev = install_findlib_ev then + begin + info (f_ "Removing findlib library '%s'") data; + OASISExec.run ~ctxt:!BaseContext.default + (ocamlfind ()) ["remove"; data] + end + else + failwithf (f_ "Unknown log event '%s'") ev; + BaseLog.unregister ev data) + (* We process event in reverse order *) + (List.rev + (BaseLog.filter + [install_file_ev; + install_dir_ev; + install_findlib_ev;])) + +end + + +# 5233 "setup.ml" +module OCamlbuildCommon = struct +(* # 21 "src/plugins/ocamlbuild/OCamlbuildCommon.ml" *) + + (** Functions common to OCamlbuild build and doc plugin + *) + + open OASISGettext + open BaseEnv + open BaseStandardVar + + let ocamlbuild_clean_ev = + "ocamlbuild-clean" + + let ocamlbuildflags = + var_define + ~short_desc:(fun () -> "OCamlbuild additional flags") + "ocamlbuildflags" + (fun () -> "") + + (** Fix special arguments depending on environment *) + let fix_args args extra_argv = + List.flatten + [ + if (os_type ()) = "Win32" then + [ + "-classic-display"; + "-no-log"; + "-no-links"; + "-install-lib-dir"; + (Filename.concat (standard_library ()) "ocamlbuild") + ] + else + []; + + if not (bool_of_string (is_native ())) || (os_type ()) = "Win32" then + [ + "-byte-plugin" + ] + else + []; + args; + + if bool_of_string (debug ()) then + ["-tag"; "debug"] + else + []; + + if bool_of_string (profile ()) then + ["-tag"; "profile"] + else + []; + + OASISString.nsplit (ocamlbuildflags ()) ' '; + + Array.to_list extra_argv; + ] + + (** Run 'ocamlbuild -clean' if not already done *) + let run_clean extra_argv = + let extra_cli = + String.concat " " (Array.to_list extra_argv) + in + (* Run if never called with these args *) + if not (BaseLog.exists ocamlbuild_clean_ev extra_cli) then + begin + OASISExec.run ~ctxt:!BaseContext.default + (ocamlbuild ()) (fix_args ["-clean"] extra_argv); + BaseLog.register ocamlbuild_clean_ev extra_cli; + at_exit + (fun () -> + try + BaseLog.unregister ocamlbuild_clean_ev extra_cli + with _ -> + ()) + end + + (** Run ocamlbuild, unregister all clean events *) + let run_ocamlbuild args extra_argv = + (* TODO: enforce that target in args must be UNIX encoded i.e. toto/index.html + *) + OASISExec.run ~ctxt:!BaseContext.default + (ocamlbuild ()) (fix_args args extra_argv); + (* Remove any clean event, we must run it again *) + List.iter + (fun (e, d) -> BaseLog.unregister e d) + (BaseLog.filter [ocamlbuild_clean_ev]) + + (** Determine real build directory *) + let build_dir extra_argv = + let rec search_args dir = + function + | "-build-dir" :: dir :: tl -> + search_args dir tl + | _ :: tl -> + search_args dir tl + | [] -> + dir + in + search_args "_build" (fix_args [] extra_argv) + +end + +module OCamlbuildPlugin = struct +(* # 21 "src/plugins/ocamlbuild/OCamlbuildPlugin.ml" *) + + (** Build using ocamlbuild + @author Sylvain Le Gall + *) + + open OASISTypes + open OASISGettext + open OASISUtils + open BaseEnv + open OCamlbuildCommon + open BaseStandardVar + open BaseMessage + + let cond_targets_hook = + ref (fun lst -> lst) + + let build pkg argv = + + (* Return the filename in build directory *) + let in_build_dir fn = + Filename.concat + (build_dir argv) + fn + in + + (* Return the unix filename in host build directory *) + let in_build_dir_of_unix fn = + in_build_dir (OASISHostPath.of_unix fn) + in + + let cond_targets = + List.fold_left + (fun acc -> + function + | Library (cs, bs, lib) when var_choose bs.bs_build -> + begin + let evs, unix_files = + BaseBuilt.of_library + in_build_dir_of_unix + (cs, bs, lib) + in + + let ends_with nd fn = + let nd_len = + String.length nd + in + (String.length fn >= nd_len) + && + (String.sub + fn + (String.length fn - nd_len) + nd_len) = nd + in + + let tgts = + List.flatten + (List.filter + (fun l -> l <> []) + (List.map + (List.filter + (fun fn -> + ends_with ".cma" fn + || ends_with ".cmxs" fn + || ends_with ".cmxa" fn + || ends_with (ext_lib ()) fn + || ends_with (ext_dll ()) fn)) + unix_files)) + in + + match tgts with + | _ :: _ -> + (evs, tgts) :: acc + | [] -> + failwithf + (f_ "No possible ocamlbuild targets for library %s") + cs.cs_name + end + + | Executable (cs, bs, exec) when var_choose bs.bs_build -> + begin + let evs, unix_exec_is, unix_dll_opt = + BaseBuilt.of_executable + in_build_dir_of_unix + (cs, bs, exec) + in + + let target ext = + let unix_tgt = + (OASISUnixPath.concat + bs.bs_path + (OASISUnixPath.chop_extension + exec.exec_main_is))^ext + in + let evs = + (* Fix evs, we want to use the unix_tgt, without copying *) + List.map + (function + | BaseBuilt.BExec, nm, lst when nm = cs.cs_name -> + BaseBuilt.BExec, nm, [[in_build_dir_of_unix unix_tgt]] + | ev -> + ev) + evs + in + evs, [unix_tgt] + in + + (* Add executable *) + let acc = + match bs.bs_compiled_object with + | Native -> + (target ".native") :: acc + | Best when bool_of_string (is_native ()) -> + (target ".native") :: acc + | Byte + | Best -> + (target ".byte") :: acc + in + acc + end + + | Library _ | Executable _ | Test _ + | SrcRepo _ | Flag _ | Doc _ -> + acc) + [] + (* Keep the pkg.sections ordered *) + (List.rev pkg.sections); + in + + (* Check and register built files *) + let check_and_register (bt, bnm, lst) = + List.iter + (fun fns -> + if not (List.exists OASISFileUtil.file_exists_case fns) then + failwithf + (f_ "No one of expected built files %s exists") + (String.concat (s_ ", ") (List.map (Printf.sprintf "'%s'") fns))) + lst; + (BaseBuilt.register bt bnm lst) + in + + let cond_targets = + (* Run the hook *) + !cond_targets_hook cond_targets + in + + (* Run a list of target... *) + run_ocamlbuild + (List.flatten + (List.map snd cond_targets)) + argv; + (* ... and register events *) + List.iter + check_and_register + (List.flatten (List.map fst cond_targets)) + + + let clean pkg extra_args = + run_clean extra_args; + List.iter + (function + | Library (cs, _, _) -> + BaseBuilt.unregister BaseBuilt.BLib cs.cs_name + | Executable (cs, _, _) -> + BaseBuilt.unregister BaseBuilt.BExec cs.cs_name; + BaseBuilt.unregister BaseBuilt.BExecLib cs.cs_name + | _ -> + ()) + pkg.sections + +end + +module OCamlbuildDocPlugin = struct +(* # 21 "src/plugins/ocamlbuild/OCamlbuildDocPlugin.ml" *) + + (* Create documentation using ocamlbuild .odocl files + @author Sylvain Le Gall + *) + + open OASISTypes + open OASISGettext + open OASISMessage + open OCamlbuildCommon + open BaseStandardVar + + + + let doc_build path pkg (cs, doc) argv = + let index_html = + OASISUnixPath.make + [ + path; + cs.cs_name^".docdir"; + "index.html"; + ] + in + let tgt_dir = + OASISHostPath.make + [ + build_dir argv; + OASISHostPath.of_unix path; + cs.cs_name^".docdir"; + ] + in + run_ocamlbuild [index_html] argv; + List.iter + (fun glb -> + BaseBuilt.register + BaseBuilt.BDoc + cs.cs_name + [OASISFileUtil.glob ~ctxt:!BaseContext.default + (Filename.concat tgt_dir glb)]) + ["*.html"; "*.css"] + + let doc_clean t pkg (cs, doc) argv = + run_clean argv; + BaseBuilt.unregister BaseBuilt.BDoc cs.cs_name + +end + + +# 5558 "setup.ml" +open OASISTypes;; + +let setup_t = + { + BaseSetup.configure = InternalConfigurePlugin.configure; + build = OCamlbuildPlugin.build; + test = []; + doc = [("API", OCamlbuildDocPlugin.doc_build ".")]; + install = InternalInstallPlugin.install; + uninstall = InternalInstallPlugin.uninstall; + clean = [OCamlbuildPlugin.clean]; + clean_test = []; + clean_doc = [("API", OCamlbuildDocPlugin.doc_clean ".")]; + distclean = []; + distclean_test = []; + distclean_doc = []; + package = + { + oasis_version = "0.3"; + ocaml_version = None; + findlib_version = None; + name = "benchmark"; + version = "1.3"; + license = + OASISLicense.DEP5License + (OASISLicense.DEP5Unit + { + OASISLicense.license = "LGPL"; + excption = Some "OCaml linking"; + version = OASISLicense.Version "3.0"; + }); + license_file = None; + copyrights = []; + maintainers = []; + authors = + ["Christophe Troestler <Christophe.Troestler@umons.ac.be>"]; + homepage = Some "http://ocaml-benchmark.forge.ocamlcore.org/"; + synopsis = "Benchmark running times of code."; + description = + Some + "This module provides a set of tools to measure the running times\nof your functions and to easily compare the results. A statistical\ntest is used to determine whether the results truly differ."; + categories = []; + conf_type = (`Configure, "internal", Some "0.3"); + conf_custom = + { + pre_command = [(OASISExpr.EBool true, None)]; + post_command = [(OASISExpr.EBool true, None)]; + }; + build_type = (`Build, "ocamlbuild", Some "0.3"); + build_custom = + { + pre_command = [(OASISExpr.EBool true, None)]; + post_command = [(OASISExpr.EBool true, None)]; + }; + install_type = (`Install, "internal", Some "0.3"); + install_custom = + { + pre_command = [(OASISExpr.EBool true, None)]; + post_command = [(OASISExpr.EBool true, None)]; + }; + uninstall_custom = + { + pre_command = [(OASISExpr.EBool true, None)]; + post_command = [(OASISExpr.EBool true, None)]; + }; + clean_custom = + { + pre_command = [(OASISExpr.EBool true, None)]; + post_command = [(OASISExpr.EBool true, None)]; + }; + distclean_custom = + { + pre_command = [(OASISExpr.EBool true, None)]; + post_command = [(OASISExpr.EBool true, None)]; + }; + files_ab = []; + sections = + [ + Flag + ({ + cs_name = "examples"; + cs_data = PropList.Data.create (); + cs_plugin_data = []; + }, + { + flag_description = + Some "Whether to compile the examples."; + flag_default = [(OASISExpr.EBool true, false)]; + }); + Library + ({ + cs_name = "benchmark"; + cs_data = PropList.Data.create (); + cs_plugin_data = []; + }, + { + bs_build = [(OASISExpr.EBool true, true)]; + bs_install = [(OASISExpr.EBool true, true)]; + bs_path = "."; + bs_compiled_object = Best; + bs_build_depends = [FindlibPackage ("unix", None)]; + bs_build_tools = [ExternalTool "ocamlbuild"]; + bs_c_sources = []; + bs_data_files = []; + bs_ccopt = [(OASISExpr.EBool true, [])]; + bs_cclib = [(OASISExpr.EBool true, [])]; + bs_dlllib = [(OASISExpr.EBool true, [])]; + bs_dllpath = [(OASISExpr.EBool true, [])]; + bs_byteopt = [(OASISExpr.EBool true, [])]; + bs_nativeopt = [(OASISExpr.EBool true, [])]; + }, + { + lib_modules = ["Benchmark"]; + lib_pack = false; + lib_internal_modules = []; + lib_findlib_parent = None; + lib_findlib_name = None; + lib_findlib_containers = []; + }); + Doc + ({ + cs_name = "API"; + cs_data = PropList.Data.create (); + cs_plugin_data = []; + }, + { + doc_type = (`Doc, "OCamlbuild", Some "0.3"); + doc_custom = + { + pre_command = [(OASISExpr.EBool true, None)]; + post_command = [(OASISExpr.EBool true, None)]; + }; + doc_build = + [ + (OASISExpr.ENot (OASISExpr.EFlag "docs"), false); + (OASISExpr.EFlag "docs", true) + ]; + doc_install = [(OASISExpr.EBool true, true)]; + doc_install_dir = "$docdir/api"; + doc_title = "API reference for Benchmark"; + doc_authors = []; + doc_abstract = None; + doc_format = OtherDoc; + doc_data_files = []; + doc_build_tools = + [ExternalTool "ocamldoc"; ExternalTool "ocamlbuild"]; + }); + Executable + ({ + cs_name = "ar_ba"; + cs_data = PropList.Data.create (); + cs_plugin_data = []; + }, + { + bs_build = + [ + (OASISExpr.EBool true, false); + (OASISExpr.EFlag "examples", true) + ]; + bs_install = [(OASISExpr.EBool true, false)]; + bs_path = "examples/"; + bs_compiled_object = Best; + bs_build_depends = + [ + InternalLibrary "benchmark"; + FindlibPackage ("bigarray", None) + ]; + bs_build_tools = [ExternalTool "ocamlbuild"]; + bs_c_sources = []; + bs_data_files = []; + bs_ccopt = [(OASISExpr.EBool true, [])]; + bs_cclib = [(OASISExpr.EBool true, [])]; + bs_dlllib = [(OASISExpr.EBool true, [])]; + bs_dllpath = [(OASISExpr.EBool true, [])]; + bs_byteopt = [(OASISExpr.EBool true, [])]; + bs_nativeopt = [(OASISExpr.EBool true, [])]; + }, + {exec_custom = false; exec_main_is = "ar_ba.ml"; }); + Executable + ({ + cs_name = "composition"; + cs_data = PropList.Data.create (); + cs_plugin_data = []; + }, + { + bs_build = + [ + (OASISExpr.EBool true, false); + (OASISExpr.EFlag "examples", true) + ]; + bs_install = [(OASISExpr.EBool true, false)]; + bs_path = "examples/"; + bs_compiled_object = Best; + bs_build_depends = [InternalLibrary "benchmark"]; + bs_build_tools = [ExternalTool "ocamlbuild"]; + bs_c_sources = []; + bs_data_files = []; + bs_ccopt = [(OASISExpr.EBool true, [])]; + bs_cclib = [(OASISExpr.EBool true, [])]; + bs_dlllib = [(OASISExpr.EBool true, [])]; + bs_dllpath = [(OASISExpr.EBool true, [])]; + bs_byteopt = [(OASISExpr.EBool true, [])]; + bs_nativeopt = [(OASISExpr.EBool true, [])]; + }, + {exec_custom = false; exec_main_is = "composition.ml"; }); + Executable + ({ + cs_name = "iter"; + cs_data = PropList.Data.create (); + cs_plugin_data = []; + }, + { + bs_build = + [ + (OASISExpr.EBool true, false); + (OASISExpr.EFlag "examples", true) + ]; + bs_install = [(OASISExpr.EBool true, false)]; + bs_path = "examples/"; + bs_compiled_object = Best; + bs_build_depends = + [ + InternalLibrary "benchmark"; + FindlibPackage ("bigarray", None) + ]; + bs_build_tools = [ExternalTool "ocamlbuild"]; + bs_c_sources = []; + bs_data_files = []; + bs_ccopt = [(OASISExpr.EBool true, [])]; + bs_cclib = [(OASISExpr.EBool true, [])]; + bs_dlllib = [(OASISExpr.EBool true, [])]; + bs_dllpath = [(OASISExpr.EBool true, [])]; + bs_byteopt = [(OASISExpr.EBool true, [])]; + bs_nativeopt = [(OASISExpr.EBool true, [])]; + }, + {exec_custom = false; exec_main_is = "iter.ml"; }); + Executable + ({ + cs_name = "let_try"; + cs_data = PropList.Data.create (); + cs_plugin_data = []; + }, + { + bs_build = + [ + (OASISExpr.EBool true, false); + (OASISExpr.EFlag "examples", true) + ]; + bs_install = [(OASISExpr.EBool true, false)]; + bs_path = "examples/"; + bs_compiled_object = Best; + bs_build_depends = + [ + InternalLibrary "benchmark"; + FindlibPackage ("bigarray", None) + ]; + bs_build_tools = [ExternalTool "ocamlbuild"]; + bs_c_sources = []; + bs_data_files = []; + bs_ccopt = [(OASISExpr.EBool true, [])]; + bs_cclib = [(OASISExpr.EBool true, [])]; + bs_dlllib = [(OASISExpr.EBool true, [])]; + bs_dllpath = [(OASISExpr.EBool true, [])]; + bs_byteopt = [(OASISExpr.EBool true, [])]; + bs_nativeopt = [(OASISExpr.EBool true, [])]; + }, + {exec_custom = false; exec_main_is = "let_try.ml"; }); + Executable + ({ + cs_name = "loops"; + cs_data = PropList.Data.create (); + cs_plugin_data = []; + }, + { + bs_build = + [ + (OASISExpr.EBool true, false); + (OASISExpr.EFlag "examples", true) + ]; + bs_install = [(OASISExpr.EBool true, false)]; + bs_path = "examples/"; + bs_compiled_object = Best; + bs_build_depends = [InternalLibrary "benchmark"]; + bs_build_tools = [ExternalTool "ocamlbuild"]; + bs_c_sources = []; + bs_data_files = []; + bs_ccopt = [(OASISExpr.EBool true, [])]; + bs_cclib = [(OASISExpr.EBool true, [])]; + bs_dlllib = [(OASISExpr.EBool true, [])]; + bs_dllpath = [(OASISExpr.EBool true, [])]; + bs_byteopt = [(OASISExpr.EBool true, [])]; + bs_nativeopt = [(OASISExpr.EBool true, [])]; + }, + {exec_custom = false; exec_main_is = "loops.ml"; }); + Executable + ({ + cs_name = "match_array"; + cs_data = PropList.Data.create (); + cs_plugin_data = []; + }, + { + bs_build = + [ + (OASISExpr.EBool true, false); + (OASISExpr.EFlag "examples", true) + ]; + bs_install = [(OASISExpr.EBool true, false)]; + bs_path = "examples/"; + bs_compiled_object = Best; + bs_build_depends = [InternalLibrary "benchmark"]; + bs_build_tools = [ExternalTool "ocamlbuild"]; + bs_c_sources = []; + bs_data_files = []; + bs_ccopt = [(OASISExpr.EBool true, [])]; + bs_cclib = [(OASISExpr.EBool true, [])]; + bs_dlllib = [(OASISExpr.EBool true, [])]; + bs_dllpath = [(OASISExpr.EBool true, [])]; + bs_byteopt = [(OASISExpr.EBool true, [])]; + bs_nativeopt = [(OASISExpr.EBool true, [])]; + }, + {exec_custom = false; exec_main_is = "match_array.ml"; }); + Executable + ({ + cs_name = "numbers"; + cs_data = PropList.Data.create (); + cs_plugin_data = []; + }, + { + bs_build = + [ + (OASISExpr.EBool true, false); + (OASISExpr.EFlag "examples", true) + ]; + bs_install = [(OASISExpr.EBool true, false)]; + bs_path = "examples/"; + bs_compiled_object = Best; + bs_build_depends = [InternalLibrary "benchmark"]; + bs_build_tools = [ExternalTool "ocamlbuild"]; + bs_c_sources = []; + bs_data_files = []; + bs_ccopt = [(OASISExpr.EBool true, [])]; + bs_cclib = [(OASISExpr.EBool true, [])]; + bs_dlllib = [(OASISExpr.EBool true, [])]; + bs_dllpath = [(OASISExpr.EBool true, [])]; + bs_byteopt = [(OASISExpr.EBool true, [])]; + bs_nativeopt = [(OASISExpr.EBool true, [])]; + }, + {exec_custom = false; exec_main_is = "numbers.ml"; }); + Executable + ({ + cs_name = "regexps"; + cs_data = PropList.Data.create (); + cs_plugin_data = []; + }, + { + bs_build = + [ + (OASISExpr.EBool true, false); + (OASISExpr.EFlag "examples", true) + ]; + bs_install = [(OASISExpr.EBool true, false)]; + bs_path = "examples/"; + bs_compiled_object = Best; + bs_build_depends = + [ + InternalLibrary "benchmark"; + FindlibPackage ("str", None); + FindlibPackage ("pcre", None) + ]; + bs_build_tools = [ExternalTool "ocamlbuild"]; + bs_c_sources = []; + bs_data_files = []; + bs_ccopt = [(OASISExpr.EBool true, [])]; + bs_cclib = [(OASISExpr.EBool true, [])]; + bs_dlllib = [(OASISExpr.EBool true, [])]; + bs_dllpath = [(OASISExpr.EBool true, [])]; + bs_byteopt = [(OASISExpr.EBool true, [])]; + bs_nativeopt = [(OASISExpr.EBool true, [])]; + }, + {exec_custom = false; exec_main_is = "regexps.ml"; }); + Executable + ({ + cs_name = "try_if"; + cs_data = PropList.Data.create (); + cs_plugin_data = []; + }, + { + bs_build = + [ + (OASISExpr.EBool true, false); + (OASISExpr.EFlag "examples", true) + ]; + bs_install = [(OASISExpr.EBool true, false)]; + bs_path = "examples/"; + bs_compiled_object = Best; + bs_build_depends = + [ + InternalLibrary "benchmark"; + FindlibPackage ("bigarray", None) + ]; + bs_build_tools = [ExternalTool "ocamlbuild"]; + bs_c_sources = []; + bs_data_files = []; + bs_ccopt = [(OASISExpr.EBool true, [])]; + bs_cclib = [(OASISExpr.EBool true, [])]; + bs_dlllib = [(OASISExpr.EBool true, [])]; + bs_dllpath = [(OASISExpr.EBool true, [])]; + bs_byteopt = [(OASISExpr.EBool true, [])]; + bs_nativeopt = [(OASISExpr.EBool true, [])]; + }, + {exec_custom = false; exec_main_is = "try_if.ml"; }); + Executable + ({ + cs_name = "func_record"; + cs_data = PropList.Data.create (); + cs_plugin_data = []; + }, + { + bs_build = + [ + (OASISExpr.EBool true, false); + (OASISExpr.EFlag "examples", true) + ]; + bs_install = [(OASISExpr.EBool true, false)]; + bs_path = "examples/"; + bs_compiled_object = Best; + bs_build_depends = [InternalLibrary "benchmark"]; + bs_build_tools = [ExternalTool "ocamlbuild"]; + bs_c_sources = []; + bs_data_files = []; + bs_ccopt = [(OASISExpr.EBool true, [])]; + bs_cclib = [(OASISExpr.EBool true, [])]; + bs_dlllib = [(OASISExpr.EBool true, [])]; + bs_dllpath = [(OASISExpr.EBool true, [])]; + bs_byteopt = [(OASISExpr.EBool true, [])]; + bs_nativeopt = [(OASISExpr.EBool true, [])]; + }, + {exec_custom = false; exec_main_is = "func_record.ml"; }); + Executable + ({ + cs_name = "long_run"; + cs_data = PropList.Data.create (); + cs_plugin_data = []; + }, + { + bs_build = + [ + (OASISExpr.EBool true, false); + (OASISExpr.EFlag "tests", true) + ]; + bs_install = [(OASISExpr.EBool true, false)]; + bs_path = "tests"; + bs_compiled_object = Best; + bs_build_depends = + [ + InternalLibrary "benchmark"; + FindlibPackage ("unix", None) + ]; + bs_build_tools = [ExternalTool "ocamlbuild"]; + bs_c_sources = []; + bs_data_files = []; + bs_ccopt = [(OASISExpr.EBool true, [])]; + bs_cclib = [(OASISExpr.EBool true, [])]; + bs_dlllib = [(OASISExpr.EBool true, [])]; + bs_dllpath = [(OASISExpr.EBool true, [])]; + bs_byteopt = [(OASISExpr.EBool true, [])]; + bs_nativeopt = [(OASISExpr.EBool true, [])]; + }, + {exec_custom = false; exec_main_is = "long_run.ml"; }); + SrcRepo + ({ + cs_name = "trunk"; + cs_data = PropList.Data.create (); + cs_plugin_data = []; + }, + { + src_repo_type = Svn; + src_repo_location = + "svn://scm.ocamlcore.org/svn/ocaml-benchmark/trunk"; + src_repo_browser = + Some + "https://forge.ocamlcore.org/scm/browser.php?group_id=197"; + src_repo_module = None; + src_repo_branch = None; + src_repo_tag = None; + src_repo_subdir = None; + }) + ]; + plugins = [(`Extra, "META", Some "0.2")]; + schema_data = PropList.Data.create (); + plugin_data = []; + }; + oasis_fn = Some "_oasis"; + oasis_version = "0.3.0"; + oasis_digest = + Some "\175\132\143\187\165\140\142\184\127(9\134{\018\211s"; + oasis_exec = None; + oasis_setup_args = []; + setup_update = false; + };; + +let setup () = BaseSetup.setup setup_t;; + +# 6063 "setup.ml" +(* OASIS_STOP *) +let () = setup ();; diff --git a/tests/long_run.ml b/tests/long_run.ml new file mode 100644 index 0000000..db71d83 --- /dev/null +++ b/tests/long_run.ml @@ -0,0 +1,16 @@ +(* Test that the test is performed correctly even when the test + execution time exceeds the max throughput time. *) + +open Benchmark + +let long () = + let s = ref 0. in + for i = 1 to 100_000 do + for j = 1 to 2_000 do + s := !s +. 1. + done + done + +let () = + let t = throughputN 1 ["long", long, ()] in + tabulate t |