├── opam ├── descr └── opam ├── test ├── Makefile ├── lib │ ├── unix │ │ ├── foo_stub.h │ │ ├── foo.ml │ │ └── foo_stub.c │ ├── lwt │ │ └── foo.ml │ └── async │ │ └── foo.ml ├── app │ └── app.ml ├── .gitignore ├── opam │ └── opam └── myocamlbuild.ml ├── demo ├── multi │ ├── lib │ │ ├── lwt │ │ │ ├── a.ml │ │ │ ├── b.ml │ │ │ └── b.mli │ │ └── async │ │ │ ├── a.ml │ │ │ ├── b.ml │ │ │ └── b.mli │ ├── Makefile │ ├── .gitignore │ ├── app │ │ └── my_app.ml │ └── myocamlbuild.ml ├── packed │ ├── lib │ │ ├── a.ml │ │ ├── b.ml │ │ └── b.mli │ ├── Makefile │ └── myocamlbuild.ml ├── files-in-subdir │ ├── lib │ │ ├── a.ml │ │ ├── b.ml │ │ └── b.mli │ ├── Makefile │ ├── .gitignore │ ├── app │ │ └── my_app.ml │ └── myocamlbuild.ml └── javascript │ ├── Makefile │ ├── hello.ml │ ├── readme.md │ └── myocamlbuild.ml ├── lib ├── solvuu-build.mllib ├── solvuu_build.mlpack ├── std.ml ├── solvuu.mk ├── solvuu_build_findlib.mli ├── solvuu_build_findlib.ml ├── solvuu_build_eliom.mli ├── util.mli ├── util.ml ├── solvuu_build_eliom.ml ├── tools.mli ├── project.mli ├── project.ml └── tools.ml ├── .merlin ├── .gitignore ├── _tags ├── .ocamlinit ├── Makefile ├── LICENSE ├── Changes.md ├── bin └── make-helper.ml └── README.md /opam/descr: -------------------------------------------------------------------------------- 1 | Solvuu's build system. -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | ../lib/solvuu.mk -------------------------------------------------------------------------------- /demo/multi/lib/lwt/a.ml: -------------------------------------------------------------------------------- 1 | let x = 42 2 | -------------------------------------------------------------------------------- /demo/packed/lib/a.ml: -------------------------------------------------------------------------------- 1 | let x = 42 2 | -------------------------------------------------------------------------------- /demo/packed/lib/b.ml: -------------------------------------------------------------------------------- 1 | let y = A.x 2 | -------------------------------------------------------------------------------- /demo/packed/lib/b.mli: -------------------------------------------------------------------------------- 1 | val y : int 2 | -------------------------------------------------------------------------------- /demo/multi/lib/async/a.ml: -------------------------------------------------------------------------------- 1 | let x = 42 2 | -------------------------------------------------------------------------------- /demo/multi/lib/async/b.ml: -------------------------------------------------------------------------------- 1 | let y = A.x 2 | -------------------------------------------------------------------------------- /demo/multi/lib/async/b.mli: -------------------------------------------------------------------------------- 1 | val y : int 2 | -------------------------------------------------------------------------------- /demo/multi/lib/lwt/b.ml: -------------------------------------------------------------------------------- 1 | let y = A.x 2 | -------------------------------------------------------------------------------- /demo/multi/lib/lwt/b.mli: -------------------------------------------------------------------------------- 1 | val y : int 2 | -------------------------------------------------------------------------------- /lib/solvuu-build.mllib: -------------------------------------------------------------------------------- 1 | Solvuu_build 2 | -------------------------------------------------------------------------------- /test/lib/unix/foo_stub.h: -------------------------------------------------------------------------------- 1 | #define K 42 2 | -------------------------------------------------------------------------------- /demo/files-in-subdir/lib/a.ml: -------------------------------------------------------------------------------- 1 | let x = 42 2 | -------------------------------------------------------------------------------- /demo/files-in-subdir/lib/b.ml: -------------------------------------------------------------------------------- 1 | let y = A.x 2 | -------------------------------------------------------------------------------- /demo/files-in-subdir/lib/b.mli: -------------------------------------------------------------------------------- 1 | val y : int 2 | -------------------------------------------------------------------------------- /test/lib/lwt/foo.ml: -------------------------------------------------------------------------------- 1 | let x = Lwt.return 42 2 | -------------------------------------------------------------------------------- /demo/multi/Makefile: -------------------------------------------------------------------------------- 1 | include $(shell opam config var solvuu-build:lib)/solvuu.mk 2 | -------------------------------------------------------------------------------- /test/lib/async/foo.ml: -------------------------------------------------------------------------------- 1 | open Async.Std 2 | 3 | let x = Deferred.return 42 4 | 5 | -------------------------------------------------------------------------------- /demo/javascript/Makefile: -------------------------------------------------------------------------------- 1 | include $(shell opam config var solvuu-build:lib)/solvuu.mk 2 | -------------------------------------------------------------------------------- /demo/packed/Makefile: -------------------------------------------------------------------------------- 1 | include $(shell opam config var solvuu-build:lib)/solvuu.mk 2 | -------------------------------------------------------------------------------- /test/app/app.ml: -------------------------------------------------------------------------------- 1 | let x = This_is_lwt.Foo.x 2 | 3 | let () = This_is_unix.Foo.foo () 4 | -------------------------------------------------------------------------------- /test/lib/unix/foo.ml: -------------------------------------------------------------------------------- 1 | let x = 1 2 | 3 | external foo : unit -> unit = "foo_stub" 4 | -------------------------------------------------------------------------------- /demo/files-in-subdir/Makefile: -------------------------------------------------------------------------------- 1 | include $(shell opam config var solvuu-build:lib)/solvuu.mk 2 | -------------------------------------------------------------------------------- /demo/javascript/hello.ml: -------------------------------------------------------------------------------- 1 | Js_of_ocaml.Firebug.console##log (Js.string "Hello from the other side") 2 | -------------------------------------------------------------------------------- /demo/multi/.gitignore: -------------------------------------------------------------------------------- 1 | /_build 2 | /*.native 3 | /*.byte 4 | /.merlin 5 | /.ocamlinit 6 | /*.install 7 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | /_build 2 | /this_is.install 3 | /.merlin 4 | /.ocamlinit 5 | /*.byte 6 | /*.native 7 | -------------------------------------------------------------------------------- /demo/multi/app/my_app.ml: -------------------------------------------------------------------------------- 1 | open Printf 2 | open My_project_async 3 | 4 | let () = printf "B.y = %d\n" B.y 5 | -------------------------------------------------------------------------------- /demo/files-in-subdir/.gitignore: -------------------------------------------------------------------------------- 1 | /_build 2 | /*.native 3 | /*.byte 4 | /.merlin 5 | /.ocamlinit 6 | /*.install 7 | -------------------------------------------------------------------------------- /demo/files-in-subdir/app/my_app.ml: -------------------------------------------------------------------------------- 1 | open Printf 2 | open My_project 3 | 4 | let () = printf "B.y = %d\n" B.y 5 | -------------------------------------------------------------------------------- /lib/solvuu_build.mlpack: -------------------------------------------------------------------------------- 1 | Std 2 | 3 | Project 4 | Solvuu_build_eliom 5 | Solvuu_build_findlib 6 | Tools 7 | Util 8 | -------------------------------------------------------------------------------- /.merlin: -------------------------------------------------------------------------------- 1 | FLG -w A-4-30-33-41-42-44-45-48 2 | SRC ./lib/** 3 | B ./_build/lib/** 4 | PKG findlib 5 | PKG ocamlbuild 6 | PKG ocamlgraph 7 | -------------------------------------------------------------------------------- /lib/std.ml: -------------------------------------------------------------------------------- 1 | module Project = Project 2 | module Eliom = Solvuu_build_eliom 3 | module Findlib = Solvuu_build_findlib 4 | module Tools = Tools 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /_build 2 | /solvuu-build.install 3 | /tmp 4 | .DS_Store 5 | /demo/*/_build 6 | /demo/*/.merlin 7 | /demo/*/.ocamlinit 8 | /demo/*/*.install 9 | -------------------------------------------------------------------------------- /lib/solvuu.mk: -------------------------------------------------------------------------------- 1 | OCAMLBUILD=ocamlbuild -use-ocamlfind -plugin-tag "package(solvuu-build)" 2 | 3 | include _build/project.mk 4 | 5 | _build/project.mk: 6 | $(OCAMLBUILD) $(notdir $@) 7 | -------------------------------------------------------------------------------- /demo/javascript/readme.md: -------------------------------------------------------------------------------- 1 | Building JavaScript using `js_of_ocaml`. 2 | 3 | 4 | ``` 5 | % make _build/hello.js 6 | [...] 7 | % node _build/hello.js 8 | Hello from the other side 9 | ``` 10 | -------------------------------------------------------------------------------- /_tags: -------------------------------------------------------------------------------- 1 | true: warn(A-4-30-33-41-42-44-45-48),annot,bin_annot,debug,short_paths 2 | true: package(findlib), package(ocamlbuild), package(ocamlgraph) 3 | and not "lib/solvuu_build.cmx": for-pack(Solvuu_build) 4 | "lib": include 5 | -------------------------------------------------------------------------------- /test/lib/unix/foo_stub.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "foo_stub.h" 7 | 8 | value foo_stub(value unit) { 9 | printf("foo! %d\n", K); 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /.ocamlinit: -------------------------------------------------------------------------------- 1 | let () = 2 | try Topdirs.dir_directory (Sys.getenv "OCAML_TOPLEVEL_PATH") 3 | with Not_found -> () 4 | ;; 5 | 6 | #use "topfind";; 7 | #thread;; 8 | #require "ocamlbuild ocamlgraph";; 9 | #directory "_build/lib";; 10 | #load "solvuu-build.cma";; 11 | open Solvuu_build.Std;; 12 | open Solvuu_build.Util;; 13 | -------------------------------------------------------------------------------- /test/opam/opam: -------------------------------------------------------------------------------- 1 | opam-version: "1.2" 2 | maintainer: "philippe.veber@gmail.com" 3 | authors: ["Solvuu LLC"] 4 | license: "ISC" 5 | 6 | build: [ 7 | [make "byte"] 8 | [make "native"] 9 | ] 10 | 11 | install: [ 12 | [make "META"] 13 | [make "%{name}%.install"] 14 | ] 15 | 16 | depends: [ 17 | "ocamlfind" {build} 18 | "ocamlbuild" {build} 19 | "solvuu_build" {build & = "dev"} 20 | "core" 21 | ] 22 | 23 | depopts: [ 24 | "async" 25 | "lwt" 26 | ] 27 | -------------------------------------------------------------------------------- /demo/packed/myocamlbuild.ml: -------------------------------------------------------------------------------- 1 | open Printf 2 | open Solvuu_build.Std 3 | 4 | let project_name = "my_project" 5 | let version = "dev" 6 | 7 | let lib : Project.item = Project.lib project_name 8 | ~dir:"lib" 9 | ~style:(`Pack project_name) 10 | ~install:(`Findlib project_name) 11 | 12 | let ocamlinit_postfix = [ 13 | sprintf "open %s" (String.capitalize project_name); 14 | ] 15 | 16 | ;; 17 | let () = 18 | Project.basic1 ~project_name ~version [lib] ~ocamlinit_postfix 19 | -------------------------------------------------------------------------------- /demo/files-in-subdir/myocamlbuild.ml: -------------------------------------------------------------------------------- 1 | open Printf 2 | open Solvuu_build.Std 3 | 4 | let project_name = "my_project" 5 | let version = "dev" 6 | 7 | let lib : Project.item = Project.lib project_name 8 | ~dir:"lib" 9 | ~style:(`Pack project_name) 10 | ~install:(`Findlib project_name) 11 | 12 | let app : Project.item = Project.app "my-app" 13 | ~file:"app/my_app.ml" 14 | ~internal_deps:[lib] 15 | 16 | let ocamlinit_postfix = [ 17 | sprintf "open %s" (String.capitalize project_name); 18 | ] 19 | 20 | ;; 21 | let () = Project.basic1 ~project_name ~version [lib;app] 22 | ~ocamlinit_postfix 23 | -------------------------------------------------------------------------------- /opam/opam: -------------------------------------------------------------------------------- 1 | opam-version: "1.2" 2 | maintainer: "Ashish Agarwal " 3 | authors: "Solvuu" 4 | homepage: "https://github.com/solvuu/solvuu-build" 5 | bug-reports: "https://github.com/solvuu/solvuu-build/issues" 6 | dev-repo: "https://github.com/solvuu/solvuu-build.git" 7 | license: "ISC" 8 | tags: ["org:solvuu"] 9 | 10 | build: [ 11 | [make "byte"] 12 | [make "native"] 13 | [make "_build/META"] 14 | [make "%{name}%.install"] 15 | ] 16 | 17 | depends: [ 18 | "ocamlfind" 19 | "ocamlbuild" 20 | "ocamlgraph" 21 | ] 22 | 23 | available: [ 24 | ocaml-version >= "4.02.0" 25 | ] 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROJECT=solvuu-build 2 | PACK=solvuu_build 3 | VERSION=dev 4 | OCAMLBUILD=ocamlbuild -use-ocamlfind 5 | DEMOS=$(sort $(dir $(wildcard demo/*/))) 6 | 7 | default: byte 8 | 9 | native: $(PROJECT).cmxa $(PROJECT).cmxs 10 | byte: $(PROJECT).cma 11 | 12 | %.cma %.cmxa %.cmxs: 13 | $(OCAMLBUILD) $@ 14 | 15 | clean: 16 | $(OCAMLBUILD) -clean 17 | rm -f $(PROJECT).install 18 | $(foreach demo,$(DEMOS),make -C $(demo) clean;) 19 | 20 | _build/META: 21 | mkdir -p _build 22 | ocaml bin/make-helper.ml META $(PROJECT) $(VERSION) > $@ 23 | 24 | solvuu-build.install: 25 | ocaml bin/make-helper.ml install $(PROJECT) $(PACK) > $@ 26 | 27 | test: 28 | $(foreach demo,$(DEMOS),make -C $(demo);) 29 | 30 | .PHONY: byte native clean test 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Solvuu LLC 2 | 3 | Permission to use, copy, modify, and/or distribute this software for 4 | any purpose with or without fee is hereby granted, provided that the 5 | above copyright notice and this permission notice appear in all 6 | copies. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 9 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 10 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 11 | AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 12 | DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 13 | PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 14 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 | PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /test/myocamlbuild.ml: -------------------------------------------------------------------------------- 1 | open Solvuu_build 2 | 3 | include Make(struct 4 | let name = "this_is" 5 | let version = "dev" 6 | 7 | let info = Info.of_list [ 8 | { 9 | Info.name = `Lib "unix"; 10 | libs = []; 11 | pkgs = ["core"]; 12 | build_if = []; 13 | }; 14 | 15 | { 16 | Info.name = `Lib "async"; 17 | libs = ["unix"]; 18 | pkgs = ["async"]; 19 | build_if = [`Pkgs_installed]; 20 | }; 21 | 22 | { 23 | Info.name = `Lib "lwt"; 24 | libs = ["unix"]; 25 | pkgs = ["lwt.preemptive"]; 26 | build_if = [`Pkgs_installed]; 27 | }; 28 | 29 | { 30 | Info.name = `App "app"; 31 | libs = ["unix" ; "lwt"]; 32 | pkgs = []; 33 | build_if = []; 34 | }; 35 | ] 36 | 37 | let ocamlinit_postfix = [] 38 | 39 | end) 40 | 41 | let () = dispatch() 42 | -------------------------------------------------------------------------------- /demo/multi/myocamlbuild.ml: -------------------------------------------------------------------------------- 1 | open Printf 2 | open Solvuu_build.Std 3 | open Solvuu_build.Util 4 | 5 | let project_name = "my_project" 6 | let version = "dev" 7 | 8 | let thread = () 9 | 10 | let make_lib ?findlib_deps ?internal_deps lib_name = 11 | let wrap_name = sprintf "%s_%s" project_name lib_name in 12 | Project.lib wrap_name 13 | ~thread 14 | ?findlib_deps ?internal_deps 15 | ~dir:(sprintf "lib/%s" lib_name) 16 | ~style:(`Pack wrap_name) 17 | ~install:(`Findlib (sprintf "%s.%s" project_name lib_name)) 18 | 19 | let make_app = Project.app ~thread 20 | 21 | let async = make_lib "async" 22 | ~findlib_deps:["async"] 23 | 24 | let lwt = make_lib "lwt" 25 | ~findlib_deps:["lwt"] 26 | 27 | let app = make_app "my_app" 28 | ~file:"app/my_app.ml" 29 | ~internal_deps:[async] 30 | 31 | let optional_pkgs = ["async"; "lwt"] 32 | 33 | let items = 34 | [async;lwt;app] |> 35 | List.filter ~f:(fun x -> Project.dep_opts_sat x optional_pkgs) 36 | 37 | ;; 38 | let () = Project.basic1 ~project_name ~version items 39 | -------------------------------------------------------------------------------- /demo/javascript/myocamlbuild.ml: -------------------------------------------------------------------------------- 1 | open Solvuu_build.Std 2 | open Solvuu_build.Util 3 | 4 | let app = Project.app "hello" 5 | ~file:"hello.ml" 6 | ~findlib_deps:[ 7 | "js_of_ocaml"; 8 | "js_of_ocaml.ppx"; 9 | ] 10 | 11 | 12 | let () = 13 | let items = [app] in 14 | 15 | ignore (Project.Graph.of_list items); 16 | 17 | let libs = Project.filter_libs items in 18 | let apps = Project.filter_apps items in 19 | 20 | Ocamlbuild_plugin.dispatch @@ function 21 | | Ocamlbuild_plugin.After_rules -> ( 22 | Ocamlbuild_plugin.clear_rules(); 23 | 24 | List.iter libs ~f:Project.build_lib; 25 | List.iter apps ~f:Project.build_app; 26 | List.iter items ~f:Project.build_js_of_ocaml; 27 | 28 | Project.build_static_file ".merlin" 29 | (fun () -> Project.merlin_file items); 30 | Project.build_static_file ".ocamlinit" 31 | (fun () -> Project.ocamlinit_file items); 32 | Project.build_static_file "project.mk" 33 | (fun () -> Project.makefile ~project_name:"hello" items); 34 | ) 35 | | _ -> () 36 | -------------------------------------------------------------------------------- /Changes.md: -------------------------------------------------------------------------------- 1 | # solvuu-build release notes 2 | 3 | ## solvuu-build 0.3.0 2017-03-14 4 | * Support additional OCaml compiler flags, particularly those relating 5 | to flambda and compilation of C code. See #42, #50, 6 | 77c80482da04911eceab93311c86cfb70361fff0. 7 | * Improve build times of targets in generated Makefile. See #58. 8 | * :warning: Allow faster building of static files. Backwards 9 | incompatibility: type of `Project.build_static_file` has 10 | changed. See #58. 11 | * :warning: Make installation of libs optional. Backwards 12 | incompatibility: `pkg` argument to make lib is now optional, renamed 13 | to `install`, and of a different type. See #35. 14 | * App installation is now optional. See #60. 15 | * Bug fixes. See #21, #51, #57. 16 | 17 | ## solvuu-build 0.2.0 2016-12-09 18 | * Add support for Eliom. 19 | * Improve generated .merlin file. 20 | * Support additional OCaml compiler flags. 21 | * Allow projects building only an app. 22 | * Various bug fixes. 23 | 24 | ## solvuu-build 0.1.0 2016-09-27 25 | * Renamed project to solvuu-build instead of solvuu_build. 26 | * Completely new API. 27 | * Add high level documentation in README. 28 | 29 | ## solvuu-build 0.0.2 2016-02-23 30 | * Support compilation of C files. 31 | * Installation of each sub-library of a project now goes into its own 32 | sub-directory. 33 | 34 | ## solvuu-build 0.0.1 2016-02-15 35 | * Initial release. 36 | -------------------------------------------------------------------------------- /bin/make-helper.ml: -------------------------------------------------------------------------------- 1 | open Printf 2 | 3 | module List = struct 4 | include List 5 | include ListLabels 6 | end 7 | 8 | (** Print .install file to stdout. *) 9 | let make_install_file ~project_name ~pack_name : unit = 10 | let module_files = 11 | Sys.readdir "lib" |> Array.to_list |> 12 | List.filter ~f:(fun x -> Filename.check_suffix x ".ml") |> 13 | List.map ~f:Filename.chop_extension |> 14 | List.map ~f:(fun x -> 15 | ["annot";"cmt";"cmti";"ml";"mli"] |> 16 | List.map ~f:(fun suffix -> sprintf "?_build/lib/%s.%s" x suffix) 17 | ) |> 18 | List.flatten 19 | in 20 | let pack_files = 21 | ["o";"obj";"cmi";"cmo";"cmt";"cmx"] |> 22 | List.map ~f:(fun suffix -> 23 | sprintf "?_build/lib/%s.%s" pack_name suffix 24 | ) 25 | in 26 | let lib_files = 27 | ["a";"cma";"cmxa";"cmxs"] |> 28 | List.map ~f:(fun suffix -> 29 | sprintf "?_build/lib/%s.%s" project_name suffix 30 | ) 31 | in 32 | let all_files = 33 | ["lib/solvuu.mk";"_build/META"]@lib_files@pack_files@module_files 34 | in 35 | let lines = 36 | ["lib: ["] 37 | @(List.map all_files ~f:(sprintf " \"%s\"")) 38 | @["]"] 39 | in 40 | List.iter lines ~f:print_endline 41 | 42 | let make_META_file ~project_name ~version = 43 | [ 44 | sprintf "version = \"%s\"" version; 45 | sprintf "archive(byte) = \"%s.cma\"" project_name; 46 | sprintf "archive(native) = \"%s.cmxa\"" project_name; 47 | sprintf "exists_if = \"%s.cma\"" project_name; 48 | sprintf "requires = \"findlib ocamlbuild ocamlgraph\""; 49 | ] |> 50 | List.iter ~f:print_endline 51 | 52 | ;; 53 | let () = match Sys.argv with 54 | | [|_; "install"; project_name; pack_name|] -> 55 | make_install_file ~project_name ~pack_name 56 | | [|_; "META"; project_name; version|] -> 57 | make_META_file ~project_name ~version 58 | | _ -> 59 | failwith "make-helper.ml: invalid usage" 60 | -------------------------------------------------------------------------------- /lib/solvuu_build_findlib.mli: -------------------------------------------------------------------------------- 1 | (** Findlib operations. Findlib supports package hierarchies, which 2 | are denoted as dot separated path. For example, "foo.bar" 3 | represents the "bar" sub-package of the parent package "foo". 4 | *) 5 | 6 | type pkg = string 7 | 8 | val installed : pkg -> bool 9 | 10 | (** Generate ocamlbuild's "use_foo" tag for given package 11 | name. Example: [to_use_tag "foo.bar"] returns ["use_foo_bar"]. *) 12 | val to_use_tag : pkg -> string 13 | 14 | (** Split [pkg] on dots to expose its path structure. *) 15 | val to_path : pkg -> string list 16 | 17 | val build_meta_file : ?prod:string -> Fl_metascanner.pkg_expr -> unit 18 | (** Register a rule to print out contents of given [pkg_expr] to 19 | path [prod], which by default is "./META". *) 20 | 21 | (******************************************************************************) 22 | (** {2 Graph Operations} *) 23 | (******************************************************************************) 24 | 25 | (** Items types and functions wrapped into a module. *) 26 | module T : sig 27 | type t = pkg 28 | val compare : t -> t -> int 29 | val equal : t -> t -> bool 30 | val hash : t -> int 31 | end 32 | 33 | module Graph : sig 34 | include module type of Graph.Persistent.Digraph.Concrete(T) 35 | 36 | module Dfs : module type of Graph.Traverse.Dfs( 37 | Graph.Persistent.Digraph.Concrete(T) 38 | ) 39 | 40 | module Topological : sig 41 | include module type of Graph.Topological.Make( 42 | Graph.Persistent.Digraph.Concrete(T) 43 | ) 44 | 45 | (** Return a topologically sorted vertex list of given graph. *) 46 | val sort : t -> V.t list 47 | end 48 | 49 | module Gml : sig 50 | val print : t -> string 51 | end 52 | 53 | val roots : t -> V.t list 54 | 55 | (** Construct a graph from a list of items. Raise exception if there 56 | are cycles or any other errors. *) 57 | val of_list : V.t list -> t 58 | end 59 | -------------------------------------------------------------------------------- /lib/solvuu_build_findlib.ml: -------------------------------------------------------------------------------- 1 | open Printf 2 | open Util 3 | 4 | type pkg = string 5 | 6 | (* This is necessary to query findlib *) 7 | let () = Findlib.init () 8 | 9 | let all_packages = 10 | Fl_package_base.list_packages () 11 | 12 | let installed x = List.mem x ~set:all_packages 13 | 14 | let to_use_tag x = 15 | String.map (function '.' -> '_' | c -> c) x 16 | |> sprintf "use_%s" 17 | 18 | let to_path x = 19 | String.split x ~on:'.' 20 | 21 | let build_meta_file ?(prod="META") x = 22 | Ocamlbuild_plugin.rule prod ~prod (fun _ _ -> 23 | let oc = open_out prod in 24 | Fl_metascanner.print oc x; 25 | close_out oc; 26 | Ocamlbuild_plugin.Nop 27 | ) 28 | 29 | 30 | (******************************************************************************) 31 | (** {2 Graph Operations} *) 32 | (******************************************************************************) 33 | module T = struct 34 | type nonrec t = pkg 35 | let compare = compare 36 | let hash = Hashtbl.hash 37 | let equal = (=) 38 | end 39 | 40 | module Graph = struct 41 | include Graph.Persistent.Digraph.Concrete(T) 42 | 43 | module Dfs = Graph.Traverse.Dfs( 44 | Graph.Persistent.Digraph.Concrete(T) 45 | ) 46 | 47 | module Topological = struct 48 | include Graph.Topological.Make( 49 | Graph.Persistent.Digraph.Concrete(T) 50 | ) 51 | 52 | let sort g = fold (fun x l -> x::l) g [] 53 | end 54 | 55 | module Gml = struct 56 | include Graph.Gml.Print 57 | (Graph.Persistent.Digraph.Concrete(T)) 58 | (struct 59 | let node x = [x, Graph.Gml.List []] 60 | let edge () = ["", Graph.Gml.List []] 61 | end) 62 | 63 | let print t = 64 | let buf = Buffer.create 30 in 65 | let fmt = Format.formatter_of_buffer buf in 66 | print fmt t; 67 | Buffer.contents buf 68 | 69 | end 70 | 71 | let roots g = 72 | fold_vertex 73 | (fun x accum -> if in_degree g x = 0 then x::accum else accum) 74 | g 75 | [] 76 | 77 | let of_list xs = 78 | let concat (prefix:string list) (x:string) = 79 | String.concat ~sep:"." (prefix@[x]) 80 | in 81 | if not (List.is_uniq ~cmp:T.compare xs) then 82 | failwith "multiple findlib packages have an identical name" 83 | else 84 | let g = 85 | List.fold_left xs ~init:empty ~f:(fun g x -> 86 | let rec loop g prefix parts = match parts with 87 | | [] -> g 88 | | x::[] -> ( 89 | let x' = concat prefix x in 90 | add_vertex g x' 91 | ) 92 | | x::y::l -> ( 93 | let x' = concat prefix x in 94 | let y' = concat (prefix@[x]) y in 95 | loop (add_edge g x' y') (prefix@[x]) (y::l) 96 | ) 97 | in 98 | loop g [] (to_path x) 99 | ) 100 | in 101 | if Dfs.has_cycle g then 102 | failwith "findlib package names form a cycle" 103 | else 104 | g 105 | 106 | end 107 | -------------------------------------------------------------------------------- /lib/solvuu_build_eliom.mli: -------------------------------------------------------------------------------- 1 | (** Build Eliom projects. This module is analogous to the main 2 | {!Project} module but tailored to Eliom. In your own projects, you 3 | will likely use either this module or {!Project}, not both. 4 | 5 | With Eliom, a single set of source files is compiled twice, once 6 | for the server side and once for the client side. The two 7 | libraries work in unison and thus the main [lib] type here really 8 | defines 2 libraries. Several constructs are parameterized over a 9 | [host] type, which can be either [`Server] or [`Client], to 10 | indicate this. 11 | 12 | We assume a lot more here than in {!Project}. For example, 13 | {!Project} very much supports the definition of multiple libraries 14 | within a single project. Here, we assume you're building just a 15 | single library ("single" for each of client and server). 16 | *) 17 | 18 | (** Findlib package name. *) 19 | type pkg = Solvuu_build_findlib.pkg 20 | 21 | type host = [ `Server | `Client ] 22 | 23 | type filename = string 24 | (** A filename without any directory component. A value [x] of this 25 | type is always used in the context of some [lib], and thus the 26 | full path of the file is understood to be [lib.dir/x]. *) 27 | 28 | type lib = private { 29 | name : string; 30 | style : [ `Pack of string ]; 31 | dir : string; 32 | findlib_deps : (host -> pkg list); 33 | ml_files : (host -> filename list); 34 | mli_files : (host -> filename list); 35 | 36 | annot : unit option; 37 | bin_annot : unit option; 38 | g : unit option; 39 | safe_string : unit option; 40 | short_paths : unit option; 41 | thread : unit option; 42 | w : string option; 43 | } 44 | 45 | (** Construct a lib from the given directory. Raise exception in case 46 | of any errors. *) 47 | val lib 48 | : ?annot:unit 49 | -> ?bin_annot:unit 50 | -> ?g:unit 51 | -> ?safe_string:unit 52 | -> ?short_paths:unit 53 | -> ?thread:unit 54 | -> ?w:string 55 | -> ?findlib_deps:(host -> pkg list) 56 | -> ml_files:(host -> filename list) 57 | -> mli_files:(host -> filename list) 58 | -> style : [ `Pack of string ] 59 | -> dir:string 60 | -> string 61 | -> lib 62 | 63 | (******************************************************************************) 64 | (** {2 Rules} *) 65 | (******************************************************************************) 66 | val build_lib : lib -> unit 67 | 68 | 69 | (******************************************************************************) 70 | (** {2 Low-level Functions} *) 71 | (******************************************************************************) 72 | val lib_path : host -> lib -> string 73 | (** Return path of library file for given host. The result is provided 74 | without any suffix. Add whichever you need, such as .cma, .cmx, or 75 | .js. *) 76 | 77 | val pack_path : host -> lib -> string 78 | (** Return path to packed module for given lib without any suffix. Add 79 | whichever you need, such as .cmo or .cmx. Raise exception if given 80 | [lib]'s style is not [`Pack _]. *) 81 | 82 | val module_path : host -> lib -> filename -> string 83 | (** [module_path host lib src] returns the path to the compiled module 84 | for given [src] file of [lib]. Result doesn't include any 85 | suffix. Add whichever you need, such as .cmi, .cmo, or .cmx. *) 86 | 87 | val intf_of_impl : string -> string 88 | (** Map given file's extension from .ml to .mli, or .eliom to 89 | .eliomi. Raise exception if given extension is neither of 90 | these. *) 91 | 92 | val intf_exists : host -> lib -> filename -> bool 93 | (** [intf_exists lib impl] returns true if an interface file is 94 | defined for the given implementation file [impl]. *) 95 | 96 | val is_shared_impl : lib -> filename -> bool 97 | (** [is_shared_impl lib x] returns true if [x] is an implementation 98 | file for both the client and server sides of [lib]. *) 99 | -------------------------------------------------------------------------------- /lib/util.mli: -------------------------------------------------------------------------------- 1 | val failwithf : ('r, unit, string, unit -> 'a) format4 -> 'r 2 | 3 | (** Raise exception if any item in given list is [exn], else return 4 | the list of all good pathnames. Note the input argument is the 5 | output type of Ocamlbuild's [builder]. *) 6 | val assert_all_outcomes : 7 | (Ocamlbuild_plugin.Pathname.t, exn) Ocamlbuild_plugin.Outcome.t list -> 8 | Ocamlbuild_plugin.Pathname.t list 9 | 10 | module Fn : sig 11 | val id : 'a -> 'a 12 | end 13 | 14 | module Char : sig 15 | include module type of Char 16 | val is_whitespace : char -> bool 17 | end 18 | 19 | module String : sig 20 | include module type of String 21 | 22 | val concat : sep:string -> string list -> string 23 | 24 | val for_all : string -> f:(char -> bool) -> bool 25 | 26 | val hash : string -> int 27 | val equal : string -> string -> bool 28 | val split : string -> on:char -> string list 29 | 30 | val is_suffix : t -> suffix:t -> bool 31 | val is_prefix : string -> prefix:string -> bool 32 | 33 | val drop_suffix : t -> int -> t 34 | val drop_prefix : t -> int -> t 35 | 36 | val chop_suffix_exn : t -> suffix:t -> t 37 | val chop_prefix_exn : t -> prefix:t -> t 38 | val chop_suffix : t -> suffix:t -> t option 39 | val chop_prefix : t -> prefix:t -> t option 40 | 41 | val suffix : t -> int -> t 42 | val prefix : t -> int -> t 43 | 44 | module Map : sig 45 | include module type of Map.Make(String) 46 | val to_list : 'a t -> (string * 'a) list 47 | end 48 | 49 | module Set : module type of Set.Make(String) 50 | end 51 | 52 | module List : sig 53 | include module type of List 54 | include module type of ListLabels 55 | 56 | val filter_map : 'a list -> f:('a -> 'b option) -> 'b list 57 | 58 | (** [diff a b] returns all items in [a] that are not in [b]. *) 59 | val diff : 'a list -> 'a list -> 'a list 60 | 61 | val sort_uniq : cmp:('a -> 'a -> int) -> 'a list -> 'a list 62 | 63 | (** [is_uniq cmp l] returns true if every item in list [l] is unique 64 | according to [cmp]. *) 65 | val is_uniq : cmp:('a -> 'a -> int) -> 'a list -> bool 66 | 67 | val last : 'a list -> 'a option 68 | val last_exn : 'a list -> 'a 69 | 70 | val intersperse : 'a list -> sep:'a -> 'a list 71 | 72 | module Assoc : sig 73 | val find : ('a * 'b) list -> 'a -> 'b option 74 | end 75 | 76 | end 77 | 78 | module Option : sig 79 | val map : 'a option -> f:('a -> 'b) -> 'b option 80 | val compare : ('a -> 'a -> int) -> 'a option -> 'a option -> int 81 | val is_some : 'a option -> bool 82 | val is_none : 'a option -> bool 83 | val value_exn : 'a option -> 'a 84 | end 85 | 86 | module Unit : sig 87 | val compare : unit -> unit -> int 88 | end 89 | 90 | module Filename : sig 91 | include module type of Filename 92 | 93 | (** [replace_suffix old new s] replaces suffix [old] in [s] with 94 | [new]. Returns [None] if [s] doesn't end with suffix [old]. *) 95 | val replace_suffix : old:string -> new_:string -> string -> string option 96 | 97 | (** Like [replace_suffix] but raise exception if [s] doesn't end 98 | with [old]. *) 99 | val replace_suffix_exn : old:string -> new_:string -> string -> string 100 | 101 | (** Make some effort to normalize paths, so that semantically 102 | equivalent paths will be syntactically equivalent. In particular, 103 | this helps to workaround an 104 | {{:https://github.com/ocaml/ocamlbuild/issues/76}ocamlbuild bug}. *) 105 | val normalize : string -> string 106 | end 107 | 108 | module Sys : sig 109 | include module type of Sys 110 | 111 | (** [sub_dirs dir] returns the sub-directories of [dir]. Result does 112 | not include [dir] itself and paths are relative to 113 | [dir]. Traversal is done to [depth], e.g. passing [~depth:1] 114 | will give only the immediate sub-directories. Default: [depth] 115 | is 1. *) 116 | val sub_dirs : ?depth:int -> string -> string list 117 | 118 | end 119 | 120 | (** Functions related to Ocamlbuild's rules. *) 121 | module Rule : sig 122 | open Ocamlbuild_plugin 123 | 124 | (** Generate a name from given [deps] and [prods]. This saves the 125 | trouble of having to think of a name manually, and makes rule names 126 | systematic. *) 127 | val name : deps:string list -> prods:string list -> string 128 | 129 | (** Like Ocamlbuild's [rule] function, except: 130 | 131 | - [name]: By default it is computed by the [name] function 132 | above. 133 | 134 | - [dep],[prod]: Not provided as you can always use [deps], 135 | [prods]. 136 | 137 | - [deps], [prods]: All items are passed through 138 | [Filename.normalize]. 139 | *) 140 | val rule : 141 | ?name:string -> 142 | ?deps:string list -> 143 | ?prods:string list -> 144 | ?stamp:string -> 145 | ?insert:[`top | `before of string | `after of string | `bottom] -> 146 | ?doc:string -> 147 | action -> 148 | unit 149 | 150 | end 151 | 152 | (******************************************************************************) 153 | (** Command.spec helper functions *) 154 | (******************************************************************************) 155 | module Spec : sig 156 | open Ocamlbuild_plugin 157 | 158 | val string : 159 | delim:[`Space | `None | `Equal] -> 160 | string -> string option -> spec option list 161 | 162 | val string_list : 163 | delim:[`Space | `None | `Equal] -> 164 | string -> string list option -> spec option list 165 | 166 | val unit : string -> unit option -> spec option list 167 | 168 | val int : 169 | delim:[`Space | `None | `Equal] -> 170 | string -> int option -> spec option list 171 | 172 | val specs_to_command : spec option list list -> Command.t 173 | 174 | (** Return a spec from given command. Raise exception if not 175 | possible. *) 176 | val spec_of_command : Command.t -> spec 177 | end 178 | -------------------------------------------------------------------------------- /lib/util.ml: -------------------------------------------------------------------------------- 1 | open Printf 2 | 3 | let failwithf fmt = ksprintf (fun s () -> failwith s) fmt 4 | 5 | (* Code in this module copied from the Core suite 6 | [https://github.com/janestreet/]. See there for license 7 | information. *) 8 | module Core = struct 9 | module List = struct 10 | let rev_filter_map l ~f = 11 | let rec loop l accum = 12 | match l with 13 | | [] -> accum 14 | | hd :: tl -> 15 | match f hd with 16 | | Some x -> loop tl (x :: accum) 17 | | None -> loop tl accum 18 | in 19 | loop l [] 20 | 21 | let filter_map l ~f = List.rev (rev_filter_map l ~f) 22 | 23 | let intersperse t ~sep = 24 | match t with 25 | | [] -> [] 26 | | x :: xs -> x :: List.fold_right (fun y acc -> sep :: y :: acc) xs [] 27 | 28 | end 29 | 30 | module Char = struct 31 | let equal = (=) 32 | end 33 | 34 | module String = struct 35 | module String = BytesLabels 36 | 37 | let for_all = 38 | let rec loop s i ~len ~f = 39 | i = len || (f s.[i] && loop s (i + 1) ~len ~f) 40 | in 41 | fun s ~f -> loop s 0 ~len:(String.length s) ~f 42 | 43 | let rec char_list_mem l (c:char) = 44 | match l with 45 | | [] -> false 46 | | hd::tl -> hd = c || char_list_mem tl c 47 | 48 | let split_gen str ~on = 49 | let is_delim = 50 | match on with 51 | | `char c' -> (fun c -> c = c') 52 | | `char_list l -> (fun c -> char_list_mem l c) 53 | in 54 | let len = String.length str in 55 | let rec loop acc last_pos pos = 56 | if pos = -1 then 57 | String.sub str ~pos:0 ~len:last_pos :: acc 58 | else 59 | if is_delim str.[pos] then 60 | let pos1 = pos + 1 in 61 | let sub_str = String.sub str ~pos:pos1 ~len:(last_pos - pos1) in 62 | loop (sub_str :: acc) pos (pos - 1) 63 | else loop acc last_pos (pos - 1) 64 | in 65 | loop [] len (len - 1) 66 | 67 | let split str ~on = split_gen str ~on:(`char on) ;; 68 | 69 | let is_suffix_gen = 70 | let rec loop s ~suffix ~char_equal idx_suff idx = 71 | idx_suff < 0 72 | || ((char_equal suffix.[idx_suff] s.[idx]) 73 | && loop s ~suffix ~char_equal (idx_suff - 1) (idx - 1)) 74 | in 75 | fun s ~suffix ~char_equal -> 76 | let len = String.length s in 77 | let len_suffix = String.length suffix in 78 | len >= len_suffix 79 | && loop s ~suffix ~char_equal (len_suffix - 1) (len - 1) 80 | ;; 81 | 82 | let is_prefix_gen = 83 | let rec loop s ~prefix ~char_equal i = 84 | i < 0 85 | || ((char_equal prefix.[i] s.[i]) 86 | && loop s ~prefix ~char_equal (i - 1)) 87 | in 88 | fun s ~prefix ~char_equal -> 89 | let prefix_len = String.length prefix in 90 | String.length s >= prefix_len 91 | && loop s ~prefix ~char_equal (prefix_len - 1) 92 | ;; 93 | 94 | let is_suffix s ~suffix = is_suffix_gen s ~suffix ~char_equal:Char.equal 95 | let is_prefix s ~prefix = is_prefix_gen s ~prefix ~char_equal:Char.equal 96 | 97 | let wrap_sub_n t n ~name ~pos ~len ~on_error = 98 | if n < 0 then 99 | invalid_arg (name ^ " expecting nonnegative argument") 100 | else 101 | try 102 | String.sub t ~pos ~len 103 | with _ -> 104 | on_error 105 | 106 | let drop_prefix t n = 107 | wrap_sub_n ~name:"drop_prefix" t n ~pos:n 108 | ~len:(String.length t - n) ~on_error:"" 109 | let drop_suffix t n = 110 | wrap_sub_n ~name:"drop_suffix" t n ~pos:0 111 | ~len:(String.length t - n) ~on_error:"" 112 | let prefix t n = 113 | wrap_sub_n ~name:"prefix" t n ~pos:0 ~len:n ~on_error:t 114 | let suffix t n = 115 | wrap_sub_n ~name:"suffix" t n ~pos:(String.length t - n) 116 | ~len:n ~on_error:t 117 | 118 | let chop_prefix s ~prefix = 119 | if is_prefix s ~prefix then 120 | Some (drop_prefix s (String.length prefix)) 121 | else 122 | None 123 | 124 | let chop_prefix_exn s ~prefix = 125 | match chop_prefix s ~prefix with 126 | | Some str -> str 127 | | None -> 128 | raise (Invalid_argument 129 | (Printf.sprintf "chop_prefix_exn %S %S" s prefix)) 130 | 131 | let chop_suffix s ~suffix = 132 | if is_suffix s ~suffix then 133 | Some (drop_suffix s (String.length suffix)) 134 | else 135 | None 136 | 137 | let chop_suffix_exn s ~suffix = 138 | match chop_suffix s ~suffix with 139 | | Some str -> str 140 | | None -> 141 | raise (Invalid_argument 142 | (Printf.sprintf "chop_suffix_exn %S %S" s suffix)) 143 | 144 | end 145 | end 146 | 147 | module Fn = struct 148 | let id x = x 149 | end 150 | 151 | module Char = struct 152 | include Char 153 | 154 | let is_whitespace = function 155 | | ' ' | '\r' | '\n' | '\t' -> true 156 | | _ -> false 157 | 158 | end 159 | 160 | module String = struct 161 | include String 162 | 163 | let concat = StringLabels.concat 164 | let for_all = Core.String.for_all 165 | 166 | let hash = Hashtbl.hash 167 | let equal = ( = ) 168 | let split = Core.String.split 169 | 170 | let is_suffix = Core.String.is_suffix 171 | let is_prefix = Core.String.is_prefix 172 | let drop_suffix = Core.String.drop_suffix 173 | let drop_prefix = Core.String.drop_prefix 174 | let chop_suffix_exn = Core.String.chop_suffix_exn 175 | let chop_prefix_exn = Core.String.chop_prefix_exn 176 | let chop_suffix = Core.String.chop_suffix 177 | let chop_prefix = Core.String.chop_prefix 178 | let suffix = Core.String.suffix 179 | let prefix = Core.String.prefix 180 | 181 | module Map = struct 182 | include Map.Make(String) 183 | 184 | let to_list t = 185 | fold (fun key a accum -> (key,a)::accum) t [] |> 186 | List.rev 187 | end 188 | 189 | module Set = Set.Make(String) 190 | end 191 | 192 | module List = struct 193 | include List 194 | include ListLabels 195 | 196 | let filter_map = Core.List.filter_map 197 | 198 | let diff a b = 199 | filter a ~f:(fun x -> not (mem x ~set:b)) 200 | 201 | (* ListLabels.sort_uniq was introduced in OCaml 4.03.0. Prior 202 | versions (since 4.02.0) had only List.sort_uniq without a labeled 203 | argument. The implementation below, including turning off warning 204 | 6, allows us to compile this code on all OCaml >= 4.02.0. *) 205 | let sort_uniq ~cmp l = 206 | sort_uniq cmp l [@@ocaml.warning "-6"] 207 | 208 | let is_uniq ~cmp (l : 'a list) : bool = 209 | let m = length l in 210 | let n = length (sort_uniq ~cmp l) in 211 | m = n 212 | 213 | let last l = match List.rev l with 214 | | [] -> None 215 | | x::_ -> Some x 216 | 217 | let last_exn l = List.rev l |> List.hd 218 | 219 | let intersperse = Core.List.intersperse 220 | 221 | module Assoc = struct 222 | let find l x = try Some (assoc x l) with Not_found -> None 223 | end 224 | 225 | end 226 | 227 | module Option = struct 228 | let map x ~f = match x with None -> None | Some y -> Some (f y) 229 | 230 | let compare cmp x y = 231 | match x,y with 232 | | None, None -> 0 233 | | None, Some _ -> -1 234 | | Some _, None -> 1 235 | | Some x, Some y -> cmp x y 236 | 237 | let is_some = function Some _ -> true | None -> false 238 | let is_none = function Some _ -> false | None -> true 239 | 240 | let value_exn = function 241 | | Some x -> x 242 | | None -> failwith "value_exn: got None" 243 | 244 | end 245 | 246 | module Unit = struct 247 | let compare () () = 0 248 | end 249 | 250 | module Filename = struct 251 | include Filename 252 | 253 | let replace_suffix ~old ~new_ s = 254 | match check_suffix s old with 255 | | false -> None 256 | | true -> Some (sprintf "%s%s" (chop_suffix s old) new_) 257 | 258 | let replace_suffix_exn ~old ~new_ s = 259 | match replace_suffix ~old ~new_ s with 260 | | Some x -> x 261 | | None -> failwithf "%s doesn't end with %s" s old () 262 | 263 | let normalize x = 264 | String.split ~on:'/' x |> 265 | List.filter_map ~f:(function "." | "" -> None | x -> Some x) |> 266 | String.concat ~sep:"/" |> function 267 | | "" -> "." 268 | | x -> x 269 | 270 | end 271 | 272 | module Rule = struct 273 | 274 | let name0 ~deps ~prods = 275 | sprintf "%s -> %s" 276 | (String.concat ~sep:"," deps) 277 | (String.concat ~sep:"," prods) 278 | 279 | let name = name0 280 | 281 | let rule ?name ?deps ?prods ?stamp ?insert ?doc action = 282 | let normalize_l = function 283 | | None -> None 284 | | Some l -> Some (List.map l ~f:Filename.normalize) 285 | in 286 | let deps = normalize_l deps in 287 | let prods = normalize_l prods in 288 | let name = match name with 289 | | Some x -> x 290 | | None -> 291 | let deps = match deps with None -> [] | Some x -> x in 292 | let stamp = match stamp with None -> [] | Some x -> [x] in 293 | let prods = (match prods with None -> [] | Some x -> x)@stamp in 294 | name0 ~deps ~prods 295 | in 296 | Ocamlbuild_plugin.rule name ?deps ?prods ?stamp ?insert ?doc action 297 | 298 | end 299 | 300 | module Sys = struct 301 | include Sys 302 | 303 | let sub_dirs ?(depth=1) root = 304 | let root = Filename.normalize root in 305 | let (/) = Filename.concat in 306 | let immediate_sub_dirs dir = 307 | Sys.readdir dir |> Array.to_list |> 308 | List.filter_map ~f:(fun x -> 309 | let x = dir/x in 310 | match file_exists x && is_directory x with 311 | | true -> Some x 312 | | false -> None 313 | ) 314 | in 315 | let rec loop depth accum dirs = 316 | let immediate_sub_dirs = 317 | List.map dirs ~f:immediate_sub_dirs |> 318 | List.flatten 319 | in 320 | let accum = accum@immediate_sub_dirs in 321 | if depth <= 1 322 | then accum 323 | else loop (depth-1) accum immediate_sub_dirs 324 | in 325 | loop depth [] [root] |> 326 | List.map ~f:(String.chop_prefix_exn ~prefix:(sprintf "%s/" root)) 327 | 328 | end 329 | 330 | module Spec = struct 331 | module List0 = List 332 | open Ocamlbuild_plugin 333 | module List = List0 334 | 335 | let string ~delim (flag:string) (value:string option) = 336 | match value with 337 | | None -> [None] 338 | | Some value -> 339 | match delim with 340 | | `Space -> [Some (A flag); Some (A value)] 341 | | `None -> [Some (A (flag ^ value))] 342 | | `Equal -> [Some (A (sprintf "%s=%s" flag value))] 343 | 344 | let string_list ~delim (flag:string) (value:string list option) = 345 | match value with 346 | | None -> [None] 347 | | Some l -> 348 | List.map l ~f:(fun x -> 349 | string ~delim flag (Some x) 350 | ) |> 351 | List.flatten 352 | 353 | let unit (flag:string) (value:unit option) = match value with 354 | | None -> [None] 355 | | Some () -> [Some (A flag)] 356 | 357 | let int ~delim (flag:string) (value:int option) = match value with 358 | | None -> [None] 359 | | Some value -> 360 | match delim with 361 | | `Space -> [Some (A flag); Some (A (string_of_int value))] 362 | | `None -> [Some (A (flag ^ (string_of_int value)))] 363 | | `Equal -> [Some (A (sprintf "%s=%d" flag value))] 364 | 365 | let specs_to_command (specs : spec option list list) : Command.t = 366 | List.flatten specs 367 | |> List.filter_map ~f:Fn.id 368 | |> fun l -> Cmd (S l) 369 | 370 | let spec_of_command (x:Command.t) : Command.spec = 371 | match x with 372 | | Command.Cmd x -> x 373 | | Command.Seq _ -> failwith "cannot extract spec from sequence of commands" 374 | | Command.Echo _ -> failwith "cannot convert Command.Echo to spec" 375 | | Command.Nop -> failwith "cannot convert Command.Nop to spec" 376 | 377 | end 378 | 379 | let assert_all_outcomes l = 380 | let rec loop accum = function 381 | | [] -> accum 382 | | (Ocamlbuild_plugin.Outcome.Good x)::l -> loop (x::accum) l 383 | | (Ocamlbuild_plugin.Outcome.Bad exn)::_ -> raise exn 384 | in 385 | loop [] l |> 386 | List.rev 387 | -------------------------------------------------------------------------------- /lib/solvuu_build_eliom.ml: -------------------------------------------------------------------------------- 1 | open Printf 2 | open Util 3 | open Util.Filename 4 | let (/) = Util.Filename.concat 5 | 6 | type pkg = Solvuu_build_findlib.pkg 7 | type host = [ `Server | `Client ] 8 | type filename = string 9 | 10 | type lib = { 11 | name : string; 12 | style : [ `Pack of string ]; 13 | dir : string; 14 | findlib_deps : (host -> pkg list); 15 | ml_files : (host -> filename list); 16 | mli_files : (host -> filename list); 17 | 18 | annot : unit option; 19 | bin_annot : unit option; 20 | g : unit option; 21 | safe_string : unit option; 22 | short_paths : unit option; 23 | thread : unit option; 24 | w : string option; 25 | } 26 | 27 | (* Logically one should include eliom.ppx.server and eliom.ppx.client 28 | in [findlib_deps]. However one must not do so when using eliom's 29 | command line tools. They add these packages automatically and 30 | adding them again causes mysterious runtime errors. *) 31 | let eliom_tools_workaround findlib_deps = function 32 | | `Server -> ( 33 | findlib_deps `Server |> 34 | List.filter ~f:((<>) "eliom.ppx.server") 35 | ) 36 | | `Client -> ( 37 | findlib_deps `Client |> 38 | List.filter ~f:((<>) "eliom.ppx.client") 39 | ) 40 | 41 | let lib 42 | ?annot ?bin_annot ?g ?safe_string ?short_paths ?thread ?w 43 | ?findlib_deps ~ml_files ~mli_files ~style ~dir name 44 | = 45 | { 46 | annot; 47 | bin_annot; 48 | g; 49 | safe_string; 50 | short_paths; 51 | thread; 52 | w; 53 | findlib_deps = (match findlib_deps with 54 | | Some f -> f 55 | | None -> function `Server | `Client -> [] 56 | ); 57 | ml_files; 58 | mli_files; 59 | style; 60 | dir; 61 | name; 62 | } 63 | 64 | (******************************************************************************) 65 | (** {2 Low-level Functions} *) 66 | (******************************************************************************) 67 | let outdir = function `Server -> "_server" | `Client -> "_client" 68 | 69 | let lib_path host lib = 70 | (outdir host)/(dirname lib.dir)/lib.name 71 | 72 | let pack_path host lib = 73 | match lib.style with 74 | | `Pack pack_name -> (outdir host)/(dirname lib.dir)/pack_name 75 | 76 | let module_path host lib file = 77 | (outdir host)/lib.dir/(chop_extension file) 78 | 79 | let intf_of_impl x = 80 | match String.chop_suffix x ~suffix:".ml" with 81 | | Some base -> base^".mli" 82 | | None -> 83 | match String.chop_suffix x ~suffix:".eliom" with 84 | | Some base -> base^".eliomi" 85 | | None -> 86 | failwithf "implementation file %s has unexpected extension" x () 87 | 88 | let intf_exists host lib impl = 89 | intf_of_impl impl |> 90 | List.mem ~set:(lib.mli_files host) 91 | 92 | let is_shared_impl lib x = 93 | (List.mem ~set:(lib.ml_files `Client) x) 94 | && (List.mem ~set:(lib.ml_files `Server) x) 95 | 96 | let obj_suffix = function `Byte -> ".cmo" | `Native -> ".cmx" 97 | let lib_suffix = 98 | function `Byte -> ".cma" | `Native -> ".cmxa" | `JavaScript -> ".js" 99 | 100 | 101 | (******************************************************************************) 102 | (** {2 Rules} *) 103 | (******************************************************************************) 104 | let mkdir dir = 105 | let cmd = sprintf "mkdir -p %s" dir in 106 | match Sys.command cmd with 107 | | 0 -> () 108 | | x -> failwithf "%s: returned with exit code %d" cmd x () 109 | 110 | (* Assure all source files have been copied (or built in case any are 111 | generated) to build directory. *) 112 | let build_all_src_files build lib = 113 | List.map [`Server; `Client] ~f:(fun host -> 114 | [lib.ml_files host; lib.mli_files host] 115 | ) |> 116 | List.flatten |> 117 | List.flatten |> 118 | List.sort_uniq ~cmp:String.compare |> 119 | List.map ~f:(fun x -> [lib.dir/x]) |> 120 | build |> assert_all_outcomes |> ignore 121 | 122 | (* Compile all given implementation files in dependency order. Return 123 | list of generated object files, also in dependency order. *) 124 | let build_impl_files_sorted build host mode ~package impl_files = 125 | let suffix = obj_suffix mode in 126 | let obj_files = 127 | Tools.run_eliomdep_sort ~ppx:() ~package host impl_files |> 128 | List.map ~f:(fun x -> (outdir host)/(chop_extension x)^suffix) 129 | in 130 | printf "building sorted object files: %s\n" 131 | (String.concat ~sep:"," obj_files) 132 | ; 133 | let () = List.iter obj_files ~f:(fun x -> 134 | match build [[x]] with 135 | | [Ocamlbuild_plugin.Outcome.Good _] -> () 136 | | [Ocamlbuild_plugin.Outcome.Bad exn] -> raise exn 137 | | _ -> assert false 138 | ) 139 | in 140 | obj_files 141 | 142 | (* Determine [file]'s dependencies and build them, where [typ] 143 | controls which type of file you care about the dependencies of. For 144 | example, given [file = "foo.ml"], you might either want to build 145 | foo.cmx's or foo.cmo's dependencies. If [file] is an mli, surely 146 | you want [typ = `cmi]. *) 147 | let build_deps build host typ ~pathI ~package lib file = 148 | 149 | (* Doing this always might be overkill, but it definitely matters in 150 | some cases. If all source files aren't present, sometimes you get 151 | incomplete dependencies. *) 152 | build_all_src_files build lib; 153 | 154 | let base = chop_extension file in 155 | let suffix = match typ with 156 | | `type_mli -> ".type_mli" 157 | | `cmx -> ".cmx" 158 | | `cmo -> ".cmo" 159 | | `cmi -> ".cmi" 160 | in 161 | let target = (outdir host)/base^suffix in 162 | let deps = 163 | Tools.run_eliomdep ~fix320:() ~pathI ~package ~ppx:() host [file] 164 | |> fun l -> 165 | match List.Assoc.find l target with 166 | | None -> [] 167 | | Some deps -> deps 168 | in 169 | printf "building dynamic dependencies of %s: %s\n" 170 | target (String.concat ~sep:"," deps) 171 | ; 172 | List.map deps ~f:(fun x -> [x]) |> 173 | build |> assert_all_outcomes |> ignore 174 | 175 | let build_lib_of_host host lib = 176 | let lib = { 177 | lib with 178 | findlib_deps = eliom_tools_workaround lib.findlib_deps; 179 | } 180 | in 181 | 182 | (****************************************************************************) 183 | (* Parameters *) 184 | (****************************************************************************) 185 | let annot = lib.annot in 186 | let bin_annot = lib.bin_annot in 187 | let g = lib.g in 188 | let safe_string = lib.safe_string in 189 | let short_paths = lib.short_paths in 190 | let thread = lib.thread in 191 | let w = lib.w in 192 | 193 | let ml_files = lib.ml_files host in 194 | let mli_files = lib.mli_files host in 195 | let package = lib.findlib_deps host in 196 | let for_pack = match lib.style with 197 | | `Pack x -> Some (String.capitalize_ascii x) 198 | in 199 | let pathI = [(outdir host)/lib.dir] in 200 | mkdir ("_build"/(outdir host)/lib.dir); 201 | 202 | (****************************************************************************) 203 | (* Partially apply several functions *) 204 | (****************************************************************************) 205 | let lib_path = lib_path host lib in 206 | let pack_path = pack_path host lib in 207 | let module_path_server file = (* needed even for `Client *) 208 | module_path `Server lib file 209 | in 210 | let module_path file = module_path host lib file in 211 | let intf_exists x = intf_exists host lib x in 212 | let is_shared_impl x = is_shared_impl lib x in 213 | 214 | let compile ?c ?a ?o ?for_pack ?pack ?linkall mode files = 215 | match host,mode with 216 | | `Server,`Byte -> 217 | Tools.eliomc ?c ?a ?o ?for_pack ?pack ?linkall files 218 | ?annot ?bin_annot ?g ?safe_string ?short_paths ?thread ?w 219 | ~package ~pathI ~ppx:() 220 | | `Server,`Native -> 221 | Tools.eliomopt ?c ?a ?o ?for_pack ?pack ?linkall files 222 | ?annot ?bin_annot ?g ?safe_string ?short_paths ?thread ?w 223 | ~package ~pathI ~ppx:() 224 | | `Client,`Byte -> 225 | Tools.js_of_eliom ?c ?a ?o ?for_pack ?pack ?linkall:None ?linkall files 226 | ?annot ?bin_annot ?g ?safe_string ?short_paths ?thread ?w 227 | ~package ~pathI ~ppx:() 228 | | `Client,`Native -> 229 | failwith "native compilation for client side is not supported" 230 | in 231 | 232 | let build_deps build mode x = 233 | build_deps ~package ~pathI:[lib.dir] build host mode lib x 234 | in 235 | 236 | (****************************************************************************) 237 | (* Register rules *) 238 | (****************************************************************************) 239 | (* .mli/.eliomi -> .cmi *) 240 | List.iter mli_files ~f:(fun intf -> 241 | let intf_path = lib.dir/intf in 242 | let cmi = (module_path intf)^".cmi" in 243 | Rule.rule ~deps:[intf_path] ~prods:[cmi] (fun _ build -> 244 | build_deps build `cmi intf_path; 245 | compile `Byte ~c:() ~o:cmi ?for_pack [intf_path] 246 | ) 247 | ); 248 | 249 | ((* .ml/.eliom -> .type_mli *) 250 | match host with 251 | | `Client -> () 252 | | `Server -> 253 | List.iter ml_files ~f:(fun impl -> 254 | let impl_path = lib.dir/impl in 255 | let prod = (module_path impl)^".type_mli" in 256 | Rule.rule ~deps:[impl_path] ~prods:[prod] (fun _ build -> 257 | build_deps build `type_mli impl_path; 258 | 259 | Tools.eliomc ~infer:() ~o:prod [impl_path] 260 | ~package:("eliom.ppx.type"::package) 261 | ?annot ?bin_annot ?g ?safe_string ?short_paths ?thread ?w 262 | ~pathI ~ppx:() 263 | ) 264 | ) 265 | ); 266 | 267 | (* .ml,.eliom -> .cmo/.cmx, and .cmi if no corresponding .mli/.eliomi *) 268 | List.iter [`Byte;`Native] ~f:(fun mode -> 269 | match host,mode with 270 | | `Client,`Native -> () 271 | | _ -> 272 | let typ = match mode with `Native -> `cmx | `Byte -> `cmo in 273 | List.iter ml_files ~f:(fun impl -> 274 | let impl_path = lib.dir/impl in 275 | let obj = (module_path impl)^(obj_suffix mode) in 276 | let cmi = (module_path impl)^".cmi" in 277 | let type_mli = (module_path_server impl)^ ".type_mli" in 278 | let intf_exists = intf_exists impl in 279 | let deps = 280 | let l = if intf_exists then [impl_path; cmi] else [impl_path] in 281 | if is_shared_impl impl then type_mli::l else l 282 | in 283 | let prods = if intf_exists then [obj] else [obj;cmi] in 284 | Rule.rule ~deps ~prods (fun _ build -> 285 | build_deps build typ impl_path; 286 | compile mode ~c:() ~o:obj ?for_pack [lib.dir/impl] 287 | ) 288 | ) 289 | ); 290 | 291 | ((* .cmo*/.cmx* -> packed .cmo/.cmx *) 292 | List.iter [`Byte; `Native] ~f:(fun mode -> 293 | match host,mode with 294 | | `Client,`Native -> () 295 | | _ -> 296 | match lib.style with 297 | | `Pack _ -> 298 | let prod = pack_path^(obj_suffix mode) in 299 | let ml_paths = List.map ml_files ~f:(fun x -> lib.dir/x) in 300 | Rule.rule ~deps:ml_paths ~prods:[prod] (fun _ build -> 301 | let deps = 302 | build_impl_files_sorted build host mode ~package ml_paths 303 | in 304 | compile mode ~pack:() ~o:prod deps 305 | ) 306 | ) 307 | ); 308 | 309 | ((* .cmo*/.cmx* -> .cma,.cmxa *) 310 | List.iter [`Byte;`Native] ~f:(fun mode -> 311 | match host,mode with 312 | | `Client,`Native -> () 313 | | _ -> 314 | let ml_lib = lib_path^(lib_suffix mode) in 315 | match lib.style with 316 | | `Pack _ -> 317 | let obj = pack_path^(obj_suffix mode) in 318 | Rule.rule ~deps:[obj] ~prods:[ml_lib] (fun _ _ -> 319 | compile mode ~a:() ~linkall:() ~o:ml_lib [obj] 320 | ) 321 | ) 322 | ); 323 | 324 | ((* .cma -> .js *) 325 | match host with 326 | | `Server -> () 327 | | `Client -> 328 | let ml = lib_path^(lib_suffix `Byte) in 329 | let js = lib_path^(lib_suffix `JavaScript) in 330 | Rule.rule ~deps:[ml] ~prods:[js] (fun _ _ -> 331 | compile `Byte ~linkall:() ~o:js [ml] 332 | ) 333 | ) 334 | 335 | let build_lib lib = 336 | List.iter [`Server;`Client] ~f:(fun host -> 337 | build_lib_of_host host lib 338 | ) 339 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Solvuu's build system. 2 | 3 | `solvuu-build` makes it easy for you to build OCaml projects. You 4 | express your build in a single OCaml file. Simple projects require 5 | only a few lines of code and then a call to `make` will do lots of 6 | useful things: compile the OCaml code of course but also generate 7 | .merlin, .ocamlinit, Makefile files and more. Since the build 8 | configuration is in OCaml, you have the full power of OCaml to add 9 | complex (or simple) build rules if needed. 10 | 11 | ## Quickstart 12 | Install by doing `opam install solvuu-build`. In your repo, create a 13 | Makefile with the single line 14 | 15 | ```make 16 | include $(shell opam config var solvuu-build:lib)/solvuu.mk 17 | ``` 18 | 19 | Write some OCaml code in one or more files, and place them in a 20 | sub-directory called `lib` (or another name of your choice). 21 | 22 | Create a file called `myocamlbuild.ml` at your repo's root with the 23 | following content: 24 | 25 | ```ocaml 26 | open Solvuu_build.Std 27 | 28 | let project_name = "my-project" 29 | let version = "dev" 30 | 31 | let mylib = Project.lib project_name 32 | ~dir:"lib" 33 | ~style:`Basic 34 | ~pkg:project_name 35 | 36 | let () = Project.basic1 ~project_name ~version [mylib] 37 | ``` 38 | 39 | Type `make`. If anything goes wrong, you might launch utop and do 40 | `#use myocamlbuild.ml` to debug. 41 | 42 | In the call to `Project.lib` above, the unlabeled argument is the name 43 | of the library, which dictates the basename of `.cma` and `.cmxa` 44 | files. Then, we specified the directory in which your library's files 45 | reside. By default all `.ml`, `.mli`, and `.c` files therein will be 46 | compiled into your library. The `pkg` argument is the findlib package 47 | name you want to assign to this library. Finally, the style is set to 48 | `Basic`. Also supported is `Pack` and there is an open feature request 49 | to support _module aliases as a namespace_. Compilation can be 50 | customized via several optional arguments, such as `safe_string`. 51 | 52 | Here's what you get: 53 | 54 | - `.cmo`, `.cmi`, `.cma`, etc: Files output by `ocamlc`. By default, 55 | only byte code compilation is done to save time during 56 | development. Run `make native` to also get the corresponding 57 | `ocamlopt` output. 58 | 59 | - `.ocamlinit`: An ocamlinit file for use during development. It 60 | automatically loads the necessary findlib dependencies (none in our 61 | simple example above) and your compiled `.cma`. Thus, doing `utop` 62 | from your repo root let's you immediately use your own code. 63 | 64 | - `.merlin`: A merlin file that correctly includes all the source and 65 | build directories, and findlib dependencies. This is generated 66 | first, so even if you have a compilation error in your OCaml code, 67 | you can start benefiting from merlin right away in your editor. 68 | 69 | - `_build/project.mk`: You might be wondering how all the above 70 | happens with just a call to `make`. A makefile is auto-generated and 71 | the single line of code you wrote in your Makefile ends up including 72 | this one. Note in particular the rule targeting `_build/%`. You can 73 | thus ask to build any artefact under _build with a simple `make 74 | _build/lib/foo.cmo` for example. The corresponding call to 75 | ocamlbuild is more verbose. 76 | 77 | - `_build/META`: A findlib META file. It correctly handles 78 | dependencies and multi-library projects. Not built by default. Run 79 | `make _build/META` to generate it. 80 | 81 | - `.install`: A .install file as needed by OPAM. Not 82 | built by default. Run `make .install` to generate it. 83 | 84 | See the `demos` directory for other small examples. 85 | 86 | ## Users 87 | Some real world examples where solvuu-build is being used: 88 | 89 | - [biocaml](https://github.com/biocaml/biocaml/): Multiple libraries 90 | and multiple executable apps. One of the libraries contains a C 91 | file. Libraries include Async and Lwt variations, and they are 92 | compiled only if `async` or `lwt` are installed, respectively. It 93 | uses `m4` to automatically insert the git commit ID and project 94 | version into an `about.ml` file, which is compiled into the library. 95 | 96 | - [cufp.org](https://github.com/CUFP/cufp.org): A library and app are 97 | compiled. The logic of doing the static site generation is encoded 98 | in the Makefile. These rules could all have been in 99 | `myocamlbuild.ml`, but this project demonstrates that sometimes Make 100 | is still better. Use both in combination. 101 | 102 | - An [Eliom](#eliom) based website. No public project yet available. 103 | 104 | - Also: [Coclobas](https://github.com/hammerlab/coclobas), 105 | [Future](https://github.com/solvuu/future), 106 | [Phat](https://github.com/solvuu/phat). 107 | 108 | 109 | ## Design Goals 110 | Our motivation for this project was to stop thinking about building 111 | code, so we can focus on writing code. Here's what we think is 112 | required for that to happen: 113 | 114 | - Entire build should be expressed in a single file. 115 | 116 | - The language the build is expressed in should be OCaml. 117 | 118 | - Nothing should happen by default. You should have to call at least 119 | one function to register any rules. 120 | 121 | - API should be functional as much as possible. 122 | 123 | - Should be possible to override every default. We do not fully 124 | succeed. Several decisions are currently hardcoded, and in some 125 | cases it isn't obvious how to make the decisions configurable. Or 126 | rather, making them all configurable would remove all benefits; 127 | the API would end up being "call the OCaml command line 128 | tools yourself". 129 | 130 | - Complicated things should be possible. Examples: pre-process your file through 131 | cppo, convert an .md file to .html, download a file from the 132 | internet, run ocamldep and capture and parse its output during 133 | build, and much more. All of this should be possible. Actually, we 134 | hesitate to call these _complicated_ things. They're conceptually 135 | trivial, but for some reason very difficult to do within most build 136 | systems. 137 | 138 | Technically `solvuu-build` is an ocamlbuild plugin. However, it avoids 139 | ocamlbuild's builtin rules by calling 140 | `Ocamlbuild_plugin.clear_rules()` as its first step. Ocamlbuild is 141 | used only for its rule engine, not for any of the other features most 142 | people associate it with. Here is a list of reasons we preferred to 143 | avoid ocamlbuild's default rules: 144 | 145 | - They are all registered by default. So even if your project has no 146 | parser in it, a rule for compiling `mly` files is registered. This 147 | invariably leads to esoteric error messages about how ocamlbuild 148 | knows of no rule to generate `foo.mly`, when actually your error 149 | is something entirely different. 150 | 151 | - The rules are almost never what you need, which ocamlbuild 152 | recognizes. You certainly have your own choice for the `-w` warnings 153 | flag or whether or not to use `-safe-string`. Ocamlbuild's solution to 154 | this is to make all the rules it registers be rather 155 | complicated. They don't just make calls to the OCaml tools. Rather 156 | they all check whether an `_tags` file and various other side 157 | effecting functions have been called in your `myocamlbuild.ml` 158 | file. Based on a bunch of mutable state, each rule creates the 159 | specific underlying command that finally gets called. In other 160 | words, the default rules are actually parameterized (good) using a 161 | very complicated mechanism (bad). Solvuu-build uses a different 162 | technique to parameterize the rules that get registered: 163 | functions. Various functions are provided that when called will 164 | register one or more rules. We can make these functions take an 165 | arbitrarily rich amount of arguments, and it is clear how to call 166 | them because you already know OCaml. The only reason not to do it 167 | this way is if you want to avoid users having to call an OCaml 168 | function to configure their build, but we consider it a feature to 169 | use OCaml instead of other ad hoc syntax. 170 | 171 | - Build configuration is split across multiple files. Any non-trivial 172 | project ends up having `myocamlbuild.ml`, `_tags`, and multiple `.cllib` 173 | and `.mllib` files. 174 | 175 | - Much of the configuration can be done in `myocamlbuild.ml`, which 176 | ostensibly meets our goal of using OCaml as the configuration 177 | language. However, the API isn't really what we think of as 178 | OCaml. It is entirely imperative in nature. 179 | 180 | - The implementation is essentially impossible to understand. The code 181 | uses mutable state everywhere and is largely undocumented. 182 | 183 | - All paths are intrinsically relative to the `_build` directory, but 184 | some files are supposed to be generated outside `_build` 185 | (e.g. `.merlin`) and sometimes there is no benefit to copying or 186 | symlinking your source code into `_build`. This shouldn't be forced on 187 | you. It is also not the best UI; typing `ocamlbuild foo.cmo` actually 188 | builds `_build/foo.cmo`. 189 | 190 | 191 | ## Known Limitations 192 | 193 | - Files for a single library cannot be spread across multiple 194 | directories. This might be unreasonable for very large projects, but 195 | most projects anyway adhere to this. 196 | 197 | - Build paths are hardcoded. For example, if your library `foo`'s 198 | files are in a directory `lib`, then the `cma` file for that library 199 | will be built at `_build/foo.cma`. Some people might prefer to have 200 | it at `_build/lib/foo.cma`, but you cannot change this. 201 | 202 | - True dynamic dependencies. We are limited by our use of ocamlbuild's 203 | rule engine, which kind of supports dynamic rules by passing a 204 | `build` function to the action of your rule. You can call `build` 205 | within your own action, and thus compute other targets at build 206 | time and dynamically call `build` to build them. This is nice and 207 | ends up working. However, you never truly generate a rule; you just 208 | run code that ends up doing stuff. The better solution would be for 209 | rules to form a monad, as in 210 | [Jenga](https://github.com/janestreet/jenga). 211 | 212 | As an example of why this matters, note that we are forced to 213 | mis-state the true dependencies of a packed `cmo`. We say the 214 | dependencies are all the `ml` files, but really they are all the 215 | `cmo` files produced by the `ml` files. The output of ocamlbuild's 216 | `-documentation` feature is thus misleading. 217 | 218 | 219 | ## Eliom 220 | 221 | [Ocsigen](http://ocsigen.org/) provides a suite of libraries for web 222 | programming. Most, such as [lwt](http://ocsigen.org/lwt/) and 223 | [tyxml](http://ocsigen.org/tyxml/), can be used without any special 224 | support. You simply add these to your list of findlib dependencies 225 | when using the `Project` module. However, 226 | [eliom](http://ocsigen.org/eliom/) works rather differently. Your 227 | source files are compiled twice, once each for the server and client 228 | side. We provide the `Eliom` module to support this. 229 | 230 | Assume your source code is in a sub-directory "src" with files a.ml, 231 | a.mli, b.eliom, b.eliomi, and c.ml. Further, assume module A is for 232 | the server side only, but modules B and C are for both the client and 233 | server side. Then, here's a complete `myocamlbuild.ml` file to compile 234 | both native and byte code server libraries, and a JavaScript client 235 | library. 236 | 237 | ```ocaml 238 | open Solvuu_build.Std 239 | 240 | let ml_files = function 241 | | `Server -> ["a.ml"; "b.eliom"; "c.ml"] 242 | | `Client -> [ "b.eliom"; "c.ml"] 243 | 244 | let mli_files = function 245 | | `Server -> ["a.mli"; "b.eliomi"] 246 | | `Client -> [ "b.eliomi"] 247 | 248 | let findlib_deps = function 249 | | `Server -> ["eliom.server"; "eliom.ppx.server"; "js_of_ocaml.ppx"; "core"] 250 | | `Client -> ["eliom.client"; "eliom.ppx.client"; "js_of_ocaml.ppx"] 251 | 252 | let mylib = 253 | Eliom.lib "myweb" 254 | ~style:(`Pack "myweb") 255 | ~dir:"src" 256 | ~findlib_deps 257 | ~ml_files 258 | ~mli_files 259 | ~short_paths:() 260 | ~w:"A-4-33-41-42-44-45-48" 261 | 262 | let () = Ocamlbuild_plugin.dispatch @@ function 263 | | Ocamlbuild_plugin.After_rules -> ( 264 | Ocamlbuild_plugin.clear_rules(); 265 | Eliom.build_lib mylib; 266 | ) 267 | | _ -> () 268 | ``` 269 | 270 | We recommend a Makefile like this: 271 | 272 | ```make 273 | OCAMLBUILD=ocamlbuild -verbose 1 -use-ocamlfind -plugin-tag "package(solvuu-build)" 274 | 275 | FORCE: 276 | _build/%: FORCE 277 | $(OCAMLBUILD) $(patsubst _build/%, %, $@) 278 | 279 | clean: 280 | rm -rf _build 281 | 282 | ``` 283 | 284 | Now type `make _build/_client/myweb.js` and `make _build/_server/myweb.cma`. 285 | 286 | The `Eliom` module is less mature than `Project`. You don't get all 287 | the extra nice things `Project` provides, such `.ocamlinit`, 288 | `.merlin`, etc. Also, note that `Eliom` doesn't play well with 289 | `Project`. At present, any given project should use only one or the 290 | other, but this shouldn't be too hard to resolve. 291 | -------------------------------------------------------------------------------- /lib/tools.mli: -------------------------------------------------------------------------------- 1 | (** Command line tools. We provided convenient constructors for 2 | command line calls as needed by ocamlbuild. Each function 3 | corresponds to a Unix command of the same (or similar) name with 4 | flags mapped to labeled arguments. 5 | 6 | Most functions return a value of type [Command.t], which is what 7 | you need to define ocamlbuild rules. Sometimes we provide a 8 | function to register a rule directly. For example, {!ocamllex} 9 | constructs a command and {!ocamllex_rule} registers a 10 | corresponding rule that takes care of defining the dependency and 11 | target for you. Finally, sometimes you want to run a tool right 12 | away, as opposed to registering it to be run later. We provide 13 | some convenience functions for this too, e.g. {!run_ocamldep} 14 | immediately runs ocamldep, captures its output, and returns the 15 | parsed result. 16 | 17 | Command line flags are mapped to labeled arguments with the exact 18 | same name whenever possible, e.g. ocamlc's [-c] flag is represented 19 | by a [~c] argument to the {!ocamlc} function provided 20 | here. Sometimes this is not possible and we resolve the mapping as 21 | follows: 22 | 23 | - The flag is an OCaml keyword, in which case we suffix with an 24 | underscore. For example, ocamlc takes an [-open] flag, which is 25 | mapped to an [~open_] argument here. 26 | 27 | - The flag begins with a capital letter, in which case we choose 28 | an alternate name that represents the meaning of the flag. A 29 | commonly occuring case of this is the [-I] flag, which we map to 30 | [~pathI]. Other cases are documented where they occur. 31 | 32 | Command line tools sometimes allow a flag to be passed multiple 33 | times. We represent this by making the type of the corresponding 34 | argument a list. For example, ocamlc's [-open] argument takes a 35 | string value, and this can be given any number of times. Thus the 36 | [~open_] argument is of type [string list]. 37 | 38 | The majority of the functions provided here correspond to OCaml 39 | tools, but see the very end for other common Unix tools, e.g. [cp] 40 | and [git]. 41 | *) 42 | open Ocamlbuild_plugin 43 | 44 | (******************************************************************************) 45 | (** {2 Abstraction over ocamlc/ocamlopt} *) 46 | (******************************************************************************) 47 | 48 | (** Arguments common to both ocamlc and ocamlopt. *) 49 | type 'a ocaml_compiler_args = 50 | ?a:unit -> 51 | ?absname:unit -> 52 | ?annot:unit -> 53 | ?bin_annot:unit -> 54 | ?c:unit -> 55 | ?cc:string -> 56 | ?cclib:string -> 57 | ?ccopt:string -> 58 | ?color:[`auto | `always | `never] -> 59 | ?config:unit -> 60 | ?for_pack:string -> 61 | ?g:unit -> 62 | ?i:unit -> 63 | ?pathI:string list -> 64 | ?impl:string -> 65 | ?intf:string -> 66 | ?intf_suffix:string -> 67 | ?labels:unit -> 68 | ?linkall:unit -> 69 | ?make_runtime:unit -> 70 | ?no_alias_deps:unit -> 71 | ?no_app_funct:unit -> 72 | ?noassert:unit -> 73 | ?noautolink:unit -> 74 | ?nolabels:unit -> 75 | ?nostdlib:unit -> 76 | ?o:string -> 77 | ?open_:string list -> 78 | ?output_obj:unit -> 79 | ?pack:unit -> 80 | ?pp:string -> 81 | ?ppx:string -> 82 | ?principal:unit -> 83 | ?rectypes:unit -> 84 | ?runtime_variant:string -> 85 | ?safe_string:unit -> 86 | ?short_paths:unit -> 87 | ?strict_sequence:unit -> 88 | ?strict_formats:unit -> 89 | ?thread:unit -> 90 | ?unsafe:unit -> 91 | ?unsafe_string:unit -> 92 | ?use_runtime:string -> 93 | ?v:unit -> 94 | ?verbose:unit -> 95 | ?version:unit -> 96 | ?w:string -> 97 | ?warn_error:string -> 98 | ?warn_help:unit -> 99 | ?where:unit -> 100 | ?nopervasives:unit -> 101 | ?dsource:unit -> 102 | ?dparsetree:unit -> 103 | ?dtypedtree:unit -> 104 | ?drawlambda:unit -> 105 | ?dlambda:unit -> 106 | ?dinstr:unit -> 107 | ?help:unit -> 108 | 'a 109 | 110 | (** Abstraction over ocamlc and ocamlopt to help construct commands 111 | uniformly over either compiler. If you need options specific to 112 | either compiler, please see {!ocamlc} and {!ocamlopt} below. *) 113 | val ocaml_compiler : 114 | ([`Byte | `Native] -> Pathname.t list -> Command.t) ocaml_compiler_args 115 | 116 | (** Extra arguments common to ocamlfind ocamlc/ocamlopt. *) 117 | type 'a ocamlfind_ocaml_compiler_args = 118 | ?package:string list -> 119 | ?linkpkg:unit -> 120 | ?predicates:string -> 121 | ?dontlink:string list -> 122 | ?ppopt:string -> 123 | ?ppxopt:(string * string) list -> 124 | ?dllpath_pkg:string list -> 125 | ?dllpath_all:unit -> 126 | ?ignore_error:unit -> 127 | ?passopt:string list -> 128 | ?only_show:unit -> 129 | 'a 130 | 131 | (** Abstraction over [ocamlfind ocamlc/ocamlopt]. *) 132 | val ocamlfind_ocaml_compiler : 133 | ( 134 | [`Byte | `Native] -> Pathname.t list -> Command.t 135 | ) ocaml_compiler_args ocamlfind_ocaml_compiler_args 136 | 137 | 138 | (******************************************************************************) 139 | (** {2 ocamlc} *) 140 | (******************************************************************************) 141 | type 'a ocamlc_args = ( 142 | ?compat_32:unit -> 143 | ?custom:unit -> 144 | ?dllib:string -> 145 | ?dllpath:string -> 146 | ?vmthread:unit -> 147 | ?no_check_prims:unit -> 148 | 'a 149 | ) ocaml_compiler_args 150 | 151 | val ocamlc : (Pathname.t list -> Command.t) ocamlc_args 152 | 153 | val ocamlfind_ocamlc : 154 | (Pathname.t list -> Command.t) ocamlc_args ocamlfind_ocaml_compiler_args 155 | 156 | 157 | (******************************************************************************) 158 | (** {2 ocamlopt} *) 159 | (******************************************************************************) 160 | 161 | (** 162 | Some flag names have to be changed due to OCaml syntax restrictions. These 163 | are: 164 | - [~optimize2] is [-O2] 165 | - [~optimize3] is [-O3] 166 | - [~optimize_classic] is [-Oclassic] 167 | - [~keep_assembly] is [-S] 168 | *) 169 | type 'a ocamlopt_args = ( 170 | ?compact:unit -> 171 | ?inline:string -> 172 | ?inline_alloc_cost:string -> 173 | ?inline_branch_cost:string -> 174 | ?inline_branch_factor:string -> 175 | ?inline_call_cost:string -> 176 | ?inline_indirect_cost:string -> 177 | ?inline_lifting_benefit:string -> 178 | ?inline_max_depth:string -> 179 | ?inline_max_unroll:string -> 180 | ?inline_prim_cost:string -> 181 | ?inlining_report:unit -> 182 | ?no_unbox_free_vars_of_closures:unit -> 183 | ?no_unbox_specialised_args:unit -> 184 | ?nodynlink:unit -> 185 | ?optimize_classic:unit -> 186 | ?optimize2:unit -> 187 | ?optimize3:unit -> 188 | ?p:unit -> 189 | ?remove_unused_arguments:unit -> 190 | ?rounds:int -> 191 | ?keep_assembly:unit -> 192 | ?shared:unit -> 193 | ?unbox_closures:unit -> 194 | 'a 195 | ) ocaml_compiler_args 196 | 197 | val ocamlopt : (Pathname.t list -> Command.t) ocamlopt_args 198 | 199 | val ocamlfind_ocamlopt : 200 | (Pathname.t list -> Command.t) ocamlopt_args ocamlfind_ocaml_compiler_args 201 | 202 | 203 | (******************************************************************************) 204 | (** {2 ocamlmklib} *) 205 | (******************************************************************************) 206 | 207 | (** The [~pathL] labeled argument corresponds to ocamlmklib's [-L] 208 | flag. *) 209 | val ocamlmklib : 210 | ?cclib:string -> 211 | ?ccopt:string -> 212 | ?custom:unit -> 213 | ?g:unit -> 214 | ?dllpath:string -> 215 | ?framework:string -> 216 | ?pathI:string list -> 217 | ?failsafe:unit -> 218 | ?ldopt:string -> 219 | ?linkall:unit -> 220 | ?l:string -> 221 | ?pathL:string list -> 222 | ?ocamlc:string -> 223 | ?ocamlcflags:string -> 224 | ?ocamlopt:string -> 225 | ?ocamloptflags:string -> 226 | ?o:string -> 227 | ?oc:string -> 228 | ?verbose:unit -> 229 | Pathname.t list -> 230 | Command.t 231 | 232 | 233 | (******************************************************************************) 234 | (** {2 ocamldep} *) 235 | (******************************************************************************) 236 | type 'a ocamldep_args0 = 237 | ?absname:unit -> 238 | ?all:unit -> 239 | ?pathI:string list -> 240 | ?impl:string list -> 241 | ?intf:string list -> 242 | ?ml_synonym:string -> 243 | ?mli_synonym:string -> 244 | ?modules:unit -> 245 | ?native:unit -> 246 | ?one_line:unit -> 247 | ?open_:string list -> 248 | ?pp:string -> 249 | ?ppx:string -> 250 | ?slash:unit -> 251 | 'a 252 | 253 | type 'a ocamldep_args = 254 | ( 255 | ?sort:unit -> 256 | ?version:unit -> 257 | 'a 258 | ) ocamldep_args0 259 | 260 | val ocamldep : (Pathname.t list -> Command.t) ocamldep_args 261 | 262 | (** Return an association list mapping each given input file to its 263 | list of dependencies. Note ocamldep ignores files that don't 264 | exist. You may want to assert that the given files exist prior to 265 | calling this function. 266 | 267 | To simplify parsing the output, the [?one_line] argument is 268 | ignored and interally always set. 269 | *) 270 | val run_ocamldep 271 | : (Pathname.t list -> (string * string list) list) ocamldep_args 272 | 273 | (** Similar to [run_ocamldep] but more convenient when you want the 274 | dependencies of a single file. We directly return the dependencies 275 | of the given file. In this case, we also raise an exception if the 276 | given file doesn't already exist since ocamldep can't compute 277 | anything in this case. *) 278 | val run_ocamldep1 279 | : (Pathname.t -> string list) ocamldep_args 280 | 281 | (** Sort given files in dependency order, i.e. later files depend on 282 | earlier ones. Note that ocamldep ignores files that don't exist, 283 | so there is no guarantee that the returned list contains all files 284 | in the input list. *) 285 | val run_ocamldep_sort : (Pathname.t list -> Pathname.t list) ocamldep_args0 286 | 287 | type 'a ocamlfind_ocamldep_args = 288 | ?package:string list -> 289 | ?predicates:string -> 290 | ?native_filter:unit -> 291 | ?bytecode_filter:unit -> 292 | ?only_show:unit -> 293 | ?verbose:unit -> 294 | 'a 295 | 296 | val ocamlfind_ocamldep 297 | : (Pathname.t list -> Command.t) ocamldep_args ocamlfind_ocamldep_args 298 | 299 | (** In the following [run_ocamlfind_ocamldep*] functions, the 300 | [~verbose] option is ignored and internally not set. Otherwise the 301 | output contains text that can't be parsed. *) 302 | val run_ocamlfind_ocamldep : 303 | ( 304 | Pathname.t list -> (string * string list) list 305 | ) ocamldep_args ocamlfind_ocamldep_args 306 | 307 | val run_ocamlfind_ocamldep1 308 | : (Pathname.t -> string list) ocamldep_args ocamlfind_ocamldep_args 309 | 310 | val run_ocamlfind_ocamldep_sort : 311 | (Pathname.t list -> Pathname.t list) ocamldep_args0 ocamlfind_ocamldep_args 312 | 313 | 314 | (******************************************************************************) 315 | (** {2 ocamllex/menhir} *) 316 | (******************************************************************************) 317 | val ocamllex : ?ml:unit -> ?q:unit -> ?o:string -> Pathname.t -> Command.t 318 | 319 | (** Register a rule to run ocamllex. By default, [dep = "%.mll"] and 320 | [prod = "%.ml"]. *) 321 | val ocamllex_rule : 322 | ?ml:unit -> ?q:unit -> ?dep:string -> ?prod:string -> unit -> unit 323 | 324 | val menhir : ?base:string -> Pathname.t -> Command.t 325 | 326 | (** Register a rule to run menhir. By default, [dep = "%.mly"]. *) 327 | val menhir_rule : ?base:string -> ?dep:string -> unit -> unit 328 | 329 | 330 | (******************************************************************************) 331 | (** {2 js_of_ocaml} *) 332 | (******************************************************************************) 333 | type 'a js_of_ocaml_rule_args = 334 | ?custom_header:string -> 335 | ?debug:string -> 336 | ?debug_info:unit -> 337 | ?disable:string -> 338 | ?enable:string -> 339 | ?no_inline:unit -> 340 | ?no_runtime:unit -> 341 | ?o:string -> 342 | ?opt:int -> 343 | ?pretty:unit -> 344 | ?set:(string * string) list -> 345 | ?source_map_inline:unit -> 346 | ?source_map_no_source:unit -> 347 | ?source_map_root:string -> 348 | ?source_map:unit -> 349 | ?extern_fs:unit -> 350 | ?file:string list -> 351 | ?pathI:string list -> 352 | ?ofs:string -> 353 | ?linkall:unit -> 354 | ?no_cmis:unit -> 355 | ?toplevel:unit -> 356 | 'a 357 | 358 | type 'a js_of_ocaml_args = 359 | ?custom_header:string -> 360 | ?debug:string -> 361 | ?debug_info:unit -> 362 | ?disable:string -> 363 | ?enable:string -> 364 | ?no_inline:unit -> 365 | ?no_runtime:unit -> 366 | ?o:string -> 367 | ?opt:int -> 368 | ?pretty:unit -> 369 | ?quiet:unit -> 370 | ?set:(string * string) list -> 371 | ?source_map_inline:unit -> 372 | ?source_map_no_source:unit -> 373 | ?source_map_root:string -> 374 | ?source_map:unit -> 375 | ?version:unit -> 376 | ?extern_fs:unit -> 377 | ?file:string list -> 378 | ?pathI:string list -> 379 | ?ofs:string -> 380 | ?linkall:unit -> 381 | ?no_cmis:unit -> 382 | ?toplevel:unit -> 383 | 'a 384 | 385 | val js_of_ocaml : 386 | (Pathname.t list -> Pathname.t -> Command.t) js_of_ocaml_args 387 | 388 | val js_of_ocaml_rule : 389 | (?extra_js:Pathname.t list -> Pathname.t -> unit) js_of_ocaml_rule_args 390 | 391 | (******************************************************************************) 392 | (** {2 eliomc/eliomopt} *) 393 | (******************************************************************************) 394 | 395 | (** Arguments specific to eliomc/eliomopt. Note that -ppx is 396 | duplicated here and in ocamlc/ocamlopt. Thus, you can pass [~ppx] 397 | twice to {!eliomc} and {!eliomopt}. This mimics the (flawed) 398 | command line {{:https://github.com/ocsigen/eliom/issues/273}API of 399 | eliomc/eliomopt}. *) 400 | type 'a eliom_args = 401 | ?package:string list -> 402 | ?no_autoload:unit -> 403 | ?type_conv:unit -> 404 | ?infer:unit -> 405 | ?dir:string -> 406 | ?type_dir:string -> 407 | ?server_types_ext:string -> 408 | ?ppopt:string -> 409 | ?predicates:string -> 410 | ?ppx:unit -> 411 | 'a 412 | 413 | val eliomc : (Pathname.t list -> Command.t) ocamlc_args eliom_args 414 | val eliomopt : (Pathname.t list -> Command.t) ocamlopt_args eliom_args 415 | 416 | 417 | (******************************************************************************) 418 | (** {2 eliomdep} *) 419 | (******************************************************************************) 420 | type 'a eliomdep_args = 421 | ?dir:string -> 422 | ?type_dir:string -> 423 | ?eliom_inc:string list -> 424 | ?package:string list -> 425 | ?no_autoload:unit -> 426 | ?type_conv:unit -> 427 | ?ppopt:string list -> 428 | ?predicates:string -> 429 | ?verbose:unit -> 430 | ?ppx:unit -> 431 | 'a 432 | 433 | val eliomdep : 434 | ( 435 | [`Client | `Server] -> Pathname.t list -> Command.t 436 | ) ocamldep_args eliomdep_args 437 | 438 | (** If [~fix320] is set, modify output to workaround [eliomdep] issue 439 | {:{https://github.com/ocsigen/eliom/issues/320}320}. *) 440 | val run_eliomdep : 441 | ( 442 | ?fix320:unit -> 443 | [`Client | `Server] -> 444 | Pathname.t list -> 445 | (string * string list) list 446 | ) ocamldep_args eliomdep_args 447 | 448 | val run_eliomdep_sort : 449 | ( 450 | [`Client | `Server] -> Pathname.t list -> Pathname.t list 451 | ) ocamldep_args0 eliomdep_args 452 | 453 | 454 | (******************************************************************************) 455 | (** {2 js_of_eliom} *) 456 | (******************************************************************************) 457 | type 'a js_of_eliom_args = 458 | ?package:string list -> 459 | ?no_autoload:unit -> 460 | ?type_conv:unit -> 461 | ?dir:string -> 462 | ?type_dir:string -> 463 | ?server_types_ext:string -> 464 | ?jsopt:string -> 465 | ?ppopt:string -> 466 | ?predicates:string -> 467 | ?ppx:unit -> 468 | ?dont_force_linkall:unit -> 469 | 'a 470 | 471 | val js_of_eliom : 472 | (Pathname.t list -> Command.t) ocamlc_args js_of_ocaml_args js_of_eliom_args 473 | 474 | 475 | (******************************************************************************) 476 | (** {2 atdgen} *) 477 | (******************************************************************************) 478 | val atdgen : ?j:unit -> ?j_std:unit -> ?t:unit -> Pathname.t -> Command.t 479 | 480 | (** Register a rule to run [atdgen -t] on the given [dep], which must 481 | have a ".atd" suffix. Default [dep] is "%.atd". Files produced 482 | will be "%_t.ml" and "%_t.mli". *) 483 | val atdgen_t_rule : ?dep:string -> ?j_std:unit -> unit -> unit 484 | 485 | (** Register a rule to run [atdgen -j] on the given [dep], which must 486 | have a ".atd" suffix. Default [dep] is "%.atd". Files produced 487 | will be "%_j.ml" and "%_j.mli". *) 488 | val atdgen_j_rule : ?dep:string -> ?j_std:unit -> unit -> unit 489 | 490 | 491 | (******************************************************************************) 492 | (** Other Unix tools. *) 493 | (******************************************************************************) 494 | val cp : ?f:unit -> Pathname.t -> Pathname.t -> Command.t 495 | val cp_rule : ?f:unit -> dep:Pathname.t -> prod:Pathname.t -> unit 496 | 497 | (** Return most recent git commit ID if possible. Assumes there is a 498 | .git directory in the current working directory. *) 499 | val git_last_commit : unit -> string option 500 | 501 | val m4 : 502 | ?_D:(string * string option) list -> 503 | infile:Pathname.t -> 504 | outfile:Pathname.t -> 505 | Command.t 506 | 507 | (** Defaults: [prod = "%.ml"] and [dep = prod ^ ".m4"]. *) 508 | val m4_rule : 509 | ?_D:(string * string option) list -> 510 | ?dep:string -> 511 | ?prod:string -> 512 | unit -> 513 | unit 514 | -------------------------------------------------------------------------------- /lib/project.mli: -------------------------------------------------------------------------------- 1 | (** Type of items that can be built, i.e. an app or lib. Defined by: 2 | 3 | - [typ]: Type of item, either an executable app or an OCaml 4 | library. 5 | 6 | - [name]: Base name of the item. For an app, this would be the 7 | desired filename of the executable. However, .byte and .native 8 | extensions might be present at various stages. For a library, 9 | this name will have suffixes such as .cmo and .cma added. In 10 | both cases, dashes are valid. 11 | 12 | - [internal_deps]: List of other items that this item directly 13 | depends on. The normal case is to depend on libraries. However, 14 | we allow depending on apps too, e.g. building a library might 15 | require first building an app that will be used to auto-generate 16 | some of the code for the library. The list should include only 17 | other items that will be built by the same project. Only {i 18 | direct} dependencies should be listed; indirect dependencies 19 | will be inferred automatically. 20 | 21 | - [findlib_deps]: In addition to internal dependencies, a given 22 | item can depend on other findlib packages. Again, only {i 23 | direct} dependencies should be listed. 24 | 25 | 26 | Libs additionally have the fields: 27 | 28 | - [pack_name]: Make the library consist of a single module with 29 | given name, constructed as a pack of all the modules that would 30 | be directly accessible. 31 | 32 | - [dir]: The [dir] in which the library's modules are implemented, 33 | relative to the repo root. It is assumed that all files in this 34 | [dir] and no other files comprise the library. 35 | 36 | - [install]: [`No] means do not install. [`Findlib x] means 37 | install as a findlib package named [x]. If your project installs 38 | a single package, you likely want this equal to the project 39 | name. If it installs several packages, you possibly want this 40 | equal to "project_name.lib_name", in which case the dot is 41 | interpreted to mean a findlib sub-package. 42 | 43 | 44 | Apps additionally have the fields: 45 | 46 | - [file]: Path to the file implementing the app, relative to the 47 | repo root. 48 | 49 | - [install]: [`No] means do not install. [`Opam] means install in 50 | location dictated by opam. 51 | *) 52 | open Ocamlbuild_plugin 53 | 54 | (** Findlib package name. *) 55 | type findlib_pkg = Solvuu_build_findlib.pkg 56 | 57 | type app = { 58 | name : string; 59 | internal_deps : item list; 60 | findlib_deps : findlib_pkg list; 61 | file : string; 62 | install : [`No | `Opam]; 63 | 64 | annot : unit option; 65 | bin_annot : unit option; 66 | cc : string option; 67 | cclib : string option; 68 | ccopt : string option; 69 | color : [`auto | `always | `never] option; 70 | g : unit option; 71 | inline : string option; 72 | inline_alloc_cost : string option; 73 | inline_branch_cost : string option; 74 | inline_branch_factor : string option; 75 | inline_call_cost : string option; 76 | inline_indirect_cost : string option; 77 | inline_lifting_benefit : string option; 78 | inline_max_depth : string option; 79 | inline_max_unroll : string option; 80 | inline_prim_cost : string option; 81 | inlining_report : unit option; 82 | no_check_prims : unit option ; 83 | no_unbox_free_vars_of_closures : unit option; 84 | no_unbox_specialised_args : unit option; 85 | optimize_classic : unit option; 86 | optimize2 : unit option; 87 | optimize3 : unit option; 88 | remove_unused_arguments : unit option; 89 | rounds : int option; 90 | safe_string : unit option; 91 | short_paths : unit option; 92 | strict_formats : unit option; 93 | strict_sequence : unit option; 94 | thread : unit option; 95 | unbox_closures : unit option; 96 | w : string option; 97 | warn_error : string option; 98 | } 99 | 100 | and lib = { 101 | name : string; 102 | internal_deps : item list; 103 | findlib_deps : findlib_pkg list; 104 | style : [ `Basic | `Pack of string ]; 105 | dir : string; 106 | ml_files : string list; 107 | mli_files : string list; 108 | c_files : string list; 109 | h_files : string list; 110 | install : [`No | `Findlib of findlib_pkg]; 111 | build_plugin : bool; 112 | 113 | annot : unit option; 114 | bin_annot : unit option; 115 | cc : string option; 116 | cclib : string option; 117 | ccopt : string option; 118 | color : [`auto | `always | `never] option; 119 | g : unit option; 120 | inline : string option; 121 | inline_alloc_cost : string option; 122 | inline_branch_cost : string option; 123 | inline_branch_factor : string option; 124 | inline_call_cost : string option; 125 | inline_indirect_cost : string option; 126 | inline_lifting_benefit : string option; 127 | inline_max_depth : string option; 128 | inline_max_unroll : string option; 129 | inline_prim_cost : string option; 130 | inlining_report : unit option; 131 | no_check_prims : unit option ; 132 | no_unbox_free_vars_of_closures : unit option; 133 | no_unbox_specialised_args : unit option; 134 | optimize_classic : unit option; 135 | optimize2 : unit option; 136 | optimize3 : unit option; 137 | remove_unused_arguments : unit option; 138 | rounds : int option; 139 | safe_string : unit option; 140 | short_paths : unit option; 141 | strict_formats : unit option; 142 | strict_sequence : unit option; 143 | thread : unit option; 144 | unbox_closures : unit option; 145 | w : string option; 146 | warn_error : string option; 147 | 148 | linkall : unit option; 149 | } 150 | 151 | and item = Lib of lib | App of app 152 | 153 | type 'a with_options = 154 | ?annot:unit 155 | -> ?bin_annot:unit 156 | -> ?cc:string 157 | -> ?cclib:string 158 | -> ?ccopt:string 159 | -> ?color:[`auto | `always | `never] 160 | -> ?g:unit 161 | -> ?inline:string 162 | -> ?inline_alloc_cost:string 163 | -> ?inline_branch_cost:string 164 | -> ?inline_branch_factor:string 165 | -> ?inline_call_cost:string 166 | -> ?inline_indirect_cost:string 167 | -> ?inline_lifting_benefit:string 168 | -> ?inline_max_depth:string 169 | -> ?inline_max_unroll:string 170 | -> ?inline_prim_cost:string 171 | -> ?inlining_report:unit 172 | -> ?no_check_prims:unit 173 | -> ?no_unbox_free_vars_of_closures:unit 174 | -> ?no_unbox_specialised_args:unit 175 | -> ?optimize_classic:unit 176 | -> ?optimize2:unit 177 | -> ?optimize3:unit 178 | -> ?remove_unused_arguments:unit 179 | -> ?rounds:int 180 | -> ?safe_string:unit 181 | -> ?short_paths:unit 182 | -> ?strict_formats:unit 183 | -> ?strict_sequence:unit 184 | -> ?thread:unit 185 | -> ?unbox_closures:unit 186 | -> ?w:string 187 | -> ?warn_error:string 188 | -> 'a 189 | 190 | 191 | (** [lib ~style ~dir name] constructs a [lib]. Most arguments 192 | correspond to the OCaml compiler or to fields in type [lib]. Other 193 | arguments are: 194 | 195 | - [ml_files], [mli_files]: The default list of [ml_files] and 196 | [mli_files] is the files statically present in [dir]. You can 197 | [`Add] to these, e.g. when you will be declaring rules to 198 | generate ml or mli files, or entirely [`Replace] the lists with 199 | ones you provide. All paths should be relative to [dir]. 200 | 201 | - [build_plugin]: Default is true, which means compile cmxs files. 202 | 203 | - [install]: Default is [`Findlib name]. This is certainly 204 | incorrect for projects containing multiple libs. You should 205 | provide a distinct findlib package name for each lib. 206 | *) 207 | val lib : ( 208 | ?linkall:unit 209 | -> ?internal_deps:item list 210 | -> ?findlib_deps:findlib_pkg list 211 | -> ?ml_files:[`Add of string list | `Replace of string list] 212 | -> ?mli_files:[`Add of string list | `Replace of string list] 213 | -> ?c_files:[`Add of string list | `Replace of string list] 214 | -> ?h_files:[`Add of string list | `Replace of string list] 215 | -> ?build_plugin:bool 216 | -> ?install : [`No | `Findlib of findlib_pkg] 217 | -> style : [ `Basic | `Pack of string ] 218 | -> dir:string 219 | -> string 220 | -> item 221 | ) with_options 222 | 223 | (** [app ~file name] constructs an app. Arguments are: 224 | 225 | - [file]: The file implementing the executable. 226 | - [name]: Desired name of the executable. 227 | - [install]: Default: [`Opam]. 228 | *) 229 | val app : ( 230 | ?internal_deps:item list 231 | -> ?findlib_deps:findlib_pkg list 232 | -> ?install:[`No | `Opam] 233 | -> file:string 234 | -> string 235 | -> item 236 | ) with_options 237 | 238 | (******************************************************************************) 239 | (** {2 Static Files} *) 240 | (******************************************************************************) 241 | type content = string list 242 | (** Content of a file represented as a list of lines. *) 243 | 244 | (** [merlin_file items] returns content of a .merlin file. It provides 245 | S and B lines for merlin to find the source and build directories 246 | of all [items] and PKG lines to let merlin search over all findlib 247 | packages used by [items] (the package "solvuu-build" is also 248 | added). 249 | 250 | The optional arguments correspond to flags that can be specified 251 | for each item of [items]. If you set them differently, it isn't 252 | clear what value to use in the .merlin file. By default, we use a 253 | heuristic to pick, but you can provide a value explicitly if 254 | desired. *) 255 | val merlin_file 256 | : ?safe_string:unit option 257 | -> ?short_paths:unit option 258 | -> ?strict_formats:unit option 259 | -> ?strict_sequence:unit option 260 | -> ?thread:unit option 261 | -> ?w:string option 262 | -> ?warn_error:string option 263 | -> item list 264 | -> content 265 | 266 | val meta_file : version:string -> lib list -> Fl_metascanner.pkg_expr option 267 | (** Return a findlib META file for given libs, where [version] should 268 | be the version of your project. Libs whose [install] field is 269 | [`No] are ignored. Raise exception if remaining libs form a graph 270 | without exactly 1 root. Return None if resulting list is empty. *) 271 | 272 | val install_file : item list -> content 273 | val ocamlinit_file : ?postfix:string list -> item list -> content 274 | val makefile : project_name:string -> item list -> content 275 | 276 | 277 | (******************************************************************************) 278 | (** {2 Rules} *) 279 | (******************************************************************************) 280 | val build_lib : lib -> unit 281 | val build_app : app -> unit 282 | 283 | (** Register a rule to build [lib] using js_of_ocaml *) 284 | val build_js_of_ocaml : 285 | (?extra_js:Pathname.t list -> item -> unit) Tools.js_of_ocaml_rule_args 286 | 287 | (** [static_file path f] registers a rule to create a file at 288 | [path] with content [f ()]. *) 289 | val build_static_file : string -> (unit -> content) -> unit 290 | 291 | (******************************************************************************) 292 | (** {2 Plugins} *) 293 | (******************************************************************************) 294 | val basic1 : 295 | ?additional_rules:((unit -> unit) list) -> 296 | ?ocamlinit_postfix:string list -> 297 | project_name:string -> 298 | version:string -> 299 | item list -> 300 | unit 301 | 302 | val solvuu1 : 303 | ?additional_rules:((unit -> unit) list) -> 304 | ?ocamlinit_postfix:string list -> 305 | project_name:string -> 306 | version:string -> 307 | item list -> 308 | unit 309 | 310 | 311 | (******************************************************************************) 312 | (** {2 Dependency Operations} *) 313 | (******************************************************************************) 314 | val internal_deps : item -> item list 315 | val findlib_deps : item -> findlib_pkg list 316 | 317 | val internal_deps_all : item -> item list 318 | val findlib_deps_all : item -> findlib_pkg list 319 | 320 | (** Return all findlib packages mentioned in all given items. *) 321 | val all_findlib_pkgs : item list -> findlib_pkg list 322 | 323 | 324 | (******************************************************************************) 325 | (** {2 Item Module} *) 326 | (******************************************************************************) 327 | module Item : sig 328 | type t = item 329 | val compare : t -> t -> int 330 | val equal : t -> t -> bool 331 | val hash : t -> int 332 | 333 | type typ = [`Lib | `App] 334 | val typ : t -> typ 335 | val typ_to_string : typ -> string 336 | end 337 | 338 | 339 | (******************************************************************************) 340 | (** {2 Graph Operations} *) 341 | (******************************************************************************) 342 | module Graph : sig 343 | include module type of Graph.Persistent.Digraph.Concrete(Item) 344 | 345 | module Dfs : module type of Graph.Traverse.Dfs( 346 | Graph.Persistent.Digraph.Concrete(Item) 347 | ) 348 | 349 | module Topological : sig 350 | include module type of Graph.Topological.Make( 351 | Graph.Persistent.Digraph.Concrete(Item) 352 | ) 353 | 354 | (** Return a topologically sorted vertex list of given graph. *) 355 | val sort : t -> V.t list 356 | end 357 | 358 | (** Construct a graph from a list of items. Raise exception if there 359 | are cycles or any other errors. *) 360 | val of_list : Item.t list -> t 361 | end 362 | 363 | 364 | (******************************************************************************) 365 | (** {2 Low-level Functions} *) 366 | (******************************************************************************) 367 | val name : item -> string 368 | 369 | val is_lib : item -> bool 370 | val is_app : item -> bool 371 | 372 | val filter_libs : item list -> lib list 373 | val filter_apps : item list -> app list 374 | 375 | val dep_opts_sat : item -> findlib_pkg list -> bool 376 | (** [dep_opt_sat x pkgs] returns true if the optional dependencies 377 | [pkgs] are satisfied for [x], i.e. either [x] doesn't depend on 378 | the [pkgs] or any package it does depend on is installed. *) 379 | 380 | val path_of_lib : suffix:string -> lib -> string 381 | (** Return path to lib file with given suffix. Files are siblings of 382 | the lib's [dir]. For example, given a lib [x] with [x.dir = 383 | "src/mylib"], we choose to install files in the directory 384 | "src/". Thus, [path_of_lib ~suffix:".cma" x] will return 385 | "src/mylib.cma". *) 386 | 387 | val path_of_pack : suffix:string -> lib -> string 388 | (** Return path to packed module file with given suffix. Raise 389 | exception if given [lib]'s style is not [`Pack _]. *) 390 | 391 | val path_of_app : suffix:string -> app -> string 392 | (** Return path to app file with given suffix. Files are in the same 393 | directory as the app's implementation [file]. For example, assume 394 | app [x] defines [x.file = "app/myapp.ml"]. Then [path_of_app 395 | ~suffix:".byte" x] will return "app/myapp.byte". *) 396 | 397 | val file_base_of_module : lib -> (string -> string option) 398 | (** For given lib, return a function that can be used to get the base 399 | path for a module. Example, say [lib] is defined with: 400 | [lib.ml_files = ["foo.ml"; "bar.ml";]] and [lib.dir = "src"]. Then the 401 | returned function [f] will give: 402 | 403 | - [f "Foo" = Some "src/foo"]. There is no suffix on the result, so 404 | that you can append whatever suffix you need. 405 | 406 | - [f "foo" = Some "src/foo"]. The module name is automatically 407 | capitalized, so you don't have to do this yourself. 408 | 409 | - [f "Car" = None]. No file in lib corresponds to the requested 410 | module, so we return None. 411 | *) 412 | 413 | val module_paths : style_matters:bool -> lib -> string list 414 | (** Return module paths for the modules of given lib. A {i module 415 | path} is the path to a file implementing a module without any 416 | suffix. For example, if [lib.ml_files] includes "src/foo.ml", then 417 | "src/foo" will be in the returned list. 418 | 419 | If [style_matters] is [false], the returned list is based directly 420 | on [lib.ml_files] and [lib.mli_files]. Setting [style_matters] to 421 | [true] means the real final modules comprising the library are 422 | returned. For example, a library that is [Pack]ed actually 423 | consists of only a single module. 424 | *) 425 | 426 | val module_dirs : style_matters:bool -> lib -> string list 427 | (** Return directory where the modules (i.e. cmo, cmx, cmi files) of 428 | [lib] will be output. See {!module_paths} above for explanation of 429 | [style_matters]. 430 | *) 431 | 432 | val internal_deps_files : [`Byte | `Native] -> item -> string list 433 | (** Return list of file paths corresponding to the internal 434 | dependencies of given item, for either byte or native mode. 435 | *) 436 | 437 | val obj_suffix : [`Byte | `Native] -> string 438 | (** Return suffix of object file for given mode, either ".cmo" or 439 | ".cmx". See also {!obj_suffixes} below. *) 440 | 441 | val obj_suffixes : [`Byte | `Native] -> string list 442 | (** Return suffixes of files representing a compiled 443 | implementation. For byte code, there is just one file, ".cmo". For 444 | native code, there are two, ".cmx" and ".o". 445 | 446 | Usually you only need to refer to the .cmx since ocaml will 447 | automatically look for the corresponding .o, which {!obj_suffix} 448 | above provides. In other contexts, e.g. when describing 449 | depedencies, you may need to know the full set of file extensions, 450 | which this function provides. *) 451 | 452 | val add_o_to_cmx : string list -> string list 453 | (** For every .cmx file in given list, add a corresponding .o 454 | file. Example: [a.cmi; b.cmx; c.cmx] returns [a.cmi; b.cmx; b.o; 455 | c.cmx; c.o]. The order of the original files is not altered. *) 456 | 457 | val lib_suffix : [`Byte | `Native] -> string 458 | (** Return suffix of a library for the given mode, either ".cma" or 459 | ".cmxa". *) 460 | 461 | val exe_suffix : [`Byte | `Native] -> string 462 | (** Return suffix of an executable for the given mode, either ".byte" 463 | or ".native". These are a choice we make. OCaml doesn't require 464 | these, and we do not use them when installing executables. *) 465 | -------------------------------------------------------------------------------- /lib/project.ml: -------------------------------------------------------------------------------- 1 | open Printf 2 | module Findlib = Solvuu_build_findlib 3 | open Tools 4 | open Util 5 | open Util.Filename 6 | let (/) = Util.Filename.concat 7 | 8 | type findlib_pkg = Solvuu_build_findlib.pkg 9 | 10 | type app = { 11 | name : string; 12 | internal_deps : item list; 13 | findlib_deps : findlib_pkg list; 14 | file : string; 15 | install : [`No | `Opam]; 16 | 17 | annot : unit option; 18 | bin_annot : unit option; 19 | cc : string option; 20 | cclib : string option; 21 | ccopt : string option; 22 | color : [`auto | `always | `never] option; 23 | g : unit option; 24 | inline : string option; 25 | inline_alloc_cost : string option; 26 | inline_branch_cost : string option; 27 | inline_branch_factor : string option; 28 | inline_call_cost : string option; 29 | inline_indirect_cost : string option; 30 | inline_lifting_benefit : string option; 31 | inline_max_depth : string option; 32 | inline_max_unroll : string option; 33 | inline_prim_cost : string option; 34 | inlining_report : unit option; 35 | no_check_prims : unit option ; 36 | no_unbox_free_vars_of_closures : unit option; 37 | no_unbox_specialised_args : unit option; 38 | optimize_classic : unit option; 39 | optimize2 : unit option; 40 | optimize3 : unit option; 41 | remove_unused_arguments : unit option; 42 | rounds : int option; 43 | safe_string : unit option; 44 | short_paths : unit option; 45 | strict_formats : unit option; 46 | strict_sequence : unit option; 47 | thread : unit option; 48 | unbox_closures : unit option; 49 | w : string option; 50 | warn_error : string option; 51 | } 52 | 53 | and lib = { 54 | name : string; 55 | internal_deps : item list; 56 | findlib_deps : findlib_pkg list; 57 | style : [ `Basic | `Pack of string ]; 58 | dir : string; 59 | ml_files : string list; 60 | mli_files : string list; 61 | c_files : string list; 62 | h_files : string list; 63 | install : [`No | `Findlib of findlib_pkg]; 64 | build_plugin : bool; 65 | 66 | annot : unit option; 67 | bin_annot : unit option; 68 | cc : string option; 69 | cclib : string option; 70 | ccopt : string option; 71 | color : [`auto | `always | `never] option; 72 | g : unit option; 73 | inline : string option; 74 | inline_alloc_cost : string option; 75 | inline_branch_cost : string option; 76 | inline_branch_factor : string option; 77 | inline_call_cost : string option; 78 | inline_indirect_cost : string option; 79 | inline_lifting_benefit : string option; 80 | inline_max_depth : string option; 81 | inline_max_unroll : string option; 82 | inline_prim_cost : string option; 83 | inlining_report : unit option; 84 | no_check_prims : unit option ; 85 | no_unbox_free_vars_of_closures : unit option; 86 | no_unbox_specialised_args : unit option; 87 | optimize_classic : unit option; 88 | optimize2 : unit option; 89 | optimize3 : unit option; 90 | remove_unused_arguments : unit option; 91 | rounds : int option; 92 | safe_string : unit option; 93 | short_paths : unit option; 94 | strict_formats : unit option; 95 | strict_sequence : unit option; 96 | thread : unit option; 97 | unbox_closures : unit option; 98 | w : string option; 99 | warn_error : string option; 100 | 101 | linkall : unit option; 102 | } 103 | 104 | and item = Lib of lib | App of app 105 | 106 | type 'a with_options = 107 | ?annot:unit 108 | -> ?bin_annot:unit 109 | -> ?cc:string 110 | -> ?cclib:string 111 | -> ?ccopt:string 112 | -> ?color:[`auto | `always | `never] 113 | -> ?g:unit 114 | -> ?inline:string 115 | -> ?inline_alloc_cost:string 116 | -> ?inline_branch_cost:string 117 | -> ?inline_branch_factor:string 118 | -> ?inline_call_cost:string 119 | -> ?inline_indirect_cost:string 120 | -> ?inline_lifting_benefit:string 121 | -> ?inline_max_depth:string 122 | -> ?inline_max_unroll:string 123 | -> ?inline_prim_cost:string 124 | -> ?inlining_report:unit 125 | -> ?no_check_prims:unit 126 | -> ?no_unbox_free_vars_of_closures:unit 127 | -> ?no_unbox_specialised_args:unit 128 | -> ?optimize_classic:unit 129 | -> ?optimize2:unit 130 | -> ?optimize3:unit 131 | -> ?remove_unused_arguments:unit 132 | -> ?rounds:int 133 | -> ?safe_string:unit 134 | -> ?short_paths:unit 135 | -> ?strict_formats:unit 136 | -> ?strict_sequence:unit 137 | -> ?thread:unit 138 | -> ?unbox_closures:unit 139 | -> ?w:string 140 | -> ?warn_error:string 141 | -> 'a 142 | 143 | let lib 144 | ?annot ?bin_annot ?cc ?cclib ?ccopt ?color ?g 145 | ?inline 146 | ?inline_alloc_cost ?inline_branch_cost ?inline_branch_factor 147 | ?inline_call_cost ?inline_indirect_cost ?inline_lifting_benefit 148 | ?inline_max_depth ?inline_max_unroll ?inline_prim_cost 149 | ?inlining_report 150 | ?no_check_prims 151 | ?no_unbox_free_vars_of_closures ?no_unbox_specialised_args 152 | ?optimize_classic ?optimize2 ?optimize3 153 | ?remove_unused_arguments ?rounds 154 | ?safe_string ?short_paths ?strict_formats ?strict_sequence 155 | ?thread ?unbox_closures ?w ?warn_error ?linkall 156 | ?(internal_deps=[]) ?(findlib_deps=[]) 157 | ?ml_files ?mli_files ?c_files ?h_files 158 | ?(build_plugin=true) ?install ~style ~dir name 159 | = 160 | let install = match install with 161 | | Some x -> x 162 | | None -> `Findlib name 163 | in 164 | let dir_files dir = 165 | try Sys.readdir dir |> Array.to_list 166 | with _ -> [] 167 | in 168 | let all_files = 169 | let root_files = dir_files dir in 170 | let sub_dir_files = 171 | Sys.sub_dirs ~depth:5 dir |> 172 | List.map ~f:(fun sub_dir -> 173 | dir_files (dir/sub_dir) |> 174 | List.map ~f:(fun x -> sub_dir/x) 175 | ) |> 176 | List.flatten 177 | in 178 | root_files@sub_dir_files 179 | in 180 | let select_files ?add_replace suffix = 181 | List.filter all_files ~f:(fun x -> check_suffix x suffix) |> fun l -> 182 | (match add_replace with 183 | | None -> l 184 | | Some (`Add x) -> x@l 185 | | Some (`Replace x) -> x 186 | ) |> 187 | List.sort_uniq ~cmp:String.compare 188 | in 189 | let ml_files = select_files ".ml" ?add_replace:ml_files in 190 | let mli_files = select_files ".mli" ?add_replace:mli_files in 191 | let c_files = select_files ".c" ?add_replace:c_files in 192 | let h_files = select_files ".h" ?add_replace:h_files in 193 | Lib { 194 | name; internal_deps; findlib_deps; style; 195 | dir; ml_files; mli_files; c_files; h_files; install; build_plugin; 196 | annot; bin_annot; cc; cclib; ccopt; color; g; 197 | inline; 198 | inline_alloc_cost; inline_branch_cost; inline_branch_factor; 199 | inline_call_cost; inline_indirect_cost; inline_lifting_benefit; 200 | inline_max_depth; inline_max_unroll; inline_prim_cost; 201 | inlining_report; no_check_prims ; 202 | no_unbox_free_vars_of_closures; no_unbox_specialised_args; 203 | optimize_classic; optimize2; optimize3; 204 | remove_unused_arguments; rounds; 205 | safe_string; short_paths; strict_formats; strict_sequence; 206 | thread; unbox_closures; w; warn_error; linkall; 207 | } 208 | 209 | let app 210 | ?annot ?bin_annot ?cc ?cclib ?ccopt ?color ?g 211 | ?inline 212 | ?inline_alloc_cost ?inline_branch_cost ?inline_branch_factor 213 | ?inline_call_cost ?inline_indirect_cost ?inline_lifting_benefit 214 | ?inline_max_depth ?inline_max_unroll ?inline_prim_cost 215 | ?inlining_report ?no_check_prims 216 | ?no_unbox_free_vars_of_closures ?no_unbox_specialised_args 217 | ?optimize_classic ?optimize2 ?optimize3 218 | ?remove_unused_arguments ?rounds 219 | ?safe_string ?short_paths ?strict_formats ?strict_sequence 220 | ?thread ?unbox_closures ?w ?warn_error 221 | ?(internal_deps=[]) ?(findlib_deps=[]) ?(install=`Opam) 222 | ~file name 223 | = 224 | App { 225 | name; internal_deps; findlib_deps; file; install; 226 | annot; bin_annot; cc; cclib; ccopt; color; g; 227 | inline; 228 | inline_alloc_cost; inline_branch_cost; inline_branch_factor; 229 | inline_call_cost; inline_indirect_cost; inline_lifting_benefit; 230 | inline_max_depth; inline_max_unroll; inline_prim_cost; 231 | inlining_report; no_check_prims ; 232 | no_unbox_free_vars_of_closures; no_unbox_specialised_args; 233 | optimize_classic; optimize2; optimize3; 234 | remove_unused_arguments; rounds; 235 | safe_string; short_paths; strict_formats; strict_sequence; 236 | thread; unbox_closures; w; warn_error; 237 | } 238 | 239 | 240 | (******************************************************************************) 241 | (** {2 Dependency Operations} *) 242 | (******************************************************************************) 243 | let internal_deps = function 244 | | Lib x -> x.internal_deps | App x -> x.internal_deps 245 | 246 | let findlib_deps = function 247 | | Lib x -> x.findlib_deps | App x -> x.findlib_deps 248 | 249 | let internal_deps_all t = 250 | let count = ref 0 in 251 | let rec loop t = 252 | incr count; 253 | if !count > 10000 then 254 | failwith "max recursion count exceeded, likely cycle in internal_deps" 255 | else 256 | let direct = internal_deps t in 257 | let further = 258 | List.map direct ~f:loop 259 | |> List.flatten 260 | in 261 | List.sort_uniq ~cmp:compare (direct@further) 262 | in 263 | loop t 264 | 265 | let findlib_deps_all t = 266 | let count = ref 0 in 267 | let rec loop t = 268 | incr count; 269 | if !count > 10000 then 270 | failwith "max recursion count exceeded, likely cycle in internal_deps" 271 | else 272 | let direct = findlib_deps t in 273 | let further = 274 | List.map (internal_deps t) ~f:loop 275 | |> List.flatten 276 | in 277 | List.sort_uniq ~cmp:String.compare (direct@further) 278 | in 279 | loop t 280 | 281 | let all_findlib_pkgs t = 282 | List.map t ~f:findlib_deps 283 | |> List.flatten 284 | |> List.sort_uniq ~cmp:String.compare 285 | 286 | 287 | (******************************************************************************) 288 | (** {2 Low-level Functions} *) 289 | (******************************************************************************) 290 | let name = function Lib x -> x.name | App x -> x.name 291 | 292 | let is_lib = function Lib _ -> true | App _ -> false 293 | let is_app = function App _ -> true | Lib _ -> false 294 | 295 | let filter_libs t = 296 | List.filter_map t ~f:(function Lib x -> Some x | App _ -> None) 297 | 298 | let filter_apps t = 299 | List.filter_map t ~f:(function App x -> Some x | Lib _ -> None) 300 | 301 | let dep_opts_sat x optional_deps = 302 | let all_deps = findlib_deps_all x in 303 | List.for_all optional_deps ~f:(fun optional_dep -> 304 | not (List.mem optional_dep ~set:all_deps) 305 | || Findlib.installed optional_dep 306 | ) 307 | 308 | let path_of_lib ~suffix (x:lib) : string = 309 | sprintf "%s/%s%s" (dirname x.dir) x.name suffix 310 | 311 | let path_of_pack ~suffix (x:lib) : string = 312 | match x.style with 313 | | `Basic -> failwithf "path_of_pack undefined for un-packed lib %s" x.name () 314 | | `Pack pack_name -> sprintf "%s/%s%s" (dirname x.dir) pack_name suffix 315 | 316 | let path_of_app ~suffix (x:app) : string = 317 | sprintf "%s/%s%s" (dirname x.file) x.name suffix 318 | 319 | let file_base_of_module x = 320 | let ml_bases = 321 | List.map x.ml_files ~f:(fun y -> x.dir/y) |> 322 | List.map ~f:(fun x -> chop_suffix x ".ml") 323 | in 324 | let mli_bases = 325 | List.map x.mli_files ~f:(fun y -> x.dir/y) |> 326 | List.map ~f:(fun x -> chop_suffix x ".mli") 327 | in 328 | let bases = List.sort_uniq ~cmp:String.compare (ml_bases@mli_bases) in 329 | fun mod_name -> 330 | List.filter bases ~f:(fun x -> String.capitalize_ascii (basename x) = mod_name) 331 | |> function 332 | | [] -> None (* Module is presumably from an external library. *) 333 | | x::[] -> Some x 334 | | l -> failwithf "module %s defined by multiple files: %s" 335 | mod_name 336 | (List.map l ~f:(fun x -> x ^ ".ml[i]") |> String.concat ~sep:",") 337 | () 338 | 339 | (* Must consider mli files too, not just ml files. Though it is a 340 | rarely used feature, technically OCaml does allow inferring an 341 | implementation from only a signature. *) 342 | let module_paths ~style_matters lib = 343 | let module_paths_orig = 344 | (lib.ml_files@lib.mli_files) |> 345 | List.sort_uniq ~cmp:String.compare |> 346 | List.map ~f:(fun x -> lib.dir/(Filename.chop_extension x)) 347 | in 348 | match style_matters with 349 | | false -> module_paths_orig 350 | | true -> match lib.style with 351 | | `Basic -> module_paths_orig 352 | | `Pack _ -> [path_of_pack ~suffix:"" lib] 353 | 354 | let module_dirs ~style_matters lib = 355 | match style_matters,lib.style with 356 | | true, `Pack _ -> 357 | [dirname lib.dir] 358 | | false, _ 359 | | true, `Basic -> 360 | (lib.ml_files@lib.mli_files@lib.c_files@lib.h_files) |> 361 | List.map ~f:dirname |> 362 | List.sort_uniq ~cmp:String.compare |> 363 | List.map ~f:(fun x -> lib.dir/x) 364 | 365 | let obj_suffix = function `Byte -> ".cmo" | `Native -> ".cmx" 366 | let obj_suffixes = function `Byte -> [".cmo"] | `Native -> [".cmx"; ".o"] 367 | let lib_suffix = function `Byte -> ".cma" | `Native -> ".cmxa" 368 | let exe_suffix = function `Byte -> ".byte" | `Native -> ".native" 369 | 370 | let add_o_to_cmx l = 371 | let rec loop accum = function 372 | | [] -> accum 373 | | x::l -> 374 | match String.chop_suffix x ~suffix:".cmx" with 375 | | None -> loop (x::accum) l 376 | | Some base -> loop ((base^".o")::x::accum) l 377 | in 378 | loop [] l 379 | |> List.rev 380 | 381 | let internal_deps_files mode x = 382 | internal_deps x |> 383 | List.map ~f:(function 384 | | Lib x -> path_of_lib x ~suffix:(lib_suffix mode) 385 | | App x -> path_of_app x ~suffix:(exe_suffix mode) 386 | ) 387 | 388 | 389 | (******************************************************************************) 390 | (** {2 Item Module} *) 391 | (******************************************************************************) 392 | module Item = struct 393 | type t = item 394 | 395 | type typ = [`Lib | `App] 396 | let typ = function Lib _ -> `Lib | App _ -> `App 397 | let typ_to_string = function `Lib -> "lib" | `App -> "app" 398 | 399 | let hash t = Hashtbl.hash (typ t, name t) 400 | 401 | let compare t u = 402 | match compare (typ t) (typ u) with 403 | | -1 -> -1 404 | | 1 -> 1 405 | | 0 -> String.compare (name t) (name u) 406 | | _ -> assert false 407 | 408 | let equal t u = compare t u = 0 409 | 410 | end 411 | 412 | (******************************************************************************) 413 | (** {2 Graph and Dependency Operations} *) 414 | (******************************************************************************) 415 | module Graph = struct 416 | include Graph.Persistent.Digraph.Concrete(Item) 417 | 418 | module Dfs = Graph.Traverse.Dfs( 419 | Graph.Persistent.Digraph.Concrete(Item) 420 | ) 421 | 422 | module Topological = struct 423 | include Graph.Topological.Make( 424 | Graph.Persistent.Digraph.Concrete(Item) 425 | ) 426 | 427 | let sort g = fold (fun x l -> x::l) g [] 428 | end 429 | 430 | let of_list items = 431 | if not (List.is_uniq ~cmp:compare items) then 432 | failwith "multiple libs or apps have an identical name" 433 | else 434 | let g = List.fold_left items ~init:empty ~f:(fun g x -> 435 | match internal_deps x with 436 | | [] -> add_vertex g x 437 | | _ -> 438 | List.fold_left (internal_deps x) ~init:g ~f:(fun g y -> 439 | add_edge g x y 440 | ) 441 | ) 442 | in 443 | if Dfs.has_cycle g then 444 | failwith "internal dependencies form a cycle" 445 | else 446 | g 447 | 448 | end 449 | 450 | 451 | (******************************************************************************) 452 | (** {2 Static Files} *) 453 | (******************************************************************************) 454 | type content = string list 455 | 456 | let merlin_file 457 | ?safe_string ?short_paths ?strict_formats ?strict_sequence 458 | ?thread ?w ?warn_error 459 | items 460 | : string list 461 | = 462 | let pick ~cmp l = 463 | let l' = List.sort_uniq ~cmp:(Option.compare cmp) l in 464 | match l' with 465 | | [] -> (* [items] is empty *) 466 | None 467 | | [x] -> (* all [items] specify same value, possibly None *) 468 | x 469 | | _ -> (* [items] have different values, randomly pick last *) 470 | List.last_exn l 471 | in 472 | let safe_string : unit option = match safe_string with 473 | | Some x -> x 474 | | None -> 475 | List.map items ~f:(function 476 | | Lib x -> x.safe_string 477 | | App x -> x.safe_string 478 | ) |> 479 | pick ~cmp:Unit.compare 480 | in 481 | let short_paths : unit option = match short_paths with 482 | | Some x -> x 483 | | None -> 484 | List.map items ~f:(function 485 | | Lib x -> x.short_paths 486 | | App x -> x.short_paths 487 | ) |> 488 | pick ~cmp:Unit.compare 489 | in 490 | let strict_formats : unit option = match strict_formats with 491 | | Some x -> x 492 | | None -> 493 | List.map items ~f:(function 494 | | Lib x -> x.strict_formats 495 | | App x -> x.strict_formats 496 | ) |> 497 | pick ~cmp:Unit.compare 498 | in 499 | let strict_sequence : unit option = match strict_sequence with 500 | | Some x -> x 501 | | None -> 502 | List.map items ~f:(function 503 | | Lib x -> x.strict_sequence 504 | | App x -> x.strict_sequence 505 | ) |> 506 | pick ~cmp:Unit.compare 507 | in 508 | let thread : unit option = match thread with 509 | | Some x -> x 510 | | None -> 511 | List.map items ~f:(function 512 | | Lib x -> x.thread 513 | | App x -> x.thread 514 | ) |> 515 | pick ~cmp:Unit.compare 516 | in 517 | let w : string option = match w with 518 | | Some x -> x 519 | | None -> 520 | List.map items ~f:(function 521 | | Lib x -> x.w 522 | | App x -> x.w 523 | ) |> 524 | pick ~cmp:String.compare 525 | in 526 | let warn_error : string option = match warn_error with 527 | | Some x -> x 528 | | None -> 529 | List.map items ~f:(function 530 | | Lib x -> x.warn_error 531 | | App x -> x.warn_error 532 | ) |> 533 | pick ~cmp:String.compare 534 | in 535 | 536 | [ 537 | (match thread with Some () -> ["B +threads"] | None -> []); 538 | (match safe_string with Some () -> ["FLG -safe-string"] | None -> []); 539 | (match short_paths with Some () -> ["FLG -short-paths"] | None -> []); 540 | (match strict_formats with 541 | | Some () -> ["FLG -strict-formats"] 542 | | None -> [] 543 | ); 544 | (match strict_sequence with 545 | | Some () -> ["FLG -strict-sequence"] 546 | | None -> [] 547 | ); 548 | (match w with Some w -> [sprintf "FLG -w %s" w] | None -> []); 549 | (match warn_error with 550 | | Some x -> [sprintf "FLG -warn-error %s" x] 551 | | None -> [] 552 | ); 553 | 554 | (* source directories *) 555 | List.map items ~f:(function 556 | | Lib x -> sprintf "S %s" x.dir 557 | | App x -> sprintf "S %s" (dirname x.file) 558 | ) |> 559 | List.sort_uniq ~cmp:String.compare; 560 | 561 | (* build directories *) 562 | List.map items ~f:(function 563 | | Lib x -> 564 | [ 565 | sprintf "B _build/%s" x.dir; 566 | sprintf "B _build/%s" (dirname x.dir); 567 | ] 568 | | App x -> 569 | [sprintf "B _build/%s" (dirname x.file)] 570 | ) |> 571 | List.concat |> 572 | List.sort_uniq ~cmp:String.compare; 573 | 574 | (* findlib packages *) 575 | ( 576 | "solvuu-build"::(all_findlib_pkgs items) 577 | |> List.map ~f:(sprintf "PKG %s") 578 | |> List.sort_uniq ~cmp:String.compare 579 | ); 580 | ] 581 | |> List.concat 582 | 583 | let meta_file ~version libs : Fl_metascanner.pkg_expr option = 584 | let libs = List.filter libs 585 | ~f:(fun (x:lib) -> match x.install with `Findlib _ -> true | `No -> false) 586 | in 587 | let findlib_pkg (x:lib) = match x.install with 588 | | `Findlib x -> x 589 | | `No -> assert false 590 | in 591 | let def ?(preds=[]) var value = 592 | { 593 | Fl_metascanner.def_var = var; 594 | def_preds = preds; 595 | def_flav = `BaseDef; 596 | def_value = value; 597 | } 598 | in 599 | let pkg_defs_of_lib (x:lib) = 600 | [ 601 | def "directory" 602 | (findlib_pkg x |> Findlib.to_path |> List.tl |> String.concat ~sep:"/"); 603 | def "version" version; 604 | def ~preds:[`Pred "byte"] "archive" 605 | (sprintf "%s.cma" x.name); 606 | def ~preds:[`Pred "byte"; `Pred "plugin"] "archive" 607 | (sprintf "%s.cma" x.name); 608 | def ~preds:[`Pred "native"] "archive" 609 | (sprintf "%s.cmxa" x.name); 610 | def ~preds:[`Pred "native"; `Pred "plugin"] "archive" 611 | (sprintf "%s.cmxs" x.name); 612 | def "requires" ( 613 | (findlib_deps_all (Lib x)) 614 | @( 615 | internal_deps (Lib x) 616 | |> filter_libs 617 | |> List.map ~f:findlib_pkg 618 | ) 619 | |> String.concat ~sep:" " 620 | ); 621 | def "exists_if" (sprintf "%s.cma" x.name); 622 | ] 623 | in 624 | let pkgs = List.map libs ~f:findlib_pkg in 625 | let graph = Findlib.Graph.of_list pkgs in 626 | let pkg_defs (pkg:Findlib.pkg) = 627 | try pkg_defs_of_lib @@ List.find libs 628 | ~f:(fun x -> findlib_pkg x = pkg) 629 | with _ -> [] 630 | in 631 | let rec pkg_expr (pkg:Findlib.pkg) = 632 | { 633 | Fl_metascanner.pkg_defs = pkg_defs pkg; 634 | pkg_children = 635 | List.map (Findlib.Graph.succ graph pkg) ~f:(fun x -> 636 | (Findlib.to_path x |> List.last_exn), 637 | (pkg_expr x) 638 | ) 639 | } 640 | in 641 | match libs with 642 | | [] -> None 643 | | _ -> 644 | match Findlib.Graph.roots graph with 645 | | root::[] -> Some (pkg_expr root) 646 | | [] -> assert false (* non-empty graph can't have no roots *) 647 | | l -> failwithf "cannot create META file for findlib packages with \ 648 | multiple roots: %s" (String.concat ~sep:"," l) () 649 | 650 | (** Return true if a META file should be built. *) 651 | let build_meta_file items = 652 | filter_libs items 653 | |> meta_file ~version:"" 654 | |> Option.is_some 655 | 656 | let install_file items : string list = 657 | 658 | (* Return lines for given section, or [None] if [items] is empty. *) 659 | let section name (items : (string * string option) list) 660 | : string list option 661 | = 662 | match items with 663 | | [] -> None 664 | | _ -> 665 | let items = 666 | List.map items ~f:(fun (src,dst) -> 667 | let src = Filename.normalize src in 668 | match dst with 669 | | None -> sprintf " \"%s\"" src 670 | | Some dst -> 671 | let dst = Filename.normalize dst in 672 | sprintf " \"%s\" {\"%s\"}" src dst 673 | ) 674 | in 675 | Some ((sprintf "%s: [" name)::items@["]"]) 676 | in 677 | 678 | (* Directory to install most lib files in for given dir. *) 679 | let install_dir (x:lib) = 680 | match x.install with 681 | | `No -> None 682 | | `Findlib pkg -> 683 | Findlib.to_path pkg |> 684 | List.tl |> 685 | List.fold_left ~init:"" ~f:Filename.concat |> 686 | fun x -> Some x 687 | in 688 | 689 | (* META file for lib section. *) 690 | let meta : (string * string option) list = 691 | if build_meta_file items 692 | then ["_build/META",None] 693 | else [] 694 | in 695 | 696 | (* C lib files for lib section. *) 697 | let clibs : (string * string option) list = 698 | filter_libs items |> 699 | List.filter_map ~f:(fun (x:lib) -> 700 | match install_dir x with 701 | | None -> None 702 | | Some install_dir -> 703 | match x.c_files with 704 | | [] -> None 705 | | _ -> 706 | let file = sprintf "lib%s.a" x.name in 707 | let src = sprintf "?_build/%s/%s" (dirname x.dir) file in 708 | let dst = Some (install_dir/file) in 709 | Some (src,dst) 710 | ) 711 | in 712 | 713 | (* OCaml lib files for lib section. *) 714 | let libs : (string * string option) list = 715 | filter_libs items |> 716 | List.filter_map ~f:(fun (x:lib) -> 717 | Option.map (install_dir x) ~f:(fun install_dir -> 718 | [".a"; ".cma"; ".cmxa"; ".cmxs"; ".dll"; ".o"] |> 719 | List.map ~f:(fun suffix -> 720 | let src = "?_build"/(path_of_lib ~suffix x) in 721 | let base = basename src in 722 | let dst = Some (install_dir/base) in 723 | src,dst 724 | ) 725 | ) 726 | ) |> 727 | List.flatten 728 | in 729 | 730 | (* Files related to modules for lib section. *) 731 | let module_files : (string * string option) list = 732 | filter_libs items |> 733 | List.filter_map ~f:(fun (x:lib) -> 734 | Option.map (install_dir x) ~f:(fun install_dir -> 735 | let module_paths = module_paths ~style_matters:true x in 736 | let suffixes = [".annot";".cmi";".cmo";".cmt";".cmti";".cmx"] in 737 | List.map suffixes ~f:(fun suffix -> 738 | List.map module_paths ~f:(fun module_path -> 739 | let src = "?_build"/(module_path ^ suffix) in 740 | let base = basename src in 741 | let dst = Some (install_dir/base) in 742 | src,dst 743 | ) 744 | ) |> 745 | List.flatten 746 | ) 747 | ) |> 748 | List.flatten 749 | in 750 | 751 | let lib = 752 | section "lib" (meta@clibs@libs@module_files) 753 | in 754 | 755 | let stublibs = section "stublibs" ( 756 | filter_libs items |> 757 | List.filter_map ~f:(fun (x:lib) -> 758 | match x.install with 759 | | `No -> None 760 | | `Findlib _ -> 761 | match x.c_files with 762 | | [] -> None 763 | | _ -> ( 764 | let file = sprintf "dll%s.so" x.name in 765 | let src = sprintf "?_build/%s/%s" (dirname x.dir) file in 766 | let dst = Some file in 767 | Some (src,dst) 768 | ) 769 | ) 770 | ) 771 | in 772 | 773 | let bin = section "bin" ( 774 | filter_apps items |> 775 | List.filter ~f:(fun x -> 776 | match x.install with `Opam -> true | `No -> false 777 | ) |> 778 | List.map ~f:(fun (x:app) -> 779 | List.map [`Byte; `Native] ~f:(fun mode -> 780 | let src = "?_build"/(path_of_app x ~suffix:(exe_suffix mode)) in 781 | let dst = Some x.name in 782 | src,dst 783 | ) 784 | ) |> 785 | List.flatten 786 | ) 787 | in 788 | 789 | [lib; stublibs; bin] |> 790 | List.filter_map ~f:Fn.id |> 791 | List.flatten 792 | 793 | 794 | let ocamlinit_file ?(postfix=[]) items = 795 | let graph = Graph.of_list items in 796 | [ 797 | [ 798 | "let () ="; 799 | " try Topdirs.dir_directory (Sys.getenv \"OCAML_TOPLEVEL_PATH\")"; 800 | " with Not_found -> ()"; 801 | ";;" ; 802 | ""; 803 | ]; 804 | [ 805 | "#use \"topfind\";;"; 806 | "#thread;;"; 807 | ""; 808 | ]; 809 | ( 810 | let pkgs = "solvuu-build"::(all_findlib_pkgs items) in 811 | [sprintf "#require \"%s\";;"(String.concat ~sep:" " pkgs); ""] 812 | ); 813 | [ 814 | "(* Load each lib provided by this project. *)"; 815 | ]; 816 | ( 817 | Graph.Topological.sort graph |> 818 | filter_libs |> 819 | List.map ~f:(fun x -> 820 | sprintf "#directory \"_build/%s\";;%s" (dirname x.dir) 821 | (match x.style with 822 | | `Pack _ -> "" 823 | | `Basic -> sprintf "\n#directory \"_build/%s\";;" x.dir) 824 | ) 825 | |> List.sort_uniq ~cmp:String.compare 826 | ); 827 | ( 828 | Graph.Topological.sort graph |> 829 | filter_libs |> 830 | List.map ~f:(fun (x:lib) -> 831 | sprintf "#load \"%s.cma\";;" x.name 832 | ) 833 | ); 834 | [""]; 835 | postfix; 836 | ] 837 | |> List.concat 838 | 839 | let makefile ~project_name items : string list = 840 | let default = ["default: .merlin .ocamlinit byte"] in 841 | let ln file = [ 842 | sprintf "%s: _build/%s" file file; 843 | "\tln -fs $< $@"; 844 | ] 845 | in 846 | let libs mode = 847 | filter_libs items |> 848 | List.map ~f:(fun x -> path_of_lib x ~suffix:(lib_suffix mode)) 849 | in 850 | let apps mode = 851 | filter_apps items |> 852 | List.map ~f:(fun x -> path_of_app x ~suffix:(exe_suffix mode)) 853 | in 854 | let plugins = 855 | filter_libs items |> 856 | List.filter_map ~f:(fun x -> 857 | match x.build_plugin with 858 | | false -> None 859 | | true -> Some (path_of_lib x ~suffix:".cmxs") 860 | ) 861 | in 862 | let byte = 863 | [libs `Byte; apps `Byte] |> 864 | List.concat |> 865 | String.concat ~sep:" " |> fun targets -> 866 | ["byte:"; sprintf "\t$(OCAMLBUILD) %s" targets] 867 | in 868 | let native = 869 | [libs `Native; plugins; apps `Native] |> 870 | List.concat |> 871 | String.concat ~sep:" " |> fun targets -> 872 | ["native:"; sprintf "\t$(OCAMLBUILD) %s" targets] 873 | in 874 | let outsource_to_ocamlbuild = [ 875 | "_build/%: FORCE"; 876 | "\t$(OCAMLBUILD) $(patsubst _build/%,%,$@)"; 877 | "\trm -f $(notdir $@)"; 878 | ] 879 | in 880 | let meta = 881 | if build_meta_file items then 882 | [ 883 | "META:"; 884 | "\t@echo WARNING: The META target will be deleted in a future release. \ 885 | Directly request _build/META instead."; 886 | "\tmake _build/META"; 887 | ] 888 | else 889 | [] 890 | in 891 | let clean = [ 892 | "clean:"; 893 | "\trm -rf _build"; 894 | sprintf "\trm -f .merlin .ocamlinit %s.install" project_name; 895 | ] 896 | in 897 | let phony = [".PHONY: default byte native clean"] in 898 | [ 899 | default; 900 | outsource_to_ocamlbuild; 901 | ln ".merlin"; 902 | ln ".ocamlinit"; 903 | ln @@ sprintf "%s.install" project_name; 904 | byte; 905 | native; 906 | meta; 907 | clean; 908 | phony; 909 | ["FORCE:"]; 910 | ] |> 911 | List.intersperse ~sep:[""] |> 912 | List.flatten 913 | 914 | 915 | (******************************************************************************) 916 | (** {2 Rules} *) 917 | (******************************************************************************) 918 | 919 | (* Compile all given ml files in dependency order. Return list of 920 | generated object files, also in dependency order. *) 921 | let build_ml_files_sorted mode build ~package ml_files = 922 | let suffix = obj_suffix mode in 923 | let obj_files = 924 | run_ocamlfind_ocamldep_sort ~package ml_files |> 925 | List.map ~f:(replace_suffix_exn ~old:".ml" ~new_:suffix) 926 | in 927 | let () = List.iter obj_files ~f:(fun x -> 928 | match build [[x]] with 929 | | [Ocamlbuild_plugin.Outcome.Good _] -> () 930 | | [Ocamlbuild_plugin.Outcome.Bad exn] -> raise exn 931 | | _ -> assert false 932 | ) 933 | in 934 | obj_files 935 | 936 | (* Build cmi files for all dependencies of given [file]. *) 937 | let build_deps_cmi_files build ~pathI ~package file_base_of_module file = 938 | run_ocamlfind_ocamldep1 ~modules:() ~pathI ~package file |> 939 | List.filter_map ~f:file_base_of_module |> 940 | List.map ~f:(fun x -> [x^".cmi"]) |> 941 | build |> 942 | assert_all_outcomes |> 943 | ignore 944 | 945 | let build_lib (x:lib) = 946 | 947 | (****************************************************************************) 948 | (* Parameters *) 949 | (****************************************************************************) 950 | let annot = x.annot in 951 | let bin_annot = x.bin_annot in 952 | let cc = x.cc in 953 | let cclib = x.cclib in 954 | let ccopt = x.ccopt in 955 | let color = x.color in 956 | let g = x.g in 957 | let inline = x.inline in 958 | let inline_alloc_cost = x.inline_alloc_cost in 959 | let inline_branch_cost = x.inline_branch_cost in 960 | let inline_branch_factor = x.inline_branch_factor in 961 | let inline_call_cost = x.inline_call_cost in 962 | let inline_indirect_cost = x.inline_indirect_cost in 963 | let inline_lifting_benefit = x.inline_lifting_benefit in 964 | let inline_max_depth = x.inline_max_depth in 965 | let inline_max_unroll = x.inline_max_unroll in 966 | let inline_prim_cost = x.inline_prim_cost in 967 | let inlining_report = x.inlining_report in 968 | let no_check_prims = x.no_check_prims in 969 | let no_unbox_free_vars_of_closures = x.no_unbox_free_vars_of_closures in 970 | let no_unbox_specialised_args = x.no_unbox_specialised_args in 971 | let optimize_classic = x.optimize_classic in 972 | let optimize2 = x.optimize2 in 973 | let optimize3 = x.optimize3 in 974 | let remove_unused_arguments = x.remove_unused_arguments in 975 | let rounds = x.rounds in 976 | let safe_string = x.safe_string in 977 | let short_paths = x.short_paths in 978 | let strict_formats = x.strict_formats in 979 | let strict_sequence = x.strict_sequence in 980 | let unbox_closures = x.unbox_closures in 981 | let thread = x.thread in 982 | let w = x.w in 983 | let warn_error = x.warn_error in 984 | let linkall = x.linkall in 985 | 986 | (****************************************************************************) 987 | (* Partially apply several functions *) 988 | (****************************************************************************) 989 | let ocamlc ?pack ?o ?a ?c ?pathI ?package ?for_pack ?custom files = 990 | ocamlfind_ocamlc files 991 | ?pack ?o ?a ?c ?pathI ?package ?for_pack ?custom 992 | ?annot ?bin_annot ?cc ?cclib ?ccopt 993 | ?color ?g ?safe_string ?short_paths ?strict_formats ?strict_sequence 994 | ?thread ?w ?warn_error ?linkall ?no_check_prims 995 | in 996 | 997 | let ocamlopt ?pack ?o ?a ?shared ?c ?pathI ?package ?for_pack files = 998 | ocamlfind_ocamlopt files 999 | ?pack ?o ?a ?shared ?c ?pathI ?package ?for_pack 1000 | ?annot ?bin_annot ?cc ?cclib ?ccopt ?color ?g 1001 | ?inline 1002 | ?inline_alloc_cost ?inline_branch_cost ?inline_branch_factor 1003 | ?inline_call_cost ?inline_indirect_cost ?inline_lifting_benefit 1004 | ?inline_max_depth ?inline_max_unroll ?inline_prim_cost 1005 | ?inlining_report 1006 | ?no_unbox_free_vars_of_closures ?no_unbox_specialised_args 1007 | ?optimize_classic ?optimize2 ?optimize3 1008 | ?remove_unused_arguments ?rounds 1009 | ?safe_string ?short_paths ?strict_formats ?strict_sequence 1010 | ?thread ?unbox_closures ?w ?warn_error ?linkall 1011 | in 1012 | 1013 | (* Abstraction of ocamlc/ocamlopt above. Use above if any options 1014 | specific to just ocamlc or ocamlopt are required. This function is 1015 | for when the flags are identical regardless of byte or native 1016 | compilation. *) 1017 | let ocaml ?pack ?o ?a ?c ?pathI ?package ?for_pack mode files = 1018 | match mode with 1019 | | `Byte -> 1020 | ocamlc ?pack ?o ?a ?c ?pathI ?package ?for_pack files 1021 | | `Native -> 1022 | ocamlopt ?pack ?o ?a ?c ?pathI ?package ?for_pack files 1023 | in 1024 | 1025 | let package = findlib_deps_all (Lib x) in 1026 | let ml_files = List.map x.ml_files ~f:(fun y -> x.dir/y) in 1027 | let mli_files = List.map x.mli_files ~f:(fun y -> x.dir/y) in 1028 | let c_files = List.map x.c_files ~f:(fun y -> x.dir/y) in 1029 | let h_files = List.map x.h_files ~f:(fun y -> x.dir/y) in 1030 | 1031 | let pathI = 1032 | (module_dirs ~style_matters:false x) 1033 | ::( 1034 | filter_libs x.internal_deps |> 1035 | List.map ~f:(module_dirs ~style_matters:true) 1036 | ) |> 1037 | List.flatten |> 1038 | List.sort_uniq ~cmp:String.compare 1039 | in 1040 | 1041 | let file_base_of_module : string -> string option = file_base_of_module x in 1042 | 1043 | (****************************************************************************) 1044 | (* Register rules *) 1045 | (****************************************************************************) 1046 | (* .mli -> .cmi *) 1047 | List.iter mli_files ~f:(fun mli -> 1048 | let base = chop_suffix mli ".mli" in 1049 | let cmi = sprintf "%s.cmi" base in 1050 | let internal_deps = internal_deps_files `Byte (Lib x) in 1051 | Rule.rule ~deps:(mli::internal_deps) ~prods:[cmi] 1052 | (fun _ build -> 1053 | build_deps_cmi_files build ~pathI ~package file_base_of_module mli; 1054 | ocaml `Byte ~c:() ~pathI ~package ~o:cmi [mli] 1055 | ) 1056 | ); 1057 | 1058 | (* .ml -> ... *) 1059 | List.iter ml_files ~f:(fun ml -> 1060 | let base = chop_suffix ml ".ml" in 1061 | let mli = sprintf "%s.mli" base in 1062 | let cmi = sprintf "%s.cmi" base in 1063 | let mli_exists = List.mem ~set:mli_files mli in 1064 | 1065 | let c = () in 1066 | 1067 | (* .ml -> .cmo/.cmx,.o and .cmi if no corresponding .mli *) 1068 | List.iter [`Byte; `Native] ~f:(fun mode -> 1069 | let obj = base ^ (obj_suffix mode) in 1070 | let internal_deps = internal_deps_files mode (Lib x) in 1071 | let deps = 1072 | if mli_exists 1073 | then ml::cmi::internal_deps 1074 | else ml::internal_deps 1075 | in 1076 | let prods = 1077 | let objs = add_o_to_cmx [obj] in 1078 | if mli_exists then objs else cmi::objs 1079 | in 1080 | Rule.rule ~deps ~prods 1081 | (fun _ build -> 1082 | build_deps_cmi_files build ~pathI ~package file_base_of_module ml; 1083 | match x.style with 1084 | | `Basic -> 1085 | ocaml mode ~c ~pathI ~package ~o:obj [ml] 1086 | | `Pack pack_name -> 1087 | let for_pack = String.capitalize_ascii pack_name in 1088 | ocaml mode ~c ~pathI ~package ~for_pack ~o:obj [ml] 1089 | ) 1090 | ) 1091 | ); 1092 | 1093 | (* .c -> .o *) 1094 | List.iter c_files ~f:(fun c_file -> 1095 | let base = chop_suffix c_file ".c" in 1096 | let obj = sprintf "%s.o" base in 1097 | let c = () in 1098 | let deps = c_file::h_files in 1099 | Rule.rule ~deps ~prods:[obj] (fun _ _ -> 1100 | Ocamlbuild_plugin.(Seq [ 1101 | ocamlc ~c ~pathI ~package [c_file]; 1102 | 1103 | (* OCaml < 4.04.0 treat combination of -o and -c poorly. See 4.04.0 1104 | release notes on PR#6475. We workaround this by not using -o above 1105 | and instead manually mv'ing the output. *) 1106 | Cmd (S [A"mv"; A"-f"; A(basename obj); A obj]); 1107 | ]) 1108 | ) 1109 | ); 1110 | 1111 | ((* .cmo*/.cmx,.o* -> packed .cmo/.cmx,.o *) 1112 | match x.style with 1113 | | `Basic -> () 1114 | | `Pack _ -> 1115 | List.iter [`Byte; `Native] ~f:(fun mode -> 1116 | let obj = path_of_pack x ~suffix:(obj_suffix mode) in 1117 | let prods = add_o_to_cmx [obj] in 1118 | Rule.rule ~deps:ml_files ~prods (fun _ build -> 1119 | let deps = build_ml_files_sorted mode build ~package ml_files in 1120 | ocaml mode ~pack:() ~o:obj deps 1121 | ) 1122 | ) 1123 | ); 1124 | 1125 | ((* .cmx,.o -> .cmxs *) 1126 | match x.build_plugin with 1127 | | false -> () 1128 | | true -> 1129 | let plugin = path_of_lib x ~suffix:".cmxs" in 1130 | match c_files with 1131 | | [] -> ( (* No C files. Call ocamlc/ocamlopt directly. *) 1132 | match x.style with 1133 | | `Pack _ -> 1134 | let objs = [path_of_pack x ~suffix:(obj_suffix `Native)] in 1135 | let deps = add_o_to_cmx objs in 1136 | Rule.rule ~deps ~prods:[plugin] (fun _ _ -> 1137 | ocamlopt ~shared:() ~o:plugin objs 1138 | ) 1139 | | `Basic -> 1140 | Rule.rule ~deps:ml_files ~prods:[plugin] (fun _ build -> 1141 | let deps = 1142 | build_ml_files_sorted `Native build ~package ml_files 1143 | in 1144 | ocamlopt ~shared:() ~o:plugin deps 1145 | ) 1146 | ) 1147 | | _ -> (* There is C code. FIXME: figure out what to do here. *) 1148 | () 1149 | ); 1150 | 1151 | ((* .cmo/.cmx,.o -> .cma/.cmxa *) 1152 | let ml_packed_obj mode = path_of_pack x ~suffix:(obj_suffix mode) in 1153 | let ml_lib mode = path_of_lib x ~suffix:(lib_suffix mode) in 1154 | match c_files with 1155 | | [] -> ( (* No C files. Call ocamlc/ocamlopt directly. *) 1156 | List.iter [`Byte; `Native] ~f:(fun mode -> 1157 | let prod = ml_lib mode in 1158 | match x.style with 1159 | | `Pack _ -> ( 1160 | let obj = ml_packed_obj mode in 1161 | let deps = add_o_to_cmx [obj] in 1162 | Rule.rule ~deps ~prods:[prod] 1163 | (fun _ _ -> ocaml mode ~a:() ~o:prod [obj]) 1164 | ) 1165 | | `Basic -> ( (* Ensure all cmo files exist *) 1166 | Rule.rule ~deps:ml_files ~prods:[prod] (fun _ build -> 1167 | let deps = build_ml_files_sorted mode build ~package ml_files in 1168 | ocaml mode ~a:() ~o:prod deps 1169 | ) 1170 | ) 1171 | ) 1172 | ) 1173 | | _ -> ( (* There is C code. Call ocamlmklib. *) 1174 | 1175 | let clibs = [ 1176 | sprintf "%s/dll%s.so" (dirname x.dir) x.name; 1177 | sprintf "%s/lib%s.a" (dirname x.dir) x.name; 1178 | ] 1179 | in 1180 | 1181 | List.iter [`Byte;`Native] ~f:(fun mode -> 1182 | let objs = 1183 | match x.style with 1184 | | `Pack _ -> [ ml_packed_obj mode] 1185 | | `Basic -> 1186 | let suffix = obj_suffix mode in 1187 | (* Does order matter here? *) 1188 | List.map ml_files ~f:(replace_suffix_exn ~old:".ml" ~new_:suffix) 1189 | in 1190 | let prod = ml_lib mode in 1191 | let o = 1192 | sprintf "%s/%s" (dirname x.dir) x.name |> 1193 | Filename.normalize 1194 | in 1195 | let deps = (add_o_to_cmx objs)@clibs in 1196 | Rule.rule ~deps ~prods:[prod] (fun _ _ -> 1197 | ocamlmklib ~o objs 1198 | ) 1199 | ); 1200 | 1201 | ((* .c -> .o *) 1202 | let deps = List.map c_files ~f:(fun c_file -> 1203 | sprintf "%s.o" (chop_suffix c_file ".c") ) 1204 | in 1205 | let o = 1206 | sprintf "%s/%s" (dirname x.dir) x.name |> 1207 | Filename.normalize 1208 | in 1209 | Rule.rule ~deps ~prods:clibs (fun _ _ -> 1210 | ocamlmklib ~o deps 1211 | ) 1212 | ) 1213 | ) 1214 | ) 1215 | ;; 1216 | 1217 | let build_app (x:app) = 1218 | let annot = x.annot in 1219 | let bin_annot = x.bin_annot in 1220 | let cc = x.cc in 1221 | let cclib = x.cclib in 1222 | let ccopt = x.ccopt in 1223 | let color = x.color in 1224 | let g = x.g in 1225 | let inline = x.inline in 1226 | let inline_alloc_cost = x.inline_alloc_cost in 1227 | let inline_branch_cost = x.inline_branch_cost in 1228 | let inline_branch_factor = x.inline_branch_factor in 1229 | let inline_call_cost = x.inline_call_cost in 1230 | let inline_indirect_cost = x.inline_indirect_cost in 1231 | let inline_lifting_benefit = x.inline_lifting_benefit in 1232 | let inline_max_depth = x.inline_max_depth in 1233 | let inline_max_unroll = x.inline_max_unroll in 1234 | let inline_prim_cost = x.inline_prim_cost in 1235 | let inlining_report = x.inlining_report in 1236 | let no_check_prims = x.no_check_prims in 1237 | let no_unbox_free_vars_of_closures = x.no_unbox_free_vars_of_closures in 1238 | let no_unbox_specialised_args = x.no_unbox_specialised_args in 1239 | let optimize_classic = x.optimize_classic in 1240 | let optimize2 = x.optimize2 in 1241 | let optimize3 = x.optimize3 in 1242 | let remove_unused_arguments = x.remove_unused_arguments in 1243 | let rounds = x.rounds in 1244 | let safe_string = x.safe_string in 1245 | let short_paths = x.short_paths in 1246 | let strict_formats = x.strict_formats in 1247 | let strict_sequence = x.strict_sequence in 1248 | let thread = x.thread in 1249 | let unbox_closures = x.unbox_closures in 1250 | let w = x.w in 1251 | let warn_error = x.warn_error in 1252 | let package = findlib_deps_all (App x) in 1253 | let pathI = 1254 | internal_deps_all (App x) |> 1255 | List.filter_map ~f:(function 1256 | | Lib x -> Some (module_dirs ~style_matters:true x) 1257 | | App _ -> None 1258 | ) |> 1259 | List.flatten |> 1260 | List.sort_uniq ~cmp:String.compare 1261 | in 1262 | let ocaml mode ?o files = match mode with 1263 | | `Byte -> 1264 | ocamlfind_ocamlc files 1265 | ?o 1266 | ?annot ?bin_annot ?cc ?cclib ?ccopt ?color ?g 1267 | ?safe_string ?short_paths ?strict_formats ?strict_sequence 1268 | ?thread ?w ?warn_error 1269 | ~package ~pathI ~linkpkg:() 1270 | | `Native -> 1271 | ocamlfind_ocamlopt files 1272 | ?o 1273 | ?annot ?bin_annot ?cc ?cclib ?ccopt ?color ?g 1274 | ?inline 1275 | ?inline_alloc_cost ?inline_branch_cost ?inline_branch_factor 1276 | ?inline_call_cost ?inline_indirect_cost ?inline_lifting_benefit 1277 | ?inline_max_depth ?inline_max_unroll ?inline_prim_cost 1278 | ?inlining_report 1279 | ?no_unbox_free_vars_of_closures ?no_unbox_specialised_args 1280 | ?optimize_classic ?optimize2 ?optimize3 1281 | ?remove_unused_arguments ?rounds 1282 | ?safe_string ?short_paths ?strict_formats ?strict_sequence 1283 | ?thread ?unbox_closures ?w ?warn_error 1284 | ~package ~pathI ~linkpkg:() 1285 | in 1286 | List.iter [`Byte; `Native] ~f:(fun mode -> 1287 | let path_of_lib (x:lib) = path_of_lib x ~suffix:(lib_suffix mode) in 1288 | let path_of_app (x:app) = path_of_app x ~suffix:(exe_suffix mode) in 1289 | let files = 1290 | ( 1291 | internal_deps_all (App x) |> 1292 | Graph.of_list |> 1293 | Graph.Topological.sort |> 1294 | List.filter_map ~f:(function 1295 | | Lib x -> Some (path_of_lib x) 1296 | | App _ -> None 1297 | ) 1298 | )@ 1299 | [x.file] 1300 | in 1301 | let deps = 1302 | ( 1303 | List.map x.internal_deps ~f:(function 1304 | | Lib x -> path_of_lib x 1305 | | App x -> path_of_app x 1306 | ) 1307 | )@ 1308 | [x.file] 1309 | in 1310 | let prod = path_of_app x in 1311 | Rule.rule ~deps ~prods:[prod] 1312 | (fun _ _ -> ocaml mode ~o:prod files) 1313 | ) 1314 | ;; 1315 | 1316 | let build_js_of_ocaml 1317 | ?custom_header ?debug ?debug_info ?disable ?enable 1318 | ?no_inline ?no_runtime ?o ?opt ?pretty ?set 1319 | ?source_map_inline ?source_map_no_source 1320 | ?source_map_root ?source_map 1321 | ?extern_fs ?file ?pathI ?ofs 1322 | ?linkall ?no_cmis ?toplevel 1323 | ?extra_js (item : item) = 1324 | let name = name item in 1325 | let dep = name ^ (match item with Lib _ -> lib_suffix `Byte | App _ -> exe_suffix `Byte) in 1326 | Tools.js_of_ocaml_rule 1327 | ?custom_header ?debug ?debug_info ?disable ?enable 1328 | ?no_inline ?no_runtime ?o ?opt ?pretty ?set 1329 | ?source_map_inline ?source_map_no_source 1330 | ?source_map_root ?source_map 1331 | ?extern_fs ?file ?pathI ?ofs 1332 | ?linkall ?no_cmis ?toplevel 1333 | ?extra_js dep 1334 | ;; 1335 | 1336 | let build_static_file path build_content = 1337 | let open Ocamlbuild_plugin in 1338 | let open Util in 1339 | let path = Filename.normalize path in 1340 | Rule.rule ~prods:[path] (fun _ _ -> 1341 | let content = build_content () |> List.map ~f:(sprintf "%s\n") in 1342 | Seq [ 1343 | Cmd (Sh (sprintf "mkdir -p %s" (dirname path))); 1344 | Echo (content,path); 1345 | ] 1346 | ) 1347 | ;; 1348 | 1349 | (******************************************************************************) 1350 | (** {2 Plugins} *) 1351 | (******************************************************************************) 1352 | let basic1 1353 | ?(additional_rules=[]) ?(ocamlinit_postfix=[]) 1354 | ~project_name ~version items 1355 | = 1356 | (* Compute graph to check for cycles and other errors. *) 1357 | ignore (Graph.of_list items); 1358 | 1359 | let libs = filter_libs items in 1360 | let apps = filter_apps items in 1361 | 1362 | Ocamlbuild_plugin.dispatch @@ function 1363 | | Ocamlbuild_plugin.After_rules -> ( 1364 | Ocamlbuild_plugin.clear_rules(); 1365 | 1366 | List.iter additional_rules ~f:(fun f -> f()); 1367 | 1368 | List.iter libs ~f:build_lib; 1369 | List.iter apps ~f:build_app; 1370 | 1371 | build_static_file ".merlin" 1372 | (fun () -> merlin_file items); 1373 | 1374 | build_static_file ".ocamlinit" 1375 | (fun () -> ocamlinit_file ~postfix:ocamlinit_postfix items); 1376 | 1377 | build_static_file "project.mk" 1378 | (fun () -> makefile ~project_name items); 1379 | 1380 | (match meta_file ~version libs with 1381 | | Some x -> Findlib.build_meta_file x 1382 | | None -> () 1383 | ); 1384 | 1385 | build_static_file (sprintf "%s.install" project_name) 1386 | (fun () -> install_file items); 1387 | ) 1388 | | _ -> () 1389 | 1390 | let solvuu1 1391 | ?(additional_rules=[]) ?(ocamlinit_postfix=[]) 1392 | ~project_name ~version items 1393 | = 1394 | (* Compute graph to check for cycles and other errors. *) 1395 | ignore (Graph.of_list items); 1396 | 1397 | let annot = Some () in 1398 | let bin_annot = Some () in 1399 | let g = Some () in 1400 | let safe_string = None in 1401 | let short_paths = Some () in 1402 | let strict_sequence = Some () in 1403 | let thread = Some () in 1404 | let w = Some "A-4-33-41-42-44-45-48" in 1405 | 1406 | let items = List.map items ~f:(function 1407 | | Lib x -> 1408 | Lib {x with annot; bin_annot; g; safe_string; short_paths; 1409 | strict_sequence;thread; w} 1410 | | App x -> 1411 | App {x with annot; bin_annot; g; safe_string; short_paths; 1412 | strict_sequence; thread; w} 1413 | ) 1414 | in 1415 | 1416 | let libs = filter_libs items in 1417 | let apps = filter_apps items in 1418 | 1419 | Ocamlbuild_plugin.dispatch @@ function 1420 | | Ocamlbuild_plugin.Before_options -> ( 1421 | Ocamlbuild_plugin.Options.use_ocamlfind := true 1422 | ) 1423 | | Ocamlbuild_plugin.After_rules -> ( 1424 | Ocamlbuild_plugin.clear_rules(); 1425 | 1426 | List.iter additional_rules ~f:(fun f -> f()); 1427 | 1428 | m4_rule () 1429 | ~_D:[ 1430 | "GIT_COMMIT", Some (match git_last_commit() with 1431 | | None -> "None" 1432 | | Some x -> sprintf "Some \"%s\"" x 1433 | ); 1434 | "VERSION", Some version; 1435 | ]; 1436 | 1437 | atdgen_t_rule ~j_std:() (); 1438 | atdgen_j_rule ~j_std:() (); 1439 | 1440 | menhir_rule (); 1441 | ocamllex_rule (); 1442 | 1443 | List.iter libs ~f:build_lib; 1444 | List.iter apps ~f:build_app; 1445 | 1446 | build_static_file ".merlin" 1447 | (fun () -> merlin_file items); 1448 | 1449 | build_static_file ".ocamlinit" 1450 | (fun () -> ocamlinit_file ~postfix:ocamlinit_postfix items); 1451 | 1452 | build_static_file "project.mk" 1453 | (fun () -> makefile ~project_name items); 1454 | 1455 | (match meta_file ~version libs with 1456 | | Some x -> Findlib.build_meta_file x 1457 | | None -> () 1458 | ); 1459 | 1460 | build_static_file (sprintf "%s.install" project_name) 1461 | (fun () -> install_file items); 1462 | ) 1463 | | _ -> () 1464 | -------------------------------------------------------------------------------- /lib/tools.ml: -------------------------------------------------------------------------------- 1 | (* Implementation notes: 2 | 3 | The list of arguments in many functions of this module seem unruly, 4 | but they should be copied/pasted in almost all cases. Thus, the 5 | work to write and maintain the code is simpler than it might at 6 | first seem. Factoring the type definitions as done here is key to 7 | this maintainability. 8 | 9 | Many functions are implemented in two steps, first a function 10 | [foo_specs] is defined and then [foo]. This is to support factoring 11 | out code dependent on the long list of arguments that are needed in 12 | multiple places. For instance [ocamlc_args_specs] is needed to 13 | implement [ocamlc] and [ocamlfind_ocamlc]. 14 | *) 15 | open Printf 16 | open Ocamlbuild_plugin 17 | open Util 18 | open Util.Spec 19 | 20 | (******************************************************************************) 21 | (** {2 Abstraction over ocamlc/ocamlopt} *) 22 | (******************************************************************************) 23 | type 'a ocaml_compiler_args = 24 | ?a:unit -> 25 | ?absname:unit -> 26 | ?annot:unit -> 27 | ?bin_annot:unit -> 28 | ?c:unit -> 29 | ?cc:string -> 30 | ?cclib:string -> 31 | ?ccopt:string -> 32 | ?color:[`auto | `always | `never] -> 33 | ?config:unit -> 34 | ?for_pack:string -> 35 | ?g:unit -> 36 | ?i:unit -> 37 | ?pathI:string list -> 38 | ?impl:string -> 39 | ?intf:string -> 40 | ?intf_suffix:string -> 41 | ?labels:unit -> 42 | ?linkall:unit -> 43 | ?make_runtime:unit -> 44 | ?no_alias_deps:unit -> 45 | ?no_app_funct:unit -> 46 | ?noassert:unit -> 47 | ?noautolink:unit -> 48 | ?nolabels:unit -> 49 | ?nostdlib:unit -> 50 | ?o:string -> 51 | ?open_:string list -> 52 | ?output_obj:unit -> 53 | ?pack:unit -> 54 | ?pp:string -> 55 | ?ppx:string -> 56 | ?principal:unit -> 57 | ?rectypes:unit -> 58 | ?runtime_variant:string -> 59 | ?safe_string:unit -> 60 | ?short_paths:unit -> 61 | ?strict_sequence:unit -> 62 | ?strict_formats:unit -> 63 | ?thread:unit -> 64 | ?unsafe:unit -> 65 | ?unsafe_string:unit -> 66 | ?use_runtime:string -> 67 | ?v:unit -> 68 | ?verbose:unit -> 69 | ?version:unit -> 70 | ?w:string -> 71 | ?warn_error:string -> 72 | ?warn_help:unit -> 73 | ?where:unit -> 74 | ?nopervasives:unit -> 75 | ?dsource:unit -> 76 | ?dparsetree:unit -> 77 | ?dtypedtree:unit -> 78 | ?drawlambda:unit -> 79 | ?dlambda:unit -> 80 | ?dinstr:unit -> 81 | ?help:unit -> 82 | 'a 83 | 84 | let ocaml_compiler_args_specs 85 | 86 | (* ocaml_compiler_args *) 87 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 88 | ?config ?for_pack ?g ?i ?pathI 89 | ?impl ?intf ?intf_suffix ?labels ?linkall ?make_runtime 90 | ?no_alias_deps ?no_app_funct ?noassert ?noautolink ?nolabels 91 | ?nostdlib ?o ?open_ ?output_obj ?pack ?pp ?ppx ?principal 92 | ?rectypes ?runtime_variant ?safe_string ?short_paths 93 | ?strict_sequence ?strict_formats ?thread ?unsafe 94 | ?unsafe_string ?use_runtime ?v ?verbose ?version 95 | ?w ?warn_error ?warn_help ?where 96 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 97 | ?drawlambda ?dlambda ?dinstr 98 | ?help 99 | 100 | () 101 | : spec option list list = 102 | let string = string ~delim:`Space in 103 | let string_list = string_list ~delim:`Space in 104 | [ 105 | unit "-a" a; 106 | unit "-absname" absname; 107 | unit "-annot" annot; 108 | unit "-bin-annot" bin_annot; 109 | unit "-c" c; 110 | string "-cc" cc; 111 | string "-cclib" cclib; 112 | string "-ccopt" ccopt; 113 | string "-color" (match color with 114 | | None -> None 115 | | Some `auto -> Some "auto" 116 | | Some `always -> Some "always" 117 | | Some `never -> Some "never" 118 | ); 119 | unit "-config" config; 120 | string "-for-pack" for_pack; 121 | unit "-g" g; 122 | unit "-i" i; 123 | string_list "-I" pathI; 124 | string "-impl" impl; 125 | string "-intf" intf; 126 | string "-intf-suffix" intf_suffix; 127 | unit "-labels" labels; 128 | unit "-linkall" linkall; 129 | unit "-make-runtime" make_runtime; 130 | unit "-no-alias-deps" no_alias_deps; 131 | unit "-no-app-funct" no_app_funct; 132 | unit "-noassert" noassert; 133 | unit "-noautolink" noautolink; 134 | unit "-nolabels" nolabels; 135 | unit "-nostdlib" nostdlib; 136 | string "-o" o; 137 | string_list "-open" open_; 138 | unit "-output-obj" output_obj; 139 | unit "-pack" pack; 140 | string "-pp" pp; 141 | string "-ppx" ppx; 142 | unit "-principal" principal; 143 | unit "-rectypes" rectypes; 144 | string "-runtime-variant" runtime_variant; 145 | unit "-safe-string" safe_string; 146 | unit "-short-paths" short_paths; 147 | unit "-strict-sequence" strict_sequence; 148 | unit "-strict-formats" strict_formats; 149 | unit "-thread" thread; 150 | unit "-unsafe" unsafe; 151 | unit "-unsafe-string" unsafe_string; 152 | string "-use-runtime" use_runtime; 153 | unit "-v" v; 154 | unit "-verbose" verbose; 155 | unit "-version" version; 156 | string "-w" w; 157 | string "-warn-error" warn_error; 158 | unit "-warn-help" warn_help; 159 | unit "-where" where; 160 | unit "-nopervasives" nopervasives; 161 | unit "-dsource" dsource; 162 | unit "-dparsetree" dparsetree; 163 | unit "-dtypedtree" dtypedtree; 164 | unit "-drawlambda" drawlambda; 165 | unit "-dlambda" dlambda; 166 | unit "-dinstr" dinstr; 167 | unit "-help" help; 168 | ] 169 | 170 | let ocaml_compiler 171 | 172 | (* ocaml_compiler_args *) 173 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 174 | ?config ?for_pack ?g ?i ?pathI 175 | ?impl ?intf ?intf_suffix ?labels ?linkall ?make_runtime 176 | ?no_alias_deps ?no_app_funct ?noassert ?noautolink ?nolabels 177 | ?nostdlib ?o ?open_ ?output_obj ?pack ?pp ?ppx ?principal 178 | ?rectypes ?runtime_variant ?safe_string ?short_paths 179 | ?strict_sequence ?strict_formats ?thread ?unsafe 180 | ?unsafe_string ?use_runtime ?v ?verbose ?version 181 | ?w ?warn_error ?warn_help ?where 182 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 183 | ?drawlambda ?dlambda ?dinstr 184 | ?help 185 | 186 | mode files 187 | = 188 | [[Some (A (match mode with `Byte -> "ocamlc" | `Native -> "ocamlopt"))]] 189 | @(ocaml_compiler_args_specs 190 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 191 | ?config ?for_pack ?g ?i ?pathI 192 | ?impl ?intf ?intf_suffix ?labels ?linkall ?make_runtime 193 | ?no_alias_deps ?no_app_funct ?noassert ?noautolink ?nolabels 194 | ?nostdlib ?o ?open_ ?output_obj ?pack ?pp ?ppx ?principal 195 | ?rectypes ?runtime_variant ?safe_string ?short_paths 196 | ?strict_sequence ?strict_formats ?thread ?unsafe 197 | ?unsafe_string ?use_runtime ?v ?verbose ?version 198 | ?w ?warn_error ?warn_help ?where 199 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 200 | ?drawlambda ?dlambda ?dinstr 201 | ?help () 202 | )@[List.map files ~f:(fun file -> Some (A file))] 203 | |> specs_to_command 204 | 205 | 206 | type 'a ocamlfind_ocaml_compiler_args = 207 | ?package:string list -> 208 | ?linkpkg:unit -> 209 | ?predicates:string -> 210 | ?dontlink:string list -> 211 | ?ppopt:string -> 212 | ?ppxopt:(string * string) list -> 213 | ?dllpath_pkg:string list -> 214 | ?dllpath_all:unit -> 215 | ?ignore_error:unit -> 216 | ?passopt:string list -> 217 | ?only_show:unit -> 218 | 'a 219 | 220 | let ocamlfind_ocaml_compiler_args_specs 221 | 222 | (* ocamlfind_ocaml_compiler_args *) 223 | ?package ?linkpkg ?predicates ?dontlink 224 | ?ppopt ?ppxopt ?dllpath_pkg ?dllpath_all 225 | ?ignore_error ?passopt ?only_show 226 | 227 | () 228 | : spec option list list 229 | = 230 | let delim = `Space in 231 | let string = string ~delim in 232 | let string_list = string_list ~delim in 233 | [ 234 | (match package with 235 | | None -> [None] 236 | | Some x -> 237 | string "-package" (Some (String.concat ~sep:"," x)) 238 | ); 239 | unit "-linkpkg" linkpkg; 240 | string "-predicates" predicates; 241 | string_list "-dontlink" dontlink; 242 | string "-ppopt" ppopt; 243 | ( 244 | let ppxopt = match ppxopt with 245 | | None -> None 246 | | Some l -> Some (List.map l ~f:(fun (x,y) -> sprintf "%s,%s" x y)) 247 | in 248 | string_list "-ppxopt" ppxopt 249 | ); 250 | string_list "-dllpath-pkg" dllpath_pkg; 251 | unit "-dllpath-all" dllpath_all; 252 | unit "-ignore-error" ignore_error; 253 | string_list "-passopt" passopt; 254 | unit "-only-show" only_show; 255 | ] 256 | 257 | let ocamlfind_ocaml_compiler 258 | 259 | (* ocamlfind_ocaml_compiler_args *) 260 | ?package ?linkpkg ?predicates ?dontlink 261 | ?ppopt ?ppxopt ?dllpath_pkg ?dllpath_all 262 | ?ignore_error ?passopt ?only_show 263 | 264 | (* ocaml_compiler_args *) 265 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 266 | ?config ?for_pack ?g ?i ?pathI 267 | ?impl ?intf ?intf_suffix ?labels ?linkall ?make_runtime 268 | ?no_alias_deps ?no_app_funct ?noassert ?noautolink ?nolabels 269 | ?nostdlib ?o ?open_ ?output_obj ?pack ?pp ?ppx ?principal 270 | ?rectypes ?runtime_variant ?safe_string ?short_paths 271 | ?strict_sequence ?strict_formats ?thread ?unsafe 272 | ?unsafe_string ?use_runtime ?v ?verbose ?version 273 | ?w ?warn_error ?warn_help ?where 274 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 275 | ?drawlambda ?dlambda ?dinstr 276 | ?help 277 | 278 | mode files 279 | = 280 | [[ 281 | Some (A "ocamlfind"); 282 | Some (A (match mode with `Byte -> "ocamlc" | `Native -> "ocamlopt")); 283 | ]] 284 | @( 285 | ocaml_compiler_args_specs 286 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 287 | ?config ?for_pack ?g ?i ?pathI 288 | ?impl ?intf ?intf_suffix ?labels ?linkall ?make_runtime 289 | ?no_alias_deps ?no_app_funct ?noassert ?noautolink ?nolabels 290 | ?nostdlib ?o ?open_ ?output_obj ?pack ?pp ?ppx ?principal 291 | ?rectypes ?runtime_variant ?safe_string ?short_paths 292 | ?strict_sequence ?strict_formats ?thread ?unsafe 293 | ?unsafe_string ?use_runtime ?v ?verbose ?version 294 | ?w ?warn_error ?warn_help ?where 295 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 296 | ?drawlambda ?dlambda ?dinstr 297 | ?help () 298 | ) 299 | @(ocamlfind_ocaml_compiler_args_specs 300 | ?package ?linkpkg ?predicates ?dontlink 301 | ?ppopt ?ppxopt ?dllpath_pkg ?dllpath_all 302 | ?ignore_error ?passopt ?only_show () 303 | ) 304 | @[List.map files ~f:(fun file -> Some (A file))] 305 | |> specs_to_command 306 | 307 | 308 | (******************************************************************************) 309 | (** {2 ocamlc} *) 310 | (******************************************************************************) 311 | type 'a ocamlc_args = ( 312 | ?compat_32:unit -> 313 | ?custom:unit -> 314 | ?dllib:string -> 315 | ?dllpath:string -> 316 | ?vmthread:unit -> 317 | ?no_check_prims:unit -> 318 | 'a 319 | ) ocaml_compiler_args 320 | 321 | let ocamlc_args_specs 322 | 323 | (* ocaml_compiler_args *) 324 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 325 | ?config ?for_pack ?g ?i ?pathI 326 | ?impl ?intf ?intf_suffix ?labels ?linkall ?make_runtime 327 | ?no_alias_deps ?no_app_funct ?no_check_prims ?noassert 328 | ?noautolink ?nolabels 329 | ?nostdlib ?o ?open_ ?output_obj ?pack ?pp ?ppx ?principal 330 | ?rectypes ?runtime_variant ?safe_string ?short_paths 331 | ?strict_sequence ?strict_formats ?thread ?unsafe 332 | ?unsafe_string ?use_runtime ?v ?verbose ?version 333 | ?w ?warn_error ?warn_help ?where 334 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 335 | ?drawlambda ?dlambda ?dinstr 336 | ?help 337 | 338 | (* ocamlc_args *) 339 | ?compat_32 ?custom ?dllib ?dllpath ?vmthread 340 | 341 | () 342 | 343 | : spec option list list 344 | = 345 | let string = string ~delim:`Space in 346 | (ocaml_compiler_args_specs 347 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 348 | ?config ?for_pack ?g ?i ?pathI 349 | ?impl ?intf ?intf_suffix ?labels ?linkall ?make_runtime 350 | ?no_alias_deps ?no_app_funct ?noassert ?noautolink ?nolabels 351 | ?nostdlib ?o ?open_ ?output_obj ?pack ?pp ?ppx ?principal 352 | ?rectypes ?runtime_variant ?safe_string ?short_paths 353 | ?strict_sequence ?strict_formats ?thread ?unsafe 354 | ?unsafe_string ?use_runtime ?v ?verbose ?version 355 | ?w ?warn_error ?warn_help ?where 356 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 357 | ?drawlambda ?dlambda ?dinstr 358 | ?help () 359 | )@[ 360 | unit "-compat-32" compat_32; 361 | unit "-custom" custom; 362 | string "-dllib" dllib; 363 | string "-dllpath" dllpath; 364 | unit "-vmthread" vmthread; 365 | unit "-no-check-prims" no_check_prims; 366 | ] 367 | 368 | let ocamlc 369 | 370 | (* ocaml_compiler_args *) 371 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 372 | ?config ?for_pack ?g ?i ?pathI 373 | ?impl ?intf ?intf_suffix ?labels ?linkall ?make_runtime 374 | ?no_alias_deps ?no_app_funct ?noassert ?noautolink ?nolabels 375 | ?nostdlib ?o ?open_ ?output_obj ?pack ?pp ?ppx ?principal 376 | ?rectypes ?runtime_variant ?safe_string ?short_paths 377 | ?strict_sequence ?strict_formats ?thread ?unsafe 378 | ?unsafe_string ?use_runtime ?v ?verbose ?version 379 | ?w ?warn_error ?warn_help ?where 380 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 381 | ?drawlambda ?dlambda ?dinstr 382 | ?help 383 | 384 | (* ocamlc_args *) 385 | ?compat_32 ?custom ?dllib ?dllpath ?vmthread ?no_check_prims 386 | 387 | files 388 | = 389 | [[Some (A "ocamlc")]] 390 | @(ocamlc_args_specs 391 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 392 | ?config ?custom ?dllib ?dllpath ?for_pack ?g ?i ?pathI 393 | ?impl ?intf ?intf_suffix ?labels ?linkall ?make_runtime 394 | ?no_alias_deps ?no_app_funct ?no_check_prims ?noassert 395 | ?noautolink ?nolabels 396 | ?nostdlib ?o ?open_ ?output_obj ?pack ?pp ?ppx ?principal 397 | ?rectypes ?runtime_variant ?safe_string ?short_paths 398 | ?strict_sequence ?strict_formats ?thread ?unsafe 399 | ?unsafe_string ?use_runtime ?v ?verbose ?version 400 | ?w ?warn_error ?warn_help ?where 401 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 402 | ?drawlambda ?dlambda ?dinstr 403 | ?help 404 | ?compat_32 ?vmthread () 405 | ) 406 | @[List.map files ~f:(fun file -> Some (A file))] 407 | |> specs_to_command 408 | 409 | 410 | let ocamlfind_ocamlc 411 | 412 | (* ocamlfind_ocaml_compiler_args *) 413 | ?package ?linkpkg ?predicates ?dontlink 414 | ?ppopt ?ppxopt ?dllpath_pkg ?dllpath_all 415 | ?ignore_error ?passopt ?only_show 416 | 417 | (* ocaml_compiler_args *) 418 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 419 | ?config ?for_pack ?g ?i ?pathI 420 | ?impl ?intf ?intf_suffix ?labels ?linkall ?make_runtime 421 | ?no_alias_deps ?no_app_funct ?noassert ?noautolink ?nolabels 422 | ?nostdlib ?o ?open_ ?output_obj ?pack ?pp ?ppx ?principal 423 | ?rectypes ?runtime_variant ?safe_string ?short_paths 424 | ?strict_sequence ?strict_formats ?thread ?unsafe 425 | ?unsafe_string ?use_runtime ?v ?verbose ?version 426 | ?w ?warn_error ?warn_help ?where 427 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 428 | ?drawlambda ?dlambda ?dinstr 429 | ?help 430 | 431 | (* ocamlc_args *) 432 | ?compat_32 ?custom ?dllib ?dllpath ?vmthread ?no_check_prims 433 | 434 | files 435 | = 436 | [[Some (A "ocamlfind"); Some (A "ocamlc")]] 437 | @(ocamlc_args_specs 438 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 439 | ?config ?for_pack ?g ?i ?pathI 440 | ?impl ?intf ?intf_suffix ?labels ?linkall ?make_runtime 441 | ?no_alias_deps ?no_app_funct ?no_check_prims ?noassert 442 | ?noautolink ?nolabels 443 | ?nostdlib ?o ?open_ ?output_obj ?pack ?pp ?ppx ?principal 444 | ?rectypes ?runtime_variant ?safe_string ?short_paths 445 | ?strict_sequence ?strict_formats ?thread ?unsafe 446 | ?unsafe_string ?use_runtime ?v ?verbose ?version 447 | ?w ?warn_error ?warn_help ?where 448 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 449 | ?drawlambda ?dlambda ?dinstr 450 | ?help 451 | ?compat_32 ?custom ?dllib ?dllpath ?vmthread () 452 | ) 453 | @(ocamlfind_ocaml_compiler_args_specs 454 | ?package ?linkpkg ?predicates ?dontlink 455 | ?ppopt ?ppxopt ?dllpath_pkg ?dllpath_all 456 | ?ignore_error ?passopt ?only_show () 457 | ) 458 | @[List.map files ~f:(fun file -> Some (A file))] 459 | |> specs_to_command 460 | 461 | 462 | (******************************************************************************) 463 | (** {2 ocamlopt} *) 464 | (******************************************************************************) 465 | type 'a ocamlopt_args = ( 466 | ?compact:unit -> 467 | ?inline:string -> 468 | ?inline_alloc_cost:string -> 469 | ?inline_branch_cost:string -> 470 | ?inline_branch_factor:string -> 471 | ?inline_call_cost:string -> 472 | ?inline_indirect_cost:string -> 473 | ?inline_lifting_benefit:string -> 474 | ?inline_max_depth:string -> 475 | ?inline_max_unroll:string -> 476 | ?inline_prim_cost:string -> 477 | ?inlining_report:unit -> 478 | ?no_unbox_free_vars_of_closures:unit -> 479 | ?no_unbox_specialised_args:unit -> 480 | ?nodynlink:unit -> 481 | ?optimize_classic:unit -> 482 | ?optimize2:unit -> 483 | ?optimize3:unit -> 484 | ?p:unit -> 485 | ?remove_unused_arguments:unit -> 486 | ?rounds:int -> 487 | ?keep_assembly:unit -> 488 | ?shared:unit -> 489 | ?unbox_closures:unit -> 490 | 'a 491 | ) ocaml_compiler_args 492 | 493 | let ocamlopt_args_specs 494 | 495 | (* ocaml_compiler_args *) 496 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 497 | ?config ?for_pack ?g ?i ?pathI 498 | ?impl ?intf ?intf_suffix ?labels ?linkall ?make_runtime 499 | ?no_alias_deps ?no_app_funct ?noassert ?noautolink ?nolabels 500 | ?nostdlib ?o ?open_ ?output_obj ?pack ?pp ?ppx ?principal 501 | ?rectypes ?runtime_variant ?safe_string ?short_paths 502 | ?strict_sequence ?strict_formats ?thread ?unsafe 503 | ?unsafe_string ?use_runtime ?v ?verbose ?version 504 | ?w ?warn_error ?warn_help ?where 505 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 506 | ?drawlambda ?dlambda ?dinstr 507 | ?help 508 | 509 | (* ocamlopt_args *) 510 | ?compact ?inline 511 | ?inline_alloc_cost ?inline_branch_cost ?inline_branch_factor 512 | ?inline_call_cost ?inline_indirect_cost ?inline_lifting_benefit 513 | ?inline_max_depth ?inline_max_unroll ?inline_prim_cost 514 | ?inlining_report 515 | ?no_unbox_free_vars_of_closures ?no_unbox_specialised_args 516 | ?nodynlink 517 | ?optimize_classic ?optimize2 ?optimize3 518 | ?p ?remove_unused_arguments ?rounds ?keep_assembly ?shared 519 | ?unbox_closures 520 | 521 | () 522 | : spec option list list 523 | = 524 | (ocaml_compiler_args_specs 525 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 526 | ?config ?for_pack ?g ?i ?pathI 527 | ?impl ?intf ?intf_suffix ?labels ?linkall ?make_runtime 528 | ?no_alias_deps ?no_app_funct ?noassert ?noautolink ?nolabels 529 | ?nostdlib ?o ?open_ ?output_obj ?pack ?pp ?ppx ?principal 530 | ?rectypes ?runtime_variant ?safe_string ?short_paths 531 | ?strict_sequence ?strict_formats ?thread ?unsafe 532 | ?unsafe_string ?use_runtime ?v ?verbose ?version 533 | ?w ?warn_error ?warn_help ?where 534 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 535 | ?drawlambda ?dlambda ?dinstr 536 | ?help () 537 | )@[ 538 | unit "-compact" compact; 539 | string ~delim:`Space "-inline" inline; 540 | string ~delim:`Space "-inline-alloc-cost" inline_alloc_cost; 541 | string ~delim:`Space "-inline-branch-cost" inline_branch_cost; 542 | string ~delim:`Space "-inline-branch-factor" inline_branch_factor; 543 | string ~delim:`Space "-inline-call-cost" inline_call_cost; 544 | string ~delim:`Space "-inline-indirect-cost" inline_indirect_cost; 545 | string ~delim:`Space "-inline-lifting-benefit" inline_lifting_benefit; 546 | string ~delim:`Space "-inline-max-depth" inline_max_depth; 547 | string ~delim:`Space "-inline-max-unroll" inline_max_unroll; 548 | string ~delim:`Space "-inline-prim-cost" inline_prim_cost; 549 | unit "-inlining-report" inlining_report; 550 | unit "-no-unbox-free-vars-of-closures" no_unbox_free_vars_of_closures; 551 | unit "-no-unbox-specialized-args" no_unbox_specialised_args; 552 | unit "-nodynlink" nodynlink; 553 | unit "-Oclassic" optimize_classic; 554 | unit "-O2" optimize2; 555 | unit "-O3" optimize3; 556 | unit "-p" p; 557 | unit "-remove-unused-arguments" remove_unused_arguments; 558 | int ~delim:`Space "-rounds" rounds; 559 | unit "-S" keep_assembly; 560 | unit "-shared" shared; 561 | unit "-unbox-closures" unbox_closures; 562 | ] 563 | 564 | let ocamlopt 565 | 566 | (* ocaml_compiler_args *) 567 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 568 | ?config ?for_pack ?g ?i ?pathI 569 | ?impl ?intf ?intf_suffix ?labels ?linkall ?make_runtime 570 | ?no_alias_deps ?no_app_funct ?noassert ?noautolink ?nolabels 571 | ?nostdlib ?o ?open_ ?output_obj ?pack ?pp ?ppx ?principal 572 | ?rectypes ?runtime_variant ?safe_string ?short_paths 573 | ?strict_sequence ?strict_formats ?thread ?unsafe 574 | ?unsafe_string ?use_runtime ?v ?verbose ?version 575 | ?w ?warn_error ?warn_help ?where 576 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 577 | ?drawlambda ?dlambda ?dinstr 578 | ?help 579 | 580 | (* ocamlopt_args *) 581 | ?compact ?inline 582 | ?inline_alloc_cost ?inline_branch_cost ?inline_branch_factor 583 | ?inline_call_cost ?inline_indirect_cost ?inline_lifting_benefit 584 | ?inline_max_depth ?inline_max_unroll ?inline_prim_cost 585 | ?inlining_report 586 | ?no_unbox_free_vars_of_closures ?no_unbox_specialised_args 587 | ?nodynlink 588 | ?optimize_classic ?optimize2 ?optimize3 589 | ?p ?remove_unused_arguments ?rounds ?keep_assembly ?shared 590 | ?unbox_closures 591 | 592 | files 593 | = 594 | [[Some (A "ocamlopt")]] 595 | @(ocamlopt_args_specs 596 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 597 | ?config ?for_pack ?g ?i ?pathI 598 | ?impl ?intf ?intf_suffix ?labels ?linkall ?make_runtime 599 | ?no_alias_deps ?no_app_funct ?noassert ?noautolink ?nolabels 600 | ?nostdlib ?o ?open_ ?output_obj ?pack ?pp ?ppx ?principal 601 | ?rectypes ?runtime_variant ?safe_string ?short_paths 602 | ?strict_sequence ?strict_formats ?thread ?unsafe 603 | ?unsafe_string ?use_runtime ?v ?verbose ?version 604 | ?w ?warn_error ?warn_help ?where 605 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 606 | ?drawlambda ?dlambda ?dinstr 607 | ?help 608 | ?compact ?inline 609 | ?inline_alloc_cost ?inline_branch_cost ?inline_branch_factor 610 | ?inline_call_cost ?inline_indirect_cost ?inline_lifting_benefit 611 | ?inline_max_depth ?inline_max_unroll ?inline_prim_cost 612 | ?inlining_report 613 | ?no_unbox_free_vars_of_closures ?no_unbox_specialised_args 614 | ?nodynlink 615 | ?optimize_classic ?optimize2 ?optimize3 616 | ?p ?remove_unused_arguments ?rounds ?keep_assembly ?shared 617 | ?unbox_closures 618 | () 619 | )@[List.map files ~f:(fun file -> Some (A file))] 620 | |> specs_to_command 621 | 622 | 623 | let ocamlfind_ocamlopt 624 | 625 | (* ocamlfind_ocaml_compiler_args *) 626 | ?package ?linkpkg ?predicates ?dontlink 627 | ?ppopt ?ppxopt ?dllpath_pkg ?dllpath_all 628 | ?ignore_error ?passopt ?only_show 629 | 630 | (* ocaml_compiler_args *) 631 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 632 | ?config ?for_pack ?g ?i ?pathI 633 | ?impl ?intf ?intf_suffix ?labels ?linkall ?make_runtime 634 | ?no_alias_deps ?no_app_funct ?noassert ?noautolink ?nolabels 635 | ?nostdlib ?o ?open_ ?output_obj ?pack ?pp ?ppx ?principal 636 | ?rectypes ?runtime_variant ?safe_string ?short_paths 637 | ?strict_sequence ?strict_formats ?thread ?unsafe 638 | ?unsafe_string ?use_runtime ?v ?verbose ?version 639 | ?w ?warn_error ?warn_help ?where 640 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 641 | ?drawlambda ?dlambda ?dinstr 642 | ?help 643 | 644 | (* ocamlopt_args *) 645 | ?compact ?inline 646 | ?inline_alloc_cost ?inline_branch_cost ?inline_branch_factor 647 | ?inline_call_cost ?inline_indirect_cost ?inline_lifting_benefit 648 | ?inline_max_depth ?inline_max_unroll ?inline_prim_cost 649 | ?inlining_report 650 | ?no_unbox_free_vars_of_closures ?no_unbox_specialised_args 651 | ?nodynlink 652 | ?optimize_classic ?optimize2 ?optimize3 653 | ?p ?remove_unused_arguments ?rounds ?keep_assembly ?shared 654 | ?unbox_closures 655 | 656 | files 657 | = 658 | [[Some (A "ocamlfind"); Some (A "ocamlopt")]] 659 | @(ocamlopt_args_specs 660 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 661 | ?config ?for_pack ?g ?i ?pathI 662 | ?impl ?intf ?intf_suffix ?labels ?linkall ?make_runtime 663 | ?no_alias_deps ?no_app_funct ?noassert ?noautolink ?nolabels 664 | ?nostdlib ?o ?open_ ?output_obj ?pack ?pp ?ppx ?principal 665 | ?rectypes ?runtime_variant ?safe_string ?short_paths 666 | ?strict_sequence ?strict_formats ?thread ?unsafe 667 | ?unsafe_string ?use_runtime ?v ?verbose ?version 668 | ?w ?warn_error ?warn_help ?where 669 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 670 | ?drawlambda ?dlambda ?dinstr 671 | ?help 672 | ?compact ?inline 673 | ?inline_alloc_cost ?inline_branch_cost ?inline_branch_factor 674 | ?inline_call_cost ?inline_indirect_cost ?inline_lifting_benefit 675 | ?inline_max_depth ?inline_max_unroll ?inline_prim_cost 676 | ?inlining_report 677 | ?no_unbox_free_vars_of_closures ?no_unbox_specialised_args 678 | ?nodynlink 679 | ?optimize_classic ?optimize2 ?optimize3 680 | ?p ?remove_unused_arguments ?rounds ?keep_assembly ?shared 681 | ?unbox_closures 682 | () 683 | ) 684 | @(ocamlfind_ocaml_compiler_args_specs 685 | ?package ?linkpkg ?predicates ?dontlink 686 | ?ppopt ?ppxopt ?dllpath_pkg ?dllpath_all 687 | ?ignore_error ?passopt ?only_show 688 | () 689 | ) 690 | @[List.map files ~f:(fun file -> Some (A file))] 691 | |> specs_to_command 692 | 693 | 694 | (******************************************************************************) 695 | (** {2 ocamlmklib} *) 696 | (******************************************************************************) 697 | let ocamlmklib 698 | ?cclib ?ccopt ?custom ?g ?dllpath ?framework ?pathI 699 | ?failsafe ?ldopt ?linkall ?l ?pathL 700 | ?ocamlc ?ocamlcflags ?ocamlopt ?ocamloptflags 701 | ?o ?oc ?verbose 702 | files 703 | = 704 | let string = string ~delim:`Space in 705 | [ 706 | [Some (A "ocamlmklib")]; 707 | string "-cclib" cclib; 708 | string "-ccopt" ccopt; 709 | unit "-custom" custom; 710 | unit "-g" g; 711 | string "-dllpath" dllpath; 712 | string "-framework" framework; 713 | string_list ~delim:`Space "-I" pathI; 714 | unit "-failsafe" failsafe; 715 | string "-ldopt" ldopt; 716 | unit "-linkall" linkall; 717 | string "-l" l; 718 | string_list ~delim:`None "-L" pathL; 719 | string "-ocamlc" ocamlc; 720 | string "-ocamlcflags" ocamlcflags; 721 | string "-ocamlopt" ocamlopt; 722 | string "-ocamloptflags" ocamloptflags; 723 | string "-o" o; 724 | string "-oc" oc; 725 | unit "-verbose" verbose; 726 | (List.map files ~f:(fun file -> Some (A file))); 727 | ] 728 | |> specs_to_command 729 | 730 | 731 | (******************************************************************************) 732 | (** {2 ocamldep} *) 733 | (******************************************************************************) 734 | type 'a ocamldep_args0 = 735 | ?absname:unit -> 736 | ?all:unit -> 737 | ?pathI:string list -> 738 | ?impl:string list -> 739 | ?intf:string list -> 740 | ?ml_synonym:string -> 741 | ?mli_synonym:string -> 742 | ?modules:unit -> 743 | ?native:unit -> 744 | ?one_line:unit -> 745 | ?open_:string list -> 746 | ?pp:string -> 747 | ?ppx:string -> 748 | ?slash:unit -> 749 | 'a 750 | 751 | type 'a ocamldep_args = 752 | ( 753 | ?sort:unit -> 754 | ?version:unit -> 755 | 'a 756 | ) ocamldep_args0 757 | 758 | let ocamldep_args0_specs 759 | 760 | (* ocamldep_args0 *) 761 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 762 | ?modules ?native ?one_line ?open_ ?pp ?ppx ?slash 763 | 764 | () 765 | : spec option list list 766 | = 767 | let string = string ~delim:`Space in 768 | let string_list = string_list ~delim:`Space in 769 | [ 770 | unit "-absname" absname; 771 | unit "-all" all; 772 | string_list "-I" pathI; 773 | string_list "-impl" impl; 774 | string_list "-intf" intf; 775 | string "-ml-synonym" ml_synonym; 776 | string "-mli-synonym" mli_synonym; 777 | unit "-modules" modules; 778 | unit "-native" native; 779 | unit "-one-line" one_line; 780 | string_list "-open" open_; 781 | string "-pp" pp; 782 | string "-ppx" ppx; 783 | unit "-slash" slash; 784 | ] 785 | 786 | let ocamldep_args_specs 787 | 788 | (* ocamldep_args0 *) 789 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 790 | ?modules ?native ?one_line ?open_ ?pp ?ppx ?slash 791 | 792 | (* ocamldep_args *) 793 | ?sort ?version 794 | 795 | () 796 | : spec option list list 797 | = 798 | (ocamldep_args0_specs 799 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 800 | ?modules ?native ?one_line ?open_ ?pp ?ppx ?slash () 801 | ) 802 | @[ 803 | unit "-sort" sort; 804 | unit "-version" version; 805 | ] 806 | 807 | let ocamldep 808 | 809 | (* ocamldep_args *) 810 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 811 | ?modules ?native ?one_line ?open_ ?pp ?ppx ?slash 812 | ?sort ?version 813 | 814 | files 815 | = 816 | [[Some (A "ocamldep")]] 817 | @(ocamldep_args_specs 818 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 819 | ?modules ?native ?one_line ?open_ ?pp ?ppx ?slash 820 | ?sort ?version () 821 | ) 822 | @[List.map files ~f:(fun x -> Some (A x))] 823 | |> specs_to_command 824 | 825 | let run_and_parse_ocamldep_cmd (cmd:spec) : (string * string list) list = 826 | Command.string_of_command_spec cmd |> fun cmd -> 827 | Ocamlbuild_pack.My_unix.run_and_read cmd |> 828 | String.split ~on:'\n' |> 829 | List.filter ~f:(function "" -> false | _ -> true) |> 830 | List.map ~f:(fun line -> 831 | String.split line ~on:':' |> function 832 | | target::deps::[] -> 833 | let target = String.trim target in 834 | let deps = 835 | String.split deps ~on:' ' |> 836 | List.map ~f:String.trim |> 837 | List.filter ~f:(function "" -> false | _ -> true) |> 838 | List.sort_uniq ~cmp:String.compare 839 | in 840 | target,deps 841 | | _ -> failwithf "unexpected output from %s, invalid line \"%s\"" 842 | cmd line () 843 | ) |> 844 | List.filter ~f:(function "",[] -> false | _ -> true) |> 845 | List.map ~f:(function x,[""] -> x,[] | x,y -> x,y) |> 846 | List.fold_left ~init:String.Map.empty ~f:(fun map (x,y) -> 847 | let y_new = 848 | match String.Map.mem x map with 849 | | false -> y 850 | | true -> (String.Map.find x map)@y 851 | in 852 | String.Map.add x y_new map 853 | ) |> 854 | String.Map.to_list 855 | 856 | let run_ocamldep 857 | 858 | (* ocamldep_args *) 859 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 860 | ?modules ?native ?one_line:_ ?open_ ?pp ?ppx ?slash 861 | ?sort ?version 862 | 863 | files 864 | = 865 | let one_line = Some () in 866 | ocamldep 867 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 868 | ?modules ?native ?one_line ?open_ ?pp ?ppx ?slash 869 | ?sort ?version 870 | files 871 | |> spec_of_command 872 | |> run_and_parse_ocamldep_cmd 873 | 874 | let run_ocamldep1 875 | 876 | (* ocamldep_args *) 877 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 878 | ?modules ?native ?one_line:_ ?open_ ?pp ?ppx ?slash 879 | ?sort ?version 880 | 881 | file 882 | = 883 | if not (Sys.file_exists file) then 884 | failwithf "run_ocamldep1: %s does not exist" file () 885 | ; 886 | 887 | let one_line = Some () in 888 | 889 | run_ocamldep 890 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 891 | ?modules ?native ?one_line ?open_ ?pp ?ppx ?slash 892 | ?sort ?version 893 | [file] 894 | |> function 895 | | [] -> failwithf "ocamldep returned no output for existing file %s" 896 | file () 897 | | (x,deps)::[] -> 898 | if x = file then deps 899 | else failwithf "ocamldep returned output for unexpected file %s when \ 900 | called on %s" x file () 901 | | _ -> failwithf "ocamldep returned multiple outputs for single file %s" 902 | file () 903 | 904 | let run_ocamldep_sort 905 | 906 | (* ocamldep_args0 *) 907 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 908 | ?modules ?native ?one_line ?open_ ?pp ?ppx ?slash 909 | 910 | files 911 | = 912 | let sort = Some () in 913 | let cmd = ocamldep 914 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 915 | ?modules ?native ?one_line ?open_ ?pp ?ppx ?slash 916 | ?sort files 917 | in 918 | spec_of_command cmd |> 919 | Ocamlbuild_plugin.Command.string_of_command_spec |> 920 | Ocamlbuild_pack.My_unix.run_and_read |> 921 | String.split ~on:' ' |> 922 | List.filter ~f:(fun x -> not @@ String.for_all x ~f:Char.is_whitespace) 923 | 924 | type 'a ocamlfind_ocamldep_args = 925 | ?package:string list -> 926 | ?predicates:string -> 927 | ?native_filter:unit -> 928 | ?bytecode_filter:unit -> 929 | ?only_show:unit -> 930 | ?verbose:unit -> 931 | 'a 932 | 933 | let ocamlfind_ocamldep_args_specs 934 | 935 | (* ocamlfind_ocamldep_args *) 936 | ?package ?predicates ?native_filter ?bytecode_filter 937 | ?only_show ?verbose 938 | 939 | () 940 | = 941 | let delim = `Space in 942 | let string_list = string_list ~delim in 943 | let string = string ~delim in 944 | [ 945 | string_list "-package" package; 946 | string "-predicates" predicates; 947 | unit "-native-filter" native_filter; 948 | unit "-bytecode-filter" bytecode_filter; 949 | unit "-only-show" only_show; 950 | unit "-verbose" verbose; 951 | ] 952 | 953 | let ocamlfind_ocamldep 954 | 955 | (* ocamlfind_ocamldep_args *) 956 | ?package ?predicates ?native_filter ?bytecode_filter 957 | ?only_show ?verbose 958 | 959 | (* ocamldep_args *) 960 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 961 | ?modules ?native ?one_line ?open_ ?pp ?ppx ?slash 962 | ?sort ?version 963 | 964 | files 965 | = 966 | [[Some (A "ocamlfind")]; [Some (A "ocamldep")]] 967 | @(ocamlfind_ocamldep_args_specs 968 | ?package ?predicates ?native_filter ?bytecode_filter 969 | ?only_show ?verbose () 970 | ) 971 | @(ocamldep_args_specs 972 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 973 | ?modules ?native ?one_line ?open_ ?pp ?ppx ?slash 974 | ?sort ?version () 975 | ) 976 | @[List.map files ~f:(fun x -> Some (A x))] 977 | |> specs_to_command 978 | 979 | let run_ocamlfind_ocamldep 980 | 981 | (* ocamlfind_ocamldep_args *) 982 | ?package ?predicates ?native_filter ?bytecode_filter 983 | ?only_show ?verbose:_ 984 | 985 | (* ocamldep_args *) 986 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 987 | ?modules ?native ?one_line:_ ?open_ ?pp ?ppx ?slash 988 | ?sort ?version 989 | 990 | files 991 | = 992 | (* Set options to make output parseable. *) 993 | let one_line = Some () in 994 | let verbose = None in 995 | 996 | ocamlfind_ocamldep 997 | ?package ?predicates ?native_filter ?bytecode_filter 998 | ?only_show ?verbose 999 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 1000 | ?modules ?native ?one_line ?open_ ?pp ?ppx ?slash 1001 | ?sort ?version 1002 | files 1003 | |> spec_of_command 1004 | |> run_and_parse_ocamldep_cmd 1005 | 1006 | 1007 | let run_ocamlfind_ocamldep1 1008 | 1009 | (* ocamlfind_ocamldep_args *) 1010 | ?package ?predicates ?native_filter ?bytecode_filter 1011 | ?only_show ?verbose:_ 1012 | 1013 | (* ocamldep_args *) 1014 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 1015 | ?modules ?native ?one_line:_ ?open_ ?pp ?ppx ?slash 1016 | ?sort ?version 1017 | 1018 | file 1019 | = 1020 | assert (Sys.file_exists file); 1021 | 1022 | (* Set options to make output parseable. *) 1023 | let one_line = Some () in 1024 | let verbose = None in 1025 | 1026 | run_ocamlfind_ocamldep 1027 | ?package ?predicates ?native_filter ?bytecode_filter 1028 | ?only_show ?verbose 1029 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 1030 | ?modules ?native ?one_line ?open_ ?pp ?ppx ?slash 1031 | ?sort ?version 1032 | [file] 1033 | |> function 1034 | | [] -> failwithf "ocamldep returned no output for existing file %s" 1035 | file () 1036 | | (x,deps)::[] -> 1037 | if x = file then deps 1038 | else failwithf "ocamldep returned output for unexpected file %s when \ 1039 | called on %s" x file () 1040 | | _ -> failwithf "ocamldep returned multiple outputs for single file %s" 1041 | file () 1042 | 1043 | let run_ocamlfind_ocamldep_sort 1044 | 1045 | (* ocamlfind_ocamldep_args *) 1046 | ?package ?predicates ?native_filter ?bytecode_filter 1047 | ?only_show ?verbose:_ 1048 | 1049 | (* ocamldep_args0 *) 1050 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 1051 | ?modules ?native ?one_line ?open_ ?pp ?ppx ?slash 1052 | 1053 | files 1054 | = 1055 | (* Set options to make output parseable. *) 1056 | let sort = Some () in 1057 | let verbose = None in 1058 | 1059 | let cmd = ocamlfind_ocamldep 1060 | ?package ?predicates ?native_filter ?bytecode_filter 1061 | ?only_show ?verbose 1062 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 1063 | ?modules ?native ?one_line ?open_ ?pp ?ppx ?slash 1064 | ?sort files 1065 | in 1066 | spec_of_command cmd |> 1067 | Ocamlbuild_plugin.Command.string_of_command_spec |> 1068 | Ocamlbuild_pack.My_unix.run_and_read |> 1069 | String.split ~on:' ' |> 1070 | List.filter ~f:(fun x -> not @@ String.for_all x ~f:Char.is_whitespace) 1071 | 1072 | 1073 | (******************************************************************************) 1074 | (** {2 ocamllex/menhir} *) 1075 | (******************************************************************************) 1076 | let ocamllex ?ml ?q ?o mll = 1077 | let string = string ~delim:`Space in 1078 | specs_to_command [ 1079 | [Some (A "ocamllex")]; 1080 | unit "-ml" ml; 1081 | string "-o" o; 1082 | unit "-q" q; 1083 | [Some (A mll)]; 1084 | ] 1085 | 1086 | let ocamllex_rule ?ml ?q ?(dep="%.mll") ?(prod="%.ml") () = 1087 | Rule.rule ~deps:[dep] ~prods:[prod] (fun env _ -> 1088 | ocamllex ?ml ?q ~o:(env prod) (env dep) 1089 | ) 1090 | 1091 | let menhir ?base mly = 1092 | let string = string ~delim:`Space in 1093 | specs_to_command [ 1094 | [Some (A "menhir")]; 1095 | string "--base" base; 1096 | [Some (A mly)]; 1097 | ] 1098 | 1099 | let menhir_rule ?base ?(dep="%.mly") () = 1100 | let prods = 1101 | let base = match base with 1102 | | None -> Filename.chop_extension dep 1103 | | Some x -> x 1104 | in 1105 | [base ^ ".ml"; base ^ ".mli"] 1106 | in 1107 | Rule.rule ~deps:[dep] ~prods (fun env _ -> 1108 | menhir ?base (env dep) 1109 | ) 1110 | 1111 | 1112 | (******************************************************************************) 1113 | (** {2 js_of_ocaml} *) 1114 | (******************************************************************************) 1115 | type 'a js_of_ocaml_rule_args = 1116 | ?custom_header:string -> 1117 | ?debug:string -> 1118 | ?debug_info:unit -> 1119 | ?disable:string -> 1120 | ?enable:string -> 1121 | ?no_inline:unit -> 1122 | ?no_runtime:unit -> 1123 | ?o:string -> 1124 | ?opt:int -> 1125 | ?pretty:unit -> 1126 | ?set:(string * string) list -> 1127 | ?source_map_inline:unit -> 1128 | ?source_map_no_source:unit -> 1129 | ?source_map_root:string -> 1130 | ?source_map:unit -> 1131 | ?extern_fs:unit -> 1132 | ?file:string list -> 1133 | ?pathI:string list -> 1134 | ?ofs:string -> 1135 | ?linkall:unit -> 1136 | ?no_cmis:unit -> 1137 | ?toplevel:unit -> 1138 | 'a 1139 | 1140 | type 'a js_of_ocaml_args = 1141 | ?custom_header:string -> 1142 | ?debug:string -> 1143 | ?debug_info:unit -> 1144 | ?disable:string -> 1145 | ?enable:string -> 1146 | ?no_inline:unit -> 1147 | ?no_runtime:unit -> 1148 | ?o:string -> 1149 | ?opt:int -> 1150 | ?pretty:unit -> 1151 | ?quiet:unit -> 1152 | ?set:(string * string) list -> 1153 | ?source_map_inline:unit -> 1154 | ?source_map_no_source:unit -> 1155 | ?source_map_root:string -> 1156 | ?source_map:unit -> 1157 | ?version:unit -> 1158 | ?extern_fs:unit -> 1159 | ?file:string list -> 1160 | ?pathI:string list -> 1161 | ?ofs:string -> 1162 | ?linkall:unit -> 1163 | ?no_cmis:unit -> 1164 | ?toplevel:unit -> 1165 | 'a 1166 | 1167 | let js_of_ocaml_args_specs 1168 | 1169 | (* js_of_ocaml_args *) 1170 | ?custom_header ?debug ?debug_info ?disable ?enable 1171 | ?no_inline ?no_runtime ?o ?opt ?pretty ?quiet ?set 1172 | ?source_map_inline ?source_map_no_source 1173 | ?source_map_root ?source_map 1174 | ?version ?extern_fs ?file ?pathI ?ofs 1175 | ?linkall ?no_cmis ?toplevel 1176 | 1177 | () 1178 | : spec option list list 1179 | = 1180 | [ 1181 | string ~delim:`Equal "--custom-header" custom_header; 1182 | string ~delim:`Equal "--debug" debug; 1183 | unit "--debug-info" debug_info; 1184 | string ~delim:`Equal "--disable" disable; 1185 | string ~delim:`Equal "--enable" enable; 1186 | unit "--no-inline" no_inline; 1187 | unit "--no-runtime" no_runtime; 1188 | string ~delim:`Space "-o" o; 1189 | int ~delim:`Equal "--opt" opt; 1190 | unit "--pretty" pretty; 1191 | unit "--quiet" quiet; 1192 | (match set with 1193 | | None -> [None] 1194 | | Some l -> 1195 | List.map l ~f:(fun (x,y) -> 1196 | string ~delim:`Equal "--set" (Some (sprintf "%s=%s" x y)) 1197 | ) |> 1198 | List.flatten 1199 | ); 1200 | unit "--source-map-inline" source_map_inline; 1201 | unit "--source-map-no-source" source_map_no_source; 1202 | string ~delim:`Equal "--source-map-root" source_map_root; 1203 | unit "--source-map" source_map; 1204 | unit "--version" version; 1205 | unit "--extern-fs" extern_fs; 1206 | string_list ~delim:`Equal "--file" file; 1207 | string_list ~delim:`Space "-I" pathI; 1208 | string ~delim:`Equal "--ofs" ofs; 1209 | unit "--linkall" linkall; 1210 | unit "--no-cmis" no_cmis; 1211 | unit "--toplevel" toplevel; 1212 | ] 1213 | 1214 | let js_of_ocaml 1215 | 1216 | (* js_of_ocaml_args *) 1217 | ?custom_header ?debug ?debug_info ?disable ?enable 1218 | ?no_inline ?no_runtime ?o ?opt ?pretty ?quiet ?set 1219 | ?source_map_inline ?source_map_no_source 1220 | ?source_map_root ?source_map 1221 | ?version ?extern_fs ?file ?pathI ?ofs 1222 | ?linkall ?no_cmis ?toplevel 1223 | 1224 | js_files cma 1225 | = 1226 | [[Some (A "js_of_ocaml")]] 1227 | @(js_of_ocaml_args_specs 1228 | ?custom_header ?debug ?debug_info ?disable ?enable 1229 | ?no_inline ?no_runtime ?o ?opt ?pretty ?quiet ?set 1230 | ?source_map_inline ?source_map_no_source 1231 | ?source_map_root ?source_map 1232 | ?version ?extern_fs ?file ?pathI ?ofs 1233 | ?linkall ?no_cmis ?toplevel () 1234 | ) 1235 | @[ 1236 | List.map (js_files@[cma]) ~f:(fun x -> Some (A x)) 1237 | ] 1238 | |> specs_to_command 1239 | 1240 | let js_of_ocaml_rule 1241 | ?custom_header ?debug ?debug_info ?disable ?enable 1242 | ?no_inline ?no_runtime ?o ?opt ?pretty ?set 1243 | ?source_map_inline ?source_map_no_source 1244 | ?source_map_root ?source_map 1245 | ?extern_fs ?file ?pathI ?ofs 1246 | ?linkall ?no_cmis ?toplevel 1247 | ?(extra_js=[]) dep 1248 | = 1249 | let base = Filename.chop_extension dep in 1250 | let prod = match o with None -> base ^ ".js" | Some o -> o in 1251 | Rule.rule ~deps:[dep] ~prods:[prod] (fun _ _ -> 1252 | js_of_ocaml 1253 | ?custom_header ?debug ?debug_info ?disable ?enable 1254 | ?no_inline ?no_runtime ?o ?opt ?pretty ?set 1255 | ?source_map_inline ?source_map_no_source 1256 | ?source_map_root ?source_map 1257 | ?extern_fs ?file ?pathI ?ofs 1258 | ?linkall ?no_cmis ?toplevel 1259 | extra_js dep 1260 | ) 1261 | 1262 | (******************************************************************************) 1263 | (** {2 eliomc/eliomopt} *) 1264 | (******************************************************************************) 1265 | type 'a eliom_args = 1266 | ?package:string list -> 1267 | ?no_autoload:unit -> 1268 | ?type_conv:unit -> 1269 | ?infer:unit -> 1270 | ?dir:string -> 1271 | ?type_dir:string -> 1272 | ?server_types_ext:string -> 1273 | ?ppopt:string -> 1274 | ?predicates:string -> 1275 | ?ppx:unit -> 1276 | 'a 1277 | 1278 | let eliom_args_specs 1279 | 1280 | (* eliom_args *) 1281 | ?package ?no_autoload ?type_conv ?infer 1282 | ?dir ?type_dir ?server_types_ext 1283 | ?ppopt ?predicates ?ppx 1284 | 1285 | () 1286 | = 1287 | let string = string ~delim:`Space in 1288 | [ 1289 | (match package with 1290 | | None -> [None] 1291 | | Some l -> string "-package" (Some (String.concat ~sep:"," l)) 1292 | ); 1293 | unit "-no-autoload" no_autoload; 1294 | unit "-type-conv" type_conv; 1295 | unit "-infer" infer; 1296 | string "-dir" dir; 1297 | string "-type-dir" type_dir; 1298 | string "-server-types-ext" server_types_ext; 1299 | string "-ppopt" ppopt; 1300 | string "-predicates" predicates; 1301 | unit "-ppx" ppx; 1302 | ] 1303 | 1304 | let eliomc 1305 | 1306 | (* eliom_args *) 1307 | ?package ?no_autoload ?type_conv ?infer 1308 | ?dir ?type_dir ?server_types_ext 1309 | ?ppopt ?predicates ?ppx:ppx_eliom 1310 | 1311 | (* ocaml_compiler_args *) 1312 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 1313 | ?config ?for_pack ?g ?i ?pathI 1314 | ?impl ?intf ?intf_suffix ?labels ?linkall ?make_runtime 1315 | ?no_alias_deps ?no_app_funct ?noassert ?noautolink ?nolabels 1316 | ?nostdlib ?o ?open_ ?output_obj ?pack ?pp ?ppx:ppx_ocaml ?principal 1317 | ?rectypes ?runtime_variant ?safe_string ?short_paths 1318 | ?strict_sequence ?strict_formats ?thread ?unsafe 1319 | ?unsafe_string ?use_runtime ?v ?verbose ?version 1320 | ?w ?warn_error ?warn_help ?where 1321 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 1322 | ?drawlambda ?dlambda ?dinstr 1323 | ?help 1324 | 1325 | (* ocamlc_args *) 1326 | ?compat_32 ?custom ?dllib ?dllpath ?vmthread ?no_check_prims 1327 | 1328 | files 1329 | = 1330 | [[Some (A "eliomc")]] 1331 | @(eliom_args_specs 1332 | ?package ?no_autoload ?type_conv ?infer 1333 | ?dir ?type_dir ?server_types_ext 1334 | ?ppopt ?predicates ?ppx:ppx_eliom () 1335 | ) 1336 | @(ocamlc_args_specs 1337 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 1338 | ?config ?for_pack ?g ?i ?pathI 1339 | ?impl ?intf ?intf_suffix ?labels ?linkall ?make_runtime 1340 | ?no_alias_deps ?no_app_funct ?noassert ?noautolink ?nolabels 1341 | ?nostdlib ?o ?open_ ?output_obj ?pack ?pp ?ppx:ppx_ocaml ?principal 1342 | ?rectypes ?runtime_variant ?safe_string ?short_paths 1343 | ?strict_sequence ?strict_formats ?thread ?unsafe 1344 | ?unsafe_string ?use_runtime ?v ?verbose ?version 1345 | ?w ?warn_error ?warn_help ?where 1346 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 1347 | ?drawlambda ?dlambda ?dinstr 1348 | ?help 1349 | ?compat_32 ?custom ?dllib ?dllpath ?vmthread ?no_check_prims () 1350 | ) 1351 | @[List.map files ~f:(fun x -> Some (A x))] 1352 | |> specs_to_command 1353 | 1354 | let eliomopt 1355 | 1356 | (* eliom_args *) 1357 | ?package ?no_autoload ?type_conv ?infer 1358 | ?dir ?type_dir ?server_types_ext 1359 | ?ppopt ?predicates ?ppx:ppx_eliom 1360 | 1361 | (* ocaml_compiler_args *) 1362 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 1363 | ?config ?for_pack ?g ?i ?pathI 1364 | ?impl ?intf ?intf_suffix ?labels ?linkall ?make_runtime 1365 | ?no_alias_deps ?no_app_funct ?noassert ?noautolink ?nolabels 1366 | ?nostdlib ?o ?open_ ?output_obj ?pack ?pp ?ppx:ppx_ocaml ?principal 1367 | ?rectypes ?runtime_variant ?safe_string ?short_paths 1368 | ?strict_sequence ?strict_formats ?thread ?unsafe 1369 | ?unsafe_string ?use_runtime ?v ?verbose ?version 1370 | ?w ?warn_error ?warn_help ?where 1371 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 1372 | ?drawlambda ?dlambda ?dinstr 1373 | ?help 1374 | 1375 | (* ocamlopt_args *) 1376 | ?compact ?inline 1377 | ?inline_alloc_cost ?inline_branch_cost ?inline_branch_factor 1378 | ?inline_call_cost ?inline_indirect_cost ?inline_lifting_benefit 1379 | ?inline_max_depth ?inline_max_unroll ?inline_prim_cost 1380 | ?inlining_report 1381 | ?no_unbox_free_vars_of_closures ?no_unbox_specialised_args 1382 | ?nodynlink 1383 | ?optimize_classic ?optimize2 ?optimize3 1384 | ?p ?remove_unused_arguments ?rounds ?keep_assembly ?shared 1385 | ?unbox_closures 1386 | 1387 | files 1388 | = 1389 | [[Some (A "eliomopt")]] 1390 | @(eliom_args_specs 1391 | ?package ?no_autoload ?type_conv ?infer 1392 | ?dir ?type_dir ?server_types_ext 1393 | ?ppopt ?predicates ?ppx:ppx_eliom () 1394 | ) 1395 | @(ocamlopt_args_specs 1396 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 1397 | ?config ?for_pack ?g ?i ?pathI 1398 | ?impl ?intf ?intf_suffix ?labels ?linkall ?make_runtime 1399 | ?no_alias_deps ?no_app_funct ?noassert ?noautolink ?nolabels 1400 | ?nostdlib ?o ?open_ ?output_obj ?pack ?pp ?ppx:ppx_ocaml ?principal 1401 | ?rectypes ?runtime_variant ?safe_string ?short_paths 1402 | ?strict_sequence ?strict_formats ?thread ?unsafe 1403 | ?unsafe_string ?use_runtime ?v ?verbose ?version 1404 | ?w ?warn_error ?warn_help ?where 1405 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 1406 | ?drawlambda ?dlambda ?dinstr 1407 | ?help 1408 | ?compact ?inline 1409 | ?inline_alloc_cost ?inline_branch_cost ?inline_branch_factor 1410 | ?inline_call_cost ?inline_indirect_cost ?inline_lifting_benefit 1411 | ?inline_max_depth ?inline_max_unroll ?inline_prim_cost 1412 | ?inlining_report 1413 | ?no_unbox_free_vars_of_closures ?no_unbox_specialised_args 1414 | ?nodynlink 1415 | ?optimize_classic ?optimize2 ?optimize3 1416 | ?p ?remove_unused_arguments ?rounds ?keep_assembly ?shared 1417 | ?unbox_closures 1418 | () 1419 | ) 1420 | @[List.map files ~f:(fun x -> Some (A x))] 1421 | |> specs_to_command 1422 | 1423 | 1424 | (******************************************************************************) 1425 | (** {2 eliomdep} *) 1426 | (******************************************************************************) 1427 | type 'a eliomdep_args = 1428 | ?dir:string -> 1429 | ?type_dir:string -> 1430 | ?eliom_inc:string list -> 1431 | ?package:string list -> 1432 | ?no_autoload:unit -> 1433 | ?type_conv:unit -> 1434 | ?ppopt:string list -> 1435 | ?predicates:string -> 1436 | ?verbose:unit -> 1437 | ?ppx:unit -> 1438 | 'a 1439 | 1440 | let eliomdep_args_specs 1441 | 1442 | (* eliomdep_args *) 1443 | ?dir ?type_dir ?eliom_inc ?package ?no_autoload ?type_conv 1444 | ?ppopt ?predicates ?verbose ?ppx 1445 | 1446 | () 1447 | : spec option list list 1448 | = 1449 | let string = string ~delim:`Space in 1450 | let string_list = string_list ~delim:`Space in 1451 | [ 1452 | string "-dir" dir; 1453 | string "-type-dir" type_dir; 1454 | string_list "-eliom-inc" eliom_inc; 1455 | (match package with 1456 | | None -> [None] 1457 | | Some x -> 1458 | string "-package" (Some (String.concat ~sep:"," x)) 1459 | ); 1460 | unit "-no-autoload" no_autoload; 1461 | unit "-type-conv" type_conv; 1462 | string_list "-ppopt" ppopt; 1463 | string "-predicates" predicates; 1464 | unit "-verbose" verbose; 1465 | unit "-ppx" ppx; 1466 | ] 1467 | 1468 | let eliomdep 1469 | 1470 | (* eliomdep_args *) 1471 | ?dir ?type_dir ?eliom_inc ?package ?no_autoload ?type_conv 1472 | ?ppopt ?predicates ?verbose ?ppx:ppx_eliomdep 1473 | 1474 | (* ocamldep_args *) 1475 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 1476 | ?modules ?native ?one_line ?open_ ?pp ?ppx:ppx_ocamldep ?slash 1477 | ?sort ?version 1478 | 1479 | host files 1480 | = 1481 | [[ 1482 | Some (A "eliomdep"); 1483 | (match host with 1484 | | `Client -> Some (A "-client") 1485 | | `Server -> Some (A "-server") 1486 | ); 1487 | ]] 1488 | @(eliomdep_args_specs 1489 | ?dir ?type_dir ?eliom_inc ?package ?no_autoload ?type_conv 1490 | ?ppopt ?predicates ?verbose ?ppx:ppx_eliomdep () 1491 | ) 1492 | @(ocamldep_args_specs 1493 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 1494 | ?modules ?native ?one_line ?open_ ?pp ?ppx:ppx_ocamldep ?slash 1495 | ?sort ?version () 1496 | ) 1497 | @[List.map files ~f:(fun x -> Some (A x))] 1498 | |> specs_to_command 1499 | 1500 | let run_eliomdep 1501 | 1502 | (* eliomdep_args *) 1503 | ?dir ?type_dir ?eliom_inc ?package ?no_autoload ?type_conv 1504 | ?ppopt ?predicates ?verbose:_ ?ppx:ppx_eliomdep 1505 | 1506 | (* ocamldep_args *) 1507 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 1508 | ?modules ?native ?one_line:_ ?open_ ?pp ?ppx:ppx_ocamldep ?slash 1509 | ?sort ?version 1510 | 1511 | ?fix320 host files 1512 | = 1513 | (* Set options to make output parseable. *) 1514 | let one_line = Some () in 1515 | let verbose = None in 1516 | 1517 | (* Workaround https://github.com/ocsigen/eliom/issues/320 *) 1518 | let fix_path x = 1519 | let dir = match dir with 1520 | | Some x -> x 1521 | | None -> match host with `Client -> "_client" | `Server -> "_server" 1522 | in 1523 | let type_dir = match type_dir with 1524 | | Some x -> x 1525 | | None -> "_server" 1526 | in 1527 | match String.split x ~on:'/' with 1528 | | [] -> assert false 1529 | | _::[] -> x 1530 | | dir0::_::_ -> 1531 | if dir0 = dir || dir0 = type_dir then 1532 | (* path okay, return it unmodified *) 1533 | x 1534 | else if Filename.check_suffix x ".type_mli" then 1535 | (* haven't actually seen output that would lead to this case, 1536 | but considering it anyway *) 1537 | (type_dir/x) 1538 | else 1539 | (dir/x) 1540 | in 1541 | 1542 | eliomdep host 1543 | ?dir ?type_dir ?eliom_inc ?package ?no_autoload ?type_conv 1544 | ?ppopt ?predicates ?verbose ?ppx:ppx_eliomdep 1545 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 1546 | ?modules ?native ?one_line ?open_ ?pp ?ppx:ppx_ocamldep ?slash 1547 | ?sort ?version 1548 | files 1549 | |> spec_of_command 1550 | |> run_and_parse_ocamldep_cmd |> fun l -> 1551 | match fix320 with 1552 | | None -> l 1553 | | Some () -> List.map l ~f:(fun (x,l) -> fix_path x, List.map l ~f:fix_path) 1554 | 1555 | let run_eliomdep_sort 1556 | 1557 | (* eliomdep_args *) 1558 | ?dir ?type_dir ?eliom_inc ?package ?no_autoload ?type_conv 1559 | ?ppopt ?predicates ?verbose:_ ?ppx:ppx_eliomdep 1560 | 1561 | (* ocamldep_args0 *) 1562 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 1563 | ?modules ?native ?one_line ?open_ ?pp ?ppx:ppx_ocamldep ?slash 1564 | 1565 | host files 1566 | = 1567 | let sort = Some () in 1568 | 1569 | (* Set options to make output parseable. *) 1570 | let verbose = None in 1571 | 1572 | let cmd = eliomdep 1573 | ?dir ?type_dir ?eliom_inc ?package ?no_autoload ?type_conv 1574 | ?ppopt ?predicates ?verbose ?ppx:ppx_eliomdep 1575 | ?absname ?all ?pathI ?impl ?intf ?ml_synonym ?mli_synonym 1576 | ?modules ?native ?one_line ?open_ ?pp ?ppx:ppx_ocamldep ?slash 1577 | ?sort 1578 | host files 1579 | in 1580 | spec_of_command cmd |> 1581 | Ocamlbuild_plugin.Command.string_of_command_spec |> 1582 | Ocamlbuild_pack.My_unix.run_and_read |> 1583 | String.split ~on:' ' |> 1584 | List.filter ~f:(fun x -> not @@ String.for_all x ~f:Char.is_whitespace) 1585 | 1586 | 1587 | (******************************************************************************) 1588 | (** {2 js_of_eliom} *) 1589 | (******************************************************************************) 1590 | type 'a js_of_eliom_args = 1591 | ?package:string list -> 1592 | ?no_autoload:unit -> 1593 | ?type_conv:unit -> 1594 | ?dir:string -> 1595 | ?type_dir:string -> 1596 | ?server_types_ext:string -> 1597 | ?jsopt:string -> 1598 | ?ppopt:string -> 1599 | ?predicates:string -> 1600 | ?ppx:unit -> 1601 | ?dont_force_linkall:unit -> 1602 | 'a 1603 | 1604 | let js_of_eliom_args_specs 1605 | 1606 | (* js_of_eliom_args *) 1607 | ?package ?no_autoload ?type_conv 1608 | ?dir ?type_dir ?server_types_ext 1609 | ?jsopt ?ppopt ?predicates ?ppx 1610 | ?dont_force_linkall 1611 | 1612 | () 1613 | = 1614 | let string = string ~delim:`Space in 1615 | [ 1616 | (match package with 1617 | | None -> [None] 1618 | | Some l -> string "-package" (Some (String.concat ~sep:"," l)) 1619 | ); 1620 | unit "-no-autoload" no_autoload; 1621 | unit "-type-conv" type_conv; 1622 | string "-dir" dir; 1623 | string "-type-dir" type_dir; 1624 | string "-server-types-ext" server_types_ext; 1625 | string "-jsopt" jsopt; 1626 | string "-ppopt" ppopt; 1627 | string "-predicates" predicates; 1628 | unit "-ppx" ppx; 1629 | unit "-dont-force-linkall" dont_force_linkall; 1630 | ] 1631 | 1632 | let js_of_eliom 1633 | 1634 | (* js_of_eliom_args *) 1635 | ?package ?no_autoload ?type_conv 1636 | ?dir ?type_dir ?server_types_ext 1637 | ?jsopt ?ppopt ?predicates ?ppx:ppx_eliom 1638 | ?dont_force_linkall 1639 | 1640 | (* js_of_ocaml_args 1641 | - Beware some are overwritten by ocamlc_args below. I guess it 1642 | is a bug in the js_of_eliom command line API. 1643 | *) 1644 | ?custom_header ?debug ?debug_info ?disable ?enable 1645 | ?no_inline ?no_runtime ?o:o_js_of_ocaml ?opt ?pretty ?quiet ?set 1646 | ?source_map_inline ?source_map_no_source 1647 | ?source_map_root ?source_map 1648 | ?version:version_js_of_ocaml ?extern_fs ?file ?pathI:pathI_js_of_ocaml ?ofs 1649 | ?linkall:linkall_js_of_ocaml ?no_cmis ?toplevel 1650 | 1651 | (* ocaml_compiler_args *) 1652 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 1653 | ?config ?for_pack ?g ?i ?pathI:pathI_ocaml 1654 | ?impl ?intf ?intf_suffix ?labels ?linkall:linkall_ocaml ?make_runtime 1655 | ?no_alias_deps ?no_app_funct ?noassert ?noautolink ?nolabels 1656 | ?nostdlib ?o:o_ocaml ?open_ ?output_obj ?pack ?pp ?ppx:ppx_ocaml ?principal 1657 | ?rectypes ?runtime_variant ?safe_string ?short_paths 1658 | ?strict_sequence ?strict_formats ?thread ?unsafe 1659 | ?unsafe_string ?use_runtime ?v ?verbose ?version:version_ocaml 1660 | ?w ?warn_error ?warn_help ?where 1661 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 1662 | ?drawlambda ?dlambda ?dinstr 1663 | ?help 1664 | 1665 | (* ocamlc_args *) 1666 | ?compat_32 ?custom ?dllib ?dllpath ?vmthread ?no_check_prims 1667 | 1668 | files 1669 | = 1670 | [[Some (A "js_of_eliom")]] 1671 | @(js_of_eliom_args_specs 1672 | ?package ?no_autoload ?type_conv 1673 | ?dir ?type_dir ?server_types_ext 1674 | ?jsopt ?ppopt ?predicates ?ppx:ppx_eliom 1675 | ?dont_force_linkall () 1676 | ) 1677 | @(js_of_ocaml_args_specs 1678 | ?custom_header ?debug ?debug_info ?disable ?enable 1679 | ?no_inline ?no_runtime ?o:o_js_of_ocaml ?opt ?pretty ?quiet ?set 1680 | ?source_map_inline ?source_map_no_source 1681 | ?source_map_root ?source_map 1682 | ?version:version_js_of_ocaml ?extern_fs ?file 1683 | ?pathI:pathI_js_of_ocaml ?ofs 1684 | ?linkall:linkall_js_of_ocaml ?no_cmis ?toplevel () 1685 | ) 1686 | @(ocamlc_args_specs 1687 | ?a ?absname ?annot ?bin_annot ?c ?cc ?cclib ?ccopt ?color 1688 | ?config ?for_pack ?g ?i ?pathI:pathI_ocaml 1689 | ?impl ?intf ?intf_suffix ?labels ?linkall:linkall_ocaml ?make_runtime 1690 | ?no_alias_deps ?no_app_funct ?noassert ?noautolink ?nolabels 1691 | ?nostdlib ?o:o_ocaml ?open_ ?output_obj ?pack ?pp ?ppx:ppx_ocaml 1692 | ?principal 1693 | ?rectypes ?runtime_variant ?safe_string ?short_paths 1694 | ?strict_sequence ?strict_formats ?thread ?unsafe 1695 | ?unsafe_string ?use_runtime ?v ?verbose ?version:version_ocaml 1696 | ?w ?warn_error ?warn_help ?where 1697 | ?nopervasives ?dsource ?dparsetree ?dtypedtree 1698 | ?drawlambda ?dlambda ?dinstr 1699 | ?help 1700 | ?compat_32 ?custom ?dllib ?dllpath ?vmthread ?no_check_prims () 1701 | ) 1702 | @[List.map files ~f:(fun x -> Some (A x))] 1703 | |> specs_to_command 1704 | 1705 | 1706 | (******************************************************************************) 1707 | (** {2 atdgen} *) 1708 | (******************************************************************************) 1709 | let atdgen ?j ?j_std ?t file = 1710 | [ 1711 | [Some (A "atdgen")]; 1712 | unit "-j" j; 1713 | unit "-j-std" j_std; 1714 | unit "-t" t; 1715 | [Some (P file)]; 1716 | ] |> 1717 | specs_to_command 1718 | 1719 | let atdgen_t_rule ?(dep="%.atd") ?j_std () = 1720 | let open Filename in 1721 | let base = 1722 | if check_suffix dep ".atd" 1723 | then chop_suffix dep ".atd" 1724 | else failwithf "atdgen_t_rule: invalid ~dep = %s" dep () 1725 | in 1726 | let deps = [dep] in 1727 | let prods = [sprintf "%s_t.ml" base; sprintf "%s_t.mli" base] in 1728 | let name = Rule.name ~deps ~prods in 1729 | rule name ~deps ~prods (fun env _ -> atdgen ~t:() ?j_std (env dep)) 1730 | 1731 | let atdgen_j_rule ?(dep="%.atd") ?j_std () = 1732 | let open Filename in 1733 | let base = 1734 | if check_suffix dep ".atd" 1735 | then chop_suffix dep ".atd" 1736 | else failwithf "atdgen_j_rule: invalid ~dep = %s" dep () 1737 | in 1738 | let deps = [dep] in 1739 | let prods = [sprintf "%s_j.ml" base; sprintf "%s_j.mli" base] in 1740 | let name = Rule.name ~deps ~prods in 1741 | rule name ~deps ~prods (fun env _ -> atdgen ~j:() ?j_std (env dep)) 1742 | 1743 | 1744 | (******************************************************************************) 1745 | (** Other Unix tools. *) 1746 | (******************************************************************************) 1747 | (* Developers: functions in this section should be provided in roughly 1748 | alphabetical order, both in the ml and mli. *) 1749 | open Ocamlbuild_plugin 1750 | open Printf 1751 | open Util 1752 | open Util.Spec 1753 | 1754 | let cp ?f src dst = 1755 | [ 1756 | [Some (A "cp")]; 1757 | unit "-f" f; 1758 | [Some (A src)]; 1759 | [Some (A dst)]; 1760 | ] |> 1761 | specs_to_command 1762 | 1763 | let cp_rule ?f ~dep ~prod = 1764 | Rule.rule ~deps:[dep] ~prods:[prod] (fun _ _ -> cp ?f dep prod) 1765 | 1766 | let git_last_commit () = 1767 | if Sys.file_exists ".git" then 1768 | Some ( 1769 | Ocamlbuild_pack.My_unix.run_and_read "git rev-parse HEAD" 1770 | |> fun x -> String.sub x 0 (String.length x - 1) 1771 | ) 1772 | else 1773 | None 1774 | 1775 | let m4 ?(_D=[]) ~infile ~outfile = 1776 | [ 1777 | [A "m4"]; 1778 | ( 1779 | List.map _D ~f:(fun (x,y) -> 1780 | let value = match y with 1781 | | None -> x 1782 | | Some y -> sprintf "%s=%s" x y 1783 | in 1784 | [A "-D"; A value] 1785 | ) |> List.flatten 1786 | ); 1787 | [P infile]; 1788 | [Sh ">"]; 1789 | [P outfile]; 1790 | ] |> 1791 | List.flatten |> fun l -> 1792 | Cmd (S l) 1793 | 1794 | let m4_rule ?_D ?dep ?(prod="%.ml") () = 1795 | let open Filename in 1796 | let dep = match dep with 1797 | | Some x -> x 1798 | | None -> sprintf "%s.m4" prod 1799 | in 1800 | Rule.rule ~deps:[dep] ~prods:[prod] (fun env _ -> 1801 | m4 ?_D ~infile:(env dep) ~outfile:(env prod) 1802 | ) 1803 | --------------------------------------------------------------------------------