make_tools.ml (22565B)
1 #directory "+unix";; 2 #load "unix.cma";; 3 4 module type TOOLS = sig 5 module Env : Map.S with type key = string 6 val env : string Env.t (* Environment variables *) 7 val args : string Env.t (* Command line argument variables *) 8 val inputs : string Env.t (* [env] + [args], with [args] taking priority *) 9 val vars : string Env.t ref (* Internally defined variables *) 10 val ( .$() ) : string Env.t -> string -> string (* Get value from any map *) 11 val ( $ ) : string -> string (* Value from [vars] with fallback to [inputs] *) 12 val ( <-- ) : string -> string -> unit (* Set value in [vars] *) 13 val ( <-+= ) : string -> string -> unit (* Append to value in [vars] (defaults to value in [inputs]) *) 14 val ( <--? ) : string -> string -> string (* Copy from [inputs] to [vars], or set a default *) 15 val shell : ?err_null:bool -> ?exit_status:Unix.process_status ref -> string -> string 16 val shell_input : ?exit_status:Unix.process_status ref -> string -> string -> string 17 val get_command : string -> string option 18 val is_empty : string -> bool 19 val not_empty : string -> bool 20 val has_substring : string -> string -> bool 21 val exists : string -> string -> bool 22 val info : string -> unit 23 val error : string -> 'a 24 end 25 module Make = functor (Tools : TOOLS) -> struct 26 open Tools 27 28 let output = ref [] 29 let outp s = output := s :: !output 30 31 (* [2023-05] This check is here only temporarily for a smooth transition *) 32 let () = 33 if inputs.$("STATIC") = "true" then 34 error "Variable STATIC is no longer in use. Please set appropriate \ 35 LDFLAGS, like -static, instead. See build instructions." 36 37 (********************************************************************* 38 *** Compilers ***) 39 40 let tool_prefix = "TOOL_PREFIX" <--? 41 if inputs.$("OSCOMP") = "cross" then "x86_64-w64-mingw32-" else "" 42 (* FIXME: OSCOMP is a legacy argument; probably not used by anyone *) 43 44 let ocamlc = "OCAMLC" <--? tool_prefix ^ "ocamlc" 45 let ocamlopt = "OCAMLOPT" <--? tool_prefix ^ "ocamlopt" 46 47 let ocaml_conf_var varname = shell (ocamlc ^ " -config-var " ^ varname) 48 49 (********************************************************************* 50 *** Try to automatically guess OS ***) 51 52 (* Cygwin is for doing POSIX builds in Windows. 53 MinGW is for native Windows builds in a minimal POSIX environment. 54 MSVC is for native Windows builds without POSIX environment. *) 55 56 let osarch = "OSARCH" <--? 57 match ocaml_conf_var "os_type" with 58 | "Win32" -> "Win32" (* Native Windows build *) 59 | "Cygwin" -> "Cygwin" (* POSIX build for Windows *) 60 | _ -> shell "uname" 61 (* Darwin is reported for macOS 62 SunOS is reported for Solaris, OpenSolaris and derivatives (illumos) *) 63 64 let osarch_win32 = osarch = "Win32" 65 let osarch_cygwin = osarch = "Cygwin" 66 let osarch_macos = osarch = "Darwin" 67 68 (********************************************************************* 69 *** Try to automatically guess UI style ***) 70 71 let () = 72 if inputs.$("UISTYLE") <> "" then 73 error "UISTYLE is no longer used. See build instructions." 74 75 let ocaml_libdir = "OCAMLLIBDIR" <--? ocaml_conf_var "standard_library" 76 77 let ocamlfind = get_command "ocamlfind" 78 79 let build_GUI = 80 let has_lablgtk3 = 81 match ocamlfind with 82 | Some cmd -> shell ~err_null:true (cmd ^ " query lablgtk3") |> not_empty 83 | None -> exists ocaml_libdir "lablgtk3" 84 in 85 if not has_lablgtk3 then begin 86 info "GUI library lablgtk not found. GTK GUI will not be built." 87 end; 88 has_lablgtk3 89 let () = if build_GUI then outp "guimaybe: gui" 90 91 let build_macGUI = 92 if osarch_macos then begin 93 (* If XCode is not installed, xcodebuild is just a placeholder telling 94 that XCode is not installed and any invocation of xcodebuild results 95 in a non-0 exit code. *) 96 if Sys.command "xcodebuild -version > /dev/null" = 0 then 97 true 98 else begin 99 info "Not building macOS native GUI because XCode is not installed."; 100 false 101 end 102 end else begin 103 info "Not on macOS. macOS native GUI will not be built."; 104 false 105 end 106 let () = if build_macGUI then outp "macuimaybe: macui" 107 108 (********************************************************************* 109 *** Default parameters ***) 110 111 (* Generate backtrace information for exceptions *) 112 let () = "CAMLFLAGS" <-+= "-g $(INCLFLAGS)" 113 114 (* Use 64-bit file offset if possible (for copy_stubs.c). It is 115 included here unconditionally since OCaml itself has had this 116 defined unconditionally since 2002. *) 117 let () = "CAMLCFLAGS" <-+= "-ccopt -D_FILE_OFFSET_BITS=64" 118 119 let () = 120 [ 121 "CAMLCFLAGS", "CFLAGS", "-ccopt"; 122 "CAMLCFLAGS", "CPPFLAGS", "-ccopt"; 123 "CAMLLDFLAGS", "LDFLAGS", "-cclib"; 124 "CLIBS", "LDLIBS", "-cclib"; 125 ] 126 |> List.iter (fun (varname, envname, arg) -> 127 if not_empty inputs.$(envname) then 128 varname <-+= arg ^ " \"" ^ inputs.$(envname) ^ "\"") 129 130 (* The messy situation requiring the use of OUTPUT_SEL was fixed in OCaml 4.13. 131 All usages of OUTPUT_SEL should be removed when 4.13 becomes a requirement. *) 132 let () = 133 if osarch_win32 then begin 134 (* Native Win32 build *) 135 "EXEC_EXT" <-- ".exe"; 136 "OBJ_EXT" <-- ocaml_conf_var "ext_obj"; 137 if ocaml_conf_var "ccomp_type" = "msvc" then begin 138 "OUTPUT_SEL" <-- "-Fo"; 139 "CLIBS" <-+= "-cclib user32.lib -cclib \"-link win32rc/unison.res\""; 140 outp "buildexecutable: win32rc/unison.res"; 141 end else begin 142 "OUTPUT_SEL" <-- "-o"; 143 "CLIBS" <-+= "-cclib \"-link win32rc/unison.res.lib\""; 144 outp "buildexecutable: win32rc/unison.res.lib"; 145 end; 146 (* Make Windows GUI come up with no console window *) 147 if ($)"UI_WINOS" <> "hybrid" then begin 148 "CAMLLDFLAGS_GUI" <-- "-cclib \"-subsystem windows\""; 149 (* -subsystem is a flexlink arg; 150 respective gcc args: -mwindows or -Wl,--subsystem,windows 151 respective MSVC linker arg: /SUBSYSTEM:WINDOWS *) 152 end; 153 "CWD" <-- "."; 154 "WINCOBJS" <-+= "system/system_win_stubs" ^ ($)"OBJ_EXT" ^ " lwt/lwt_unix_stubs" ^ ($)"OBJ_EXT"; 155 "WINOBJS" <-- "system/system_win.cmo"; 156 "SYSTEM" <-- "win"; 157 "building_for" <-- "Building for Windows"; 158 end else begin 159 (* Unix build, or Cygwin POSIX (GNU C) build *) 160 "OBJ_EXT" <-- ".o"; 161 "OUTPUT_SEL" <-- "'-o '"; 162 "WINOBJS" <-- ""; 163 "SYSTEM" <-- "generic"; 164 (* This is not strictly necessary as Cygwin can do a generic Unix build 165 (and is actually meant to). *) 166 if osarch_cygwin then begin 167 "CWD" <-- "."; 168 "EXEC_EXT" <-- ".exe"; 169 "CLIBS" <-+= "-cclib win32rc/unison.res.lib"; 170 outp "buildexecutable: win32rc/unison.res.lib"; 171 "building_for" <-- "Building for Cygwin"; 172 end else begin 173 "CWD" <-- shell "pwd"; 174 "EXEC_EXT" <-- ""; 175 (* openpty is in the libutil library *) 176 if not osarch_macos && osarch <> "SunOS" then begin 177 "CLIBS" <-+= "-cclib -lutil" 178 end; 179 if osarch = "SunOS" then begin 180 (* ACL functions *) 181 "CLIBS" <-+= "-cclib -lsec"; 182 "CLIBS" <-+= "-cclib -lsendfile"; 183 end; 184 "building_for" <-- "Building for Unix"; 185 end; 186 outp "manpage: manpagefile"; 187 outp "docs: docfiles"; 188 outp "clean::\n\tcd ../doc && $(MAKE) clean"; 189 end 190 191 (********************************************************************* 192 *** Compilation boilerplate ***) 193 194 let native = 195 let native = 196 let nat = ($)"NATIVE" |> String.lowercase_ascii in 197 if nat <> "true" && nat <> "false" then 198 get_command ocamlopt <> None 199 else bool_of_string nat 200 in 201 "NATIVE" <-- string_of_bool native; 202 native 203 204 let () = 205 if native then begin 206 (* Set up for native code compilation *) 207 "CAMLC" <-- ocamlopt; 208 "CAMLOBJS" <-- "$(OCAMLOBJS:.cmo=.cmx)"; 209 "CAMLOBJS_TUI" <-- "$(OCAMLOBJS_TUI:.cmo=.cmx)"; 210 "CAMLOBJS_GUI" <-- "$(OCAMLOBJS_GUI:.cmo=.cmx)"; 211 "CAMLOBJS_FSM" <-- "$(FSMOCAMLOBJS:.cmo=.cmx)"; 212 "CAMLOBJS_MAC" <-- "$(OCAMLOBJS_MAC:.cmo=.cmx)"; 213 "CAMLLIBS" <-- "$(OCAMLLIBS:.cma=.cmxa)"; 214 "CAMLLIBS_TUI" <-- "$(OCAMLLIBS_TUI:.cma=.cmxa)"; 215 "CAMLLIBS_GUI" <-- "$(OCAMLLIBS_GUI:.cma=.cmxa)"; 216 "CAMLLIBS_MAC" <-- "$(OCAMLLIBS_MAC:.cma=.cmxa)"; 217 "CAMLLIBS_FSM" <-- "$(FSMOCAMLLIBS:.cma=.cmxa)"; 218 end else begin 219 (* Set up for bytecode compilation *) 220 "CAMLC" <-- ocamlc; 221 (* -output-complete-exe is available since OCaml 4.10 222 OCaml > 5.2.0 no longer supports detection of compiler options, 223 hence the hack of comparing the output to -version. *) 224 if String.trim (shell (ocamlc ^ " -output-complete-exe -version 2>&1")) = 225 String.trim (shell (ocamlc ^ " -version")) then begin 226 "CAMLLDFLAGS" <-+= "-output-complete-exe"; (* can safely strip the binary *) 227 end else begin 228 "CAMLLDFLAGS" <-+= "-custom"; 229 end; 230 "CAMLOBJS" <-- "$(OCAMLOBJS)"; 231 "CAMLOBJS_TUI" <-- "$(OCAMLOBJS_TUI)"; 232 "CAMLOBJS_GUI" <-- "$(OCAMLOBJS_GUI)"; 233 "CAMLOBJS_MAC" <-- "$(OCAMLOBJS_MAC)"; 234 "CAMLOBJS_FSM" <-- "$(FSMOCAMLOBJS)"; 235 "CAMLLIBS" <-- "$(OCAMLLIBS)"; 236 "CAMLLIBS_TUI" <-- "$(OCAMLLIBS_TUI)"; 237 "CAMLLIBS_GUI" <-- "$(OCAMLLIBS_GUI)"; 238 "CAMLLIBS_MAC" <-- "$(OCAMLLIBS_MAC)"; 239 "CAMLLIBS_FSM" <-- "$(FSMOCAMLLIBS)"; 240 end 241 242 let () = "WINDRES" <-- 243 (if not_empty tool_prefix then tool_prefix else begin 244 let cc = Filename.basename (ocaml_conf_var "c_compiler") in 245 let rec extract_prefix pre l = 246 match l with 247 | [] -> "" (* could not detect the prefix *) 248 | x :: xs -> 249 let found_base = 250 String.length x > 1 && String.(sub x 0 2 |> lowercase_ascii) = "cl" || 251 String.length x > 2 && String.(sub x 0 3 |> lowercase_ascii) = "gcc" 252 in 253 if not found_base then extract_prefix (x :: pre) xs 254 else if pre = [] then "" else (String.concat "-" (List.rev pre)) ^ "-" 255 in 256 extract_prefix [] (String.split_on_char '-' cc) 257 end) ^ "windres" 258 259 let () = 260 if is_empty inputs.$("_NMAKE_VER") then begin 261 if is_empty inputs.$("MAKE") || has_substring "FRC true" ( 262 shell_input "_cf_test: FRC ; @echo $^ true\nFRC: ;" 263 (inputs.$("MAKE") ^ " -f -")) then 264 "ALL__SRC" <-- "$^" (* GNU and POSIX make, new versions of BSD make *) 265 else 266 "ALL__SRC" <-- "$>" (* Older versions of BSD make *) 267 end else 268 "ALL__SRC" <-- "$(**)" (* NMAKE; enclose in brackets for safety if not run by NMAKE *) 269 270 let () = "rule_sep" <-- if not_empty inputs.$("ASSUME_DMAKE") then ":=" else ":" 271 272 (********************************************************************* 273 *** User Interface setup ***) 274 275 let () = 276 if not build_GUI && (not_empty inputs.$("_NMAKE_VER") || osarch = "OpenBSD") then 277 "CAMLFLAGS_GUI" <--? "" |> ignore 278 else 279 "CAMLFLAGS_GUI" <-+= 280 match ocamlfind with 281 | Some cmd -> 282 (* The weird quoting is required for Windows, but harmless in sh *) 283 shell (cmd ^ " query -format \"-I \"\"%d\"\"\" lablgtk3") ^ " " ^ 284 shell (cmd ^ " query -format \"-I \"\"%d\"\"\" cairo2") 285 | None -> "-I +lablgtk3 -I +cairo2" 286 287 let () = 288 if osarch_macos && is_empty inputs.$("XCODEFLAGS") then 289 (* Prevent Xcode from trying to build universal binaries by default *) 290 "XCODEFLAGS" <-- "-arch " ^ (shell "uname -m") 291 292 (********************************************************************* 293 *** Filesystem monitoring ***) 294 295 let fsmon_outp = ref [] 296 297 let fsmonitor_dir = 298 match osarch with 299 | "Linux" -> "FSMDIR" <--? "fsmonitor/inotify" 300 | "SunOS" -> "FSMDIR" <--? "fsmonitor/solaris" 301 | "Win32" -> "FSMDIR" <--? "fsmonitor/windows" 302 | "FreeBSD" | "OpenBSD" | "NetBSD" | "DragonFly" -> 303 begin 304 let libnotify_exists = 305 shell "pkg-config --exists libinotify && echo true" = "true" in 306 if libnotify_exists then begin 307 let pkg_flags = shell "pkg-config --cflags libinotify 2> /dev/null" in 308 if not_empty pkg_flags then begin 309 let libinotify_inc = "-ccopt '" ^ pkg_flags ^ "'" in 310 fsmon_outp := ("CAMLCFLAGS_FSM = " ^ libinotify_inc) :: !fsmon_outp 311 end; 312 let pkg_lib = shell "pkg-config --libs libinotify 2> /dev/null" in 313 if not_empty pkg_lib then begin 314 let libinotify_lib = "-cclib '" ^ pkg_lib ^ "'" in 315 fsmon_outp := ("CLIBS_FSM = " ^ libinotify_lib) :: !fsmon_outp 316 end; 317 "FSMDIR" <--? "fsmonitor/inotify" 318 end else 319 inputs.$("FSMDIR") 320 end 321 | _ -> inputs.$("FSMDIR") 322 323 let build_fsmonitor = 324 if not_empty fsmonitor_dir then begin 325 outp ("include " ^ fsmonitor_dir ^ "/Makefile"); 326 List.iter outp !fsmon_outp; 327 true 328 end else begin 329 info "fsmonitor implementation is not available or not configured for this system. fsmonitor will not be built."; 330 false 331 end 332 let () = if build_fsmonitor then outp "fsmonitor: fsmonitorexecutable" 333 334 335 let () = "RM" <-- if is_empty inputs.$("_NMAKE_VER") then "rm -f" else "del /f" 336 337 338 (*** Output configuration ***) 339 340 let () = 341 Env.iter (fun k v -> print_string k; print_string " = "; print_endline v) !vars 342 343 let () = 344 List.iter print_endline (List.rev !output) 345 346 let () = 347 info ""; 348 info (($)"building_for"); 349 info ("NATIVE = " ^ ($)"NATIVE"); 350 info "" 351 352 end 353 354 355 module Tools = struct 356 357 module Env = Map.Make(String) 358 359 let ( .$() ) m n = 360 try Env.find n m with Not_found -> 361 try 362 Env.filter 363 (fun k _ -> String.(equal (uppercase_ascii n) (uppercase_ascii k))) m 364 |> Env.choose |> snd 365 with Not_found -> "" 366 367 let ( .%() ) m n = 368 try Some (Env.find n m) with Not_found -> 369 try 370 Some (Env.filter 371 (fun k _ -> String.(equal (uppercase_ascii n) (uppercase_ascii k))) m 372 |> Env.choose |> snd) 373 with Not_found -> None 374 375 let make_env arr = 376 let add_var map s = 377 let k, v = 378 try 379 let open String in 380 let v = index s '=' in 381 sub s 0 v, 382 try sub s (v + 1) (length s - v - 1) with Invalid_argument _ -> "" 383 with Not_found -> s, "" 384 in 385 Env.add k v map 386 in 387 Array.fold_left add_var Env.empty arr 388 389 let env = 390 Unix.handle_unix_error Unix.environment () |> make_env 391 392 let args = 393 (if Array.length Sys.argv < 2 then [||] else 394 Array.sub Sys.argv 2 (Array.length Sys.argv - 2)) 395 |> make_env 396 397 let inputs = 398 let override _ a b = 399 match a, b with 400 | Some _, None -> a 401 | _, Some _ -> b 402 | None, None -> None 403 in 404 Env.merge override env args 405 406 let vars = ref Env.empty 407 408 let ( $ ) k = 409 match !vars.%(k) with Some s -> s | None -> inputs.$(k) 410 411 let ( <-- ) k v = 412 vars := Env.add k v !vars 413 414 let ( <-+= ) k v = 415 vars := Env.add k (($)k ^ " " ^ v) !vars 416 417 let ( <--? ) k v = 418 match !vars.%(k) with 419 | Some s -> s 420 | None -> begin 421 match inputs.%(k) with 422 | Some s -> vars := Env.add k s !vars; s 423 | None -> vars := Env.add k v !vars; v 424 end 425 426 let info msg = 427 prerr_endline msg 428 429 let error msg = 430 prerr_string "Error: "; 431 prerr_endline msg; 432 exit 2 433 434 let shell_internal ?(err_null = false) ?exit_status ?(input_str = "") cmd = 435 let shell' open_proc close_proc = 436 let outp = open_proc () in 437 let buf = Buffer.create 512 in 438 let () = try Buffer.add_channel buf outp 16384 with | End_of_file -> () in 439 (* Trim the final newline *) 440 let trim_end = 441 if Buffer.length buf >= 2 then begin 442 if Buffer.(sub buf (length buf - 2) 2) = "\r\n" then 2 443 else if Buffer.(sub buf (length buf - 1) 1) = "\n" then 1 444 else 0 445 end else if Buffer.length buf >= 1 && Buffer.(sub buf (length buf - 1) 1) = "\n" then 1 446 else 0 447 in 448 let proc_status = close_proc () in 449 (proc_status, Buffer.(sub buf 0 (length buf - trim_end))) 450 in 451 let status, output = 452 Unix.handle_unix_error (fun () -> 453 if err_null || input_str <> "" then 454 let (sh_out, sh_in, _) as sh_full = 455 Unix.open_process_full cmd [|"PATH=" ^ env.$("PATH")|] in 456 if input_str <> "" then begin 457 output_string sh_in input_str; 458 flush sh_in 459 end; 460 let () = close_out sh_in in 461 shell' (fun () -> sh_out) (fun () -> Unix.close_process_full sh_full) 462 else 463 let sh_out = Unix.open_process_in cmd in 464 shell' (fun () -> sh_out) (fun () -> Unix.close_process_in sh_out) 465 ) () 466 in 467 begin match exit_status with 468 | None -> () 469 | Some ref -> ref := status 470 end; 471 output 472 473 let shell ?err_null ?exit_status cmd = 474 shell_internal ?err_null ?exit_status cmd 475 476 let shell_input ?exit_status input_str cmd = 477 shell_internal ?exit_status ~input_str cmd 478 479 let exec cmd = 480 let cmd = String.concat " " cmd in 481 print_endline cmd; 482 match Sys.command cmd with 483 | 0 -> () 484 | e -> error (string_of_int e) 485 486 let path = 487 env.$("PATH") 488 |> String.split_on_char (if Sys.win32 then ';' else ':') 489 490 let pathext = 491 if Sys.win32 || Sys.cygwin then 492 env.$("PATHEXT") |> String.split_on_char ';' 493 else 494 [""] 495 496 let search_in_path ?(path = path) name = 497 Filename.concat 498 (List.find 499 (fun dir -> List.exists 500 (fun ext -> Sys.file_exists (Filename.concat dir (name ^ ext))) 501 pathext) 502 path) 503 name 504 505 let search_in_path_opt ?path name = 506 try Some (search_in_path ?path name) with 507 | Not_found -> None 508 509 let get_command name = search_in_path_opt name 510 511 let exists dir name = 512 Sys.file_exists (Filename.concat dir name) 513 514 let rm = 515 let rm' n = 516 if String.length n > 0 && n.[0] <> '-' && n.[0] <> '/' && n.[0] <> '\\' 517 && not (String.contains n '*') then 518 try Sys.remove n with Sys_error _ -> () 519 in 520 Array.iter rm' 521 522 let is_empty s = 523 String.length (String.trim s) = 0 524 525 let not_empty s = not (is_empty s) 526 527 let has_substring needle haystack = 528 let l1 = String.length needle in 529 let l2 = String.length haystack in 530 let max_i = l2 - l1 in 531 let rec loop i = 532 if i > max_i then false 533 else if needle = String.sub haystack i l1 then true 534 else loop (i + 1) 535 in loop 0 536 537 end 538 539 540 let target_local_vars () = 541 let open Tools in 542 (* NMAKE and OpenBSD don't support target-specific local variables *) 543 (* The same applies for make in NetBSD < 10 and FreeBSD < 13 *) 544 let make_supports_local_vars = 545 is_empty inputs.$("_NMAKE_VER") && ( 546 is_empty inputs.$("MAKE") || 547 (let exit_status = ref (Unix.WEXITED (-1)) in 548 has_substring "test ok" ( 549 shell_input ~exit_status 550 "TEST_VAL = test\n\ 551 _local_var_test: LOCAL_VAR_TEST = $(TEST_VAL)\n\ 552 _local_var_test: ; @echo $(LOCAL_VAR_TEST) ok" 553 (inputs.$("MAKE") ^ " -f -")) && 554 !exit_status = Unix.WEXITED 0) 555 ) 556 in 557 if make_supports_local_vars then begin 558 let is_old_gmake = 559 not_empty inputs.$("MAKE") && 560 let s = shell ~err_null:true (inputs.$("MAKE") ^ " --version") in 561 if String.length s >= 10 then String.sub s 0 10 = "GNU Make 3" else false 562 in 563 let rule_sep = if not is_old_gmake then " $(rule_sep) " else ": " in 564 [ 565 ["$(NAME_GUI)$(EXEC_EXT) $(CAMLOBJS_GUI)"; rule_sep; "CAMLFLAGS_GUI_X = $(CAMLFLAGS_GUI)"]; 566 ["$(NAME_FSM)$(EXEC_EXT) $(CAMLOBJS_FSM) $(FSMOCAMLOBJS:.cmo=.cmi)"; rule_sep; "CAMLFLAGS_FSM_X = $(CAMLFLAGS_FSM)"]; 567 ["$(FSMCOBJS)"; rule_sep; "CAMLCFLAGS_FSM_X = $(CAMLCFLAGS_FSM)"]; 568 ["$(NAME)-blob.o $(CAMLOBJS_MAC)"; rule_sep; "CAMLFLAGS_MAC_X = $(CAMLFLAGS_MAC)"]; 569 ] 570 |> List.iter (fun l -> String.concat "" l |> print_endline) 571 end else 572 print_string 573 "CAMLFLAGS_GUI_X = $(CAMLFLAGS_GUI)\n\ 574 CAMLFLAGS_FSM_X = $(CAMLFLAGS_FSM)\n\ 575 CAMLCFLAGS_FSM_X = $(CAMLCFLAGS_FSM)" 576 577 578 let project_info () = 579 let open Tools in 580 {|let myName = "|} ^ ($)"NAME" ^ {|"|} |> print_endline; 581 {|let myVersion = "|} ^ ($)"VERSION" ^ {|"|} |> print_endline; 582 {|let myMajorVersion = "|} ^ ($)"MAJORVERSION" ^ {|"|} |> print_endline 583 584 585 let install () = 586 let open Tools in 587 if not_empty inputs.$("_NMAKE_VER") || Sys.file_exists "src/unison.exe" then begin 588 let cwd = Sys.getcwd () in 589 let map_sep = 590 if String.contains cwd '\\' then function '/' -> '\\' | c -> c 591 else fun c -> c 592 in 593 let files = 594 let check_file name l = 595 if Sys.file_exists name then 596 (String.map map_sep (Filename.concat cwd name)) :: l 597 else l 598 in 599 List.fold_right check_file 600 ["src/unison.exe"; "src/unison-gui.exe"; "src/unison-fsmonitor.exe"] 601 [] 602 in 603 if files = [] then 604 error "The application has not been built yet." 605 else 606 info ("\n\nYou can find the built application as the following files \ 607 that you can copy to your desired destination.\n " ^ 608 (String.concat "\n " files) ^ "\n"); 609 exit 0 610 end; 611 612 let install = "INSTALL" <--? "install" in 613 let install_program = "INSTALL_PROGRAM" <--? install in 614 let install_data = "INSTALL_DATA" <--? install ^ " -m 644" in 615 616 let destdir = ($)"DESTDIR" in 617 let prefix = "PREFIX" <--? "/usr/local" in 618 let exec_prefix = "EXEC_PREFIX" <--? prefix in 619 let bindir = "BINDIR" <--? exec_prefix ^ "/bin" in 620 let datarootdir = "DATAROOTDIR" <--? prefix ^ "/share" in 621 let mandir = "MANDIR" <--? datarootdir ^ "/man" in 622 let man1dir = "MAN1DIR" <--? mandir ^ "/man1" in 623 let manext = "MANEXT" <--? ".1" in 624 625 let install_if_exists dir name dest = 626 if exists dir name then exec 627 [install_program; Filename.concat dir name; Filename.concat dest name] 628 in 629 630 print_endline ("DESTDIR = " ^ destdir); 631 print_endline ("PREFIX = " ^ prefix); 632 exec [install; "-d"; destdir ^ bindir]; 633 install_if_exists "src" "unison" (destdir ^ bindir); 634 install_if_exists "src" "unison-gui" (destdir ^ bindir); 635 install_if_exists "src" "unison-fsmonitor" (destdir ^ bindir); 636 if exists "man" "unison.1" then begin 637 exec [install; "-d"; destdir ^ man1dir]; 638 exec [install_data; "man/unison.1"; destdir ^ man1dir ^ "/unison" ^ manext] 639 end; 640 if exists "src/uimac/build/Default" "Unison.app" then begin 641 print_endline ("!!! The GUI for macOS has been built but will NOT be \ 642 installed automatically. You can find the built GUI package at " ^ 643 (Filename.concat (Sys.getcwd ()) "src/uimac/build/Default/Unison.app")) 644 end 645 646 647 let run cmd_and_args = 648 let map_sep = 649 if Sys.win32 then function '/' -> '\\' | c -> c else fun c -> c 650 in 651 match Array.to_list cmd_and_args with 652 | [] -> () 653 | cmd :: args -> Tools.exec (String.map map_sep cmd :: args) 654 655 656 let () = 657 if Array.length Sys.argv < 2 then 658 Tools.error "Missing sub-command" 659 else 660 match Sys.argv.(1) with 661 | "conf" -> let module Conf = Make(Tools) in () 662 | "conf2" -> target_local_vars () 663 | "projectinfo" -> project_info () 664 | "install" -> install () 665 | "run" -> run (Array.sub Sys.argv 2 (Array.length Sys.argv - 2)) 666 | "rm" -> Tools.rm (Array.sub Sys.argv 2 (Array.length Sys.argv - 2)) 667 | s -> Tools.error ("Invalid sub-commmand '" ^ s ^ "'")