args.ml 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. open Globals
  2. open Common
  3. open CompilationContext
  4. let columns = lazy (match Terminal_size.get_columns () with None -> 80 | Some c -> c)
  5. let limit_string s offset =
  6. let rest = (Lazy.force columns) - offset in
  7. let words = ExtString.String.nsplit s " " in
  8. let rec loop i words = match words with
  9. | word :: words ->
  10. if String.length word + i + 1 > rest then (Printf.sprintf "\n%*s" offset "") :: word :: loop (String.length word) words
  11. else (if i = 0 then "" else " ") :: word :: loop (i + 1 + String.length word) words
  12. | [] ->
  13. []
  14. in
  15. String.concat "" (loop 0 words)
  16. let usage_string ?(print_cat=true) arg_spec usage =
  17. let make_label = fun names hint -> Printf.sprintf "%s %s" (String.concat ", " names) hint in
  18. let args = (List.filter (fun (cat, ok, dep, spec, hint, doc) -> (List.length ok) > 0) arg_spec) in
  19. let cat_order = ["Target";"Compilation";"Optimization";"Debug";"Batch";"Services";"Compilation Server";"Target-specific";"Miscellaneous"] in
  20. let cats = List.filter (fun x -> List.mem x (List.map (fun (cat, _, _, _, _, _) -> cat) args)) cat_order in
  21. let max_length = List.fold_left max 0 (List.map String.length (List.map (fun (_, ok, _, _, hint, _) -> make_label ok hint) args)) in
  22. usage ^ (String.concat "\n" (List.flatten (List.map (fun cat -> (if print_cat then ["\n"^cat^":"] else []) @ (List.map (fun (cat, ok, dep, spec, hint, doc) ->
  23. let label = make_label ok hint in
  24. Printf.sprintf " %s%s %s" label (String.make (max_length - (String.length label)) ' ') doc
  25. ) (List.filter (fun (cat', _, _, _, _, _) -> (if List.mem cat' cat_order then cat' else "Miscellaneous") = cat) args))) cats)))
  26. let process_args arg_spec =
  27. List.flatten(List.map (fun (cat, ok, dep, spec, hint, doc) ->
  28. (* official argument names *)
  29. (List.map (fun (arg) -> (arg, spec, doc)) ok) @
  30. let dep_fun arg spec = () in
  31. let dep_spec arg spec = match spec with
  32. | Arg.String f -> Arg.String (fun x -> dep_fun arg spec; f x)
  33. | Arg.Unit f -> Arg.Unit (fun x -> dep_fun arg spec; f x)
  34. | Arg.Bool f -> Arg.Bool (fun x -> dep_fun arg spec; f x)
  35. | _ -> spec in
  36. (List.map (fun (arg) -> (arg, dep_spec arg spec, doc)) dep)
  37. ) arg_spec)
  38. let parse_args com =
  39. let usage = Printf.sprintf
  40. "Haxe Compiler %s - (C)2005-2024 Haxe Foundation\nUsage: haxe%s <target> [options] [hxml files and dot paths...]\n"
  41. s_version_full (if Sys.os_type = "Win32" then ".exe" else "")
  42. in
  43. let actx = {
  44. classes = [([],"Std")];
  45. xml_out = None;
  46. hxb_out = None;
  47. json_out = None;
  48. cmds = [];
  49. config_macros = [];
  50. no_output = false;
  51. did_something = false;
  52. force_typing = false;
  53. pre_compilation = [];
  54. interp = false;
  55. jvm_flag = false;
  56. swf_version = false;
  57. hxb_libs = [];
  58. native_libs = [];
  59. raise_usage = (fun () -> ());
  60. display_arg = None;
  61. deprecations = [];
  62. } in
  63. let add_deprecation s =
  64. actx.deprecations <- s :: actx.deprecations
  65. in
  66. let add_native_lib file extern kind =
  67. let lib = create_native_lib file extern kind in
  68. actx.native_libs <- lib :: actx.native_libs
  69. in
  70. let basic_args_spec = [
  71. ("Target",["--js"],["-js"],Arg.String (set_platform com Js),"<file>","generate JavaScript code into target file");
  72. ("Target",["--lua"],["-lua"],Arg.String (set_platform com Lua),"<file>","generate Lua code into target file");
  73. ("Target",["--swf"],["-swf"],Arg.String (set_platform com Flash),"<file>","generate Flash SWF bytecode into target file");
  74. ("Target",["--neko"],["-neko"],Arg.String (set_platform com Neko),"<file>","generate Neko bytecode into target file");
  75. ("Target",["--php"],["-php"],Arg.String (fun dir ->
  76. actx.classes <- (["php"],"Boot") :: actx.classes;
  77. set_platform com Php dir;
  78. ),"<directory>","generate PHP code into target directory");
  79. ("Target",["--cpp"],["-cpp"],Arg.String (fun dir ->
  80. set_platform com Cpp dir;
  81. ),"<directory>","generate C++ code into target directory");
  82. ("Target",["--cppia"],["-cppia"],Arg.String (fun file ->
  83. Common.define com Define.Cppia;
  84. set_platform com Cpp file;
  85. ),"<file>","generate Cppia bytecode into target file");
  86. ("Target",["--jvm"],["-jvm"],Arg.String (fun dir ->
  87. actx.jvm_flag <- true;
  88. set_platform com Jvm dir;
  89. ),"<file>","generate JVM bytecode into target file");
  90. ("Target",["--python"],["-python"],Arg.String (fun dir ->
  91. set_platform com Python dir;
  92. ),"<file>","generate Python code into target file");
  93. ("Target",["--hl"],["-hl"],Arg.String (fun file ->
  94. set_platform com Hl file;
  95. ),"<file>","generate HashLink .hl bytecode or .c code into target file");
  96. ("Target",["--custom-target"],["-custom"],Arg.String (fun target ->
  97. let name, path = try let split = ExtString.String.split target "=" in split with _ -> target, "" in
  98. set_custom_target com name path;
  99. ),"<name[=path]>","generate code for a custom target");
  100. ("Target",[],["-x"], Arg.String (fun cl ->
  101. let cpath = Path.parse_type_path cl in
  102. (match com.main.main_class with
  103. | Some c -> if cpath <> c then raise (Arg.Bad "Multiple --main classes specified")
  104. | None -> com.main.main_class <- Some cpath);
  105. actx.classes <- cpath :: actx.classes;
  106. Common.define com Define.Interp;
  107. set_platform com Eval "";
  108. actx.interp <- true;
  109. ),"<class>","interpret the program using internal macro system");
  110. ("Target",["--interp"],[], Arg.Unit (fun() ->
  111. Common.define com Define.Interp;
  112. set_platform com Eval "";
  113. actx.interp <- true;
  114. ),"","interpret the program using internal macro system");
  115. ("Target",["--run"],[], Arg.Unit (fun() ->
  116. raise (Arg.Bad "--run requires an argument: a Haxe module name")
  117. ), "<module> [args...]","interpret a Haxe module with command line arguments");
  118. ("Compilation",["-p";"--class-path"],["-cp"],Arg.String (fun path ->
  119. com.class_paths#add (new ClassPath.directory_class_path (Path.add_trailing_slash path) User);
  120. ),"<path>","add a directory to find source files");
  121. ("Compilation",[],["-libcp"],Arg.String (fun path ->
  122. com.class_paths#add (new ClassPath.directory_class_path (Path.add_trailing_slash path) Lib);
  123. ),"<path>","add a directory to find source files");
  124. ("Compilation",["--hxb-lib"],["-hxb-lib"],Arg.String (fun file ->
  125. let lib = create_native_lib file false HxbLib in
  126. actx.hxb_libs <- lib :: actx.hxb_libs
  127. ),"<path>","add a hxb library");
  128. ("Compilation",["-m";"--main"],["-main"],Arg.String (fun cl ->
  129. if com.main.main_class <> None then raise (Arg.Bad "Multiple --main classes specified");
  130. let cpath = Path.parse_type_path cl in
  131. com.main.main_class <- Some cpath;
  132. actx.classes <- cpath :: actx.classes
  133. ),"<class>","select startup class");
  134. ("Compilation",["-L";"--library"],["-lib"],Arg.String (fun _ -> ()),"<name[:ver]>","use a haxelib library");
  135. ("Compilation",["-D";"--define"],[],Arg.String (fun var ->
  136. let flag, value = try let split = ExtString.String.split var "=" in (fst split, Some (snd split)) with _ -> var, None in
  137. match value with
  138. | Some value -> Common.external_define_value com flag value
  139. | None -> Common.external_define com flag;
  140. ),"<var[=value]>","define a conditional compilation flag");
  141. ("Compilation",["--undefine"],[],Arg.String (fun var ->
  142. Common.external_undefine com var
  143. ),"","remove a conditional compilation flag");
  144. ("Debug",["-v";"--verbose"],[],Arg.Unit (fun () ->
  145. com.verbose <- true
  146. ),"","turn on verbose mode");
  147. ("Debug",["--debug"],["-debug"], Arg.Unit (fun() ->
  148. Common.define com Define.Debug;
  149. com.debug <- true;
  150. ),"","add debug information to the compiled code");
  151. ("Miscellaneous",["--version"],["-version"],Arg.Unit (fun() ->
  152. raise (Helper.HelpMessage s_version_full);
  153. ),"","print version and exit");
  154. ("Miscellaneous", ["-h";"--help"], ["-help"], Arg.Unit (fun () ->
  155. raise (Arg.Help "")
  156. ),"","show extended help information");
  157. ("Miscellaneous",["--help-defines"],[], Arg.Unit (fun() ->
  158. let all,max_length = Define.get_documentation_list com.user_defines in
  159. let all = List.map (fun (n,doc) -> Printf.sprintf " %-*s: %s" max_length n (limit_string doc (max_length + 3))) all in
  160. raise (Helper.HelpMessage (ExtLib.String.join "\n" all));
  161. ),"","print help for all compiler specific defines");
  162. ("Miscellaneous",["--help-user-defines"],[], Arg.Unit (fun() ->
  163. actx.did_something <- true;
  164. com.callbacks#add_after_init_macros (fun() ->
  165. let all,max_length = Define.get_user_documentation_list com.user_defines in
  166. let all = List.map (fun (n,doc) -> Printf.sprintf " %-*s: %s" max_length n (limit_string doc (max_length + 3))) all in
  167. raise (Helper.HelpMessage (ExtLib.String.join "\n" all));
  168. )
  169. ),"","print help for all user defines");
  170. ("Miscellaneous",["--help-metas"],[], Arg.Unit (fun() ->
  171. let all,max_length = Meta.get_documentation_list com.user_metas in
  172. let all = List.map (fun (n,doc) -> Printf.sprintf " %-*s: %s" max_length n (limit_string doc (max_length + 3))) all in
  173. raise (Helper.HelpMessage (ExtLib.String.join "\n" all));
  174. ),"","print help for all compiler metadatas");
  175. ("Miscellaneous",["--help-user-metas"],[], Arg.Unit (fun() ->
  176. actx.did_something <- true;
  177. com.callbacks#add_after_init_macros (fun() ->
  178. let all,max_length = Meta.get_user_documentation_list com.user_metas in
  179. let all = List.map (fun (n,doc) -> Printf.sprintf " %-*s: %s" max_length n (limit_string doc (max_length + 3))) all in
  180. raise (Helper.HelpMessage (ExtLib.String.join "\n" all));
  181. )
  182. ),"","print help for all user metadatas");
  183. ] in
  184. let adv_args_spec = [
  185. ("Optimization",["--dce"],["-dce"],Arg.String (fun mode ->
  186. (match mode with
  187. | "std" | "full" | "no" -> ()
  188. | _ -> raise (Arg.Bad "Invalid DCE mode, expected std | full | no"));
  189. Common.define_value com Define.Dce mode
  190. ),"[std|full|no]","set the dead code elimination mode (default std)");
  191. ("Target-specific",["--swf-version"],["-swf-version"],Arg.Float (fun v ->
  192. if not actx.swf_version || com.flash_version < v then com.flash_version <- v;
  193. actx.swf_version <- true;
  194. ),"<version>","change the SWF version");
  195. ("Target-specific",["--swf-header"],["-swf-header"],Arg.String (fun h ->
  196. add_deprecation "-swf-header has been deprecated, use -D swf-header instead";
  197. define_value com Define.SwfHeader h
  198. ),"<header>","define SWF header (width:height:fps:color)");
  199. ("Target-specific",["--flash-strict"],[],Arg.Unit (fun () ->
  200. add_deprecation "--flash-strict has been deprecated, use -D flash-strict instead";
  201. Common.define com Define.FlashStrict
  202. ), "","more type strict flash API");
  203. ("Target-specific",["--swf-lib"],["-swf-lib"],Arg.String (fun file ->
  204. add_native_lib file false SwfLib;
  205. ),"<file>","add the SWF library to the compiled SWF");
  206. ("Target-specific",[],["--neko-lib-path"],Arg.String (fun dir ->
  207. com.neko_lib_paths <- dir :: com.neko_lib_paths
  208. ),"<directory>","add the neko library path");
  209. ("Target-specific",["--swf-lib-extern"],["-swf-lib-extern"],Arg.String (fun file ->
  210. add_native_lib file true SwfLib;
  211. ),"<file>","use the SWF library for type checking");
  212. ("Target-specific",["--java-lib"],["-java-lib"],Arg.String (fun file ->
  213. add_native_lib file false JavaLib;
  214. ),"<file>","add an external JAR or directory of JAR files");
  215. ("Target-specific",["--java-lib-extern"],[],Arg.String (fun file ->
  216. add_native_lib file true JavaLib;
  217. ),"<file>","use an external JAR or directory of JAR files for type checking");
  218. ("Compilation",["-r";"--resource"],["-resource"],Arg.String (fun res ->
  219. let file, name = (match ExtString.String.nsplit res "@" with
  220. | [file; name] -> file, name
  221. | [file] -> file, file
  222. | _ -> raise (Arg.Bad "Invalid Resource format, expected file@name")
  223. ) in
  224. let file = (try Common.find_file com file with Not_found -> file) in
  225. let data = (try
  226. let s = Std.input_file ~bin:true file in
  227. if String.length s > 12000000 then raise Exit;
  228. s;
  229. with
  230. | Sys_error _ -> failwith ("Resource file not found: " ^ file)
  231. | _ -> failwith ("Resource '" ^ file ^ "' excess the maximum size of 12MB")
  232. ) in
  233. if Hashtbl.mem com.resources name then failwith ("Duplicate resource name " ^ name);
  234. Hashtbl.add com.resources name data
  235. ),"<file>[@name]","add a named resource file");
  236. ("Debug",["--prompt"],["-prompt"], Arg.Unit (fun() -> Helper.prompt := true),"","prompt on error");
  237. ("Compilation",["--cmd"],["-cmd"], Arg.String (fun cmd ->
  238. actx.cmds <- Helper.unquote cmd :: actx.cmds
  239. ),"<command>","run the specified command after successful compilation");
  240. ("Optimization",["--no-traces"],[], Arg.Unit (fun () ->
  241. add_deprecation "--no-traces has been deprecated, use -D no-traces instead";
  242. Common.define com Define.NoTraces
  243. ), "","don't compile trace calls in the program");
  244. ("Batch",["--next"],[], Arg.Unit (fun() -> die "" __LOC__), "","separate several haxe compilations");
  245. ("Batch",["--each"],[], Arg.Unit (fun() -> die "" __LOC__), "","append preceding parameters to all Haxe compilations separated by --next");
  246. ("Services",["--display"],[], Arg.String (fun input ->
  247. actx.display_arg <- Some input;
  248. ),"","display code tips");
  249. ("Services",["--xml"],["-xml"],Arg.String (fun file ->
  250. actx.xml_out <- Some file
  251. ),"<file>","generate XML types description");
  252. ("Services",["--json"],[],Arg.String (fun file ->
  253. actx.json_out <- Some file
  254. ),"<file>","generate JSON types description");
  255. ("Services",["--hxb"],[], Arg.String (fun file ->
  256. actx.hxb_out <- Some file;
  257. ),"<file>", "generate haxe binary representation to target archive");
  258. ("Optimization",["--no-output"],[], Arg.Unit (fun() -> actx.no_output <- true),"","compiles but does not generate any file");
  259. ("Debug",["--times"],[], Arg.Unit (fun() -> Timer.measure_times := true),"","measure compilation times");
  260. ("Optimization",["--no-inline"],[],Arg.Unit (fun () ->
  261. add_deprecation "--no-inline has been deprecated, use -D no-inline instead";
  262. Common.define com Define.NoInline
  263. ), "","disable inlining");
  264. ("Optimization",["--no-opt"],[], Arg.Unit (fun() ->
  265. com.foptimize <- false;
  266. Common.define com Define.NoOpt;
  267. ), "","disable code optimizations");
  268. ("Compilation",["--remap"],[], Arg.String (fun s ->
  269. let pack, target = (try ExtString.String.split s ":" with _ -> raise (Arg.Bad "Invalid remap format, expected source:target")) in
  270. com.package_rules <- PMap.add pack (Remap target) com.package_rules;
  271. ),"<package:target>","remap a package to another one");
  272. ("Compilation",["--macro"],[], Arg.String (fun e ->
  273. actx.force_typing <- true;
  274. actx.config_macros <- e :: actx.config_macros
  275. ),"<macro>","call the given macro before typing anything else");
  276. ("Compilation Server",["--server-listen"],["--wait"], Arg.String (fun hp ->
  277. die "" __LOC__
  278. ),"[[host:]port]|stdio]","wait on the given port (or use standard i/o) for commands to run");
  279. ("Compilation Server",["--server-connect"],[], Arg.String (fun hp ->
  280. die "" __LOC__
  281. ),"[host:]port]","connect to the given port and wait for commands to run");
  282. ("Compilation Server",["--connect"],[],Arg.String (fun _ ->
  283. die "" __LOC__
  284. ),"<[host:]port>","connect on the given port and run commands there");
  285. ("Compilation",["-C";"--cwd"],[], Arg.String (fun dir ->
  286. (try Unix.chdir dir with _ -> raise (Arg.Bad ("Invalid directory: " ^ dir)));
  287. actx.did_something <- true;
  288. ),"<directory>","set current working directory");
  289. ("Compilation",["--haxelib-global"],[], Arg.Unit (fun () -> ()),"","pass --global argument to haxelib");
  290. ("Compilation",["-w"],[], Arg.String (fun s ->
  291. let p = { pfile = "-w " ^ s; pmin = 0; pmax = 0 } in
  292. let l = Warning.parse_options s p in
  293. com.warning_options <- l :: com.warning_options
  294. ),"<warning list>","enable or disable specific warnings");
  295. ] in
  296. let args_callback cl =
  297. begin try
  298. let path,name = Path.parse_path cl in
  299. if StringHelper.starts_uppercase_identifier name then
  300. actx.classes <- (path,name) :: actx.classes
  301. else begin
  302. actx.force_typing <- true;
  303. actx.config_macros <- (Printf.sprintf "include('%s', true, null, null, true)" cl) :: actx.config_macros;
  304. end
  305. with Failure _ when ignore_error com ->
  306. ()
  307. end
  308. in
  309. let all_args = (basic_args_spec @ adv_args_spec) in
  310. let all_args_spec = process_args all_args in
  311. let process args =
  312. let current = ref 0 in
  313. (try
  314. let rec loop acc args = match args with
  315. | "--display" :: arg :: args ->
  316. loop (arg :: "--display" :: acc) args
  317. | arg :: args ->
  318. loop (Helper.expand_env arg :: acc) args
  319. | [] ->
  320. List.rev acc
  321. in
  322. let args = loop [] args in
  323. Arg.parse_argv ~current (Array.of_list ("" :: args)) all_args_spec args_callback "";
  324. with
  325. | Arg.Help _ ->
  326. raise (Helper.HelpMessage (usage_string all_args usage))
  327. | Arg.Bad msg ->
  328. let first_line = List.nth (Str.split (Str.regexp "\n") msg) 0 in
  329. let new_msg = (Printf.sprintf "%s" first_line) in
  330. let r = Str.regexp "unknown option [`']?\\([-A-Za-z]+\\)[`']?" in
  331. try
  332. ignore(Str.search_forward r msg 0);
  333. let s = Str.matched_group 1 msg in
  334. let sl = List.map (fun (s,_,_) -> s) all_args_spec in
  335. let sl = StringError.get_similar s sl in
  336. begin match sl with
  337. | [] -> raise Not_found
  338. | _ ->
  339. let spec = List.filter (fun (_,sl',sl'',_,_,_) ->
  340. List.exists (fun s -> List.mem s sl) (sl' @ sl'')
  341. ) all_args in
  342. let new_msg = (Printf.sprintf "%s\nDid you mean:\n%s" first_line (usage_string ~print_cat:false spec "")) in
  343. raise (Arg.Bad new_msg)
  344. end;
  345. with Not_found ->
  346. raise (Arg.Bad new_msg));
  347. if com.platform = Globals.Cpp && not (Define.defined com.defines DisableUnicodeStrings) && not (Define.defined com.defines HxcppSmartStings) then begin
  348. Define.define com.defines HxcppSmartStings;
  349. end;
  350. if Define.raw_defined com.defines "gen_hx_classes" then begin
  351. (* TODO: this is something we're gonna remove once we have something nicer for generating flash externs *)
  352. actx.force_typing <- true;
  353. actx.pre_compilation <- (fun() ->
  354. let process_lib lib =
  355. if not (lib#has_flag NativeLibraries.FlagIsStd) then
  356. List.iter (fun path -> if path <> (["java";"lang"],"String") then actx.classes <- path :: actx.classes) lib#list_modules
  357. in
  358. List.iter process_lib com.native_libs.swf_libs;
  359. List.iter process_lib com.native_libs.java_libs;
  360. ) :: actx.pre_compilation;
  361. actx.xml_out <- Some "hx"
  362. end;
  363. in
  364. actx.raise_usage <- (fun () -> raise (Helper.HelpMessage (usage_string basic_args_spec usage)));
  365. (* Handle CLI arguments *)
  366. process com.args;
  367. actx