├── .gitignore ├── Makefile ├── README.md ├── _oasis ├── _tags ├── bin ├── rwo_html_code_highlight.ml ├── rwo_run_toplevel.ml └── rwo_syntax_highlight.ml ├── configure ├── lib ├── META ├── code_frag.ml ├── para_frag.ml ├── rwo.mllib └── xml_tree.ml ├── myocamlbuild.ml └── setup.ml /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | _build/ 3 | dist/ 4 | setup.data 5 | setup.log 6 | setup.bin 7 | *.docdir 8 | *.native 9 | *.byte 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: bc1e05bfc8b39b664f29dae8dbd3ebbb) 3 | 4 | SETUP = ocaml setup.ml 5 | 6 | build: setup.data 7 | $(SETUP) -build $(BUILDFLAGS) 8 | 9 | doc: setup.data build 10 | $(SETUP) -doc $(DOCFLAGS) 11 | 12 | test: setup.data build 13 | $(SETUP) -test $(TESTFLAGS) 14 | 15 | all: 16 | $(SETUP) -all $(ALLFLAGS) 17 | 18 | install: setup.data 19 | $(SETUP) -install $(INSTALLFLAGS) 20 | 21 | uninstall: setup.data 22 | $(SETUP) -uninstall $(UNINSTALLFLAGS) 23 | 24 | reinstall: setup.data 25 | $(SETUP) -reinstall $(REINSTALLFLAGS) 26 | 27 | clean: 28 | $(SETUP) -clean $(CLEANFLAGS) 29 | 30 | distclean: 31 | $(SETUP) -distclean $(DISTCLEANFLAGS) 32 | 33 | setup.data: 34 | $(SETUP) -configure $(CONFIGUREFLAGS) 35 | 36 | .PHONY: build doc test all install uninstall reinstall clean distclean configure 37 | 38 | # OASIS_STOP 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Deprecated: These scripts are no longer in use for RWO ## 2 | 3 | These scripts form the compilation pipeline for Real World OCaml. They aren't 4 | really designed to be useful outside of the book toolchain, but the plan is to 5 | factor out the useful bits eventually. 6 | 7 | The following binaries work against the 8 | [examples](http://github.com/realworldocaml/examples) repository: 9 | 10 | * `rwo-run-toplevel`: Runs a toplevel script in a non-interactive 11 | toplevel, and outputs the result as HTML and Markdown. 12 | * `rwo-syntax-highlight`: Called from the `code` Makefile to convert 13 | a code fragment into HTML and Markdown, either via direct parsing 14 | using [COW](http://github.com/mirage/ocaml-cow) or 15 | [Pygments](http://pygments.org). 16 | 17 | The following binaries are used by the internal book toolchain to 18 | turn it into an O'Reilly PDF and the online website. They're definitely 19 | not for external consumption until the full toolchain is published. 20 | 21 | * `rwo-html-code-highlight`: Substitute code fragments in DocBook XHTML 22 | chunked output with the right HTML ones from the code repository. 23 | 24 | -------------------------------------------------------------------------------- /_oasis: -------------------------------------------------------------------------------- 1 | OASISFormat: 0.3 2 | Name: cohttp 3 | Version: 1.0.0 4 | Synopsis: Real World OCaml build scripts 5 | Authors: Anil Madhavapeddy 6 | License: ISC 7 | Plugins: META (0.3), DevFiles (0.3) 8 | BuildTools: ocamlbuild 9 | 10 | Library rwo 11 | Path: lib 12 | Findlibname: rwo 13 | Modules: Code_frag,Para_frag,Xml_tree 14 | BuildDepends: uri (>= 1.3.8), xmlm, cmdliner, str, cow, core (>= 109.35.00), threads, ezxmlm 15 | Install: false 16 | 17 | Executable "rwo-run-toplevel" 18 | Path: bin 19 | MainIs: rwo_run_toplevel.ml 20 | Custom: false 21 | CompiledObject: byte 22 | BuildDepends: unix, compiler-libs.toplevel, rwo 23 | 24 | Executable "rwo-syntax-highlight" 25 | Path: bin 26 | MainIs: rwo_syntax_highlight.ml 27 | Custom: true 28 | CompiledObject: native 29 | BuildDepends: rwo 30 | 31 | Executable "rwo-html-code-highlight" 32 | Path: bin 33 | MainIs: rwo_html_code_highlight.ml 34 | Custom: true 35 | CompiledObject: native 36 | BuildDepends: rwo 37 | -------------------------------------------------------------------------------- /_tags: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: 5d41682b9010fb20ec7528c74ed455a9) 3 | # Ignore VCS directories, you can use the same kind of rule outside 4 | # OASIS_START/STOP if you want to exclude directories that contains 5 | # useless stuff for the build process 6 | <**/.svn>: -traverse 7 | <**/.svn>: not_hygienic 8 | ".bzr": -traverse 9 | ".bzr": not_hygienic 10 | ".hg": -traverse 11 | ".hg": not_hygienic 12 | ".git": -traverse 13 | ".git": not_hygienic 14 | "_darcs": -traverse 15 | "_darcs": not_hygienic 16 | # Library rwo 17 | "lib/rwo.cmxs": use_rwo 18 | : pkg_uri 19 | : pkg_xmlm 20 | : pkg_cmdliner 21 | : pkg_str 22 | : pkg_cow 23 | : pkg_core 24 | : pkg_threads 25 | : pkg_ezxmlm 26 | # Executable rwo-run-toplevel 27 | "bin/rwo_run_toplevel.byte": use_rwo 28 | "bin/rwo_run_toplevel.byte": pkg_unix 29 | "bin/rwo_run_toplevel.byte": pkg_compiler-libs.toplevel 30 | "bin/rwo_run_toplevel.byte": pkg_uri 31 | "bin/rwo_run_toplevel.byte": pkg_xmlm 32 | "bin/rwo_run_toplevel.byte": pkg_cmdliner 33 | "bin/rwo_run_toplevel.byte": pkg_str 34 | "bin/rwo_run_toplevel.byte": pkg_cow 35 | "bin/rwo_run_toplevel.byte": pkg_core 36 | "bin/rwo_run_toplevel.byte": pkg_threads 37 | "bin/rwo_run_toplevel.byte": pkg_ezxmlm 38 | : pkg_unix 39 | : pkg_compiler-libs.toplevel 40 | # Executable rwo-syntax-highlight 41 | "bin/rwo_syntax_highlight.native": use_rwo 42 | "bin/rwo_syntax_highlight.native": pkg_uri 43 | "bin/rwo_syntax_highlight.native": pkg_xmlm 44 | "bin/rwo_syntax_highlight.native": pkg_cmdliner 45 | "bin/rwo_syntax_highlight.native": pkg_str 46 | "bin/rwo_syntax_highlight.native": pkg_cow 47 | "bin/rwo_syntax_highlight.native": pkg_core 48 | "bin/rwo_syntax_highlight.native": pkg_threads 49 | "bin/rwo_syntax_highlight.native": pkg_ezxmlm 50 | "bin/rwo_syntax_highlight.native": custom 51 | # Executable rwo-html-code-highlight 52 | "bin/rwo_html_code_highlight.native": use_rwo 53 | "bin/rwo_html_code_highlight.native": pkg_uri 54 | "bin/rwo_html_code_highlight.native": pkg_xmlm 55 | "bin/rwo_html_code_highlight.native": pkg_cmdliner 56 | "bin/rwo_html_code_highlight.native": pkg_str 57 | "bin/rwo_html_code_highlight.native": pkg_cow 58 | "bin/rwo_html_code_highlight.native": pkg_core 59 | "bin/rwo_html_code_highlight.native": pkg_threads 60 | "bin/rwo_html_code_highlight.native": pkg_ezxmlm 61 | : use_rwo 62 | : pkg_uri 63 | : pkg_xmlm 64 | : pkg_cmdliner 65 | : pkg_str 66 | : pkg_cow 67 | : pkg_core 68 | : pkg_threads 69 | : pkg_ezxmlm 70 | "bin/rwo_html_code_highlight.native": custom 71 | # OASIS_STOP 72 | true: syntax_camlp4o, pkg_sexplib.syntax, pkg_cow.syntax, short_paths, annot 73 | -------------------------------------------------------------------------------- /bin/rwo_html_code_highlight.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Copyright (c) 2012-2013 Anil Madhavapeddy 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | * 16 | *) 17 | 18 | open Core.Std 19 | 20 | let get_code_frag fname part = 21 | let fname = sprintf "%s/%s.%d.html" Sys.argv.(1) fname part in 22 | Ezxmlm.from_channel (open_in fname) 23 | |> fun (dtd,xml) -> 24 | xml 25 | 26 | let () = 27 | let open Ezxmlm in 28 | let dtd,xml = from_channel stdin in 29 | filter_map ~tag:"pre" ~f:(fun attrs nodes -> []) xml 30 | |> filter_map ~tag:"img" ~f:(fun attrs nodes -> 31 | if String.is_prefix ~prefix:"images/" (get_attr "src" attrs) then [] else [make_tag "img" (attrs,nodes)] ) 32 | |> filter_map ~tag:"p" ~f:(fun attrs nodes -> 33 | (* Hunt for a

with a href to github *) 34 | try 35 | let attr,link = member_with_attr "a" nodes in 36 | let part = try 37 | Scanf.sscanf (String.lstrip (data_to_string nodes)) "(part %d)" (fun d -> d) 38 | with _ -> 0 in 39 | let fname = String.chop_prefix_exn 40 | ~prefix:"https://github.com/realworldocaml/examples/tree/v1/code/" (get_attr "href" attr) in 41 | let orig_node = [make_tag "p" (attrs, nodes)] in 42 | get_code_frag fname part 43 | with _ -> [make_tag "p" (attrs,nodes)] 44 | ) 45 | |> fun d -> print_endline (Cow.Html.to_string d) 46 | -------------------------------------------------------------------------------- /bin/rwo_run_toplevel.ml: -------------------------------------------------------------------------------- 1 | (* Syntax hightlight code and eval ocaml toplevel phrases. 2 | * Based on code from http://github.com/ocaml/ocaml.org 3 | * Modified by Anil Madhavapeddy in 2013 for Real World OCaml and to use Core 4 | * Much code borrowed from uTop. 5 | * TODO: license? 6 | *) 7 | 8 | open Printf 9 | open Scanf 10 | 11 | (* Run these phrases silently before any code *) 12 | let initial_phrases = [ 13 | "#use \"topfind\""; 14 | "#camlp4o"; 15 | "#require \"core\""; 16 | "#require \"core.syntax\""; 17 | "#require \"core.top\""; 18 | "open Core.Std" ] 19 | 20 | (* Initialise toploop and turn on short-paths *) 21 | let reset_toplevel () = 22 | Toploop.initialize_toplevel_env (); 23 | Toploop.input_name := "//toplevel//"; 24 | Topdirs.dir_directory (Sys.getenv "OCAML_TOPLEVEL_PATH"); 25 | Clflags.real_paths := false 26 | 27 | let build_dir = ref "." 28 | let ofile file part = sprintf "%s/%s.%d.xml" !build_dir file part 29 | let ofile_html file part = sprintf "%s/%s.%d.html" !build_dir file part 30 | 31 | type outcome = [ 32 | | `Normal of string * string * string (* exec output, stdout, stderr *) 33 | | `Error of string * string * string 34 | ] 35 | 36 | let is_ready_for_read fd = 37 | let fd_for_read, _, _ = Unix.select [fd] [] [] 0.001 in 38 | fd_for_read <> [] 39 | 40 | let string_of_fd fd = 41 | let buf = Buffer.create 1024 in 42 | let s = String.create 256 in 43 | while is_ready_for_read fd do 44 | let r = Unix.read fd s 0 256 in 45 | Buffer.add_substring buf s 0 r 46 | done; 47 | Buffer.contents buf 48 | 49 | let init_stdout = Unix.dup Unix.stdout 50 | let init_stderr = Unix.dup Unix.stderr 51 | 52 | let flush_std_out_err () = 53 | Format.pp_print_flush Format.std_formatter (); 54 | flush stdout; 55 | Format.pp_print_flush Format.err_formatter (); 56 | flush stderr 57 | 58 | let with_loc loc str = { 59 | Location.txt = str; 60 | Location.loc = loc; 61 | } 62 | 63 | (** Rewrite rules, lifted straight from uTop. *) 64 | 65 | (* A rule for rewriting a toplevel expression. *) 66 | type rewrite_rule = { 67 | required_values : Longident.t list; 68 | (* Values that must exist and be persistent for the rule to apply. *) 69 | rewrite : Location.t -> Parsetree.expression -> Parsetree.expression; 70 | (* The rewrite function. *) 71 | } 72 | 73 | (* Rewrite rules, indexed by the identifier of the type 74 | constructor. *) 75 | let rewrite_rules : (Longident.t, rewrite_rule) Hashtbl.t = Hashtbl.create 42 76 | 77 | let longident_lwt_main_run = Longident.Ldot (Longident.Lident "Lwt_main", "run") 78 | let longident_async_core_thread_safe_block_on_async_exn = 79 | Longident.parse "Async.Std.Thread_safe.block_on_async_exn" 80 | let longident_unit = Longident.Lident "()" 81 | 82 | (* Wrap into: fun () -> *) 83 | let wrap_unit loc e = 84 | let i = with_loc loc longident_unit in 85 | let p = { 86 | Parsetree.ppat_desc = Parsetree.Ppat_construct (i, None, false); 87 | Parsetree.ppat_loc = loc; 88 | } in 89 | { 90 | Parsetree.pexp_desc = Parsetree.Pexp_function ("", None, [(p, e)]); 91 | Parsetree.pexp_loc = loc; 92 | } 93 | 94 | let () = 95 | (* Rewrite Async.Std.Defered.t expressions to 96 | Async_core.Thread_safe.block_on_async_exn (fun () -> ). *) 97 | Hashtbl.add rewrite_rules (Longident.parse "Async_core.Ivar.Deferred.t") { 98 | required_values = [longident_async_core_thread_safe_block_on_async_exn]; 99 | rewrite = (fun loc e -> { 100 | Parsetree.pexp_desc = 101 | Parsetree.Pexp_apply 102 | ({ Parsetree.pexp_desc = Parsetree.Pexp_ident 103 | (with_loc loc longident_async_core_thread_safe_block_on_async_exn); 104 | Parsetree.pexp_loc = loc }, 105 | [("", wrap_unit loc e)]); 106 | Parsetree.pexp_loc = loc; 107 | }); 108 | } 109 | 110 | (* Returns whether the argument is a toplevel expression. *) 111 | let is_eval = function 112 | | { Parsetree.pstr_desc = Parsetree.Pstr_eval _ } -> true 113 | | _ -> false 114 | 115 | (* Returns whether the given path is persistent. *) 116 | let rec is_persistent_path = function 117 | | Path.Pident id -> Ident.persistent id 118 | | Path.Pdot (p, _, _) -> is_persistent_path p 119 | | Path.Papply (_, p) -> is_persistent_path p 120 | 121 | (* Convert a path to a long identifier. *) 122 | let rec longident_of_path path = 123 | match path with 124 | | Path.Pident id -> 125 | Longident.Lident (Ident.name id) 126 | | Path.Pdot (path, s, _) -> 127 | Longident.Ldot (longident_of_path path, s) 128 | | Path.Papply (p1, p2) -> 129 | Longident.Lapply (longident_of_path p1, longident_of_path p2) 130 | 131 | (* Returns the rewrite rule associated to a type, if any. *) 132 | let rec rule_of_type typ = 133 | match typ.Types.desc with 134 | | Types.Tlink typ -> 135 | rule_of_type typ 136 | | Types.Tconstr (path, _, _) -> begin 137 | match try Some (Env.find_type path !Toploop.toplevel_env) with Not_found -> None with 138 | | Some { 139 | Types.type_kind = Types.Type_abstract; 140 | Types.type_private = Asttypes.Public; 141 | Types.type_manifest = Some typ; 142 | } -> 143 | rule_of_type typ 144 | | _ -> 145 | try 146 | Some (Hashtbl.find rewrite_rules (longident_of_path path)) 147 | with Not_found -> 148 | None 149 | end 150 | | _ -> 151 | None 152 | 153 | (* Check that the given long identifier is present in the environment 154 | and is persistent. *) 155 | let is_persistent_in_env longident = 156 | try 157 | is_persistent_path (fst (Env.lookup_value longident !Toploop.toplevel_env)) 158 | with Not_found -> 159 | false 160 | 161 | let str_items_of_typed_structure tstr = tstr.Typedtree.str_items 162 | let str_desc_of_typed_str_item tstr = tstr.Typedtree.str_desc 163 | 164 | let rewrite_str_item pstr_item tstr_item = 165 | match pstr_item, str_desc_of_typed_str_item tstr_item with 166 | | ({ Parsetree.pstr_desc = Parsetree.Pstr_eval e; 167 | Parsetree.pstr_loc = loc }, 168 | Typedtree.Tstr_eval { Typedtree.exp_type = typ }) -> begin 169 | match rule_of_type typ with 170 | | Some rule -> 171 | if List.for_all is_persistent_in_env rule.required_values then begin 172 | { Parsetree.pstr_desc = Parsetree.Pstr_eval (rule.rewrite loc e); 173 | Parsetree.pstr_loc = loc } 174 | end else 175 | pstr_item 176 | | None -> 177 | pstr_item 178 | end 179 | | _ -> 180 | pstr_item 181 | 182 | let rewrite phrase = 183 | match phrase with 184 | | Parsetree.Ptop_def pstr -> 185 | if List.exists is_eval pstr then 186 | let tstr, _, _ = Typemod.type_structure !Toploop.toplevel_env pstr Location.none in 187 | let tstr = str_items_of_typed_structure tstr in 188 | Parsetree.Ptop_def (List.map2 rewrite_str_item pstr tstr) 189 | else begin 190 | phrase 191 | end 192 | | Parsetree.Ptop_dir _ -> 193 | phrase 194 | 195 | let toploop_eval phrase = 196 | if String.trim phrase = ";;" then `Normal("", "", "") 197 | else ( 198 | flush_std_out_err (); 199 | let (out_in, out_out) = Unix.pipe() in 200 | Unix.dup2 out_out Unix.stdout; (* Unix.stdout → out_out *) 201 | let (err_in, err_out) = Unix.pipe() in 202 | Unix.dup2 err_out Unix.stderr; (* Unix.stderr → err_out *) 203 | let get_stdout_stderr_and_restore () = 204 | flush_std_out_err (); 205 | let out = string_of_fd out_in in 206 | Unix.close out_in; 207 | Unix.close out_out; 208 | Unix.dup2 init_stdout Unix.stdout; (* restore initial stdout *) 209 | let err = string_of_fd err_in in 210 | Unix.close err_in; 211 | Unix.close err_out; 212 | Unix.dup2 init_stderr Unix.stderr; (* restore initial stderr *) 213 | (out, err) in 214 | try 215 | let lexbuf = Lexing.from_string phrase in 216 | let dummypos = { Lexing.pos_fname = "//toplevel//"; pos_lnum = 0; pos_bol = 0; pos_cnum = -1; } in 217 | lexbuf.Lexing.lex_start_p <- dummypos; 218 | lexbuf.Lexing.lex_curr_p <- dummypos; 219 | let phrase = !Toploop.parse_toplevel_phrase lexbuf in 220 | let phrase = rewrite phrase in 221 | ignore(Toploop.execute_phrase true Format.str_formatter phrase); 222 | let exec_output = Format.flush_str_formatter () in 223 | let out, err = get_stdout_stderr_and_restore () in 224 | `Normal(exec_output, out, err) 225 | with 226 | | e -> 227 | let out, err = get_stdout_stderr_and_restore () in 228 | print_string out; 229 | prerr_string err; 230 | (try 231 | Errors.report_error Format.str_formatter e; 232 | `Error(Format.flush_str_formatter (), out, err) 233 | with exn -> ( 234 | printf "Code.toploop_eval: the following error was raised during \ 235 | error reporting for %S:\n%s\nError backtrace:\n%s\n%!" 236 | phrase (Printexc.to_string exn) (Printexc.get_backtrace ()); 237 | `Error("", out,err) 238 | ) 239 | ); 240 | ) 241 | 242 | 243 | (*** Suppress values beginning with _. Lifted straight from uTop: 244 | * uTop_main.ml 245 | * ------------ 246 | * Copyright : (c) 2011, Jeremie Dimino 247 | * Licence : BSD3 248 | **) 249 | 250 | let orig_print_out_signature = !Toploop.print_out_signature 251 | let orig_print_out_phrase = !Toploop.print_out_phrase 252 | 253 | let rec map_items unwrap wrap items = 254 | match items with 255 | | [] -> 256 | [] 257 | | item :: items -> 258 | let sig_item, _ = unwrap item in 259 | let name, _ = 260 | match sig_item with 261 | | Outcometree.Osig_class (_, name, _, _, rs) 262 | | Outcometree.Osig_class_type (_, name, _, _, rs) 263 | | Outcometree.Osig_module (name, _, rs) 264 | | Outcometree.Osig_type ((name, _, _, _, _), rs) -> 265 | (name, rs) 266 | | Outcometree.Osig_exception (name, _) 267 | | Outcometree.Osig_modtype (name, _) 268 | | Outcometree.Osig_value (name, _, _) -> 269 | (name, Outcometree.Orec_not) 270 | in 271 | let keep = name = "" || name.[0] <> '_' in 272 | if keep then 273 | item :: map_items unwrap wrap items 274 | else 275 | (* Replace the [Orec_next] at the head of items by [Orec_first] *) 276 | let items = 277 | match items with 278 | | [] -> 279 | [] 280 | | item :: items' -> 281 | let sig_item, extra = unwrap item in 282 | match sig_item with 283 | | Outcometree.Osig_class (a, name, b, c, rs) -> 284 | if rs = Outcometree.Orec_next then 285 | wrap (Outcometree.Osig_class (a, name, b, c, Outcometree.Orec_first)) extra :: items' 286 | else 287 | items 288 | | Outcometree.Osig_class_type (a, name, b, c, rs) -> 289 | if rs = Outcometree.Orec_next then 290 | wrap (Outcometree.Osig_class_type (a, name, b, c, Outcometree.Orec_first)) extra :: items' 291 | else 292 | items 293 | | Outcometree.Osig_module (name, a, rs) -> 294 | if rs = Outcometree.Orec_next then 295 | wrap (Outcometree.Osig_module (name, a, Outcometree.Orec_first)) extra :: items' 296 | else 297 | items 298 | | Outcometree.Osig_type ((name, a, b, c, d), rs) -> 299 | if rs = Outcometree.Orec_next then 300 | wrap (Outcometree.Osig_type ((name, a, b, c, d), Outcometree.Orec_first)) extra :: items' 301 | else 302 | items 303 | | Outcometree.Osig_exception _ 304 | | Outcometree.Osig_modtype _ 305 | | Outcometree.Osig_value _ -> 306 | items 307 | in 308 | map_items unwrap wrap items 309 | 310 | let print_out_signature pp items = 311 | orig_print_out_signature pp (map_items (fun x -> (x, ())) (fun x () -> x) items) 312 | 313 | let print_out_phrase pp phrase = 314 | let phrase = 315 | match phrase with 316 | | Outcometree.Ophr_eval _ 317 | | Outcometree.Ophr_exception _ -> phrase 318 | | Outcometree.Ophr_signature items -> 319 | Outcometree.Ophr_signature (map_items (fun x -> x) (fun x y -> (x, y)) items) 320 | in 321 | orig_print_out_phrase pp phrase 322 | 323 | let () = 324 | Toploop.print_out_signature := print_out_signature; 325 | Toploop.print_out_phrase := print_out_phrase 326 | 327 | (** End of uTop code *) 328 | 329 | open Core.Std 330 | let parse_file fullfile file = 331 | eprintf "C: init\n%!"; 332 | reset_toplevel (); 333 | List.iter initial_phrases ~f:(fun phrase -> 334 | match toploop_eval (phrase ^ " ;;") with 335 | | `Normal _ -> () 336 | | `Error (s,_,_) -> eprintf "Failed (%s): %s\n" s phrase; exit (-1) 337 | ); 338 | let parts = Int.Table.create () in 339 | let html_parts = Int.Table.create () in 340 | let part = ref 0 in 341 | let wrap_lines_in_userinput buf = 342 | String.split_lines buf 343 | |> List.map ~f:(fun l -> <:xml<$str:l$ 344 | >>) 345 | in 346 | let wrap_lines_in_output buf = 347 | match buf with 348 | |"" -> [] 349 | |buf -> 350 | String.split_lines buf 351 | |> List.map ~f:(fun l -> <:xml<$str:l$ 352 | >>) in 353 | let print_part ~phrase ~sout ~serr ~out key = 354 | let i = wrap_lines_in_userinput phrase in 355 | let x = <:xml<# $list:i$$list:(wrap_lines_in_output sout)$$list:(wrap_lines_in_output serr)$$list:(wrap_lines_in_output out)$>> in 356 | match Hashtbl.find parts key with 357 | | None -> 358 | let buf = ref x in 359 | Hashtbl.replace parts ~key ~data:buf; 360 | | Some buf -> 361 | buf := !buf @ x 362 | in 363 | let print_html_part key s = 364 | match Hashtbl.find html_parts key with 365 | | None -> 366 | let buf = Buffer.create 100 in 367 | Hashtbl.replace html_parts ~key ~data:buf; 368 | Buffer.add_string buf s 369 | | Some buf -> Buffer.add_string buf s 370 | in 371 | (* Utop does this directly in findlib, but we dont link to it *) 372 | let strip_findlib_stuff b = 373 | String.split_lines b 374 | |> List.filter ~f:(fun l -> not (String.is_suffix ~suffix:"added to search path" l)) 375 | |> List.filter ~f:(fun l -> not (String.is_suffix ~suffix:": loaded" l)) 376 | |> String.concat ~sep:"\n" 377 | in 378 | let _ = 379 | In_channel.with_file file ~f:( 380 | In_channel.fold_lines ~init:[] ~f:( 381 | fun acc line -> 382 | if String.is_prefix line ~prefix:"#part" then begin 383 | part := Scanf.sscanf line "#part %d" (fun p -> p); 384 | eprintf "C: part %d -> %s\n%!" !part (ofile file !part); 385 | [] 386 | end else begin 387 | if String.is_suffix ~suffix:";;" line then begin 388 | let phrase = String.concat ~sep:"\n" (List.rev (line :: acc)) in 389 | eprintf "X: %s\n%!" phrase; 390 | match toploop_eval phrase with 391 | | `Normal(s, stdout, stderr) |`Error (s,stdout,stderr) -> 392 | eprintf "c: %s\n%!" s; 393 | eprintf "o: %s\n%!" stdout; 394 | eprintf "e: %s\n%!" stderr; 395 | let stderr = strip_findlib_stuff stderr in 396 | print_part ~phrase ~sout:stdout ~serr:stderr ~out:s !part; 397 | print_html_part !part (Cow.Html.to_string (Cow.Code.ocaml_fragment ("# " ^ phrase))); 398 | let sout = if stdout = "" then <:html<&>> else <:html<
$str:stdout$>> in 399 | let serr = if stderr = "" then <:html<&>> else <:html<
$str:stderr$>> in 400 | let s = if s ="" then " " else s in 401 | print_html_part !part (Cow.Html.to_string <:html<

$sout$$serr$$str:s$
&>>); 402 | [] 403 | end else 404 | line::acc 405 | end 406 | ); 407 | ) in 408 | Hashtbl.iter parts ~f:( 409 | fun ~key ~data -> 410 | eprintf "W: %s\n%!" (ofile file key); 411 | let data = Code_frag.wrap_in_docbook_box ~part:key ~lang:`OCaml_toplevel fullfile !data in 412 | Out_channel.write_all (ofile file key) ~data); 413 | Hashtbl.iter html_parts ~f:( 414 | fun ~key ~data -> 415 | let code = Cow.Html.of_string (String.strip (Buffer.contents data)) in 416 | let data = 417 | Code_frag.wrap_in_pretty_box ~part:key ~lang:`OCaml_toplevel fullfile code 418 | |> Cow.Html.to_string in 419 | eprintf "W: %s\n%!" (ofile_html file key); 420 | Out_channel.write_all (ofile_html file key) ~data) 421 | 422 | let () = 423 | Command.basic 424 | ~summary:"Run files through the Core toplevel" 425 | Command.Spec.(empty 426 | +> flag "-builddir" (optional_with_default "." string) 427 | ~doc:"dir prepend build directory to output files" 428 | +> flag "-fullfile" (required string) 429 | ~doc:"filename full subdir/filename for prettyprinting in HTML" 430 | +> anon (sequence ("file" %: file)) 431 | ) 432 | (fun bd fullfile files () -> build_dir := bd; List.iter ~f:(parse_file fullfile) files) 433 | |> Command.run 434 | -------------------------------------------------------------------------------- /bin/rwo_syntax_highlight.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Copyright (c) 2012-2013 Anil Madhavapeddy 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | * 16 | *) 17 | 18 | (* Syntax highlight some code, either through Pygments 19 | * or Cow's OCaml syntax highlighting *) 20 | open Core.Std 21 | open Xml_tree 22 | open Code_frag 23 | 24 | let build_dir = ref "" 25 | let ofile_html file part = sprintf "%s/%s.%d.html" !build_dir file part 26 | let ofile_md file part = sprintf "%s/%s.%d.md" !build_dir file part 27 | let ofile_xml file part = sprintf "%s/%s.%d.xml" !build_dir file part 28 | 29 | (* Run a buffer through Pygments to colorize it *) 30 | let pygmentize lang file contents = 31 | (* The contents of
 are just Data tags, so filter them through
 32 |      Pygments *)
 33 |   let data = run_through_pygmentize lang contents in
 34 |   let lang = typ_of_string lang in
 35 |   let data_html = wrap_in_pretty_box ~part:0 ~lang file data |> Cow.Html.to_string in
 36 |   Out_channel.write_all (ofile_html file 0) ~data:data_html;
 37 |   Out_channel.write_all (ofile_md file 0) ~data:contents;
 38 |   let data = wrap_in_docbook_box ~part:0 ~lang file <:xml<$str:contents$>> in
 39 |   Out_channel.write_all (ofile_xml file 0) ~data
 40 | 
 41 | let raw lang file contents =
 42 |   let data = <:html<
$str:contents$
&>> in 43 | let lang = typ_of_string lang in 44 | let data = wrap_in_pretty_box ~part:0 ~lang file data |> Cow.Html.to_string in 45 | Out_channel.write_all (ofile_html file 0) ~data; 46 | Out_channel.write_all (ofile_md file 0) ~data:contents; 47 | let data = wrap_in_docbook_box ~part:0 ~lang file <:html<$str:contents$>> in 48 | Out_channel.write_all (ofile_xml file 0) ~data 49 | 50 | let cow file contents = 51 | (* Break the OCaml code into parts *) 52 | Code_frag.extract_all_ocaml_parts file contents 53 | |> List.iter ~f:(fun (part,buf) -> 54 | let code = Cow.Code.ocaml_fragment buf in 55 | let html = wrap_in_pretty_box ~part ~lang:`OCaml file code in 56 | let data = Cow.Html.to_string html in 57 | Out_channel.write_all (ofile_html file part) ~data; 58 | Out_channel.write_all (ofile_md file part) ~data:buf; 59 | let data = wrap_in_docbook_box ~part ~lang:`OCaml file <:xml<$str:buf$>> in 60 | Out_channel.write_all (ofile_xml file part) ~data 61 | ) 62 | 63 | let rawscript file = 64 | (* Run lines starting with # through Cow, pass rest through *) 65 | Code_frag.concat_toplevel_phrases (In_channel.read_lines file) 66 | |> List.map ~f:(fun line -> 67 | let line = if line = "" then " " else line in 68 | if String.is_suffix ~suffix:";;" line then 69 | Cow.Code.ocaml_fragment line 70 | else 71 | <:html<
$str:line$
&>>) 72 | |> fun olines_html -> 73 | Code_frag.concat_toplevel_phrases (In_channel.read_lines file) 74 | |> List.map ~f:(fun line -> 75 | let line = if line = "" then " " else line in 76 | if String.is_suffix ~suffix:";;" line then 77 | <:xml<$str:line$ 78 | >> 79 | else 80 | <:xml<$str:line$ 81 | >>) 82 | |> fun olines_xml -> 83 | let buf = 84 | wrap_in_pretty_box ~part:0 ~lang:`OCaml_toplevel file (List.concat olines_html) 85 | |> Cow.Html.to_string in 86 | Out_channel.write_all (ofile_html file 0) ~data:buf; 87 | Out_channel.write_all (ofile_md file 0) ~data:(In_channel.read_all file); 88 | let data = wrap_in_docbook_box ~part:0 ~lang:`OCaml_toplevel file (List.concat olines_xml) in 89 | Out_channel.write_all (ofile_xml file 0) ~data 90 | 91 | let console file = 92 | (* Run lines starting with $ through pygments, pass rest through *) 93 | let olines = List.rev (In_channel.with_file file ~f:( 94 | In_channel.fold_lines ~init:[] ~f:(fun acc line -> 95 | let line = if line = "" then " " else line in 96 | if String.is_prefix ~prefix:"$ " line then 97 | (run_through_pygmentize "console" line) :: acc 98 | else (<:html<
$str:line$
&>>) :: acc 99 | ))) in 100 | let olines_xml = List.rev (In_channel.with_file file ~f:( 101 | In_channel.fold_lines ~init:[] ~f:(fun acc line -> 102 | let line = if line = "" then " " else line in 103 | if String.is_prefix ~prefix:"$ " line then ( 104 | let rest = String.sub line ~pos:2 ~len:(String.length line - 2) in 105 | let dollar = "$" in 106 | (<:xml<$str:dollar$ $str:rest$ 107 | >>) :: acc 108 | ) else (<:xml<$str:line$ 109 | >>) :: acc 110 | ))) in 111 | let buf = 112 | wrap_in_pretty_box ~part:0 ~lang:`Console file (List.concat olines) 113 | |> Cow.Html.to_string in 114 | Out_channel.write_all (ofile_html file 0) ~data:buf; 115 | Out_channel.write_all (ofile_md file 0) ~data:(In_channel.read_all file); 116 | let data = wrap_in_docbook_box ~part:0 ~lang:`Console file (List.concat olines_xml) in 117 | Out_channel.write_all (ofile_xml file 0) ~data 118 | 119 | let do_highlight build_dir' use_cow use_rawscript use_pygments use_raw use_console file () = 120 | build_dir := build_dir'; 121 | let buf = In_channel.read_all file in 122 | if use_cow then 123 | cow file buf 124 | else if use_console then 125 | console file 126 | else if use_rawscript then 127 | rawscript file 128 | else match use_pygments with 129 | | Some lang -> pygmentize lang file buf 130 | | None -> begin 131 | match use_raw with 132 | | Some lang -> raw lang file buf 133 | | None -> failwith "no flags" 134 | end 135 | 136 | let () = 137 | Command.basic 138 | ~summary:"Syntax highlight code to HTML and Markdown" 139 | Command.Spec.(empty 140 | +> flag "-builddir" (optional_with_default "." string) 141 | ~doc:"dir Prepend directory to output files" 142 | +> flag "-cow" no_arg 143 | ~doc:" Filter OCaml through COW, extracting only part %d " 144 | +> flag "-rawscript" no_arg 145 | ~doc:" Filter OCaml toplevel through COW" 146 | +> flag "-pygments" (optional string) 147 | ~doc:"lang Filter through Pygments with given [lang]" 148 | +> flag "-raw" (optional string) 149 | ~doc:"lang Wrap raw file with given [lang]" 150 | +> flag "-console" no_arg 151 | ~doc:" Filter shell script output into HTML" 152 | +> anon ("filename" %: file) 153 | ) 154 | do_highlight 155 | |> Command.run 156 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # OASIS_START 4 | # DO NOT EDIT (digest: 425187ed8bfdbdd207fd76392dd243a7) 5 | set -e 6 | 7 | FST=true 8 | for i in "$@"; do 9 | if $FST; then 10 | set -- 11 | FST=false 12 | fi 13 | 14 | case $i in 15 | --*=*) 16 | ARG=${i%%=*} 17 | VAL=${i##*=} 18 | set -- "$@" "$ARG" "$VAL" 19 | ;; 20 | *) 21 | set -- "$@" "$i" 22 | ;; 23 | esac 24 | done 25 | 26 | ocaml setup.ml -configure "$@" 27 | # OASIS_STOP 28 | -------------------------------------------------------------------------------- /lib/META: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: 2bac6747c041ecac35568bae950f890e) 3 | version = "1.0.0" 4 | description = "Real World OCaml build scripts" 5 | requires = "uri xmlm cmdliner str cow core threads ezxmlm" 6 | archive(byte) = "rwo.cma" 7 | archive(byte, plugin) = "rwo.cma" 8 | archive(native) = "rwo.cmxa" 9 | archive(native, plugin) = "rwo.cmxs" 10 | exists_if = "rwo.cma" 11 | # OASIS_STOP 12 | 13 | -------------------------------------------------------------------------------- /lib/code_frag.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Copyright (c) 2012-2013 Anil Madhavapeddy 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | * 16 | *) 17 | 18 | (* A fragment description, intended to be embedded in the book as 19 | ```frag 20 | ((typ xxx)(name xxx)(part 1)(header false)) 21 | ``` 22 | where the (part X) defaults to 0 and (header) defaults to true 23 | If (part X) is specified, then there will be '#' preprocessor directives 24 | in the [name] file. 25 | 26 | The [name] file should be under the `code/` subdirectory 27 | *) 28 | 29 | open Core.Std 30 | 31 | type typ = [ 32 | | `OCaml 33 | | `OCaml_toplevel 34 | | `OCaml_rawtoplevel 35 | | `Console 36 | | `JSON 37 | | `ATD 38 | | `Scheme 39 | | `OCaml_syntax 40 | | `C 41 | | `Bash 42 | | `CPP 43 | | `Java 44 | | `Ascii 45 | | `Gas 46 | ] 47 | 48 | type t = { 49 | typ: string; 50 | name: string; 51 | part: int with default(0); 52 | header: bool with default(true) 53 | } with sexp 54 | 55 | let typ_of_string s : typ = 56 | match s with 57 | | "ocaml" -> `OCaml 58 | | "ocamltop" -> `OCaml_toplevel 59 | | "ocamlrawtop" -> `OCaml_rawtoplevel 60 | | "console" -> `Console 61 | | "json" -> `JSON 62 | | "atd" -> `ATD 63 | | "scheme" -> `Scheme 64 | | "ocamlsyntax" -> `OCaml_syntax 65 | | "java" -> `Java 66 | | "c" -> `C 67 | | "sh" -> `Bash 68 | | "bash" -> `Bash 69 | | "cpp" -> `CPP 70 | | "ascii" -> `Ascii 71 | | "gas" -> `Gas 72 | | x -> raise (Failure ("Unknown fragment type " ^ x)) 73 | 74 | let typ_to_docbook_language (t:typ) = 75 | match t with 76 | | `OCaml -> "ocaml" 77 | | `OCaml_toplevel -> "ocaml" 78 | | `OCaml_rawtoplevel -> "ocaml" 79 | | `Console -> "console" 80 | | `JSON -> "json" 81 | | `ATD -> "ocaml" 82 | | `Scheme -> "scheme" 83 | | `OCaml_syntax -> "" 84 | | `Java -> "java" 85 | | `C -> "c" 86 | | `Bash -> "bash" 87 | | `CPP -> "c" 88 | | `Ascii -> "" 89 | | `Gas -> "gas" 90 | 91 | let typ_to_descr (lang:typ) = 92 | match lang with 93 | | `OCaml -> "OCaml" 94 | | `OCaml_toplevel -> "OCaml Utop" 95 | | `OCaml_rawtoplevel -> "OCaml Utop" 96 | | `Console -> "Terminal" 97 | | `JSON -> "JSON" 98 | | `ATD -> "ATD" 99 | | `Scheme -> "Scheme" 100 | | `OCaml_syntax -> "Syntax" 101 | | `Java -> "Java" 102 | | `C -> "C" 103 | | `Bash -> "Shell script" 104 | | `CPP -> "C" 105 | | `Ascii -> "Diagram" 106 | | `Gas -> "Assembly" 107 | 108 | let of_string s = 109 | try 110 | String.strip s |> Sexp.of_string |> t_of_sexp 111 | with exn -> 112 | eprintf "ERR: %s\n while parsing: %s\n%!" 113 | (Exn.to_string exn) s; raise exn 114 | 115 | (** Hunt through OCaml code and split out any comments that 116 | are of the form (* part X *) 117 | *) 118 | let extract_all_ocaml_parts filename buf = 119 | let rec iter part parts = 120 | function 121 | |line::lines when String.is_prefix ~prefix:"(* part " (String.lstrip line) -> 122 | let part = Caml.Scanf.sscanf (String.lstrip line) "(* part %d *)" (fun p -> p) in 123 | let parts = (part, (Buffer.create 100)) :: parts in 124 | iter part parts lines 125 | |line::lines -> begin 126 | match List.Assoc.find parts part with 127 | | Some buf -> 128 | Buffer.add_string buf line; 129 | Buffer.add_char buf '\n'; 130 | iter part parts lines 131 | | None -> 132 | eprintf "no part %d in %s\n\n%s%!" part filename buf; 133 | exit (-1) 134 | end 135 | |[] -> List.map parts ~f:(fun (a,b) -> (a, String.strip (Buffer.contents b))) 136 | in 137 | let parts = [ (0, Buffer.create 100) ] in 138 | iter 0 parts (String.split ~on:'\n' buf) 139 | 140 | let extract_ocaml_part filename part buf = 141 | let parts = extract_all_ocaml_parts filename buf in 142 | match List.Assoc.find parts part with 143 | | None -> eprintf "no part %d found in %s\n\n%s" part filename buf; exit (-1) 144 | | Some buf -> buf 145 | 146 | let run_through_pygmentize lang contents = 147 | let ic,oc = Unix.open_process (sprintf "pygmentize -l %s -f html" lang) in 148 | Out_channel.output_string oc contents; 149 | Out_channel.close oc; 150 | let html = Cow.Html.of_string (In_channel.input_all ic) in 151 | match html with 152 | |`El ((("","div"),[(("","class"),"highlight")]),[`El ((("","pre"),[]),data)]) :: _ -> 153 | <:html<
$data$
&>> 154 | |_ -> raise (Failure "unexpected pygments output: not
...")
155 | 
156 | (* concat toplevel phrases so that each toplevel phrase always starts with a ;; *)
157 | let concat_toplevel_phrases lines =
158 |   let combine l = String.concat ~sep:"\n" (List.rev l) in
159 |   List.fold_left lines ~init:(`output ([],[])) ~f:(fun state line ->
160 |       match state with
161 |       |`phrase (res,acc) -> begin
162 |           if String.is_suffix ~suffix:";;" line then
163 |             let res = combine (line::acc) :: res in
164 |             `output (res,[])
165 |           else `phrase (res,line::acc)
166 |         end
167 |       |`output (res,acc) -> begin
168 |           if String.is_prefix ~prefix:"#" line then begin
169 |             let res = combine acc :: res in
170 |             if String.is_suffix ~suffix:";;" line then 
171 |               `output ((line::res),[])
172 |             else `phrase (res,[line])
173 |           end else `output (res,line::acc)
174 |         end
175 |     )
176 |   |> (function
177 |       |`phrase _ -> failwith "unterminated phrase"
178 |       |`output (res,acc) -> List.rev (combine acc :: res))
179 |   |> List.filter ~f:(function |"" -> false |_ -> true)
180 | 
181 | let wrap_in_docbook_box ~part ~lang file buf =
182 |   let part =
183 |     match part with
184 |     | 0 -> []
185 |     | part -> <:html< (part $int:part$)>>
186 |   in
187 |   let dlang = typ_to_docbook_language lang in
188 |   let hlang = typ_to_descr lang in
189 |   let info = <:xml<$str:hlang$: $str:file$$part$
190 | $buf$
191 | >> in
192 |   Cow.Xml.to_string info
193 | 
194 | let wrap_in_pretty_box ~part ~lang file (buf:Cow.Xml.t) =
195 |   let repourl = sprintf "http://github.com/realworldocaml/examples/" in
196 |   let fileurl = sprintf "http://github.com/realworldocaml/examples/blob/master/code/%s" file in
197 |   let typ = typ_to_descr lang in
198 |   let part =
199 |     match part with
200 |     | 0 -> []
201 |     | part -> <:html<, continued (part $int:part$)>>
202 |   in
203 |   let info = <:html<$str:typ$ ∗ $str:file$ $part$ ∗ all code&>> in
204 |   <:html<
$buf$
$info$
&>> 205 | -------------------------------------------------------------------------------- /lib/para_frag.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Copyright (c) 2012-2013 Anil Madhavapeddy 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | * 16 | *) 17 | 18 | open Core.Std 19 | 20 | type t = { 21 | file: string; 22 | html: string; 23 | } with sexp 24 | 25 | type ts = (string * t) list with sexp 26 | -------------------------------------------------------------------------------- /lib/rwo.mllib: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: 991dd047b6a96a4a4014a752a25533fd) 3 | Code_frag 4 | Para_frag 5 | Xml_tree 6 | # OASIS_STOP 7 | -------------------------------------------------------------------------------- /lib/xml_tree.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Copyright (c) 2012-2013 Anil Madhavapeddy 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | * 16 | *) 17 | 18 | open Core.Std 19 | 20 | let in_tree i = 21 | let el tag children = `El (tag, children) in 22 | let data d = `Data d in 23 | Xmlm.input_doc_tree ~el ~data i 24 | 25 | let read_document chan : (Xmlm.dtd * Cow.Xml.t) = 26 | let i = Xmlm.make_input (`Channel chan) in 27 | let (dtd,doc) = in_tree i in 28 | (dtd, [doc]) 29 | 30 | let out_tree o t = 31 | let frag = function 32 | | `El (tag, childs) -> `El (tag, childs) 33 | | `Data d -> `Data d in 34 | Xmlm.output_doc_tree frag o t 35 | 36 | let write_document chan dtd doc = 37 | let o = Xmlm.make_output ~decl:false (`Channel chan) in 38 | match doc with 39 | |[] -> () 40 | |[hd] -> out_tree o (dtd, hd) 41 | |hd::tl -> 42 | out_tree o (dtd, hd); 43 | List.iter ~f:(fun t -> out_tree o (None, t)) tl 44 | 45 | let mk_tag ?(attrs=[]) tag_name contents = 46 | let attrs : Xmlm.attribute list = List.map ~f:(fun (k,v) -> ("",k),v) attrs in 47 | let tag = ("", tag_name), attrs in 48 | `El (tag, contents) 49 | 50 | let rec map ~tag ~f (i:Cow.Xml.t) : Cow.Xml.t = 51 | List.concat ( 52 | List.map i ~f:( 53 | function 54 | | `El ((("",t),attr),c) when t=tag -> f attr c 55 | | `El (p,c) -> [`El (p, (map ~tag ~f c))] 56 | | `Data x -> [`Data x] 57 | ) 58 | ) 59 | 60 | let rec iter ~tag ~f (i:Cow.Xml.t) : unit = 61 | List.iter i ~f:( 62 | function 63 | | `El ((("",t),attr),c) when t=tag -> f attr c 64 | | `El (_,c) -> iter ~tag ~f c 65 | | `Data _ -> () 66 | ) 67 | 68 | let to_string c = 69 | match c with 70 | | `Data x -> x 71 | | `El _ -> failwith "Xml_tree.filter_string: encounter tag in string" 72 | -------------------------------------------------------------------------------- /myocamlbuild.ml: -------------------------------------------------------------------------------- 1 | (* OASIS_START *) 2 | (* DO NOT EDIT (digest: 6c97edafe358103cc69ebffb00389236) *) 3 | module OASISGettext = struct 4 | (* # 21 "src/oasis/OASISGettext.ml" *) 5 | 6 | let ns_ str = 7 | str 8 | 9 | let s_ str = 10 | str 11 | 12 | let f_ (str : ('a, 'b, 'c, 'd) format4) = 13 | str 14 | 15 | let fn_ fmt1 fmt2 n = 16 | if n = 1 then 17 | fmt1^^"" 18 | else 19 | fmt2^^"" 20 | 21 | let init = 22 | [] 23 | 24 | end 25 | 26 | module OASISExpr = struct 27 | (* # 21 "src/oasis/OASISExpr.ml" *) 28 | 29 | 30 | 31 | open OASISGettext 32 | 33 | type test = string 34 | 35 | type flag = string 36 | 37 | type t = 38 | | EBool of bool 39 | | ENot of t 40 | | EAnd of t * t 41 | | EOr of t * t 42 | | EFlag of flag 43 | | ETest of test * string 44 | 45 | 46 | type 'a choices = (t * 'a) list 47 | 48 | let eval var_get t = 49 | let rec eval' = 50 | function 51 | | EBool b -> 52 | b 53 | 54 | | ENot e -> 55 | not (eval' e) 56 | 57 | | EAnd (e1, e2) -> 58 | (eval' e1) && (eval' e2) 59 | 60 | | EOr (e1, e2) -> 61 | (eval' e1) || (eval' e2) 62 | 63 | | EFlag nm -> 64 | let v = 65 | var_get nm 66 | in 67 | assert(v = "true" || v = "false"); 68 | (v = "true") 69 | 70 | | ETest (nm, vl) -> 71 | let v = 72 | var_get nm 73 | in 74 | (v = vl) 75 | in 76 | eval' t 77 | 78 | let choose ?printer ?name var_get lst = 79 | let rec choose_aux = 80 | function 81 | | (cond, vl) :: tl -> 82 | if eval var_get cond then 83 | vl 84 | else 85 | choose_aux tl 86 | | [] -> 87 | let str_lst = 88 | if lst = [] then 89 | s_ "" 90 | else 91 | String.concat 92 | (s_ ", ") 93 | (List.map 94 | (fun (cond, vl) -> 95 | match printer with 96 | | Some p -> p vl 97 | | None -> s_ "") 98 | lst) 99 | in 100 | match name with 101 | | Some nm -> 102 | failwith 103 | (Printf.sprintf 104 | (f_ "No result for the choice list '%s': %s") 105 | nm str_lst) 106 | | None -> 107 | failwith 108 | (Printf.sprintf 109 | (f_ "No result for a choice list: %s") 110 | str_lst) 111 | in 112 | choose_aux (List.rev lst) 113 | 114 | end 115 | 116 | 117 | # 117 "myocamlbuild.ml" 118 | module BaseEnvLight = struct 119 | (* # 21 "src/base/BaseEnvLight.ml" *) 120 | 121 | module MapString = Map.Make(String) 122 | 123 | type t = string MapString.t 124 | 125 | let default_filename = 126 | Filename.concat 127 | (Sys.getcwd ()) 128 | "setup.data" 129 | 130 | let load ?(allow_empty=false) ?(filename=default_filename) () = 131 | if Sys.file_exists filename then 132 | begin 133 | let chn = 134 | open_in_bin filename 135 | in 136 | let st = 137 | Stream.of_channel chn 138 | in 139 | let line = 140 | ref 1 141 | in 142 | let st_line = 143 | Stream.from 144 | (fun _ -> 145 | try 146 | match Stream.next st with 147 | | '\n' -> incr line; Some '\n' 148 | | c -> Some c 149 | with Stream.Failure -> None) 150 | in 151 | let lexer = 152 | Genlex.make_lexer ["="] st_line 153 | in 154 | let rec read_file mp = 155 | match Stream.npeek 3 lexer with 156 | | [Genlex.Ident nm; Genlex.Kwd "="; Genlex.String value] -> 157 | Stream.junk lexer; 158 | Stream.junk lexer; 159 | Stream.junk lexer; 160 | read_file (MapString.add nm value mp) 161 | | [] -> 162 | mp 163 | | _ -> 164 | failwith 165 | (Printf.sprintf 166 | "Malformed data file '%s' line %d" 167 | filename !line) 168 | in 169 | let mp = 170 | read_file MapString.empty 171 | in 172 | close_in chn; 173 | mp 174 | end 175 | else if allow_empty then 176 | begin 177 | MapString.empty 178 | end 179 | else 180 | begin 181 | failwith 182 | (Printf.sprintf 183 | "Unable to load environment, the file '%s' doesn't exist." 184 | filename) 185 | end 186 | 187 | let var_get name env = 188 | let rec var_expand str = 189 | let buff = 190 | Buffer.create ((String.length str) * 2) 191 | in 192 | Buffer.add_substitute 193 | buff 194 | (fun var -> 195 | try 196 | var_expand (MapString.find var env) 197 | with Not_found -> 198 | failwith 199 | (Printf.sprintf 200 | "No variable %s defined when trying to expand %S." 201 | var 202 | str)) 203 | str; 204 | Buffer.contents buff 205 | in 206 | var_expand (MapString.find name env) 207 | 208 | let var_choose lst env = 209 | OASISExpr.choose 210 | (fun nm -> var_get nm env) 211 | lst 212 | end 213 | 214 | 215 | # 215 "myocamlbuild.ml" 216 | module MyOCamlbuildFindlib = struct 217 | (* # 21 "src/plugins/ocamlbuild/MyOCamlbuildFindlib.ml" *) 218 | 219 | (** OCamlbuild extension, copied from 220 | * http://brion.inria.fr/gallium/index.php/Using_ocamlfind_with_ocamlbuild 221 | * by N. Pouillard and others 222 | * 223 | * Updated on 2009/02/28 224 | * 225 | * Modified by Sylvain Le Gall 226 | *) 227 | open Ocamlbuild_plugin 228 | 229 | (* these functions are not really officially exported *) 230 | let run_and_read = 231 | Ocamlbuild_pack.My_unix.run_and_read 232 | 233 | let blank_sep_strings = 234 | Ocamlbuild_pack.Lexers.blank_sep_strings 235 | 236 | let split s ch = 237 | let x = 238 | ref [] 239 | in 240 | let rec go s = 241 | let pos = 242 | String.index s ch 243 | in 244 | x := (String.before s pos)::!x; 245 | go (String.after s (pos + 1)) 246 | in 247 | try 248 | go s 249 | with Not_found -> !x 250 | 251 | let split_nl s = split s '\n' 252 | 253 | let before_space s = 254 | try 255 | String.before s (String.index s ' ') 256 | with Not_found -> s 257 | 258 | (* this lists all supported packages *) 259 | let find_packages () = 260 | List.map before_space (split_nl & run_and_read "ocamlfind list") 261 | 262 | (* this is supposed to list available syntaxes, but I don't know how to do it. *) 263 | let find_syntaxes () = ["camlp4o"; "camlp4r"] 264 | 265 | (* ocamlfind command *) 266 | let ocamlfind x = S[A"ocamlfind"; x] 267 | 268 | let dispatch = 269 | function 270 | | Before_options -> 271 | (* by using Before_options one let command line options have an higher priority *) 272 | (* on the contrary using After_options will guarantee to have the higher priority *) 273 | (* override default commands by ocamlfind ones *) 274 | Options.ocamlc := ocamlfind & A"ocamlc"; 275 | Options.ocamlopt := ocamlfind & A"ocamlopt"; 276 | Options.ocamldep := ocamlfind & A"ocamldep"; 277 | Options.ocamldoc := ocamlfind & A"ocamldoc"; 278 | Options.ocamlmktop := ocamlfind & A"ocamlmktop" 279 | 280 | | After_rules -> 281 | 282 | (* When one link an OCaml library/binary/package, one should use -linkpkg *) 283 | flag ["ocaml"; "link"; "program"] & A"-linkpkg"; 284 | 285 | (* For each ocamlfind package one inject the -package option when 286 | * compiling, computing dependencies, generating documentation and 287 | * linking. *) 288 | List.iter 289 | begin fun pkg -> 290 | flag ["ocaml"; "compile"; "pkg_"^pkg] & S[A"-package"; A pkg]; 291 | flag ["ocaml"; "ocamldep"; "pkg_"^pkg] & S[A"-package"; A pkg]; 292 | flag ["ocaml"; "doc"; "pkg_"^pkg] & S[A"-package"; A pkg]; 293 | flag ["ocaml"; "link"; "pkg_"^pkg] & S[A"-package"; A pkg]; 294 | flag ["ocaml"; "infer_interface"; "pkg_"^pkg] & S[A"-package"; A pkg]; 295 | end 296 | (find_packages ()); 297 | 298 | (* Like -package but for extensions syntax. Morover -syntax is useless 299 | * when linking. *) 300 | List.iter begin fun syntax -> 301 | flag ["ocaml"; "compile"; "syntax_"^syntax] & S[A"-syntax"; A syntax]; 302 | flag ["ocaml"; "ocamldep"; "syntax_"^syntax] & S[A"-syntax"; A syntax]; 303 | flag ["ocaml"; "doc"; "syntax_"^syntax] & S[A"-syntax"; A syntax]; 304 | flag ["ocaml"; "infer_interface"; "syntax_"^syntax] & S[A"-syntax"; A syntax]; 305 | end (find_syntaxes ()); 306 | 307 | (* The default "thread" tag is not compatible with ocamlfind. 308 | * Indeed, the default rules add the "threads.cma" or "threads.cmxa" 309 | * options when using this tag. When using the "-linkpkg" option with 310 | * ocamlfind, this module will then be added twice on the command line. 311 | * 312 | * To solve this, one approach is to add the "-thread" option when using 313 | * the "threads" package using the previous plugin. 314 | *) 315 | flag ["ocaml"; "pkg_threads"; "compile"] (S[A "-thread"]); 316 | flag ["ocaml"; "pkg_threads"; "doc"] (S[A "-I"; A "+threads"]); 317 | flag ["ocaml"; "pkg_threads"; "link"] (S[A "-thread"]); 318 | flag ["ocaml"; "pkg_threads"; "infer_interface"] (S[A "-thread"]) 319 | 320 | | _ -> 321 | () 322 | 323 | end 324 | 325 | module MyOCamlbuildBase = struct 326 | (* # 21 "src/plugins/ocamlbuild/MyOCamlbuildBase.ml" *) 327 | 328 | (** Base functions for writing myocamlbuild.ml 329 | @author Sylvain Le Gall 330 | *) 331 | 332 | 333 | 334 | open Ocamlbuild_plugin 335 | module OC = Ocamlbuild_pack.Ocaml_compiler 336 | 337 | type dir = string 338 | type file = string 339 | type name = string 340 | type tag = string 341 | 342 | (* # 56 "src/plugins/ocamlbuild/MyOCamlbuildBase.ml" *) 343 | 344 | type t = 345 | { 346 | lib_ocaml: (name * dir list) list; 347 | lib_c: (name * dir * file list) list; 348 | flags: (tag list * (spec OASISExpr.choices)) list; 349 | (* Replace the 'dir: include' from _tags by a precise interdepends in 350 | * directory. 351 | *) 352 | includes: (dir * dir list) list; 353 | } 354 | 355 | let env_filename = 356 | Pathname.basename 357 | BaseEnvLight.default_filename 358 | 359 | let dispatch_combine lst = 360 | fun e -> 361 | List.iter 362 | (fun dispatch -> dispatch e) 363 | lst 364 | 365 | let tag_libstubs nm = 366 | "use_lib"^nm^"_stubs" 367 | 368 | let nm_libstubs nm = 369 | nm^"_stubs" 370 | 371 | let dispatch t e = 372 | let env = 373 | BaseEnvLight.load 374 | ~filename:env_filename 375 | ~allow_empty:true 376 | () 377 | in 378 | match e with 379 | | Before_options -> 380 | let no_trailing_dot s = 381 | if String.length s >= 1 && s.[0] = '.' then 382 | String.sub s 1 ((String.length s) - 1) 383 | else 384 | s 385 | in 386 | List.iter 387 | (fun (opt, var) -> 388 | try 389 | opt := no_trailing_dot (BaseEnvLight.var_get var env) 390 | with Not_found -> 391 | Printf.eprintf "W: Cannot get variable %s" var) 392 | [ 393 | Options.ext_obj, "ext_obj"; 394 | Options.ext_lib, "ext_lib"; 395 | Options.ext_dll, "ext_dll"; 396 | ] 397 | 398 | | After_rules -> 399 | (* Declare OCaml libraries *) 400 | List.iter 401 | (function 402 | | nm, [] -> 403 | ocaml_lib nm 404 | | nm, dir :: tl -> 405 | ocaml_lib ~dir:dir (dir^"/"^nm); 406 | List.iter 407 | (fun dir -> 408 | List.iter 409 | (fun str -> 410 | flag ["ocaml"; "use_"^nm; str] (S[A"-I"; P dir])) 411 | ["compile"; "infer_interface"; "doc"]) 412 | tl) 413 | t.lib_ocaml; 414 | 415 | (* Declare directories dependencies, replace "include" in _tags. *) 416 | List.iter 417 | (fun (dir, include_dirs) -> 418 | Pathname.define_context dir include_dirs) 419 | t.includes; 420 | 421 | (* Declare C libraries *) 422 | List.iter 423 | (fun (lib, dir, headers) -> 424 | (* Handle C part of library *) 425 | flag ["link"; "library"; "ocaml"; "byte"; tag_libstubs lib] 426 | (S[A"-dllib"; A("-l"^(nm_libstubs lib)); A"-cclib"; 427 | A("-l"^(nm_libstubs lib))]); 428 | 429 | flag ["link"; "library"; "ocaml"; "native"; tag_libstubs lib] 430 | (S[A"-cclib"; A("-l"^(nm_libstubs lib))]); 431 | 432 | flag ["link"; "program"; "ocaml"; "byte"; tag_libstubs lib] 433 | (S[A"-dllib"; A("dll"^(nm_libstubs lib))]); 434 | 435 | (* When ocaml link something that use the C library, then one 436 | need that file to be up to date. 437 | *) 438 | dep ["link"; "ocaml"; "program"; tag_libstubs lib] 439 | [dir/"lib"^(nm_libstubs lib)^"."^(!Options.ext_lib)]; 440 | 441 | dep ["compile"; "ocaml"; "program"; tag_libstubs lib] 442 | [dir/"lib"^(nm_libstubs lib)^"."^(!Options.ext_lib)]; 443 | 444 | (* TODO: be more specific about what depends on headers *) 445 | (* Depends on .h files *) 446 | dep ["compile"; "c"] 447 | headers; 448 | 449 | (* Setup search path for lib *) 450 | flag ["link"; "ocaml"; "use_"^lib] 451 | (S[A"-I"; P(dir)]); 452 | ) 453 | t.lib_c; 454 | 455 | (* Add flags *) 456 | List.iter 457 | (fun (tags, cond_specs) -> 458 | let spec = 459 | BaseEnvLight.var_choose cond_specs env 460 | in 461 | flag tags & spec) 462 | t.flags 463 | | _ -> 464 | () 465 | 466 | let dispatch_default t = 467 | dispatch_combine 468 | [ 469 | dispatch t; 470 | MyOCamlbuildFindlib.dispatch; 471 | ] 472 | 473 | end 474 | 475 | 476 | # 476 "myocamlbuild.ml" 477 | open Ocamlbuild_plugin;; 478 | let package_default = 479 | { 480 | MyOCamlbuildBase.lib_ocaml = [("rwo", ["lib"])]; 481 | lib_c = []; 482 | flags = []; 483 | includes = [("bin", ["lib"])]; 484 | } 485 | ;; 486 | 487 | let dispatch_default = MyOCamlbuildBase.dispatch_default package_default;; 488 | 489 | # 490 "myocamlbuild.ml" 490 | (* OASIS_STOP *) 491 | Ocamlbuild_plugin.dispatch dispatch_default;; 492 | -------------------------------------------------------------------------------- /setup.ml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realworldocaml/scripts/c21a19f0d407a27472c11fc9e3bf00b67d3b644e/setup.ml --------------------------------------------------------------------------------