123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758(**************************************************************************)(* *)(* Copyright 2012-2020 OCamlPro *)(* Copyright 2012 INRIA *)(* *)(* All rights reserved. This file is distributed under the terms of the *)(* GNU Lesser General Public License version 2.1, with the special *)(* exception on linking described in the file LICENSE. *)(* *)(**************************************************************************)openOpamParserTypesopenOpamTypesopenOpamStateTypesopenOpamTypesBaseopenOpamStd.OpopenOpamFilename.Opletlogfmt=OpamConsole.log"ENV"fmtletslog=OpamConsole.slog(* - Environment and updates handling - *)letsplit_varv=OpamStd.Sys.split_path_variable~clean:falsevletjoin_varl=String.concat(String.make1OpamStd.Sys.path_sep)l(* To allow in-place updates, we store intermediate values of path-like as a
pair of list [(rl1, l2)] such that the value is [List.rev_append rl1 l2] and
the place where the new value should be inserted is in front of [l2] *)letunzip_toelt=letrecauxacc=function|[]->None|x::r->ifx=eltthenSome(acc,r)elseaux(x::acc)rinaux[]letrezip?insert(l1,l2)=List.rev_appendl1(matchinsertwithNone->l2|Somei->i::l2)letrezip_to_string?insertz=join_var(rezip?insertz)letapply_op_zipoparg(rl1,l2aszip)=letcolon_eq?(eqcol=false)=function(* prepend a, but keep ":"s *)|[]|[""]->[],[arg;""]|""::l->(* keep surrounding colons *)ifeqcolthenl@[""],[arg]elsel,["";arg]|l->l,[arg]inmatchopwith|Eq->[],[arg]|PlusEq->[],arg::rezipzip|EqPlus->List.rev_appendl2rl1,[arg]|EqPlusEq->rl1,arg::l2|ColonEq->letl,add=colon_eq(rezipzip)in[],add@l|EqColon->letl,add=colon_eq~eqcol:true(List.rev_appendl2rl1)inl,List.revadd(** Undoes previous updates done by opam, useful for not duplicating already
done updates; this is obviously not perfect, as all operators are not
reversible.
[cur_value] is provided as a list split at path_sep.
None is returned if the revert doesn't match. Otherwise, a zip (pair of lists
[(preceding_elements_reverted, following_elements)]) is returned, to keep the
position of the matching element and allow [=+=] to be applied later. A pair
or empty lists is returned if the variable should be unset or has an unknown
previous value. *)letreverse_env_updateopargcur_value=matchopwith|Eq->ifarg=join_varcur_valuethenSome([],[])elseNone|PlusEq|EqPlusEq->unzip_toargcur_value|EqPlus->(matchunzip_toarg(List.revcur_value)with|None->None|Some(rl1,l2)->Some(List.revl2,List.revrl1))|ColonEq->(matchunzip_toargcur_valuewith|Some([],[""])->Some([],[])|r->r)|EqColon->(matchunzip_toarg(List.revcur_value)with|Some([],[""])->Some([],[])|Some(rl1,l2)->Some(List.revl2,List.revrl1)|None->None)letupdates_from_previous_instance=lazy(matchOpamStd.Env.getopt"OPAM_SWITCH_PREFIX"with|None->None|Somepfx->letenv_file=OpamPath.Switch.env_relative_to_prefix(OpamFilename.Dir.of_stringpfx)intryOpamFile.Environment.read_optenv_filewithe->OpamStd.Exn.fatale;None)letexpand(updates:env_updatelist):env=(* Reverse all previous updates, in reverse order, on current environment *)letreverts=matchLazy.forceupdates_from_previous_instancewith|None->[]|Someupdates->List.fold_right(fun(var,op,arg,_)defs0->letv_opt,defs=OpamStd.List.pick_assocvardefs0inletv=OpamStd.Option.Op.((v_opt>>|rezip>>+fun()->OpamStd.Env.getoptvar>>|split_var)+![])inmatchreverse_env_updateopargvwith|Somev->(var,v)::defs|None->defs0)updates[]in(* And apply the new ones *)letrecapply_updatesrevertsacc=function|(var,op,arg,doc)::updates->letzip,reverts=letf,var=ifSys.win32thenString.uppercase_ascii,String.uppercase_asciivarelse(funx->x),varinmatchOpamStd.List.find_opt(fun(v,_,_)->fv=var)accwith|Some(_,z,_doc)->z,reverts|None->matchOpamStd.List.pick_assocvarrevertswith|Somez,reverts->z,reverts|None,_->matchOpamStd.Env.getoptvarwith|Somes->([],split_vars),reverts|None->([],[]),revertsinapply_updatesreverts((var,apply_op_zipopargzip,doc)::acc)updates|[]->List.rev@@List.rev_append(List.rev_map(fun(var,z,doc)->var,rezip_to_stringz,doc)acc)@@List.rev_map(fun(var,z)->var,rezip_to_stringz,Some"Reverting previous opam update")revertsinapply_updatesreverts[]updatesletadd(env:env)(updates:env_updatelist)=letenv=ifSys.win32then(*
* Environment variable names are case insensitive on Windows
*)letupdates=List.rev_map(fun(u,_,_,_)->(String.uppercase_asciiu,"","",None))updatesinList.filter(fun(k,_,_)->letk=String.uppercase_asciikinList.for_all(fun(u,_,_,_)->u<>k)updates)envelseList.filter(fun(k,_,_)->List.for_all(fun(u,_,_,_)->u<>k)updates)envinenv@expandupdatesletcompute_updates?(force_path=false)st=(* Todo: put these back into their packages!
let perl5 = OpamPackage.Name.of_string "perl5" in
let add_to_perl5lib = OpamPath.Switch.lib t.root t.switch t.switch_config perl5 in
let new_perl5lib = "PERL5LIB", "+=", OpamFilename.Dir.to_string add_to_perl5lib in
*)letfenv?opamv=tryOpamPackageVar.resolvest?opamvwithNot_found->log"Undefined variable: %s"(OpamVariable.Full.to_stringv);Noneinletbindir=OpamPath.Switch.binst.switch_global.rootst.switchst.switch_configinletpath="PATH",(ifforce_paththenPlusEqelseEqPlusEq),OpamFilename.Dir.to_stringbindir,Some("Binary dir for opam switch "^OpamSwitch.to_stringst.switch)inletman_path=letopenOpamStd.Sysinmatchos()with|OpenBSD|NetBSD|FreeBSD|Darwin|DragonFly->[](* MANPATH is a global override on those, so disabled for now *)|_->["MANPATH",EqColon,OpamFilename.Dir.to_string(OpamPath.Switch.man_dirst.switch_global.rootst.switchst.switch_config),Some"Current opam switch man dir"]inletenv_expansion?opam(name,op,str,cmt)=lets=OpamFilter.expand_string~default:(fun_->"")(fenv?opam)strinname,op,s,cmtinletswitch_env=("OPAM_SWITCH_PREFIX",Eq,OpamFilename.Dir.to_string(OpamPath.Switch.rootst.switch_global.rootst.switch),Some"Prefix of the current opam switch")::List.mapenv_expansionst.switch_config.OpamFile.Switch_config.envinletpkg_env=(* XXX: Does this need a (costly) topological sort? *)OpamPackage.Set.fold(funnvacc->matchOpamPackage.Map.find_optnvst.opamswith|Someopam->List.map(env_expansion~opam)(OpamFile.OPAM.envopam)@acc|None->acc)st.installed[]inswitch_env@pkg_env@man_path@[path]letupdates_common~set_opamroot~set_opamswitchrootswitch=letroot=ifset_opamrootthen["OPAMROOT",Eq,OpamFilename.Dir.to_stringroot,Some"Opam root in use"]else[]inletswitch=ifset_opamswitchthen["OPAMSWITCH",Eq,OpamSwitch.to_stringswitch,None]else[]inroot@switchletupdates?(set_opamroot=false)?(set_opamswitch=false)?force_pathst=updates_common~set_opamroot~set_opamswitchst.switch_global.rootst.switch@compute_updates?force_pathstletget_pure?(updates=[])()=letenv=List.map(fun(v,va)->v,va,None)(OpamStd.Env.list())inaddenvupdatesletget_opam?(set_opamroot=false)?(set_opamswitch=false)~force_pathst=add[](updates~set_opamroot~set_opamswitch~force_pathst)letget_opam_raw?(set_opamroot=false)?(set_opamswitch=false)?(base=[])~force_pathrootswitch=letenv_file=OpamPath.Switch.environmentrootswitchinletupd=OpamFile.Environment.safe_readenv_fileinletupd=("OPAM_SWITCH_PREFIX",Eq,OpamFilename.Dir.to_string(OpamPath.Switch.rootrootswitch),Some"Prefix of the current opam switch")::List.filter(function("OPAM_SWITCH_PREFIX",Eq,_,_)->false|_->true)updinletupd=ifforce_paththenList.map(function|"PATH",EqPlusEq,v,doc->"PATH",PlusEq,v,doc|e->e)updelseList.map(function|"PATH",PlusEq,v,doc->"PATH",EqPlusEq,v,doc|e->e)updinaddbase(updates_common~set_opamroot~set_opamswitchrootswitch@upd)letget_full?(set_opamroot=false)?(set_opamswitch=false)~force_path?updates:(u=[])st=letenv0=List.map(fun(v,va)->v,va,None)(OpamStd.Env.list())inletupdates=u@updates~set_opamroot~set_opamswitch~force_pathstinaddenv0updatesletis_up_to_date_raw?(skip=OpamStateConfig.(!r.no_env_notice))updates=skip||letnot_utd=List.fold_left(funnotutd(var,op,arg,_docasupd)->matchOpamStd.Env.getoptvarwith|None->upd::notutd|Somev->ifreverse_env_updateoparg(split_varv)=Nonethenupd::notutdelseList.filter(fun(v,_,_,_)->v<>var)notutd)[]updatesinletr=not_utd=[]inifnotrthenlog"Not up-to-date env variables: [%a]"(slog@@String.concat" "@*List.map(fun(v,_,_,_)->v))not_utdelselog"Environment is up-to-date";rletis_up_to_date_switchrootswitch=letenv_file=OpamPath.Switch.environmentrootswitchintrymatchOpamFile.Environment.read_optenv_filewith|Someupd->is_up_to_date_rawupd|None->truewithe->OpamStd.Exn.fatale;trueletswitch_path_update~force_pathrootswitch=letbindir=OpamPath.Switch.binrootswitch(OpamFile.Switch_config.safe_read(OpamPath.Switch.switch_configrootswitch))in["PATH",(ifforce_paththenPlusEqelseEqPlusEq),OpamFilename.Dir.to_stringbindir,Some"Current opam switch binary dir"]letpath~force_pathrootswitch=letenv=expand(switch_path_update~force_pathrootswitch)inlet(_,path_value,_)=List.find(fun(v,_,_)->v="PATH")envinpath_valueletfull_with_path~force_path?(updates=[])rootswitch=letenv0=List.map(fun(v,va)->v,va,None)(OpamStd.Env.list())inaddenv0(switch_path_update~force_pathrootswitch@updates)letis_up_to_date?skipst=is_up_to_date_raw?skip(updates~set_opamroot:false~set_opamswitch:false~force_path:falsest)(** Returns shell-appropriate statement to evaluate [cmd]. *)letshell_eval_invocationshellcmd=matchshellwith|SH_fish->Printf.sprintf"eval (%s)"cmd|SH_csh->Printf.sprintf"eval `%s`"cmd|_->Printf.sprintf"eval $(%s)"cmd(** Returns "opam env" invocation string together with optional root and switch
overrides *)letopam_env_invocation?root?switch?(set_opamswitch=false)()=letroot=OpamStd.Option.map_default(Printf.sprintf" --root=%s")""rootinletswitch=OpamStd.Option.map_default(Printf.sprintf" --switch=%s")""switchinletsetswitch=ifset_opamswitchthen" --set-switch"else""inPrintf.sprintf"opam env%s%s%s"rootswitchsetswitchleteval_stringgt?(set_opamswitch=false)switch=letroot=letopamroot_cur=OpamFilename.Dir.to_stringgt.rootinletopamroot_env=OpamStd.Option.Op.(OpamStd.Env.getopt"OPAMROOT"+!OpamFilename.Dir.to_stringOpamStateConfig.(default.root_dir))inifopamroot_cur<>opamroot_envthenSomeopamroot_curelseNoneinletswitch=(* Returns the switch only if it is different from the one determined by the
environment *)letfsw=letsw_cur=OpamSwitch.to_stringswinletsw_env=OpamStd.Option.Op.(OpamStd.Env.getopt"OPAMSWITCH"++(OpamStateConfig.get_current_switch_from_cwdgt.root>>|OpamSwitch.to_string)++(OpamFile.Config.switchgt.config>>|OpamSwitch.to_string))inifSomesw_cur<>sw_envthenSomesw_curelseNoneinOpamStd.Option.replacefswitchinletshell=OpamStd.Sys.guess_shell_compat()inshell_eval_invocationshell(opam_env_invocation?root?switch~set_opamswitch())(* -- Shell and init scripts handling -- *)(** The shells for which we generate init scripts (bash and sh are the same
entry) *)letshells_list=[SH_sh;SH_zsh;SH_csh;SH_fish]letcomplete_file=function|SH_sh|SH_bash->Some"complete.sh"|SH_zsh->Some"complete.zsh"|SH_csh|SH_fish->Noneletenv_hook_file=function|SH_sh|SH_bash->Some"env_hook.sh"|SH_zsh->Some"env_hook.zsh"|SH_csh->Some"env_hook.csh"|SH_fish->Some"env_hook.fish"letvariables_file=function|SH_sh|SH_bash|SH_zsh->"variables.sh"|SH_csh->"variables.csh"|SH_fish->"variables.fish"letinit_file=function|SH_sh|SH_bash->"init.sh"|SH_zsh->"init.zsh"|SH_csh->"init.csh"|SH_fish->"init.fish"letcomplete_script=function|SH_sh|SH_bash->SomeOpamScript.complete|SH_zsh->SomeOpamScript.complete_zsh|SH_csh|SH_fish->Noneletenv_hook_script_base=function|SH_sh|SH_bash->SomeOpamScript.env_hook|SH_zsh->SomeOpamScript.env_hook_zsh|SH_csh->SomeOpamScript.env_hook_csh|SH_fish->SomeOpamScript.env_hook_fishletexport_in_shellshell=letmake_commentcomment_opt=OpamStd.Option.to_string(Printf.sprintf"# %s\n")comment_optinletsh(k,v,comment)=Printf.sprintf"%s%s=%s; export %s;\n"(make_commentcomment)kvkinletcsh(k,v,comment)=Printf.sprintf"%sif ( ! ${?%s} ) setenv %s \"\"\nsetenv %s %s\n"(make_commentcomment)kkkvinletfish(k,v,comment)=(* Fish converts some colon-separated vars to arrays, which have to be
treated differently. MANPATH is handled automatically, so better not to
set it at all when not already defined *)letto_arr_stringv=OpamStd.List.concat_map" "(funv->ifv=Printf.sprintf"\"$%s\""kthen"$"^k(* remove quotes *)elsev)(OpamStd.String.splitv':')inmatchkwith|"PATH"->Printf.sprintf"%sset -gx %s %s;\n"(make_commentcomment)k(to_arr_stringv)|"MANPATH"->Printf.sprintf"%sif [ (count $%s) -gt 0 ]; set -gx %s %s; end;\n"(make_commentcomment)kk(to_arr_stringv)|_->(* Regular string variables *)Printf.sprintf"%sset -gx %s %s;\n"(make_commentcomment)kvinmatchshellwith|SH_zsh|SH_bash|SH_sh->sh|SH_fish->fish|SH_csh->cshletenv_hook_scriptshell=OpamStd.Option.map(funscript->export_in_shellshell("OPAMNOENVNOTICE","true",None)^script)(env_hook_script_baseshell)letsourcerootshellf=letfname=OpamFilename.to_string(OpamPath.initroot//f)inmatchshellwith|SH_csh->Printf.sprintf"if ( -f %s ) source %s >& /dev/null\n"fnamefname|SH_fish->Printf.sprintf"source %s > /dev/null 2> /dev/null; or true\n"fname|SH_sh|SH_bash->Printf.sprintf"test -r %s && . %s > /dev/null 2> /dev/null || true\n"fnamefname|SH_zsh->Printf.sprintf"[[ ! -r %s ]] || source %s > /dev/null 2> /dev/null\n"fnamefnameletif_interactive_scriptshellte=letielseelse_opt=matchelse_optwith|None->""|Somee->Printf.sprintf"else\n %s"einmatchshellwith|SH_sh|SH_bash->Printf.sprintf"if [ -t 0 ]; then\n %s%sfi\n"t@@ielsee|SH_zsh->Printf.sprintf"if [[ -o interactive ]]; then\n %s%sfi\n"t@@ielsee|SH_csh->Printf.sprintf"if ( $?prompt ) then\n %s%sendif\n"t@@ielsee|SH_fish->Printf.sprintf"if isatty\n %s%send\n"t@@ielseeletinit_scriptrootshell=letinteractive=List.map(sourcerootshell)@@OpamStd.List.filter_some[complete_fileshell;env_hook_fileshell]inString.concat"\n"@@(ifinteractive<>[]then[if_interactive_scriptshell(String.concat"\n "interactive)None]else[])@[sourcerootshell(variables_fileshell)]letstring_of_updatestshellupdates=letfenv=OpamPackageVar.resolvestinletaux(ident,symbol,string,comment)=letstring=OpamFilter.expand_string~default:(fun_->"")fenvstring|>OpamStd.Env.escape_single_quotes~using_backslashes:(shell=SH_fish)inletkey,value=ident,matchsymbolwith|Eq->Printf.sprintf"'%s'"string|PlusEq|ColonEq|EqPlusEq->Printf.sprintf"'%s':\"$%s\""stringident|EqColon|EqPlus->Printf.sprintf"\"$%s\":'%s'"identstringinexport_in_shellshell(key,value,comment)inOpamStd.List.concat_map""auxupdatesletwrite_scriptdir(name,body)=letfile=dir//nameintryOpamFilename.writefilebodywithe->OpamStd.Exn.fatale;OpamConsole.error"Could not write %s"(OpamFilename.to_stringfile)letwrite_init_shell_scriptsroot=letscripts=List.map(funshell->init_fileshell,init_scriptrootshell)shells_listinList.iter(write_script(OpamPath.initroot))scriptsletwrite_static_init_scriptsroot?completion?env_hook?(inplace=false)()=write_init_shell_scriptsroot;letupdate_scriptsfilefscriptfenable=letscripts=OpamStd.List.filter_map(funshell->matchfilefshell,scriptfshellwith|Somef,Somes->Some(f,s)|_->None)shells_listinmatchenable,inplacewith|Sometrue,_->List.iter(write_script(OpamPath.initroot))scripts|_,true->List.iter(fun((f,_)asfs)->ifOpamFilename.exists(OpamPath.initroot//f)thenwrite_script(OpamPath.initroot)fs)scripts|Somefalse,_->List.iter(fun(f,_)->OpamFilename.remove(OpamPath.initroot//f))scripts|None,_->()inupdate_scriptscomplete_filecomplete_scriptcompletion;update_scriptsenv_hook_fileenv_hook_scriptenv_hookletwrite_custom_init_scriptsrootcustom=lethookdir=OpamPath.hooks_dirrootinletkind=`MD5inList.iter(fun(name,script)->letscript_file=hookdir//nameinlethash=OpamHash.compute_from_string~kindscriptinlethash_name=name^".hash"inlethash_file=hookdir//hash_nameinifnot(OpamFilename.existshash_file)||(letsame_hash=OpamHash.of_string_opt(OpamFilename.readhash_file)=Some(OpamHash.compute~kind(OpamFilename.to_stringscript_file))insame_hash||notsame_hash&&OpamConsole.confirm~default:false"%s contains local modification, overwrite ?"(OpamFilename.to_stringscript_file))then(write_scripthookdir(name,script);OpamFilename.chmodscript_file0o777;write_scripthookdir(hash_name,OpamHash.to_stringhash)))customletwrite_dynamic_init_scriptsst=letupdates=updates~set_opamroot:false~set_opamswitch:falsestintryOpamFilename.with_flock_upgrade`Lock_write~dontblock:truest.switch_global.global_lock@@fun_->List.iter(funshell->write_script(OpamPath.initst.switch_global.root)(variables_fileshell,string_of_updatestshellupdates))[SH_sh;SH_csh;SH_fish]withOpamSystem.Locked->OpamConsole.warning"Global shell init scripts not installed (could not acquire lock)"letclear_dynamic_init_scriptsgt=List.iter(funshell->OpamFilename.remove(OpamPath.initgt.root//variables_fileshell))[SH_sh;SH_csh;SH_fish]letdot_profile_needs_updaterootdot_profile=ifnot(OpamFilename.existsdot_profile)then`yeselseletbody=OpamFilename.readdot_profileinletpattern1="opam config env"inletpattern1b="opam env"inletpattern2=OpamFilename.to_string(OpamPath.initroot//"init")inletpattern3=OpamStd.String.remove_prefix~prefix:(OpamFilename.Dir.to_stringroot)pattern2inletuncommented_repatts=Re.(compile(seq[bol;rep(diffany(set"#:"));alt(List.mapstrpatts)]))inifRe.execp(uncommented_re[pattern1;pattern1b;pattern2])bodythen`noelseifRe.execp(uncommented_re[pattern3])bodythen`otherrootelse`yesletupdate_dot_profilerootdot_profileshell=letpretty_dot_profile=OpamFilename.prettifydot_profileinletbash_src()=if(shell=SH_bash||shell=SH_sh)&&OpamFilename.(Base.to_string(basenamedot_profile))<>".bashrc"thenOpamConsole.note"Make sure that %s is well %s in your ~/.bashrc.\n"pretty_dot_profile(OpamConsole.colorise`underline"sourced")inmatchdot_profile_needs_updaterootdot_profilewith|`no->OpamConsole.msg" %s is already up-to-date.\n"pretty_dot_profile;bash_src()|`otherroot->OpamConsole.msg" %s is already configured for another opam root.\n"pretty_dot_profile|`yes->letinit_file=init_fileshellinletbody=ifOpamFilename.existsdot_profilethenOpamFilename.readdot_profileelse""inOpamConsole.msg" Updating %s.\n"pretty_dot_profile;bash_src();letbody=Printf.sprintf"%s\n\n\
# opam configuration\n\
%s"(OpamStd.String.stripbody)(sourcerootshellinit_file)inOpamFilename.writedot_profilebodyletupdate_user_setuproot?dot_profileshell=ifdot_profile<>Nonethen(OpamConsole.msg"\nUser configuration:\n";OpamStd.Option.iter(funf->update_dot_profilerootfshell)dot_profile)letcheck_and_print_env_warningst=(* if you are trying to silence this warning,
set the ~no_env_notice:true flag from OpamStateConfig,
which is checked by (is_up_to_date st). *)ifnot(is_up_to_datest)&&(OpamFile.Config.switchst.switch_global.config=Somest.switch||OpamStateConfig.(!r.switch_from<>`Command_line))thenOpamConsole.formatted_msg"# Run %s to update the current shell environment\n"(OpamConsole.colorise`bold(eval_stringst.switch_global(Somest.switch)))letsetuproot~interactive?dot_profile?update_config?env_hook?completion?inplaceshell=letupdate_dot_profile=matchupdate_config,dot_profile,interactivewith|Somefalse,_,_->None|_,None,_->invalid_arg"OpamEnv.setup"|Sometrue,Somedot_profile,_->Somedot_profile|None,_,false->None|None,Somedot_profile,true->OpamConsole.header_msg"Required setup - please read";OpamConsole.msg"\n\
\ In normal operation, opam only alters files within ~%s.opam.\n\
\n\
\ However, to best integrate with your system, some environment variables\n\
\ should be set. If you allow it to, this initialisation step will update\n\
\ your %s configuration by adding the following line to %s:\n\
\n\
\ %s\
\n\
\ Otherwise, every time you want to access your opam installation, you will\n\
\ need to run:\n\
\n\
\ %s\n\
\n\
\ You can always re-run this setup with 'opam init' later.\n\n"Filename.dir_sep(OpamConsole.colorise`bold@@string_of_shellshell)(OpamConsole.colorise`cyan@@OpamFilename.prettifydot_profile)(OpamConsole.colorise`bold@@sourcerootshell(init_fileshell))(OpamConsole.colorise`bold@@shell_eval_invocationshell(opam_env_invocation()));ifOpamCoreConfig.(!r.answer=Sometrue)thenbeginOpamConsole.warning"Shell not updated in non-interactive mode: use --shell-setup";NoneendelsematchOpamConsole.read"Do you want opam to modify %s? [N/y/f]\n\
(default is 'no', use 'f' to choose a different file)"(OpamFilename.prettifydot_profile)with|Some("y"|"Y"|"yes"|"YES")->Somedot_profile|Some("f"|"F"|"file"|"FILE")->beginmatchOpamConsole.read" Enter the name of the file to update:"with|None->OpamConsole.msg"Alright, assuming you changed your mind, not \
performing any changes.\n";None|Somef->Some(OpamFilename.of_stringf)end|_->Noneinletenv_hook=matchenv_hook,interactivewith|Someb,_->Someb|None,false->None|None,true->(* not just interactive mode *)ifupdate_config<>None||completion<>NonethenNoneelseSome(OpamConsole.confirm~default:false"A hook can be added to opam's init scripts to ensure that the \
shell remains in sync with the opam environment when they are \
loaded. Set that up?")inupdate_user_setuproot?dot_profile:update_dot_profileshell;write_static_init_scriptsroot?completion?env_hook?inplace()lethook_envroot=lethook_vnam=OpamVariable.of_string"hooks"inlethook_vval=Some(OpamVariable.dirname(OpamPath.hooks_dirroot))inOpamVariable.Map.singletonhook_vnamhook_vval