Skip to content

Commit

Permalink
Version 0.2
Browse files Browse the repository at this point in the history
  • Loading branch information
vbmithr committed Apr 24, 2014
1 parent 5db544e commit cb8879d
Show file tree
Hide file tree
Showing 18 changed files with 479 additions and 122 deletions.
7 changes: 4 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
dist
_obuild
ocp-build.*
_build
*.install
*.native
*.byte
4 changes: 4 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
0.2 (2014-04-24):
* Functor output signature is now explicit
* Build system switched to topkg+ocamlbuild

0.1 (2013-06-10):
* first alpha release
21 changes: 5 additions & 16 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,25 +1,14 @@
UNIX ?= $(shell if ocamlfind query unix >/dev/null 2>&1; then echo --flag unix; fi)
LWT ?= $(shell if ocamlfind query lwt.unix >/dev/null 2>&1; then echo --flag lwt; fi)
PREFIX=/usr/local

all: build

dist/setup:
obuild configure $(UNIX) $(LWT)

build: dist/setup
obuild build
build:
ocaml pkg/build.ml native=true native-dynlink=true unix=true lwt=true

install: build
ocamlfind install smtp dist/build/lib-smtp/* dist/build/lib-smtp_unix/* dist/build/lib-smtp_lwt/* lib/META

uninstall:
ocamlfind remove smtp

reinstall:
$(MAKE) uninstall || true
$(MAKE) install
opam-installer --prefix=$(PREFIX) *.install

PHONY: clean

clean:
obuild clean
ocamlbuild -clean
7 changes: 7 additions & 0 deletions _tags
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<**/*.{ml,mli}> : bin_annot
<src> : include
<test> : include
<src/smtp_lwt.{ml,mli}> : package(lwt)
<src/smtp_unix.{ml}> : use_unix
<test/test_lwt.{ml,native,byte}> : package(lwt.unix)
<test/test_unix.{ml,native,byte}> : use_unix
16 changes: 16 additions & 0 deletions opam
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
opam-version: "1"
maintainer: "Vincent Bernardoff <[email protected]>"
authors: ["Vincent Bernardoff <[email protected]>"]
license: "ISC"
tags: [ "smtp" ]
depends: ["ocamlfind"]
depopts: ["lwt"]
build:
[
[ "ocaml" "pkg/git.ml" ]
[ "ocaml" "pkg/build.ml" "native=%{ocaml-native}%"
"native-dynlink=%{ocaml-native}%"
"unix=%{base-unix:installed}%"
"lwt=%{lwt:installed}%"
] # TODO FIXME
]
File renamed without changes.
15 changes: 15 additions & 0 deletions pkg/build.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env ocaml
#directory "pkg"
#use "topkg.ml"

let unix = Env.bool "unix"
let lwt = Env.bool "lwt"
let () =
Pkg.describe "smtp" ~builder:`OCamlbuild [
Pkg.lib "pkg/META";
Pkg.lib ~exts:Exts.module_library "src/smtp";
Pkg.lib ~cond:lwt ~exts:Exts.library "src/smtp_lwt";
Pkg.lib ~cond:unix ~exts:Exts.library "src/smtp_unix";
Pkg.bin ~cond:lwt ~auto:true ~dst:"smtp_test_lwt" "test/test_lwt";
Pkg.bin ~cond:unix ~auto:true ~dst:"smtp_test_unix" "test/test_unix";
]
303 changes: 303 additions & 0 deletions pkg/topkg.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
(*---------------------------------------------------------------------------
Copyright (c) 2014 Daniel C. Bünzli. All rights reserved.
Distributed under the BSD3 license, see license at the end of the file.
%%NAME%% release %%VERSION%%
---------------------------------------------------------------------------*)

(* Public api *)

(** Build environment access *)
module type Env = sig
val bool : string -> bool
(** [bool key] declares [key] as being a boolean key in the environment.
Specifing key=(true|false) on the command line becomes mandatory. *)

val native : bool
(** [native] is [bool "native"]. *)

val native_dynlink : bool
(** [native_dylink] is [bool "native-dynlink"] *)
end

(** Exts defines sets of file extensions. *)
module type Exts = sig
val interface : string list
(** [interface] is [[".mli"; ".cmi"; ".cmti"]] *)

val interface_opt : string list
(** [interface_opt] is [".cmx" :: interface] *)

val library : string list
(** [library] is [[".cma"; ".cmxa"; ".cmxs"; ".a"]] *)

val module_library : string list
(** [module_library] is [(interface_opt @ library)]. *)
end

(** Package description. *)
module type Pkg = sig
type builder = [ `OCamlbuild | `Other of string * string ]
(** The type for build tools. Either [`OCamlbuild] or an
[`Other (tool, bdir)] tool [tool] that generates its build artefacts
in [bdir]. *)

type moves
(** The type for install moves. *)

type field = ?cond:bool -> ?exts:string list -> ?dst:string -> string -> moves
(** The type for field install functions. A call
[field cond exts dst path] generates install moves as follows:
{ul
{- If [cond] is [false] (defaults to [true]), no move is generated.}
{- If [exts] is present, generates a move for each path in
the list [List.map (fun e -> path ^ e) exts].}
{- If [dst] is present this path is used as the move destination
(allows to install in subdirectories). If absent [dst] is
[Filename.basename path].} *)

val lib : field
val bin : ?auto:bool -> field
(** If [auto] is true (defaults to false) generates
[path ^ ".native"] if {!Env.native} is [true] and
[path ^ ".byte"] if {!Env.native} is [false]. *)
val sbin : ?auto:bool -> field (** See {!bin}. *)
val toplevel : field
val share : field
val share_root : field
val etc : field
val doc : field
val misc : field
val stublibs : field
val man : field
val describe : string -> builder:builder -> moves list -> unit
(** [describe name builder moves] describes a package named [name] with
builder [builder] and install moves [moves]. *)
end

(* Implementation *)

module Topkg : sig
val cmd : [`Build | `Explain | `Help ]
val env : (string * bool) list
val err_parse : string -> 'a
val err_mdef : string -> 'a
val err_miss : string -> 'a
val err_file : string -> string -> 'a
val warn_unused : string -> unit
end = struct

(* Parses the command line. The actual cmd execution occurs in the call
to Pkg.describe. *)

let err fmt =
let k _ = exit 1 in
Format.kfprintf k Format.err_formatter ("%s: " ^^ fmt ^^ "@.") Sys.argv.(0)

let err_parse a = err "argument `%s' is not of the form key=(true|false)" a
let err_mdef a = err "bool `%s' is defined more than once" a
let err_miss a = err "argument `%s=(true|false)' is missing" a
let err_file f e = err "%s: %s" f e
let warn_unused k =
Format.eprintf "%s: warning: environment key `%s` unused.@." Sys.argv.(0) k

let cmd, env =
let rec parse_env acc = function (* not t.r. *)
| arg :: args ->
begin try
(* String.cut ... *)
let len = String.length arg in
let eq = String.index arg '=' in
let bool = bool_of_string (String.sub arg (eq + 1) (len - eq - 1)) in
let key = String.sub arg 0 eq in
if key = "" then raise Exit else
try ignore (List.assoc key acc); err_mdef key with
| Not_found -> parse_env ((key, bool) :: acc) args
with
| Invalid_argument _ | Not_found | Exit -> err_parse arg
end
| [] -> acc
in
match List.tl (Array.to_list Sys.argv) with
| "explain" :: args -> `Explain, parse_env [] args
| ("help" | "-h" | "--help" | "-help") :: args -> `Help, parse_env [] args
| args -> `Build, parse_env [] args
end

module Env : sig
include Env
val get : unit -> (string * bool) list
end = struct
let env = ref []
let get () = !env
let add_bool key b = env := (key, b) :: !env
let bool key =
let b = try List.assoc key Topkg.env with
| Not_found -> if Topkg.cmd = `Build then Topkg.err_miss key else true
in
add_bool key b; b

let native = bool "native"
let native_dynlink = bool "native-dynlink"
end

module Exts : Exts = struct
let interface = [".mli"; ".cmi"; ".cmti"]
let interface_opt = ".cmx" :: interface
let library = [".cma"; ".cmxa"; ".cmxs"; ".a"]
let module_library = (interface_opt @ library)
end

module Pkg : Pkg = struct
type builder = [ `OCamlbuild | `Other of string * string ]
type moves = (string * (string * string)) list
type field = ?cond:bool -> ?exts:string list -> ?dst:string -> string -> moves

let str = Printf.sprintf
let warn_unused () =
let keys = List.map fst Topkg.env in
let keys_used = List.map fst (Env.get ()) in
let unused = List.find_all (fun k -> not (List.mem k keys_used)) keys in
List.iter Topkg.warn_unused unused

let has_suffix = Filename.check_suffix
let build_strings ?(exec_sep = " ") btool bdir mvs =
let no_build = [ ".cmti"; ".cmt" ] in
let install = Buffer.create 1871 in
let exec = Buffer.create 1871 in
let rec add_mvs current = function
| (field, (src, dst)) :: mvs when field = current ->
if List.exists (has_suffix src) no_build then
Buffer.add_string install (str "\n \"?%s/%s\" {\"%s\"}" bdir src dst)
else begin
Buffer.add_string exec (str "%s%s" exec_sep src);
Buffer.add_string install (str "\n \"%s/%s\" {\"%s\"}" bdir src dst);
end;
add_mvs current mvs
| (((field, _) :: _) as mvs) ->
if current <> "" (* first *) then Buffer.add_string install " ]\n";
Buffer.add_string install (str "%s: [" field);
add_mvs field mvs
| [] -> ()
in
Buffer.add_string exec btool;
add_mvs "" mvs;
Buffer.add_string install " ]\n";
Buffer.contents install, Buffer.contents exec

let pr = Format.printf
let pr_explanation btool bdir pkg mvs =
let env = Env.get () in
let install, exec = build_strings ~exec_sep:" \\\n " btool bdir mvs in
pr "@[<v>";
pr "Package name: %s@," pkg;
pr "Build tool: %s@," btool;
pr "Build directory: %s@," bdir;
pr "Environment:@, ";
List.iter (fun (k,v) -> pr "%s=%b@, " k v) (List.sort compare env);
pr "@,Build invocation:@,";
pr " %s@,@," exec;
pr "Install file:@,";
pr "%s@," install;
pr "@]";
()

let pr_help () =
pr "Usage example:@\n %s" Sys.argv.(0);
List.iter (fun (k,v) -> pr " %s=%b" k v) (List.sort compare (Env.get ()));
pr "@."

let build btool bdir pkg mvs =
let install, exec = build_strings btool bdir mvs in
let e = Sys.command exec in
if e <> 0 then exit e else
let install_file = pkg ^ ".install" in
try
let oc = open_out install_file in
output_string oc install; flush oc; close_out oc
with Sys_error e -> Topkg.err_file install_file e

let mvs ?(drop_exts = []) field ?(cond = true) ?(exts = []) ?dst src =
if not cond then [] else
let mv src dst = (field, (src, dst)) in
let expand exts s d = List.map (fun e -> mv (s ^ e) (d ^ e)) exts in
let dst = match dst with None -> Filename.basename src | Some dst -> dst in
let files = if exts = [] then [mv src dst] else expand exts src dst in
let keep (_, (src, _)) = not (List.exists (has_suffix src) drop_exts) in
List.find_all keep files

let lib =
let drop_exts =
if Env.native && not Env.native_dynlink then [ ".cmxs" ] else
if not Env.native then [ ".a"; ".cmx"; ".cmxa"; ".cmxs" ] else []
in
mvs ~drop_exts "lib"

let share = mvs "share"
let share_root = mvs "share_root"
let etc = mvs "etc"
let toplevel = mvs "toplevel"
let doc = mvs "doc"
let misc = mvs "misc"
let stublibs = mvs "stublib"
let man = mvs "man"

let bin_drops = if not Env.native then [ ".native" ] else []
let bin_mvs field ?(auto = false) ?cond ?exts ?dst src =
let src, dst =
if not auto then src, dst else
let dst = match dst with
| None -> Some (Filename.basename src)
| Some _ as dst -> dst
in
let src = if Env.native then src ^ ".native" else src ^ ".byte" in
src, dst
in
mvs ~drop_exts:bin_drops field ?cond ?dst src

let bin = bin_mvs "bin"
let sbin = bin_mvs "sbin"

let describe pkg ~builder mvs =
let mvs = List.sort compare (List.flatten mvs) in
let btool, bdir = match builder with
| `OCamlbuild -> "ocamlbuild -use-ocamlfind -classic-display", "_build"
| `Other (btool, bdir) -> btool, bdir
in
match Topkg.cmd with
| `Explain -> pr_explanation btool bdir pkg mvs
| `Help -> pr_help ()
| `Build -> warn_unused (); build btool bdir pkg mvs
end

(*---------------------------------------------------------------------------
Copyright (c) 2014 Daniel C. Bünzli.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
3. Neither the name of Daniel C. Bünzli nor the names of
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------*)
Loading

0 comments on commit cb8879d

Please sign in to comment.