SHOW:
|
|
- or go back to the newest paste.
| 1 | - | ("-swf-script-limits",Arg.String (fun sl ->
|
| 1 | + | (* |
| 2 | * Haxe Compiler | |
| 3 | - | swf_script_limits := Some (match ExtString.String.nsplit sl ":" with |
| 3 | + | * Copyright (c)2005-2008 Nicolas Cannasse |
| 4 | * | |
| 5 | * This program is free software; you can redistribute it and/or modify | |
| 6 | * it under the terms of the GNU General Public License as published by | |
| 7 | * the Free Software Foundation; either version 2 of the License, or | |
| 8 | - | ),"<swf script limits> : define SWF script limits (recursion depth:script timeout)"); |
| 8 | + | * (at your option) any later version. |
| 9 | * | |
| 10 | * This program is distributed in the hope that it will be useful, | |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 13 | * GNU General Public License for more details. | |
| 14 | * | |
| 15 | * You should have received a copy of the GNU General Public License | |
| 16 | * along with this program; if not, write to the Free Software | |
| 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| 18 | *) | |
| 19 | open Printf | |
| 20 | open Genswf | |
| 21 | open Common | |
| 22 | open Type | |
| 23 | ||
| 24 | type context = {
| |
| 25 | com : Common.context; | |
| 26 | mutable flush : unit -> unit; | |
| 27 | mutable setup : unit -> unit; | |
| 28 | mutable messages : string list; | |
| 29 | mutable has_next : bool; | |
| 30 | mutable has_error : bool; | |
| 31 | } | |
| 32 | ||
| 33 | type cache = {
| |
| 34 | mutable c_haxelib : (string list, string list) Hashtbl.t; | |
| 35 | mutable c_files : (string, float * Ast.package) Hashtbl.t; | |
| 36 | mutable c_modules : (path * string, module_def) Hashtbl.t; | |
| 37 | } | |
| 38 | ||
| 39 | exception Abort | |
| 40 | exception Completion of string | |
| 41 | ||
| 42 | let version = 211 | |
| 43 | ||
| 44 | let measure_times = ref false | |
| 45 | let prompt = ref false | |
| 46 | let start_time = ref (get_time()) | |
| 47 | let global_cache = ref None | |
| 48 | ||
| 49 | let executable_path() = | |
| 50 | Extc.executable_path() | |
| 51 | ||
| 52 | let format msg p = | |
| 53 | if p = Ast.null_pos then | |
| 54 | msg | |
| 55 | else begin | |
| 56 | let error_printer file line = sprintf "%s:%d:" file line in | |
| 57 | let epos = Lexer.get_error_pos error_printer p in | |
| 58 | let msg = String.concat ("\n" ^ epos ^ " : ") (ExtString.String.nsplit msg "\n") in
| |
| 59 | sprintf "%s : %s" epos msg | |
| 60 | end | |
| 61 | ||
| 62 | let ssend sock str = | |
| 63 | let rec loop pos len = | |
| 64 | if len = 0 then | |
| 65 | () | |
| 66 | else | |
| 67 | let s = Unix.send sock str pos len [] in | |
| 68 | loop (pos + s) (len - s) | |
| 69 | in | |
| 70 | loop 0 (String.length str) | |
| 71 | ||
| 72 | let message ctx msg p = | |
| 73 | ctx.messages <- format msg p :: ctx.messages | |
| 74 | ||
| 75 | let error ctx msg p = | |
| 76 | message ctx msg p; | |
| 77 | ctx.has_error <- true | |
| 78 | ||
| 79 | let htmlescape s = | |
| 80 | let s = String.concat "&" (ExtString.String.nsplit s "&") in | |
| 81 | let s = String.concat "<" (ExtString.String.nsplit s "<") in | |
| 82 | let s = String.concat ">" (ExtString.String.nsplit s ">") in | |
| 83 | s | |
| 84 | ||
| 85 | let reserved_flags = [ | |
| 86 | "cross";"flash8";"js";"neko";"flash";"php";"cpp";"cs";"java"; | |
| 87 | "as3";"swc";"macro";"sys" | |
| 88 | ] | |
| 89 | ||
| 90 | let complete_fields fields = | |
| 91 | let b = Buffer.create 0 in | |
| 92 | Buffer.add_string b "<list>\n"; | |
| 93 | List.iter (fun (n,t,d) -> | |
| 94 | Buffer.add_string b (Printf.sprintf "<i n=\"%s\"><t>%s</t><d>%s</d></i>\n" n (htmlescape t) (htmlescape d)) | |
| 95 | ) (List.sort (fun (a,_,_) (b,_,_) -> compare a b) fields); | |
| 96 | Buffer.add_string b "</list>\n"; | |
| 97 | raise (Completion (Buffer.contents b)) | |
| 98 | ||
| 99 | let report_times print = | |
| 100 | let tot = ref 0. in | |
| 101 | Hashtbl.iter (fun _ t -> tot := !tot +. t.total) Common.htimers; | |
| 102 | print (Printf.sprintf "Total time : %.3fs" !tot); | |
| 103 | if !tot > 0. then begin | |
| 104 | print "------------------------------------"; | |
| 105 | let timers = List.sort (fun t1 t2 -> compare t1.name t2.name) (Hashtbl.fold (fun _ t acc -> t :: acc) Common.htimers []) in | |
| 106 | List.iter (fun t -> print (Printf.sprintf " %s : %.3fs, %.0f%%" t.name t.total (t.total *. 100. /. !tot))) timers | |
| 107 | end | |
| 108 | ||
| 109 | let make_path f = | |
| 110 | let f = String.concat "/" (ExtString.String.nsplit f "\\") in | |
| 111 | let cl = ExtString.String.nsplit f "." in | |
| 112 | let cl = (match List.rev cl with | |
| 113 | | ["hx";path] -> ExtString.String.nsplit path "/" | |
| 114 | | _ -> cl | |
| 115 | ) in | |
| 116 | let error() = failwith ("Invalid class name " ^ f) in
| |
| 117 | let invalid_char x = | |
| 118 | for i = 1 to String.length x - 1 do | |
| 119 | match x.[i] with | |
| 120 | | 'A'..'Z' | 'a'..'z' | '0'..'9' | '_' -> () | |
| 121 | | _ -> error() | |
| 122 | done; | |
| 123 | false | |
| 124 | in | |
| 125 | let rec loop = function | |
| 126 | | [] -> error() | |
| 127 | | [x] -> if String.length x = 0 || not (x.[0] = '_' || (x.[0] >= 'A' && x.[0] <= 'Z')) || invalid_char x then error() else [] , x | |
| 128 | | x :: l -> | |
| 129 | if String.length x = 0 || x.[0] < 'a' || x.[0] > 'z' || invalid_char x then error() else | |
| 130 | let path , name = loop l in | |
| 131 | x :: path , name | |
| 132 | in | |
| 133 | loop cl | |
| 134 | ||
| 135 | let unique l = | |
| 136 | let rec _unique = function | |
| 137 | | [] -> [] | |
| 138 | | x1 :: x2 :: l when x1 = x2 -> _unique (x2 :: l) | |
| 139 | | x :: l -> x :: _unique l | |
| 140 | in | |
| 141 | _unique (List.sort compare l) | |
| 142 | ||
| 143 | let rec read_type_path com p = | |
| 144 | let classes = ref [] in | |
| 145 | let packages = ref [] in | |
| 146 | let p = (match p with | |
| 147 | | x :: l -> | |
| 148 | (try | |
| 149 | match PMap.find x com.package_rules with | |
| 150 | | Directory d -> d :: l | |
| 151 | | Remap s -> s :: l | |
| 152 | | _ -> p | |
| 153 | with | |
| 154 | Not_found -> p) | |
| 155 | | _ -> p | |
| 156 | ) in | |
| 157 | List.iter (fun path -> | |
| 158 | let dir = path ^ String.concat "/" p in | |
| 159 | let r = (try Sys.readdir dir with _ -> [||]) in | |
| 160 | Array.iter (fun f -> | |
| 161 | if (try (Unix.stat (dir ^ "/" ^ f)).Unix.st_kind = Unix.S_DIR with _ -> false) then begin | |
| 162 | if f.[0] >= 'a' && f.[0] <= 'z' then begin | |
| 163 | if p = ["."] then | |
| 164 | match read_type_path com [f] with | |
| 165 | | [] , [] -> () | |
| 166 | | _ -> | |
| 167 | try | |
| 168 | match PMap.find f com.package_rules with | |
| 169 | | Forbidden -> () | |
| 170 | | Remap f -> packages := f :: !packages | |
| 171 | | Directory _ -> raise Not_found | |
| 172 | with Not_found -> | |
| 173 | packages := f :: !packages | |
| 174 | else | |
| 175 | packages := f :: !packages | |
| 176 | end; | |
| 177 | end else if file_extension f = "hx" then begin | |
| 178 | let c = Filename.chop_extension f in | |
| 179 | if String.length c < 2 || String.sub c (String.length c - 2) 2 <> "__" then classes := c :: !classes; | |
| 180 | end; | |
| 181 | ) r; | |
| 182 | ) com.class_path; | |
| 183 | List.iter (fun (_,_,extract) -> | |
| 184 | Hashtbl.iter (fun (path,name) _ -> | |
| 185 | if path = p then classes := name :: !classes else | |
| 186 | let rec loop p1 p2 = | |
| 187 | match p1, p2 with | |
| 188 | | [], _ -> () | |
| 189 | | x :: _, [] -> packages := x :: !packages | |
| 190 | | a :: p1, b :: p2 -> if a = b then loop p1 p2 | |
| 191 | in | |
| 192 | loop path p | |
| 193 | ) (extract()); | |
| 194 | ) com.swf_libs; | |
| 195 | unique !packages, unique !classes | |
| 196 | ||
| 197 | let delete_file f = try Sys.remove f with _ -> () | |
| 198 | ||
| 199 | let expand_env ?(h=None) path = | |
| 200 | let r = Str.regexp "%\\([A-Za-z0-9_]+\\)%" in | |
| 201 | Str.global_substitute r (fun s -> | |
| 202 | let key = Str.matched_group 1 s in | |
| 203 | try | |
| 204 | Sys.getenv key | |
| 205 | with Not_found -> try | |
| 206 | match h with | |
| 207 | | None -> raise Not_found | |
| 208 | | Some h -> Hashtbl.find h key | |
| 209 | with Not_found -> | |
| 210 | "%" ^ key ^ "%" | |
| 211 | ) path | |
| 212 | ||
| 213 | let unquote v = | |
| 214 | let len = String.length v in | |
| 215 | if len > 0 && v.[0] = '"' && v.[len - 1] = '"' then String.sub v 1 (len - 2) else v | |
| 216 | ||
| 217 | let parse_hxml_data data = | |
| 218 | let lines = Str.split (Str.regexp "[\r\n]+") data in | |
| 219 | List.concat (List.map (fun l -> | |
| 220 | let l = unquote (ExtString.String.strip l) in | |
| 221 | if l = "" || l.[0] = '#' then | |
| 222 | [] | |
| 223 | else if l.[0] = '-' then | |
| 224 | try | |
| 225 | let a, b = ExtString.String.split l " " in | |
| 226 | [unquote a; unquote (ExtString.String.strip b)] | |
| 227 | with | |
| 228 | _ -> [l] | |
| 229 | else | |
| 230 | [l] | |
| 231 | ) lines) | |
| 232 | ||
| 233 | let parse_hxml file = | |
| 234 | let ch = IO.input_channel (try open_in_bin file with _ -> failwith ("File not found " ^ file)) in
| |
| 235 | let data = IO.read_all ch in | |
| 236 | IO.close_in ch; | |
| 237 | parse_hxml_data data | |
| 238 | ||
| 239 | let lookup_classes com spath = | |
| 240 | let rec loop = function | |
| 241 | | [] -> [] | |
| 242 | | cp :: l -> | |
| 243 | let cp = (if cp = "" then "./" else cp) in | |
| 244 | let c = normalize_path (Extc.get_real_path (Common.unique_full_path cp)) in | |
| 245 | let clen = String.length c in | |
| 246 | if clen < String.length spath && String.sub spath 0 clen = c then begin | |
| 247 | let path = String.sub spath clen (String.length spath - clen) in | |
| 248 | (try | |
| 249 | let path = make_path path in | |
| 250 | (match loop l with | |
| 251 | | [x] when String.length (Ast.s_type_path x) < String.length (Ast.s_type_path path) -> [x] | |
| 252 | | _ -> [path]) | |
| 253 | with _ -> loop l) | |
| 254 | end else | |
| 255 | loop l | |
| 256 | in | |
| 257 | loop com.class_path | |
| 258 | ||
| 259 | let add_libs com libs = | |
| 260 | let call_haxelib() = | |
| 261 | let t = Common.timer "haxelib" in | |
| 262 | let cmd = "haxelib path " ^ String.concat " " libs in | |
| 263 | let pin, pout, perr = Unix.open_process_full cmd (Unix.environment()) in | |
| 264 | let lines = Std.input_list pin in | |
| 265 | let err = Std.input_list perr in | |
| 266 | let ret = Unix.close_process_full (pin,pout,perr) in | |
| 267 | if ret <> Unix.WEXITED 0 then failwith (match lines, err with | |
| 268 | | [], [] -> "Failed to call haxelib (command not found ?)" | |
| 269 | | [], [s] when ExtString.String.ends_with (ExtString.String.strip s) "Module not found : path" -> "The haxelib command has been strip'ed, please install it again" | |
| 270 | | _ -> String.concat "\n" (lines@err)); | |
| 271 | t(); | |
| 272 | lines | |
| 273 | in | |
| 274 | match libs with | |
| 275 | | [] -> [] | |
| 276 | | _ -> | |
| 277 | let lines = match !global_cache with | |
| 278 | | Some cache -> | |
| 279 | (try | |
| 280 | (* if we are compiling, really call haxelib since library path might have changed *) | |
| 281 | if not com.display then raise Not_found; | |
| 282 | Hashtbl.find cache.c_haxelib libs | |
| 283 | with Not_found -> | |
| 284 | let lines = call_haxelib() in | |
| 285 | Hashtbl.replace cache.c_haxelib libs lines; | |
| 286 | lines) | |
| 287 | | _ -> call_haxelib() | |
| 288 | in | |
| 289 | let extra_args = ref [] in | |
| 290 | let lines = List.fold_left (fun acc l -> | |
| 291 | let l = ExtString.String.strip l in | |
| 292 | if l = "" then acc else | |
| 293 | if l.[0] <> '-' then l :: acc else | |
| 294 | match (try ExtString.String.split l " " with _ -> l, "") with | |
| 295 | | ("-L",dir) ->
| |
| 296 | com.neko_libs <- String.sub l 3 (String.length l - 3) :: com.neko_libs; | |
| 297 | acc | |
| 298 | | param, value -> | |
| 299 | extra_args := param :: !extra_args; | |
| 300 | if value <> "" then extra_args := value :: !extra_args; | |
| 301 | acc | |
| 302 | ) [] lines in | |
| 303 | com.class_path <- lines @ com.class_path; | |
| 304 | List.rev !extra_args | |
| 305 | ||
| 306 | let run_command ctx cmd = | |
| 307 | let h = Hashtbl.create 0 in | |
| 308 | Hashtbl.add h "__file__" ctx.com.file; | |
| 309 | Hashtbl.add h "__platform__" (platform_name ctx.com.platform); | |
| 310 | let t = Common.timer "command" in | |
| 311 | let cmd = expand_env ~h:(Some h) cmd in | |
| 312 | let len = String.length cmd in | |
| 313 | if len > 3 && String.sub cmd 0 3 = "cd " then | |
| 314 | Sys.chdir (String.sub cmd 3 (len - 3)) | |
| 315 | else | |
| 316 | let binary_string s = | |
| 317 | if Sys.os_type <> "Win32" && Sys.os_type <> "Cygwin" then s else String.concat "\n" (Str.split (Str.regexp "\r\n") s) | |
| 318 | in | |
| 319 | let pout, pin, perr = Unix.open_process_full cmd (Unix.environment()) in | |
| 320 | let iout = Unix.descr_of_in_channel pout in | |
| 321 | let ierr = Unix.descr_of_in_channel perr in | |
| 322 | let berr = Buffer.create 0 in | |
| 323 | let bout = Buffer.create 0 in | |
| 324 | let tmp = String.create 1024 in | |
| 325 | let result = ref None in | |
| 326 | (* | |
| 327 | we need to read available content on process out/err if we want to prevent | |
| 328 | the process from blocking when the pipe is full | |
| 329 | *) | |
| 330 | let is_process_running() = | |
| 331 | let pid, r = Unix.waitpid [Unix.WNOHANG] (-1) in | |
| 332 | if pid = 0 then | |
| 333 | true | |
| 334 | else begin | |
| 335 | result := Some r; | |
| 336 | false; | |
| 337 | end | |
| 338 | in | |
| 339 | let rec loop ins = | |
| 340 | let (ch,_,_), timeout = (try Unix.select ins [] [] 0.02, true with _ -> ([],[],[]),false) in | |
| 341 | match ch with | |
| 342 | | [] -> | |
| 343 | (* make sure we read all *) | |
| 344 | if timeout && is_process_running() then | |
| 345 | loop ins | |
| 346 | else begin | |
| 347 | Buffer.add_string berr (IO.read_all (IO.input_channel perr)); | |
| 348 | Buffer.add_string bout (IO.read_all (IO.input_channel pout)); | |
| 349 | end | |
| 350 | | s :: _ -> | |
| 351 | let n = Unix.read s tmp 0 (String.length tmp) in | |
| 352 | Buffer.add_substring (if s == iout then bout else berr) tmp 0 n; | |
| 353 | loop (if n = 0 then List.filter ((!=) s) ins else ins) | |
| 354 | in | |
| 355 | (try loop [iout;ierr] with Unix.Unix_error _ -> ()); | |
| 356 | let serr = binary_string (Buffer.contents berr) in | |
| 357 | let sout = binary_string (Buffer.contents bout) in | |
| 358 | if serr <> "" then ctx.messages <- (if serr.[String.length serr - 1] = '\n' then String.sub serr 0 (String.length serr - 1) else serr) :: ctx.messages; | |
| 359 | if sout <> "" then ctx.com.print sout; | |
| 360 | (match (try Unix.close_process_full (pout,pin,perr) with Unix.Unix_error (Unix.ECHILD,_,_) -> (match !result with None -> assert false | Some r -> r)) with | |
| 361 | | Unix.WEXITED e -> if e <> 0 then failwith ("Command failed with error " ^ string_of_int e)
| |
| 362 | | Unix.WSIGNALED s | Unix.WSTOPPED s -> failwith ("Command stopped with signal " ^ string_of_int s));
| |
| 363 | t() | |
| 364 | ||
| 365 | let default_flush ctx = | |
| 366 | List.iter prerr_endline (List.rev ctx.messages); | |
| 367 | if ctx.has_error && !prompt then begin | |
| 368 | print_endline "Press enter to exit..."; | |
| 369 | ignore(read_line()); | |
| 370 | end; | |
| 371 | if ctx.has_error then exit 1 | |
| 372 | ||
| 373 | let create_context params = | |
| 374 | let ctx = {
| |
| 375 | com = Common.create version params; | |
| 376 | flush = (fun()->()); | |
| 377 | setup = (fun()->()); | |
| 378 | messages = []; | |
| 379 | has_next = false; | |
| 380 | has_error = false; | |
| 381 | } in | |
| 382 | ctx.flush <- (fun() -> default_flush ctx); | |
| 383 | ctx | |
| 384 | ||
| 385 | let rec process_params create pl = | |
| 386 | let each_params = ref [] in | |
| 387 | let rec loop acc = function | |
| 388 | | [] -> | |
| 389 | let ctx = create (!each_params @ (List.rev acc)) in | |
| 390 | init ctx; | |
| 391 | ctx.flush() | |
| 392 | | "--next" :: l when acc = [] -> (* skip empty --next *) | |
| 393 | loop [] l | |
| 394 | | "--next" :: l -> | |
| 395 | let ctx = create (!each_params @ (List.rev acc)) in | |
| 396 | ctx.has_next <- true; | |
| 397 | init ctx; | |
| 398 | ctx.flush(); | |
| 399 | loop [] l | |
| 400 | | "--each" :: l -> | |
| 401 | each_params := List.rev acc; | |
| 402 | loop [] l | |
| 403 | | "--cwd" :: dir :: l -> | |
| 404 | (* we need to change it immediately since it will affect hxml loading *) | |
| 405 | (try Unix.chdir dir with _ -> ()); | |
| 406 | loop (dir :: "--cwd" :: acc) l | |
| 407 | | "--connect" :: hp :: l -> | |
| 408 | (match !global_cache with | |
| 409 | | None -> | |
| 410 | let host, port = (try ExtString.String.split hp ":" with _ -> "127.0.0.1", hp) in | |
| 411 | do_connect host (try int_of_string port with _ -> raise (Arg.Bad "Invalid port")) ((List.rev acc) @ l) | |
| 412 | | Some _ -> | |
| 413 | (* already connected : skip *) | |
| 414 | loop acc l) | |
| 415 | | arg :: l -> | |
| 416 | match List.rev (ExtString.String.nsplit arg ".") with | |
| 417 | | "hxml" :: _ when (match acc with "-cmd" :: _ -> false | _ -> true) -> loop acc (parse_hxml arg @ l) | |
| 418 | | _ -> loop (arg :: acc) l | |
| 419 | in | |
| 420 | (* put --display in front if it was last parameter *) | |
| 421 | let pl = (match List.rev pl with | |
| 422 | | file :: "--display" :: pl -> "--display" :: file :: List.rev pl | |
| 423 | | "use_rtti_doc" :: "-D" :: file :: "--display" :: pl -> "--display" :: file :: List.rev pl | |
| 424 | | _ -> pl | |
| 425 | ) in | |
| 426 | loop [] pl | |
| 427 | ||
| 428 | and wait_loop boot_com host port = | |
| 429 | let sock = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in | |
| 430 | (try Unix.bind sock (Unix.ADDR_INET (Unix.inet_addr_of_string host,port)) with _ -> failwith ("Couldn't wait on " ^ host ^ ":" ^ string_of_int port));
| |
| 431 | Unix.listen sock 10; | |
| 432 | Sys.catch_break false; | |
| 433 | let verbose = boot_com.verbose in | |
| 434 | let has_parse_error = ref false in | |
| 435 | if verbose then print_endline ("Waiting on " ^ host ^ ":" ^ string_of_int port);
| |
| 436 | let bufsize = 1024 in | |
| 437 | let tmp = String.create bufsize in | |
| 438 | let cache = {
| |
| 439 | c_haxelib = Hashtbl.create 0; | |
| 440 | c_files = Hashtbl.create 0; | |
| 441 | c_modules = Hashtbl.create 0; | |
| 442 | } in | |
| 443 | global_cache := Some cache; | |
| 444 | Typeload.parse_hook := (fun com2 file p -> | |
| 445 | let sign = get_signature com2 in | |
| 446 | let ffile = Common.unique_full_path file in | |
| 447 | let ftime = file_time ffile in | |
| 448 | let fkey = ffile ^ "!" ^ sign in | |
| 449 | try | |
| 450 | let time, data = Hashtbl.find cache.c_files fkey in | |
| 451 | if time <> ftime then raise Not_found; | |
| 452 | data | |
| 453 | with Not_found -> | |
| 454 | has_parse_error := false; | |
| 455 | let data = Typeload.parse_file com2 file p in | |
| 456 | if verbose then print_endline ("Parsed " ^ ffile);
| |
| 457 | if not !has_parse_error && ffile <> (!Parser.resume_display).Ast.pfile then Hashtbl.replace cache.c_files fkey (ftime,data); | |
| 458 | data | |
| 459 | ); | |
| 460 | let cache_module m = | |
| 461 | Hashtbl.replace cache.c_modules (m.m_path,m.m_extra.m_sign) m; | |
| 462 | in | |
| 463 | let check_module_path com m p = | |
| 464 | m.m_extra.m_file = Common.unique_full_path (Typeload.resolve_module_file com m.m_path (ref[]) p) | |
| 465 | in | |
| 466 | let compilation_step = ref 0 in | |
| 467 | let compilation_mark = ref 0 in | |
| 468 | let mark_loop = ref 0 in | |
| 469 | Typeload.type_module_hook := (fun (ctx:Typecore.typer) mpath p -> | |
| 470 | let t = Common.timer "module cache check" in | |
| 471 | let com2 = ctx.Typecore.com in | |
| 472 | let sign = get_signature com2 in | |
| 473 | let dep = ref None in | |
| 474 | incr mark_loop; | |
| 475 | let mark = !mark_loop in | |
| 476 | let start_mark = !compilation_mark in | |
| 477 | let rec check m = | |
| 478 | if m.m_extra.m_dirty then begin | |
| 479 | dep := Some m; | |
| 480 | false | |
| 481 | end else if m.m_extra.m_mark = mark then | |
| 482 | true | |
| 483 | else try | |
| 484 | if m.m_extra.m_mark <= start_mark then begin | |
| 485 | (match m.m_extra.m_kind with | |
| 486 | | MFake -> () (* don't get classpath *) | |
| 487 | | MCode -> if not (check_module_path com2 m p) then raise Not_found; | |
| 488 | | MMacro when ctx.Typecore.in_macro -> if not (check_module_path com2 m p) then raise Not_found; | |
| 489 | | MMacro -> | |
| 490 | let _, mctx = Typer.get_macro_context ctx p in | |
| 491 | if not (check_module_path mctx.Typecore.com m p) then raise Not_found; | |
| 492 | ); | |
| 493 | if file_time m.m_extra.m_file <> m.m_extra.m_time then begin | |
| 494 | if m.m_extra.m_kind = MFake then Hashtbl.remove Typecore.fake_modules m.m_extra.m_file; | |
| 495 | raise Not_found; | |
| 496 | end; | |
| 497 | end; | |
| 498 | m.m_extra.m_mark <- mark; | |
| 499 | PMap.iter (fun _ m2 -> if not (check m2) then begin dep := Some m2; raise Not_found end) m.m_extra.m_deps; | |
| 500 | true | |
| 501 | with Not_found -> | |
| 502 | m.m_extra.m_dirty <- true; | |
| 503 | false | |
| 504 | in | |
| 505 | let rec add_modules m0 m = | |
| 506 | if m.m_extra.m_added < !compilation_step then begin | |
| 507 | (match m0.m_extra.m_kind, m.m_extra.m_kind with | |
| 508 | | MCode, MMacro | MMacro, MCode -> | |
| 509 | (* this was just a dependency to check : do not add to the context *) | |
| 510 | () | |
| 511 | | _ -> | |
| 512 | if verbose then print_endline ("Reusing cached module " ^ Ast.s_type_path m.m_path);
| |
| 513 | m.m_extra.m_added <- !compilation_step; | |
| 514 | List.iter (fun t -> | |
| 515 | match t with | |
| 516 | | TClassDecl c -> c.cl_restore() | |
| 517 | | TEnumDecl e -> | |
| 518 | let rec loop acc = function | |
| 519 | | [] -> () | |
| 520 | | (":realPath",[Ast.EConst (Ast.String path),_],_) :: l ->
| |
| 521 | e.e_path <- Ast.parse_path path; | |
| 522 | e.e_meta <- (List.rev acc) @ l; | |
| 523 | | x :: l -> loop (x::acc) l | |
| 524 | in | |
| 525 | loop [] e.e_meta | |
| 526 | | _ -> () | |
| 527 | ) m.m_types; | |
| 528 | Typeload.add_module ctx m p; | |
| 529 | PMap.iter (Hashtbl.add com2.resources) m.m_extra.m_binded_res; | |
| 530 | PMap.iter (fun _ m2 -> add_modules m0 m2) m.m_extra.m_deps); | |
| 531 | List.iter (Typer.call_init_macro ctx) m.m_extra.m_macro_calls | |
| 532 | end | |
| 533 | in | |
| 534 | try | |
| 535 | let m = Hashtbl.find cache.c_modules (mpath,sign) in | |
| 536 | if not (check m) then begin | |
| 537 | if verbose then print_endline ("Skipping cached module " ^ Ast.s_type_path mpath ^ (match !dep with None -> "" | Some m -> "(" ^ Ast.s_type_path m.m_path ^ ")"));
| |
| 538 | raise Not_found; | |
| 539 | end; | |
| 540 | add_modules m m; | |
| 541 | t(); | |
| 542 | Some m | |
| 543 | with Not_found -> | |
| 544 | t(); | |
| 545 | None | |
| 546 | ); | |
| 547 | let run_count = ref 0 in | |
| 548 | while true do | |
| 549 | let sin, _ = Unix.accept sock in | |
| 550 | let t0 = get_time() in | |
| 551 | Unix.set_nonblock sin; | |
| 552 | if verbose then print_endline "Client connected"; | |
| 553 | let b = Buffer.create 0 in | |
| 554 | let rec read_loop() = | |
| 555 | try | |
| 556 | let r = Unix.recv sin tmp 0 bufsize [] in | |
| 557 | if verbose then Printf.printf "Reading %d bytes\n" r; | |
| 558 | Buffer.add_substring b tmp 0 r; | |
| 559 | if r > 0 && tmp.[r-1] = '\000' then Buffer.sub b 0 (Buffer.length b - 1) else read_loop(); | |
| 560 | with Unix.Unix_error((Unix.EWOULDBLOCK|Unix.EAGAIN),_,_) -> | |
| 561 | if verbose then print_endline "Waiting for data..."; | |
| 562 | ignore(Unix.select [] [] [] 0.1); | |
| 563 | read_loop() | |
| 564 | in | |
| 565 | let rec cache_context com = | |
| 566 | if not com.display then begin | |
| 567 | List.iter cache_module com.modules; | |
| 568 | if verbose then print_endline ("Cached " ^ string_of_int (List.length com.modules) ^ " modules");
| |
| 569 | end; | |
| 570 | match com.get_macros() with | |
| 571 | | None -> () | |
| 572 | | Some com -> cache_context com | |
| 573 | in | |
| 574 | let create params = | |
| 575 | let ctx = create_context params in | |
| 576 | ctx.flush <- (fun() -> | |
| 577 | incr compilation_step; | |
| 578 | compilation_mark := !mark_loop; | |
| 579 | List.iter (fun s -> ssend sin (s ^ "\n"); if verbose then print_endline ("> " ^ s)) (List.rev ctx.messages);
| |
| 580 | if ctx.has_error then ssend sin "\x02\n" else cache_context ctx.com; | |
| 581 | ); | |
| 582 | ctx.setup <- (fun() -> | |
| 583 | Parser.display_error := (fun e p -> has_parse_error := true; ctx.com.error (Parser.error_msg e) p); | |
| 584 | if ctx.com.display then begin | |
| 585 | let file = (!Parser.resume_display).Ast.pfile in | |
| 586 | let fkey = file ^ "!" ^ get_signature ctx.com in | |
| 587 | (* force parsing again : if the completion point have been changed *) | |
| 588 | Hashtbl.remove cache.c_files fkey; | |
| 589 | (* force module reloading (if cached) *) | |
| 590 | Hashtbl.iter (fun _ m -> if m.m_extra.m_file = file then m.m_extra.m_dirty <- true) cache.c_modules | |
| 591 | end | |
| 592 | ); | |
| 593 | ctx.com.print <- (fun str -> ssend sin ("\x01" ^ String.concat "\x01" (ExtString.String.nsplit str "\n") ^ "\n"));
| |
| 594 | ctx | |
| 595 | in | |
| 596 | (try | |
| 597 | let data = parse_hxml_data (read_loop()) in | |
| 598 | Unix.clear_nonblock sin; | |
| 599 | if verbose then print_endline ("Processing Arguments [" ^ String.concat "," data ^ "]");
| |
| 600 | (try | |
| 601 | Common.display_default := false; | |
| 602 | Parser.resume_display := Ast.null_pos; | |
| 603 | Typeload.return_partial_type := false; | |
| 604 | measure_times := false; | |
| 605 | close_times(); | |
| 606 | stats.s_files_parsed := 0; | |
| 607 | stats.s_classes_built := 0; | |
| 608 | stats.s_methods_typed := 0; | |
| 609 | stats.s_macros_called := 0; | |
| 610 | Hashtbl.clear Common.htimers; | |
| 611 | let _ = Common.timer "other" in | |
| 612 | incr compilation_step; | |
| 613 | compilation_mark := !mark_loop; | |
| 614 | start_time := get_time(); | |
| 615 | process_params create data; | |
| 616 | close_times(); | |
| 617 | if !measure_times then report_times (fun s -> ssend sin (s ^ "\n")) | |
| 618 | with Completion str -> | |
| 619 | if verbose then print_endline ("Completion Response =\n" ^ str);
| |
| 620 | ssend sin str | |
| 621 | ); | |
| 622 | if verbose then begin | |
| 623 | print_endline (Printf.sprintf "Stats = %d files, %d classes, %d methods, %d macros" !(stats.s_files_parsed) !(stats.s_classes_built) !(stats.s_methods_typed) !(stats.s_macros_called)); | |
| 624 | print_endline (Printf.sprintf "Time spent : %.3fs" (get_time() -. t0)); | |
| 625 | end | |
| 626 | with Unix.Unix_error _ -> | |
| 627 | if verbose then print_endline "Connection Aborted"); | |
| 628 | Unix.close sin; | |
| 629 | (* prevent too much fragmentation by doing some compactions every X run *) | |
| 630 | incr run_count; | |
| 631 | if !run_count mod 1 = 50 then begin | |
| 632 | let t0 = get_time() in | |
| 633 | Gc.compact(); | |
| 634 | if verbose then begin | |
| 635 | let stat = Gc.quick_stat() in | |
| 636 | let size = (float_of_int stat.Gc.heap_words) *. 4. in | |
| 637 | print_endline (Printf.sprintf "Compacted memory %.3fs %.1fMB" (get_time() -. t0) (size /. (1024. *. 1024.))); | |
| 638 | end | |
| 639 | end else Gc.minor(); | |
| 640 | done | |
| 641 | ||
| 642 | and do_connect host port args = | |
| 643 | let sock = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in | |
| 644 | (try Unix.connect sock (Unix.ADDR_INET (Unix.inet_addr_of_string host,port)) with _ -> failwith ("Couldn't connect on " ^ host ^ ":" ^ string_of_int port));
| |
| 645 | let args = ("--cwd " ^ Unix.getcwd()) :: args in
| |
| 646 | ssend sock (String.concat "" (List.map (fun a -> a ^ "\n") args) ^ "\000"); | |
| 647 | let buf = Buffer.create 0 in | |
| 648 | let tmp = String.create 100 in | |
| 649 | let rec loop() = | |
| 650 | let b = Unix.recv sock tmp 0 100 [] in | |
| 651 | Buffer.add_substring buf tmp 0 b; | |
| 652 | if b > 0 then loop() | |
| 653 | in | |
| 654 | loop(); | |
| 655 | let has_error = ref false in | |
| 656 | let rec print line = | |
| 657 | match (if line = "" then '\x00' else line.[0]) with | |
| 658 | | '\x01' -> | |
| 659 | print_string (String.concat "\n" (List.tl (ExtString.String.nsplit line "\x01"))) | |
| 660 | | '\x02' -> | |
| 661 | has_error := true; | |
| 662 | | _ -> | |
| 663 | prerr_endline line; | |
| 664 | in | |
| 665 | let lines = ExtString.String.nsplit (Buffer.contents buf) "\n" in | |
| 666 | let lines = (match List.rev lines with "" :: l -> List.rev l | _ -> lines) in | |
| 667 | List.iter print lines; | |
| 668 | if !has_error then exit 1 | |
| 669 | ||
| 670 | and init ctx = | |
| 671 | let usage = Printf.sprintf | |
| 672 | "Haxe Compiler %d.%.2d - (c)2005-2012 Haxe Foundation\n Usage : haxe%s -main <class> [-swf|-js|-neko|-php|-cpp|-cs|-java|-as3] <output> [options]\n Options :" | |
| 673 | (version / 100) (version mod 100) (if Sys.os_type = "Win32" then ".exe" else "") | |
| 674 | in | |
| 675 | let com = ctx.com in | |
| 676 | let classes = ref [([],"Std")] in | |
| 677 | try | |
| 678 | let xml_out = ref None in | |
| 679 | let swf_header = ref None in | |
| 680 | let swf_script_limits = ref None in | |
| 681 | let cmds = ref [] in | |
| 682 | let config_macros = ref [] in | |
| 683 | let cp_libs = ref [] in | |
| 684 | let gen_as3 = ref false in | |
| 685 | let no_output = ref false in | |
| 686 | let did_something = ref false in | |
| 687 | let force_typing = ref false in | |
| 688 | let pre_compilation = ref [] in | |
| 689 | let interp = ref false in | |
| 690 | if version < 300 then begin | |
| 691 | for i = 0 to 4 do | |
| 692 | let v = version - i in | |
| 693 | Common.raw_define com ("haxe_" ^ string_of_int v);
| |
| 694 | done; | |
| 695 | end else begin | |
| 696 | Common.define com Define.Haxe3; | |
| 697 | Common.define_value com Define.HaxeVer (string_of_float (float_of_int version /. 100.)); | |
| 698 | end; | |
| 699 | Common.define_value com Define.Dce "std"; | |
| 700 | com.warning <- (fun msg p -> message ctx ("Warning : " ^ msg) p);
| |
| 701 | com.error <- error ctx; | |
| 702 | Parser.display_error := (fun e p -> com.error (Parser.error_msg e) p); | |
| 703 | Parser.use_doc := !Common.display_default || (!global_cache <> None); | |
| 704 | (try | |
| 705 | let p = Sys.getenv "HAXE_LIBRARY_PATH" in | |
| 706 | let rec loop = function | |
| 707 | | drive :: path :: l -> | |
| 708 | if String.length drive = 1 && ((drive.[0] >= 'a' && drive.[0] <= 'z') || (drive.[0] >= 'A' && drive.[0] <= 'Z')) then | |
| 709 | (drive ^ ":" ^ path) :: loop l | |
| 710 | else | |
| 711 | drive :: loop (path :: l) | |
| 712 | | l -> | |
| 713 | l | |
| 714 | in | |
| 715 | let parts = "" :: Str.split_delim (Str.regexp "[;:]") p in | |
| 716 | com.class_path <- List.map normalize_path (loop parts) | |
| 717 | with | |
| 718 | Not_found -> | |
| 719 | if Sys.os_type = "Unix" then | |
| 720 | com.class_path <- ["/usr/lib/haxe/std/";"/usr/local/lib/haxe/std/";"/usr/lib/haxe/std/libs/";"/usr/local/lib/haxe/std/libs/";"";"/"] | |
| 721 | else | |
| 722 | let base_path = normalize_path (Extc.get_real_path (try executable_path() with _ -> "./")) in | |
| 723 | com.class_path <- [base_path ^ "std/";base_path ^ "std/libs/";""]); | |
| 724 | com.std_path <- List.filter (fun p -> ExtString.String.ends_with p "std/" || ExtString.String.ends_with p "std\\") com.class_path; | |
| 725 | let set_platform pf file = | |
| 726 | if com.platform <> Cross then failwith "Multiple targets"; | |
| 727 | Common.init_platform com pf; | |
| 728 | com.file <- file; | |
| 729 | if (pf = Flash8 || pf = Flash) && file_extension file = "swc" then Common.define com Define.Swc; | |
| 730 | in | |
| 731 | let define f = Arg.Unit (fun () -> Common.define com f) in | |
| 732 | let extra_args = ref [] in | |
| 733 | let basic_args_spec = [ | |
| 734 | ("-cp",Arg.String (fun path ->
| |
| 735 | extra_args := !extra_args @ (add_libs com (!cp_libs)); | |
| 736 | cp_libs := []; | |
| 737 | com.class_path <- normalize_path path :: com.class_path | |
| 738 | ),"<path> : add a directory to find source files"); | |
| 739 | ("-js",Arg.String (set_platform Js),"<file> : compile code to JavaScript file");
| |
| 740 | ("-swf",Arg.String (set_platform Flash),"<file> : compile code to Flash SWF file");
| |
| 741 | ("-as3",Arg.String (fun dir ->
| |
| 742 | set_platform Flash dir; | |
| 743 | gen_as3 := true; | |
| 744 | Common.define com Define.As3; | |
| 745 | Common.define com Define.NoInline; | |
| 746 | ),"<directory> : generate AS3 code into target directory"); | |
| 747 | ("-neko",Arg.String (set_platform Neko),"<file> : compile code to Neko Binary");
| |
| 748 | ("-php",Arg.String (fun dir ->
| |
| 749 | classes := (["php"],"Boot") :: !classes; | |
| 750 | set_platform Php dir; | |
| 751 | ),"<directory> : generate PHP code into target directory"); | |
| 752 | ("-cpp",Arg.String (fun dir ->
| |
| 753 | set_platform Cpp dir; | |
| 754 | ),"<directory> : generate C++ code into target directory"); | |
| 755 | ("-cs",Arg.String (fun dir ->
| |
| 756 | set_platform Cs dir; | |
| 757 | ),"<directory> : generate C# code into target directory"); | |
| 758 | ("-java",Arg.String (fun dir ->
| |
| 759 | set_platform Java dir; | |
| 760 | ),"<directory> : generate Java code into target directory"); | |
| 761 | ("-xml",Arg.String (fun file ->
| |
| 762 | Parser.use_doc := true; | |
| 763 | xml_out := Some file | |
| 764 | ),"<file> : generate XML types description"); | |
| 765 | ("-main",Arg.String (fun cl ->
| |
| 766 | if com.main_class <> None then raise (Arg.Bad "Multiple -main"); | |
| 767 | let cpath = make_path cl in | |
| 768 | com.main_class <- Some cpath; | |
| 769 | classes := cpath :: !classes | |
| 770 | ),"<class> : select startup class"); | |
| 771 | ("-lib",Arg.String (fun l ->
| |
| 772 | cp_libs := l :: !cp_libs; | |
| 773 | Common.raw_define com l; | |
| 774 | ),"<library[:version]> : use a haxelib library"); | |
| 775 | ("-D",Arg.String (fun var ->
| |
| 776 | if var = fst (Define.infos Define.UseRttiDoc) then Parser.use_doc := true; | |
| 777 | if var = fst (Define.infos Define.NoOpt) then com.foptimize <- false; | |
| 778 | if List.mem var reserved_flags then raise (Arg.Bad (var ^ " is a reserved compiler flag and cannot be defined from command line")); | |
| 779 | Common.raw_define com var | |
| 780 | ),"<var> : define a conditional compilation flag"); | |
| 781 | ("-v",Arg.Unit (fun () ->
| |
| 782 | com.verbose <- true | |
| 783 | ),": turn on verbose mode"); | |
| 784 | ("-debug", Arg.Unit (fun() ->
| |
| 785 | Common.define com Define.Debug; | |
| 786 | com.debug <- true; | |
| 787 | ), ": add debug informations to the compiled code"); | |
| 788 | ] in | |
| 789 | let adv_args_spec = [ | |
| 790 | ("-swf-version",Arg.Float (fun v ->
| |
| 791 | com.flash_version <- v; | |
| 792 | ),"<version> : change the SWF version (6 to 10)"); | |
| 793 | ("-swf-header",Arg.String (fun h ->
| |
| 794 | try | |
| 795 | swf_header := Some (match ExtString.String.nsplit h ":" with | |
| 796 | | [width; height; fps] -> | |
| 797 | (int_of_string width,int_of_string height,float_of_string fps,0xFFFFFF) | |
| 798 | | [width; height; fps; color] -> | |
| 799 | (int_of_string width, int_of_string height, float_of_string fps, int_of_string ("0x" ^ color))
| |
| 800 | | _ -> raise Exit) | |
| 801 | with | |
| 802 | _ -> raise (Arg.Bad "Invalid SWF header format") | |
| 803 | ),"<header> : define SWF header (width:height:fps:color)"); | |
| 804 | ("-swf-script-limits",Arg.String (fun sl ->
| |
| 805 | try | |
| 806 | com.swf_script_limits := Some (match ExtString.String.nsplit sl ":" with | |
| 807 | | [recursion_depth; script_timeout] -> (int_of_string recursion_depth, int_of_string script_timeout) | |
| 808 | | _ -> raise Exit) | |
| 809 | with | |
| 810 | _ -> raise (Arg.Bad "Invalid SWF ScriptLimits format") | |
| 811 | ),"<swf script limits> : define SWF script limits (recursion depth:script timeout)"); | |
| 812 | ("-swf-lib",Arg.String (fun file ->
| |
| 813 | Genswf.add_swf_lib com file | |
| 814 | ),"<file> : add the SWF library to the compiled SWF"); | |
| 815 | ("-java-lib",Arg.String (fun file ->
| |
| 816 | Genjava.add_java_lib com file | |
| 817 | ),"<file> : add an external JAR or class directory library"); | |
| 818 | ("-x", Arg.String (fun file ->
| |
| 819 | let neko_file = file ^ ".n" in | |
| 820 | set_platform Neko neko_file; | |
| 821 | if com.main_class = None then begin | |
| 822 | let cpath = make_path file in | |
| 823 | com.main_class <- Some cpath; | |
| 824 | classes := cpath :: !classes | |
| 825 | end; | |
| 826 | cmds := ("neko " ^ neko_file) :: !cmds;
| |
| 827 | ),"<file> : shortcut for compiling and executing a neko file"); | |
| 828 | ("-resource",Arg.String (fun res ->
| |
| 829 | let file, name = (match ExtString.String.nsplit res "@" with | |
| 830 | | [file; name] -> file, name | |
| 831 | | [file] -> file, file | |
| 832 | | _ -> raise (Arg.Bad "Invalid Resource format : should be file@name") | |
| 833 | ) in | |
| 834 | let file = (try Common.find_file com file with Not_found -> file) in | |
| 835 | let data = (try | |
| 836 | let s = Std.input_file ~bin:true file in | |
| 837 | if String.length s > 12000000 then raise Exit; | |
| 838 | s; | |
| 839 | with | |
| 840 | | Sys_error _ -> failwith ("Resource file not found : " ^ file)
| |
| 841 | | _ -> failwith ("Resource '" ^ file ^ "' excess the maximum size of 12MB")
| |
| 842 | ) in | |
| 843 | if Hashtbl.mem com.resources name then failwith ("Duplicate resource name " ^ name);
| |
| 844 | Hashtbl.add com.resources name data | |
| 845 | ),"<file>[@name] : add a named resource file"); | |
| 846 | ("-prompt", Arg.Unit (fun() -> prompt := true),": prompt on error");
| |
| 847 | ("-cmd", Arg.String (fun cmd ->
| |
| 848 | cmds := unquote cmd :: !cmds | |
| 849 | ),": run the specified command after successful compilation"); | |
| 850 | ("--flash-strict", define Define.FlashStrict, ": more type strict flash API");
| |
| 851 | ("--no-traces", define Define.NoTraces, ": don't compile trace calls in the program");
| |
| 852 | ("--gen-hx-classes", Arg.Unit (fun() ->
| |
| 853 | force_typing := true; | |
| 854 | pre_compilation := (fun() -> | |
| 855 | List.iter (fun (_,_,extract) -> | |
| 856 | Hashtbl.iter (fun n _ -> classes := n :: !classes) (extract()) | |
| 857 | ) com.swf_libs; | |
| 858 | ) :: !pre_compilation; | |
| 859 | xml_out := Some "hx" | |
| 860 | ),": generate hx headers for all input classes"); | |
| 861 | ("--next", Arg.Unit (fun() -> assert false), ": separate several haxe compilations");
| |
| 862 | ("--display", Arg.String (fun file_pos ->
| |
| 863 | match file_pos with | |
| 864 | | "classes" -> | |
| 865 | pre_compilation := (fun() -> raise (Parser.TypePath (["."],None))) :: !pre_compilation; | |
| 866 | | "keywords" -> | |
| 867 | complete_fields (Hashtbl.fold (fun k _ acc -> (k,"","") :: acc) Lexer.keywords []) | |
| 868 | | _ -> | |
| 869 | let file, pos = try ExtString.String.split file_pos "@" with _ -> failwith ("Invalid format : " ^ file_pos) in
| |
| 870 | let file = unquote file in | |
| 871 | let pos = try int_of_string pos with _ -> failwith ("Invalid format : " ^ pos) in
| |
| 872 | com.display <- true; | |
| 873 | Common.display_default := true; | |
| 874 | Common.define com Define.Display; | |
| 875 | Parser.use_doc := true; | |
| 876 | Parser.resume_display := {
| |
| 877 | Ast.pfile = Common.unique_full_path file; | |
| 878 | Ast.pmin = pos; | |
| 879 | Ast.pmax = pos; | |
| 880 | }; | |
| 881 | ),": display code tips"); | |
| 882 | ("--no-output", Arg.Unit (fun() -> no_output := true),": compiles but does not generate any file");
| |
| 883 | ("--times", Arg.Unit (fun() -> measure_times := true),": measure compilation times");
| |
| 884 | ("--no-inline", define Define.NoInline, ": disable inlining");
| |
| 885 | ("--no-opt", Arg.Unit (fun() ->
| |
| 886 | com.foptimize <- false; | |
| 887 | Common.define com Define.NoOpt; | |
| 888 | ), ": disable code optimizations"); | |
| 889 | ("--js-modern", Arg.Unit (fun() ->
| |
| 890 | Common.define com Define.JsModern; | |
| 891 | ), ": wrap JS output in a closure, strict mode, and other upcoming features"); | |
| 892 | ("--php-front",Arg.String (fun f ->
| |
| 893 | if com.php_front <> None then raise (Arg.Bad "Multiple --php-front"); | |
| 894 | com.php_front <- Some f; | |
| 895 | ),"<filename> : select the name for the php front file"); | |
| 896 | ("--php-lib",Arg.String (fun f ->
| |
| 897 | if com.php_lib <> None then raise (Arg.Bad "Multiple --php-lib"); | |
| 898 | com.php_lib <- Some f; | |
| 899 | ),"<filename> : select the name for the php lib folder"); | |
| 900 | ("--php-prefix", Arg.String (fun f ->
| |
| 901 | if com.php_prefix <> None then raise (Arg.Bad "Multiple --php-prefix"); | |
| 902 | com.php_prefix <- Some f; | |
| 903 | Common.define com Define.PhpPrefix; | |
| 904 | ),"<name> : prefix all classes with given name"); | |
| 905 | ("--remap", Arg.String (fun s ->
| |
| 906 | let pack, target = (try ExtString.String.split s ":" with _ -> raise (Arg.Bad "Invalid format")) in | |
| 907 | com.package_rules <- PMap.add pack (Remap target) com.package_rules; | |
| 908 | ),"<package:target> : remap a package to another one"); | |
| 909 | ("--interp", Arg.Unit (fun() ->
| |
| 910 | Common.define com Define.Interp; | |
| 911 | set_platform Neko ""; | |
| 912 | no_output := true; | |
| 913 | interp := true; | |
| 914 | ),": interpret the program using internal macro system"); | |
| 915 | ("--macro", Arg.String (fun e ->
| |
| 916 | force_typing := true; | |
| 917 | config_macros := e :: !config_macros | |
| 918 | )," : call the given macro before typing anything else"); | |
| 919 | ("--dce", Arg.String (fun mode ->
| |
| 920 | (match mode with | |
| 921 | | "std" | "full" | "no" -> () | |
| 922 | | _ -> raise (Arg.Bad "Invalid DCE mode")); | |
| 923 | Common.define_value com Define.Dce mode | |
| 924 | ),"[std|full|no] : set the dead code elimination mode"); | |
| 925 | ("--wait", Arg.String (fun hp ->
| |
| 926 | let host, port = (try ExtString.String.split hp ":" with _ -> "127.0.0.1", hp) in | |
| 927 | wait_loop com host (try int_of_string port with _ -> raise (Arg.Bad "Invalid port")) | |
| 928 | ),"<[host:]port> : wait on the given port for commands to run)"); | |
| 929 | ("--connect",Arg.String (fun _ ->
| |
| 930 | assert false | |
| 931 | ),"<[host:]port> : connect on the given port and run commands there)"); | |
| 932 | ("--cwd", Arg.String (fun dir ->
| |
| 933 | (try Unix.chdir dir with _ -> raise (Arg.Bad "Invalid directory")) | |
| 934 | ),"<dir> : set current working directory"); | |
| 935 | ("--help-defines", Arg.Unit (fun() ->
| |
| 936 | let rec loop i = | |
| 937 | let d = Obj.magic i in | |
| 938 | if d <> Define.Last then begin | |
| 939 | let t, doc = Define.infos d in | |
| 940 | message ctx (String.concat "-" (ExtString.String.nsplit t "_") ^ " : " ^ doc) Ast.null_pos; | |
| 941 | loop (i + 1) | |
| 942 | end | |
| 943 | in | |
| 944 | loop 0; | |
| 945 | did_something := true | |
| 946 | ),": print help for all compiler specific defines"); | |
| 947 | ("-swf9",Arg.String (fun file ->
| |
| 948 | set_platform Flash file; | |
| 949 | ),"<file> : [deprecated] compile code to Flash9 SWF file"); | |
| 950 | ] in | |
| 951 | let args_callback cl = classes := make_path cl :: !classes in | |
| 952 | let process args = | |
| 953 | let current = ref 0 in | |
| 954 | Arg.parse_argv ~current (Array.of_list ("" :: List.map expand_env args)) (basic_args_spec @ adv_args_spec) args_callback usage
| |
| 955 | in | |
| 956 | process ctx.com.args; | |
| 957 | let rec loop() = | |
| 958 | extra_args := !extra_args @ add_libs com (!cp_libs); | |
| 959 | cp_libs := []; | |
| 960 | match !extra_args with | |
| 961 | | [] -> () | |
| 962 | | l -> | |
| 963 | extra_args := []; | |
| 964 | process l; | |
| 965 | loop() | |
| 966 | in | |
| 967 | loop(); | |
| 968 | (try ignore(Common.find_file com "mt/Include.hx"); Common.raw_define com "mt"; with Not_found -> ()); | |
| 969 | if com.display then begin | |
| 970 | com.warning <- message ctx; | |
| 971 | com.error <- error ctx; | |
| 972 | com.main_class <- None; | |
| 973 | let real = Extc.get_real_path (!Parser.resume_display).Ast.pfile in | |
| 974 | classes := lookup_classes com real; | |
| 975 | Common.log com ("Display file : " ^ real);
| |
| 976 | Common.log com ("Classes found : [" ^ (String.concat "," (List.map Ast.s_type_path !classes)) ^ "]");
| |
| 977 | end; | |
| 978 | let add_std dir = | |
| 979 | com.class_path <- List.filter (fun s -> not (List.mem s com.std_path)) com.class_path @ List.map (fun p -> p ^ dir ^ "/_std/") com.std_path @ com.std_path | |
| 980 | in | |
| 981 | let ext = (match com.platform with | |
| 982 | | Cross -> | |
| 983 | (* no platform selected *) | |
| 984 | set_platform Cross ""; | |
| 985 | "?" | |
| 986 | | Flash8 | Flash -> | |
| 987 | if com.flash_version >= 9. then begin | |
| 988 | let rec loop = function | |
| 989 | | [] -> () | |
| 990 | | (v,_) :: _ when v > com.flash_version -> () | |
| 991 | | (v,def) :: l -> | |
| 992 | Common.raw_define com ("flash" ^ def);
| |
| 993 | loop l | |
| 994 | in | |
| 995 | loop Common.flash_versions; | |
| 996 | Common.raw_define com "flash"; | |
| 997 | com.defines <- PMap.remove "flash8" com.defines; | |
| 998 | com.package_rules <- PMap.remove "flash" com.package_rules; | |
| 999 | add_std "flash"; | |
| 1000 | end else begin | |
| 1001 | com.package_rules <- PMap.add "flash" (Directory "flash8") com.package_rules; | |
| 1002 | com.package_rules <- PMap.add "flash8" Forbidden com.package_rules; | |
| 1003 | Common.raw_define com "flash"; | |
| 1004 | Common.raw_define com ("flash" ^ string_of_int (int_of_float com.flash_version));
| |
| 1005 | com.platform <- Flash8; | |
| 1006 | add_std "flash8"; | |
| 1007 | end; | |
| 1008 | "swf" | |
| 1009 | | Neko -> | |
| 1010 | add_std "neko"; | |
| 1011 | "n" | |
| 1012 | | Js -> | |
| 1013 | add_std "js"; | |
| 1014 | "js" | |
| 1015 | | Php -> | |
| 1016 | add_std "php"; | |
| 1017 | "php" | |
| 1018 | | Cpp -> | |
| 1019 | add_std "cpp"; | |
| 1020 | "cpp" | |
| 1021 | | Cs -> | |
| 1022 | Gencs.before_generate com; | |
| 1023 | add_std "cs"; "cs" | |
| 1024 | | Java -> | |
| 1025 | Genjava.before_generate com; | |
| 1026 | add_std "java"; "java" | |
| 1027 | ) in | |
| 1028 | (* if we are at the last compilation step, allow all packages accesses - in case of macros or opening another project file *) | |
| 1029 | if com.display && not ctx.has_next then com.package_rules <- PMap.foldi (fun p r acc -> match r with Forbidden -> acc | _ -> PMap.add p r acc) com.package_rules PMap.empty; | |
| 1030 | com.config <- get_config com; (* make sure to adapt all flags changes defined after platform *) | |
| 1031 | ||
| 1032 | (* check file extension. In case of wrong commandline, we don't want | |
| 1033 | to accidentaly delete a source file. *) | |
| 1034 | if not !no_output && file_extension com.file = ext then delete_file com.file; | |
| 1035 | List.iter (fun f -> f()) (List.rev (!pre_compilation)); | |
| 1036 | if !classes = [([],"Std")] && not !force_typing then begin | |
| 1037 | if !cmds = [] && not !did_something then Arg.usage basic_args_spec usage; | |
| 1038 | end else begin | |
| 1039 | ctx.setup(); | |
| 1040 | Common.log com ("Classpath : " ^ (String.concat ";" com.class_path));
| |
| 1041 | Common.log com ("Defines : " ^ (String.concat ";" (PMap.foldi (fun v _ acc -> v :: acc) com.defines [])));
| |
| 1042 | let t = Common.timer "typing" in | |
| 1043 | Typecore.type_expr_ref := (fun ctx e need_val -> Typer.type_expr ~need_val ctx e); | |
| 1044 | let tctx = Typer.create com in | |
| 1045 | List.iter (Typer.call_init_macro tctx) (List.rev !config_macros); | |
| 1046 | List.iter (fun cpath -> ignore(tctx.Typecore.g.Typecore.do_load_module tctx cpath Ast.null_pos)) (List.rev !classes); | |
| 1047 | Typer.finalize tctx; | |
| 1048 | t(); | |
| 1049 | if ctx.has_error then raise Abort; | |
| 1050 | if com.display then begin | |
| 1051 | if ctx.has_next then raise Abort; | |
| 1052 | failwith "No completion point was found"; | |
| 1053 | end; | |
| 1054 | let t = Common.timer "filters" in | |
| 1055 | let main, types, modules = Typer.generate tctx in | |
| 1056 | com.main <- main; | |
| 1057 | com.types <- types; | |
| 1058 | com.modules <- modules; | |
| 1059 | let filters = [ | |
| 1060 | if com.foptimize then Optimizer.reduce_expression tctx else Optimizer.sanitize tctx; | |
| 1061 | Codegen.check_local_vars_init; | |
| 1062 | Codegen.captured_vars com; | |
| 1063 | Codegen.rename_local_vars com; | |
| 1064 | ] in | |
| 1065 | List.iter (Codegen.post_process filters) com.types; | |
| 1066 | Codegen.post_process_end(); | |
| 1067 | List.iter (fun f -> f()) (List.rev com.filters); | |
| 1068 | List.iter (Codegen.save_class_state tctx) com.types; | |
| 1069 | let dce_mode = (try Common.defined_value com Define.Dce with _ -> "no") in | |
| 1070 | if not (!gen_as3 || dce_mode = "no" || Common.defined com Define.DocGen) then Dce.run com main (dce_mode = "full" && not !interp); | |
| 1071 | let type_filters = [ | |
| 1072 | Codegen.check_private_path; | |
| 1073 | Codegen.remove_generic_base; | |
| 1074 | Codegen.apply_native_paths; | |
| 1075 | Codegen.add_rtti; | |
| 1076 | Codegen.remove_extern_fields; | |
| 1077 | Codegen.add_field_inits; | |
| 1078 | Codegen.add_meta_field; | |
| 1079 | Codegen.check_remove_metadata; | |
| 1080 | ] in | |
| 1081 | List.iter (fun t -> List.iter (fun f -> f tctx t) type_filters) com.types; | |
| 1082 | if ctx.has_error then raise Abort; | |
| 1083 | (match !xml_out with | |
| 1084 | | None -> () | |
| 1085 | | Some "hx" -> | |
| 1086 | Genxml.generate_hx com | |
| 1087 | | Some file -> | |
| 1088 | Common.log com ("Generating xml : " ^ file);
| |
| 1089 | Genxml.generate com file); | |
| 1090 | if com.platform = Flash || com.platform = Cpp || com.platform = Cs then List.iter (Codegen.fix_overrides com) com.types; | |
| 1091 | if Common.defined com Define.Dump then Codegen.dump_types com; | |
| 1092 | if Common.defined com Define.DumpDependencies then Codegen.dump_dependencies com; | |
| 1093 | t(); | |
| 1094 | (match com.platform with | |
| 1095 | | _ when !no_output -> | |
| 1096 | if !interp then begin | |
| 1097 | let ctx = Interp.create com (Typer.make_macro_api tctx Ast.null_pos) in | |
| 1098 | Interp.add_types ctx com.types (fun t -> ()); | |
| 1099 | (match com.main with | |
| 1100 | | None -> () | |
| 1101 | | Some e -> ignore(Interp.eval_expr ctx e)); | |
| 1102 | end; | |
| 1103 | | Cross -> | |
| 1104 | () | |
| 1105 | | Flash8 | Flash when !gen_as3 -> | |
| 1106 | Common.log com ("Generating AS3 in : " ^ com.file);
| |
| 1107 | Genas3.generate com; | |
| 1108 | | Flash8 | Flash -> | |
| 1109 | Common.log com ("Generating swf : " ^ com.file);
| |
| 1110 | Genswf.generate com !swf_header; | |
| 1111 | | Neko -> | |
| 1112 | Common.log com ("Generating neko : " ^ com.file);
| |
| 1113 | Genneko.generate com; | |
| 1114 | | Js -> | |
| 1115 | Common.log com ("Generating js : " ^ com.file);
| |
| 1116 | Genjs.generate com | |
| 1117 | | Php -> | |
| 1118 | Common.log com ("Generating PHP in : " ^ com.file);
| |
| 1119 | Genphp.generate com; | |
| 1120 | | Cpp -> | |
| 1121 | Common.log com ("Generating Cpp in : " ^ com.file);
| |
| 1122 | Gencpp.generate com; | |
| 1123 | | Cs -> | |
| 1124 | if com.verbose then print_endline ("Generating C# in : " ^ com.file);
| |
| 1125 | Gencs.generate com; | |
| 1126 | | Java -> | |
| 1127 | if com.verbose then print_endline ("Generating Java in : " ^ com.file);
| |
| 1128 | Genjava.generate com; | |
| 1129 | ); | |
| 1130 | end; | |
| 1131 | Sys.catch_break false; | |
| 1132 | if not !no_output then List.iter (run_command ctx) (List.rev !cmds) | |
| 1133 | with | |
| 1134 | | Abort | Typecore.Fatal_error -> | |
| 1135 | () | |
| 1136 | | Common.Abort (m,p) -> | |
| 1137 | error ctx m p | |
| 1138 | | Lexer.Error (m,p) -> | |
| 1139 | error ctx (Lexer.error_msg m) p | |
| 1140 | | Parser.Error (m,p) -> | |
| 1141 | error ctx (Parser.error_msg m) p | |
| 1142 | | Typecore.Forbid_package ((pack,m,p),pl,pf) -> | |
| 1143 | if !Common.display_default && ctx.has_next then | |
| 1144 | () | |
| 1145 | else begin | |
| 1146 | error ctx (Printf.sprintf "You cannot access the %s package while %s (for %s)" pack (if pf = "macro" then "in a macro" else "targeting " ^ pf) (Ast.s_type_path m) ) p; | |
| 1147 | List.iter (error ctx " referenced here") (List.rev pl); | |
| 1148 | end | |
| 1149 | | Typecore.Error (m,p) -> | |
| 1150 | error ctx (Typecore.error_msg m) p | |
| 1151 | | Interp.Error (msg,p :: l) -> | |
| 1152 | message ctx msg p; | |
| 1153 | List.iter (message ctx "Called from") l; | |
| 1154 | error ctx "Aborted" Ast.null_pos; | |
| 1155 | | Failure msg | Arg.Bad msg -> | |
| 1156 | error ctx ("Error : " ^ msg) Ast.null_pos
| |
| 1157 | | Arg.Help msg -> | |
| 1158 | message ctx msg Ast.null_pos | |
| 1159 | | Typer.DisplayFields fields -> | |
| 1160 | let ctx = print_context() in | |
| 1161 | let fields = List.map (fun (name,t,doc) -> name, s_type ctx t, (match doc with None -> "" | Some d -> d)) fields in | |
| 1162 | let fields = if !measure_times then begin | |
| 1163 | close_times(); | |
| 1164 | let tot = ref 0. in | |
| 1165 | Hashtbl.iter (fun _ t -> tot := !tot +. t.total) Common.htimers; | |
| 1166 | let fields = ("@TOTAL", Printf.sprintf "%.3fs" (get_time() -. !start_time), "") :: fields in
| |
| 1167 | if !tot > 0. then | |
| 1168 | Hashtbl.fold (fun _ t acc -> | |
| 1169 | ("@TIME " ^ t.name, Printf.sprintf "%.3fs (%.0f%%)" t.total (t.total *. 100. /. !tot), "") :: acc
| |
| 1170 | ) Common.htimers fields | |
| 1171 | else fields | |
| 1172 | end else | |
| 1173 | fields | |
| 1174 | in | |
| 1175 | complete_fields fields | |
| 1176 | | Typer.DisplayTypes tl -> | |
| 1177 | let ctx = print_context() in | |
| 1178 | let b = Buffer.create 0 in | |
| 1179 | List.iter (fun t -> | |
| 1180 | Buffer.add_string b "<type>\n"; | |
| 1181 | Buffer.add_string b (htmlescape (s_type ctx t)); | |
| 1182 | Buffer.add_string b "\n</type>\n"; | |
| 1183 | ) tl; | |
| 1184 | raise (Completion (Buffer.contents b)) | |
| 1185 | | Parser.TypePath (p,c) -> | |
| 1186 | (match c with | |
| 1187 | | None -> | |
| 1188 | let packs, classes = read_type_path com p in | |
| 1189 | if packs = [] && classes = [] then | |
| 1190 | error ctx ("No classes found in " ^ String.concat "." p) Ast.null_pos
| |
| 1191 | else | |
| 1192 | complete_fields (List.map (fun f -> f,"","") (packs @ classes)) | |
| 1193 | | Some (c,cur_package) -> | |
| 1194 | try | |
| 1195 | let ctx = Typer.create com in | |
| 1196 | let rec lookup p = | |
| 1197 | try | |
| 1198 | Typeload.load_module ctx (p,c) Ast.null_pos | |
| 1199 | with e -> | |
| 1200 | if cur_package then | |
| 1201 | match List.rev p with | |
| 1202 | | [] -> raise e | |
| 1203 | | _ :: p -> lookup (List.rev p) | |
| 1204 | else | |
| 1205 | raise e | |
| 1206 | in | |
| 1207 | let m = lookup p in | |
| 1208 | complete_fields (List.map (fun t -> snd (t_path t),"","") (List.filter (fun t -> not (t_infos t).mt_private) m.m_types)) | |
| 1209 | with Completion c -> | |
| 1210 | raise (Completion c) | |
| 1211 | | _ -> | |
| 1212 | error ctx ("Could not load module " ^ (Ast.s_type_path (p,c))) Ast.null_pos)
| |
| 1213 | | e when (try Sys.getenv "OCAMLRUNPARAM" <> "b" || !global_cache <> None with _ -> true) -> | |
| 1214 | error ctx (Printexc.to_string e) Ast.null_pos | |
| 1215 | ||
| 1216 | ;; | |
| 1217 | let other = Common.timer "other" in | |
| 1218 | Sys.catch_break true; | |
| 1219 | let args = List.tl (Array.to_list Sys.argv) in | |
| 1220 | (try | |
| 1221 | let server = Sys.getenv "HAXE_COMPILATION_SERVER" in | |
| 1222 | let host, port = (try ExtString.String.split server ":" with _ -> "127.0.0.1", server) in | |
| 1223 | do_connect host (try int_of_string port with _ -> failwith "Invalid HAXE_COMPILATION_SERVER port") args | |
| 1224 | with Not_found -> try | |
| 1225 | process_params create_context args | |
| 1226 | with Completion c -> | |
| 1227 | prerr_endline c; | |
| 1228 | exit 0 | |
| 1229 | ); | |
| 1230 | other(); | |
| 1231 | if !measure_times then report_times prerr_endline |