diff --git a/doc/configure.txt b/doc/configure.txt new file mode 100644 index 0000000..b0a0e85 --- /dev/null +++ b/doc/configure.txt @@ -0,0 +1,25 @@ +NAME + opam-compiler-configure - Run ./configure command + +SYNOPSIS + opam-compiler configure [OPTION]... [CONFIGURE_ARGUMENT]... + +ARGUMENTS + CONFIGURE_ARGUMENT + Positional arguments are passed verbatim to ./configure. + +OPTIONS + --dry-run + Do not perform external commands. Print them and continue as if + they worked. + + --help[=FMT] (default=auto) + Show this help in format FMT. The value FMT must be one of `auto', + `pager', `groff' or `plain'. With `auto', the format is `pager` or + `plain' whenever the TERM env var is `dumb' or undefined. + + --with=FEATURES + Create a switch with this set of features. For example --with + flambda,nnp will create a switch with the flambda and + no-naked-pointers features enabled. + diff --git a/doc/dune b/doc/dune index e94a183..505f8ed 100644 --- a/doc/dune +++ b/doc/dune @@ -17,3 +17,23 @@ (alias runtest) (action (diff create.txt create.txt.gen))) + +(rule + (with-stdout-to + configure.txt.gen + (run opam-compiler configure --help=plain))) + +(rule + (alias runtest) + (action + (diff configure.txt configure.txt.gen))) + +(rule + (with-stdout-to + install.txt.gen + (run opam-compiler install --help=plain))) + +(rule + (alias runtest) + (action + (diff install.txt install.txt.gen))) diff --git a/doc/install.txt b/doc/install.txt new file mode 100644 index 0000000..7212ce0 --- /dev/null +++ b/doc/install.txt @@ -0,0 +1,16 @@ +NAME + opam-compiler-install - Run make install + +SYNOPSIS + opam-compiler install [OPTION]... + +OPTIONS + --dry-run + Do not perform external commands. Print them and continue as if + they worked. + + --help[=FMT] (default=auto) + Show this help in format FMT. The value FMT must be one of `auto', + `pager', `groff' or `plain'. With `auto', the format is `pager` or + `plain' whenever the TERM env var is `dumb' or undefined. + diff --git a/doc/opam-compiler.txt b/doc/opam-compiler.txt index cd702b5..a511bba 100644 --- a/doc/opam-compiler.txt +++ b/doc/opam-compiler.txt @@ -5,9 +5,15 @@ SYNOPSIS opam-compiler COMMAND ... COMMANDS + configure + Run ./configure command + create Create a switch from a compiler source + install + Run make install + OPTIONS --help[=FMT] (default=auto) Show this help in format FMT. The value FMT must be one of `auto', diff --git a/lib/cli.ml b/lib/cli.ml index 02b998e..0932651 100644 --- a/lib/cli.ml +++ b/lib/cli.ml @@ -8,10 +8,14 @@ type t = runner : Runner.t; github_client : Github_client.t; } + | Configure of { runner : Runner.t; args : string list } + | Install of { runner : Runner.t } let eval = function | Create { source; switch_name; configure_command; runner; github_client } -> Op.create runner github_client source switch_name ~configure_command + | Configure { runner; args } -> Op.configure runner args + | Install { runner } -> Op.install runner let configure_command_explicit = let open Cmdliner.Arg in @@ -43,29 +47,47 @@ let with_ = in value (opt (some (list conv)) None info) +let feature_args_opt = + let open Let_syntax.Cmdliner in + let+ with_ = with_ in + let ocaml_version = Ocaml_version.Releases.latest in + Option.map + (List.map (Ocaml_version.Configure_options.to_configure_flag ocaml_version)) + with_ + let configure_command = let open Let_syntax.Cmdliner in let use_command cmd = `Ok (Some cmd) in let default = `Ok None in let error m = `Error (false, m) in let ret_term = - let+ explicit = configure_command_explicit and+ with_ = with_ in - match (explicit, with_) with + let+ explicit = configure_command_explicit + and+ feature_args_opt = feature_args_opt in + match (explicit, feature_args_opt) with | Some e, None -> use_command e | None, None -> default - | None, Some opts -> - let ocaml_version = Ocaml_version.Releases.latest in - let add_opt cmd opt = - Bos.Cmd.add_arg cmd - (Ocaml_version.Configure_options.to_configure_flag ocaml_version opt) - in - let configure = Bos.Cmd.v "./configure" in - use_command (List.fold_left add_opt configure opts) + | None, Some opts -> use_command Bos.Cmd.(v "./configure" %% of_list opts) | Some _, Some _ -> error "--configure-command and --with cannot be passed together." in Cmdliner.Term.ret ret_term +let dry_run = + let open Cmdliner.Arg in + let info = + info + ~doc: + "Do not perform external commands. Print them and continue as if they \ + worked." + [ "dry-run" ] + in + value (flag info) + +let runner = + let open Let_syntax.Cmdliner in + let+ dry_run = dry_run in + if dry_run then Runner.dry_run else Runner.real + module Create = struct module Source_with_original = struct type t = { source : Source.t; original : string } @@ -100,25 +122,10 @@ module Create = struct \".\" will create a local switch in the current directory." [ "switch" ])) - let dry_run = - let open Cmdliner.Arg in - let info = - info - ~doc: - "Do not perform external commands. Print them and continue as if \ - they worked." - [ "dry-run" ] - in - value (flag info) - - let clients = + let github_client = let open Let_syntax.Cmdliner in let+ dry_run = dry_run in - let runner = if dry_run then Runner.dry_run else Runner.real in - let github_client = - if dry_run then Github_client.dry_run else Github_client.real - in - (runner, github_client) + if dry_run then Github_client.dry_run else Github_client.real let man = [ @@ -139,7 +146,8 @@ module Create = struct let+ { Source_with_original.source; _ } = source and+ switch_name = switch_name and+ configure_command = configure_command - and+ runner, github_client = clients in + and+ runner = runner + and+ github_client = github_client in Create { source; switch_name; configure_command; runner; github_client } let info = @@ -149,15 +157,60 @@ module Create = struct let command = (term, info) end +module Configure = struct + let configure_args = + let open Cmdliner.Arg in + value & pos_all string [] + & info ~docv:"CONFIGURE_ARGUMENT" + ~doc:"Positional arguments are passed verbatim to ./configure." [] + + let args = + let open Let_syntax.Cmdliner in + let+ configure_args = configure_args + and+ feature_args_opt = feature_args_opt in + let feature_args = Option.value feature_args_opt ~default:[] in + feature_args @ configure_args + + let term = + let open Let_syntax.Cmdliner in + let+ runner = runner and+ args = args in + Configure { runner; args } + + let info = Cmdliner.Term.info ~doc:"Run ./configure command" "configure" + + let command = (term, info) +end + +module Install = struct + let term = + let open Let_syntax.Cmdliner in + let+ runner = runner in + Install { runner } + + let info = Cmdliner.Term.info ~doc:"Run make install" "install" + + let command = (term, info) +end + let default = let open Cmdliner.Term in (ret (pure (`Help (`Auto, None))), info "opam-compiler") +let map_result f = function + | `Ok x -> `Ok (f x) + | (`Version | `Help | `Error _) as r -> r + +let interpret_result = + map_result (fun op -> + match eval op with + | Ok () -> 0 + | Error (`Msg msg) -> + print_endline msg; + 2) + let main () = - let result = Cmdliner.Term.eval_choice default [ Create.command ] in - (match result with - | `Ok op -> eval op |> Rresult.R.failwith_error_msg - | `Version -> () - | `Help -> () - | `Error _ -> ()); - Cmdliner.Term.exit result + let result = + Cmdliner.Term.eval_choice default + [ Create.command; Configure.command; Install.command ] + in + result |> interpret_result |> Cmdliner.Term.exit_status diff --git a/lib/import.ml b/lib/import.ml index 4e7b987..f48651d 100644 --- a/lib/import.ml +++ b/lib/import.ml @@ -42,10 +42,11 @@ let pp_cmd ppf cmd = Bos.Cmd.to_list cmd |> List.map quote_if_needed |> String.concat " " |> Format.pp_print_string ppf -type error = [ `Command_failed of Bos.Cmd.t | `Unknown ] +type error = [ `Command_failed of Bos.Cmd.t | `Configure_needed | `Unknown ] let translate_error s = let open Rresult.R in reword_error (function | `Unknown -> msgf "%s" s - | `Command_failed cmd -> msgf "%s - command failed: %a" s pp_cmd cmd) + | `Command_failed cmd -> msgf "%s - command failed: %a" s pp_cmd cmd + | `Configure_needed -> msgf "%s - configure step is required" s) diff --git a/lib/import.mli b/lib/import.mli index 9187937..ac38732 100644 --- a/lib/import.mli +++ b/lib/import.mli @@ -27,7 +27,7 @@ val pp_env : Format.formatter -> (string * string) list option -> unit val pp_cmd : Format.formatter -> Bos.Cmd.t -> unit -type error = [ `Command_failed of Bos.Cmd.t | `Unknown ] +type error = [ `Command_failed of Bos.Cmd.t | `Configure_needed | `Unknown ] val translate_error : string -> ('a, [< error ]) result -> ('a, [> Rresult.R.msg ]) result diff --git a/lib/op.ml b/lib/op.ml index 5ffaf02..df24759 100644 --- a/lib/op.ml +++ b/lib/op.ml @@ -37,3 +37,30 @@ let reinstall runner mode ~configure_command = (let* () = Opam.reinstall_compiler runner ~configure_command in reinstall_packages_if_needed runner mode) |> translate_error "Could not reinstall" + +let configure runner args = + let open Let_syntax.Result in + (let* prefix = Opam.get_prefix runner in + let cmd = Bos.Cmd.(v "./configure" % "--prefix" % prefix %% of_list args) in + Runner.run runner cmd) + |> translate_error "Could not configure" + +let file_exists runner path = + let cmd = + Bos.Cmd.( + v "grep" % "-q" % "-e" % "prefix=.*_opam" % "-e" % "prefix=.*\\.opam" + % path) + in + match Runner.run runner cmd with + | Ok () -> Ok true + | Error (`Command_failed _) -> Ok false + | Error _ as e -> e + +let install runner = + let open Let_syntax.Result in + (let* exists = file_exists runner "Makefile.config" in + if not exists then Error `Configure_needed + else + let cmd = Bos.Cmd.(v "make" % "install") in + Runner.run runner cmd) + |> translate_error "Could not install" diff --git a/lib/op.mli b/lib/op.mli index 89ede15..2968ede 100644 --- a/lib/op.mli +++ b/lib/op.mli @@ -13,3 +13,7 @@ val reinstall : reinstall_mode -> configure_command:Bos.Cmd.t option -> (unit, [> Rresult.R.msg ]) result + +val configure : Runner.t -> string list -> (unit, [> Rresult.R.msg ]) result + +val install : Runner.t -> (unit, [> Rresult.R.msg ]) result diff --git a/lib/opam.ml b/lib/opam.ml index bd12d50..cb22bd9 100644 --- a/lib/opam.ml +++ b/lib/opam.ml @@ -64,9 +64,11 @@ let set_base runner name = let update runner name = run_opam runner [ A "update"; switch name; ocaml_variants ] +let get_prefix runner = run_out_opam runner [ A "config"; A "var"; A "prefix" ] + let reinstall_configure runner ~configure_command = let open Let_syntax.Result in - let* prefix = run_out_opam runner [ A "config"; A "var"; A "prefix" ] in + let* prefix = get_prefix runner in let base_command = Option.value configure_command ~default:Bos.Cmd.(v "./configure") in diff --git a/lib/opam.mli b/lib/opam.mli index 02d4f38..ed3ba15 100644 --- a/lib/opam.mli +++ b/lib/opam.mli @@ -20,3 +20,5 @@ val reinstall_compiler : val reinstall_packages : Runner.t -> (unit, error) result val remove_switch : Runner.t -> Switch_name.t -> (unit, error) result + +val get_prefix : Runner.t -> (string, error) result diff --git a/lib/runner.ml b/lib/runner.ml index 3a46ef8..63f6ce7 100644 --- a/lib/runner.ml +++ b/lib/runner.ml @@ -47,7 +47,7 @@ module Dry_run = struct let run_out ?extra_env cmd = Format.printf "Run_out: %a%a\n" pp_env extra_env pp_cmd cmd; - Ok "output" + Format.kasprintf Rresult.R.ok "$(%a%a)" pp_env extra_env pp_cmd cmd end let dry_run = diff --git a/test/cram/configure.t b/test/cram/configure.t new file mode 100644 index 0000000..00ef43e --- /dev/null +++ b/test/cram/configure.t @@ -0,0 +1,23 @@ +Configure calls configure with the right prefix. + + $ opam-compiler configure --dry-run + Run_out: OPAMCLI=2.0 opam config var prefix + Run: ./configure --prefix "$(OPAMCLI=2.0 opam config var prefix)" + +Anything after -- is passed to the configure command. + + $ opam-compiler configure --dry-run -- --xxx --yyy + Run_out: OPAMCLI=2.0 opam config var prefix + Run: ./configure --prefix "$(OPAMCLI=2.0 opam config var prefix)" --xxx --yyy + +Shorthand versions are supported. + + $ opam-compiler configure --dry-run --with afl + Run_out: OPAMCLI=2.0 opam config var prefix + Run: ./configure --prefix "$(OPAMCLI=2.0 opam config var prefix)" --with-afl + +It is possible to mix both. + + $ opam-compiler configure --dry-run --with afl -- --xxx --yyy + Run_out: OPAMCLI=2.0 opam config var prefix + Run: ./configure --prefix "$(OPAMCLI=2.0 opam config var prefix)" --with-afl --xxx --yyy diff --git a/test/cram/install.t b/test/cram/install.t new file mode 100644 index 0000000..6468c7a --- /dev/null +++ b/test/cram/install.t @@ -0,0 +1,39 @@ + $ opam-compiler install --dry-run + Run: grep -q -e prefix=.*_opam -e prefix=.*\.opam Makefile.config + Run: make install + +"install" checks for a Makefile.config file. +If it does not exist, it prints an error: + + $ cat > Makefile < .PHONY: install + > install: + > @echo installed + > EOF + + $ opam-compiler install + grep: Makefile.config: No such file or directory + Could not install - configure step is required + [2] + +If it exists, it is inspected. The prefix is expected to point at an opam +switch. + +Global switches are recognized: + + $ echo 'prefix=/home/me/.opam/name' > Makefile.config + $ opam-compiler install + installed + +Local switches are recognized: + + $ echo 'prefix=/home/me/src/project/_opam' > Makefile.config + $ opam-compiler install + installed + +If prefix is something else, an error message is displayed: + + $ echo 'prefix=/usr/local/lib' > Makefile.config + $ opam-compiler install + Could not install - configure step is required + [2]