Source

mutated_ocaml / Build.ml

#!/usr/bin/env ocaml
#load "unix.cma"
#load "str.cma"

open Printf

(** You can use [home] if necessary. *)
let home = Sys.getenv "HOME"

(******************************************** SETTINGS. PLEASE EDIT THESE!!! *)

(* Android NDK settings *)

(** The directory of the NDK, where you can find ``ndk-buid'' *)
let ndk_home = sprintf "%s/.share/opt/android-ndk-r8" home

(** The host architecture, one of your Computer. 
    ex. "linux-x86", "darwin-x86"
*)
let host_arch = "linux-x86"

(** The target architecture, one of your Android. 
    Only "arm" is supported for now. *)
let target_arch = "arm"

(** The platform version. "14" is for android-14 for example. *)
let platform_version = "14"

(** The version of GCC in the NDK *)
let gcc_version = "4.4.3"

(* Installation directory *)

(** Where to install the OCaml cross compilation environment.
    NOTE: Do not use typical directory such as /usr/bin/, /usr/local/bin, etc.
    The compiler being installed does NOT produce execs for your Computer,
    but your Android phone!
*)
let prefix = sprintf "%s/.share/prefix/ocaml-android" home

(********************************************************** END OF SETTINGS. *)

let failwithf fmt = Printf.kprintf failwith fmt

module String = struct
  include String

  let chop_newline s =
    let len = String.length s in
    if len > 1 && s.[len-1] = '\n' then
      if len > 2 && s.[len-2] = '\r' then String.sub s 0 (len-2)
      else String.sub s 0 (len-1)
    else s

  let split_by_newline s =
    let length = String.length s in
    let rec aux st start_pos pos = 
      if pos = length then List.rev st else match s.[pos] with
      | '\r' | '\n' -> 
          let st = String.sub s start_pos (pos - start_pos) :: st in
          skip st (pos+1)
      | _ -> aux st start_pos (pos+1)
    and skip st pos = 
      if pos = length then List.rev st else match s.[pos] with
      | '\r' | '\n' -> skip st (pos+1)
      | _ -> aux st pos (pos+1)
    in
    aux [] 0 0
end

module Unix = struct
  include Unix

  let exec cmd =
    let ic, oc = Unix.open_process cmd in
    let buf = Buffer.create 100 in
    begin
      try while true do Buffer.add_channel buf ic 1 done with End_of_file -> ()
    end;
    let _ = Unix.close_process (ic, oc) in
    Buffer.contents buf

  (* CR jfuruse: Only works for Unix env. Die Systems with "\r\n"! *)
  let which cmd = match String.chop_newline (exec (Printf.sprintf "which %s" cmd)) with
    | "" -> None
    | s -> Some s
end

module Test = struct
  let program s = match Unix.which s with
    | Some s -> s
    | None -> failwithf "Program %s is not found" s

  let must_exist dir name = 
    if not (Sys.file_exists (sprintf "%s/%s" dir name)) then
      failwithf "%s is not found at %s" dir name
    else printf "%s found in %s\n%!" name dir
end

module Sys = struct
  include Sys
  let must fmt = Printf.kprintf (fun s -> assert (Sys.command s = 0)) fmt
  let cp = must "cp %s %s"
  let patch_p1 = must "patch -p1 < %s"
end

module At = struct
  (** Small @var@ replacement language *)
    
  let replace_variables tbl =
    let rex = Str.regexp "@[A-Za-z0-9_]+@" in
    let replace = 
      Str.global_substitute rex (fun s ->
        let matched = Str.matched_string s in
        let k = String.sub matched 1 (String.length matched - 2) in
        try List.assoc k tbl with Not_found -> 
          failwithf "Variable %s is not found" s)
    in
    replace
  
  let replace_file tbl path outpath =
    let ic = open_in path in
    let oc = open_out outpath in
    let rec loop () = 
      try 
        let line = input_line ic in
        output_string oc (replace_variables tbl line);
        output_char oc '\n';
        loop ()
      with
      | End_of_file -> close_in ic; close_out oc
    in
    loop ()
end

module Configure(A:sig end) = struct
  (* Host tools: ocamlrun and ocamlyacc must exist in the path. *)
  let ocamlrun  = Test.program "ocamlrun"
  let ocamlyacc = Test.program "ocamlyacc"
  let _ = printf "Host compiler tools:\n%!"
  let _ = printf "ocamlrun:  %s\n%!" ocamlrun
  let _ = printf "ocamlyacc: %s\n%!" ocamlyacc
  
  (* Android SDK compiler check *)
  
  let cross_tool_dir = 
    sprintf "%s/toolchains/%s-linux-androideabi-%s/prebuilt/%s" 
      ndk_home target_arch gcc_version host_arch
  
  let libgcc_dir = 
    sprintf "%s/toolchains/%s-linux-androideabi-%s/prebuilt/%s/lib/gcc/%s-linux-androideabi/%s" 
      ndk_home target_arch gcc_version host_arch target_arch gcc_version
  
  let libc_dir = 
    sprintf "%s/platforms/android-%s/arch-%s/usr/lib"
      ndk_home platform_version target_arch 
  
  let target_root_dir = 
    sprintf "%s/platforms/android-%s/arch-%s"
      ndk_home platform_version target_arch 
  
  let _ = 
    Test.must_exist ndk_home "ndk-build";
    Test.must_exist cross_tool_dir (sprintf "bin/%s-linux-androideabi-gcc" target_arch);
    Test.must_exist libgcc_dir "libgcc.a";
    Test.must_exist libc_dir "libc.so";
    Test.must_exist target_root_dir "usr/include/stdlib.h"
  
  (* Create config/Makefile *)
  let tbl = [ "prefix"          , prefix
            ; "cross_tool_dir"  , cross_tool_dir
            ; "libgcc_dir"      , libgcc_dir
            ; "target_root_dir" , target_root_dir
            ]
  
  let _ = At.replace_file tbl "config/Makefile.android.temp" "config/Makefile"
  
  (* Copy stdlib.h *)
  let _ =
    let path = sprintf "%s/usr/include/stdlib.h" target_root_dir in
    printf "Copying NDK stdlib.h: %s\n%!" path;
    Sys.cp (sprintf "%s/usr/include/stdlib.h" target_root_dir) "byterun"
  
  (* Patch stdlib.h *)
  let _ =
    printf "Patching copied stdlib.h...\n%!";
    Sys.patch_p1 "ocaml-android-3.12.1-stdlib_h.patch"

  let _ = printf "Configuration done\n%!"
end

module Build(A:sig end) = struct
  module C = Configure(struct end)
  open C

  (* Build target ocamlrun *)
  let _ =
    printf "Building target ocamlrun (byterun/ocamlrun.target)\n%!";
    Sys.must "(cd byterun; make all)";
    Unix.rename "byterun/ocamlrun" "byterun/ocamlrun.target"
  
  (* Build target ocamlyacc *)
  let _ =
    printf "Building target ocamlyacc (yacc/ocamlyacc.target)\n%!";
    Sys.must "(cd yacc; make all)";
    Unix.rename "yacc/ocamlyacc" "yacc/ocamlyacc.target"
  
  (* Recovering host ocamlrun and ocamlyacc *)
  let _ =
    printf "Recovering host ocamlrun and ocamlyacc\n%!";
    Sys.must "cp %s byterun" ocamlrun;
    Sys.must "cp %s yacc"    ocamlyacc
  
  (* Make world and opt *)
  let _ =
    printf "Now building!\n%!";
    Sys.must "make world opt";
    printf "Done!!\n%!"
end

let _ = 
  let print_help () = prerr_endline 
"ocaml Build.ml <command>

  commands:
    config    Test the configuration written in Build.ml
    build     Build the system
    clean     Clean the build
"
  in
  if Array.length Sys.argv = 1 then print_help ()
  else match Sys.argv.(1) with
  | "config" -> 
      let module M = Configure(struct end) in
      ()
  | "build" ->
      let module M = Build(struct end) in
      ()
  | "clean" -> Sys.must "make clean"
  | s ->
      failwithf "Unknown command %s" s
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.