diff --git a/CHANGES.org b/CHANGES.org index b2cd75bf..f9a25b97 100644 --- a/CHANGES.org +++ b/CHANGES.org @@ -19,6 +19,8 @@ + =install= + =uninstall= + =installed-libraries= + + =exec=: execute a command in an environment similar to what you + would get after =jbuilder install= - Added support for aliases (#7, Rudi Grinberg) diff --git a/bin/main.ml b/bin/main.ml index 9b8d4e18..262c2f72 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -449,6 +449,60 @@ let install_uninstall ~what = let install = install_uninstall ~what:"install" let uninstall = install_uninstall ~what:"uninstall" +let exec = + let doc = + "Execute a command in a similar environment as if installation was performed." + in + let man = + [ `S "DESCRIPTION" + ; `P {|$(b,jbuilder exec -- COMMAND) should behave in the same way as if you do:|} + ; `Pre " \\$ jbuilder install\n\ + \ \\$ COMMAND" + ; `P {|In particular if you run $(b,jbuilder exec ocaml), you will have access + to the libraries defined in the workspace using your usual directives + ($(b,#require) for instance)|} + ; `Blocks help_secs + ] + in + let go common context prog args = + set_common common; + Future.Scheduler.go ~log:(create_log ()) + (Main.setup common >>= fun setup -> + let context = + match List.find setup.contexts ~f:(fun c -> c.name = context) with + | Some ctx -> ctx + | None -> + Format.eprintf "@{Error@}: Context %S not found!@." context; + die "" + in + match Context.which context prog with + | None -> + Format.eprintf "@{Error@}: Program %S not found!@." prog; + die "" + | Some real_prog -> + let real_prog = Path.to_string real_prog in + let env = Context.env_for_exec context in + if Sys.win32 then + Future.run ~env Strict real_prog (prog :: args) + else + Unix.execve real_prog (Array.of_list (prog :: args)) env + ) + in + ( Term.(const go + $ common + $ Arg.(value + & opt string "default" + & info ["context"] ~docv:"CONTEXT" + ~doc:{|Run the command in this build context.|} + ) + $ Arg.(required + & pos 0 (some string) None (Arg.info [] ~docv:"PROG")) + $ Arg.(value + & pos_right 0 string [] (Arg.info [] ~docv:"ARGS")) + ) + , Term.info "exec" ~doc ~man) + + let all = [ installed_libraries ; build_package @@ -457,6 +511,7 @@ let all = ; runtest ; install ; uninstall + ; exec ] let default = diff --git a/src/context.ml b/src/context.ml index 6cd0c2d9..18b7db54 100644 --- a/src/context.ml +++ b/src/context.ml @@ -323,3 +323,24 @@ let install_prefix t = opam_config_var t "prefix" >>| function | Some x -> Path.absolute x | None -> Path.parent t.ocaml_bin + + +(* CR-someday jdimino: maybe we should just do this for [t.env] directly? *) +let env_for_exec t = + let sep = if Sys.win32 then ';' else ':' in + let cwd = Sys.getcwd () in + let extend_var var v = + let v = Filename.concat cwd (Path.to_string v) in + match get_env t.env var with + | None -> (var, v) + | Some prev -> (var, sprintf "%s%c%s" v sep prev) + in + let vars = + [ extend_var "CAML_LD_LIBRARY_PATH" (Config.local_install_dir ~context:t.name) + ; extend_var "OCAMLPATH" (Path.relative + (Config.local_install_dir ~context:t.name) + "lib") + ; extend_var "PATH" (Config.local_install_bin_dir ~context:t.name) + ] + in + extend_env ~env:t.env ~vars:(String_map.of_alist_exn vars) diff --git a/src/context.mli b/src/context.mli index 6fa53c7c..33578fe6 100644 --- a/src/context.mli +++ b/src/context.mli @@ -127,3 +127,5 @@ val extend_env : vars:string String_map.t -> env:string array -> string array val opam_config_var : t -> string -> string option Future.t val install_prefix : t -> Path.t Future.t + +val env_for_exec : t -> string array