123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226(*
* Copyright (c) 2018 Thomas Gazagnaire <thomas@gazagnaire.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*)openBos_setuptypeerror=R.msgletroot=matchOS.Dir.current()with|Error(`Msge)->Fmt.failwith"invalid root: %s"e|Okd->dletcurrent_dir?(sandbox=true)()=matchOS.Dir.current()with|Error(`Msge)->Fmt.failwith"invalid current directory: %s"e|Okdir->(ifFpath.equaldirrootthenNoneelsematchFpath.relativize~rootdirwith|None->assertfalse|Somed->ifsandboxthenassert(List.hd(Fpath.segsd)="_build");Somed)letlast_dir=refrootletpp_cmdppfcmd=letx=Fmt.to_to_stringCmd.ppcmdinletx=String.map(function'\n'->' '|c->c)xinFmt.stringppfxletshow?sandbox?(action=`Skip)fmt=letpp_actionppf=function|`Skip->Fmt.(styled`Yellowstring)ppf"-:"|`Done->Fmt.(styled`Greenstring)ppf"=>"inletpp_cwdppf()=matchcurrent_dir?sandbox()with|None->()|Somed->ifnot(Fpath.equald!last_dir)then(last_dir:=d;Fmt.pfppf" [in %a]\n"Fmt.(styled`UnderlineFpath.pp)d)inFmt.kstr(funs->Logs.app(funm->m"%a%a %s"pp_cwd()pp_actionactions);Ok())fmtletcmd_errorcmderr_msgstatus=match(err_msg,status)with|(None|Some""),`Exitedc->R.error_msgf"The following command exited with code %d:@\n %a"cCmd.ppcmd|(None|Some""),`Signaledc->R.error_msgf"The following command exited with signal %d:@\n %a"cCmd.ppcmd|Someerr_msg,`Exitedc->R.error_msgf"Exit code %d from command@\n `%a`:@\n%s"cCmd.ppcmderr_msg|Someerr_msg,`Signaledc->R.error_msgf"Signal %d from command @\n `%a`:@\n%s"cCmd.ppcmderr_msgletrun_gen?err~dry_run?(force=false)?sandbox~defaultvif=ifnotdry_runthenOS.Cmd.run_io?errvi|>felseifnotforcethenlet_=show?sandbox"exec:@[@ %a@]"pp_cmdvinOkdefaultelselet_=show?sandbox~action:`Done"exec:@[@ %a@]"pp_cmdvinOS.Cmd.run_io?errvi|>fletrun_quiet~dry_run?(force=false)?sandboxv=letopenOS.Cmdinrun_gen~err:err_null~dry_run~force?sandbox~default:()vin_stdinto_nullletrun~dry_run?(force=false)?sandboxv=letopenOS.Cmdinrun_gen~dry_run~force?sandbox~default:()vin_stdinto_stdoutletrun_out~dry_run?(force=false)?sandbox?err~defaultvf=run_gen?err~dry_run~force?sandbox~defaultvOS.Cmd.in_stdinftype'aresponse={output:'a;err_msg:string;status:OS.Cmd.status;run_info:OS.Cmd.run_info;}letrun_out_err~dry_run?(force=false)?sandbox~defaultvf=OS.File.tmp"dune-release-%s.stderr">>=funtmp_file->leterr=OS.Cmd.err_filetmp_fileinrun_gen~err~dry_run~force?sandbox~defaultvOS.Cmd.in_stdinf>>=fun(stdout,(run_info,status))->OS.File.readtmp_file>>|funstderr->{err_msg=stderr;status;run_info;output=stdout}letrun_io~dry_run?(force=false)?sandbox~defaultvif=run_gen~dry_run~force?sandbox~defaultvifletrun_status~dry_run?(force=false)?sandboxv=ifnotdry_runthenOS.Cmd.run_statusvelseifnotforcethenlet_=show?sandbox"exec:@[@ %a@]"pp_cmdvinOk(`Exited0)elselet_=show?sandbox~action:`Done"exec:@[@ %a@]"pp_cmdvinOS.Cmd.run_statusvletdelete_dir~dry_run?(force=false)dir=ifnotdry_runthenOS.Dir.delete~recurse:truedirelseletdir'=matchcurrent_dir()with|None->dir|Somed->Fpath.(normalize@@(d//dir))inifnotforcethenshow"rmdir %a"Fpath.ppdir'elselet_=show~action:`Done"rmdir %a"Fpath.ppdir'inOS.Dir.delete~recurse:truedirletdelete_path~dry_runp=ifnotdry_runthenOS.Path.delete~recurse:truepelseletp'=matchcurrent_dir()with|None->p|Somed->Fpath.(normalize@@(d//p))inlet_=show~action:`Done"rm %a"Fpath.ppp'inOk()letwrite_file~dry_run?(force=false)pv=ifnotdry_runthenOS.File.writepvelseifnotforcethenshow"write %a"Fpath.pppelselet_=show~action:`Done"write %a"Fpath.pppinOS.File.writepvletread_file~dry_runp=ifnotdry_runthenOS.File.readpelsematchOS.File.existspwith|Oktrue->let_=show~action:`Done"read %a"Fpath.pppinOS.File.readp|_->let_=show"read %a"Fpath.pppinOk""letfile_exists~dry_runp=ifnotdry_runthenOS.File.existspelseOS.File.existsp>>|funexists->ifexiststhenignore(show~action:`Done"exists %a"Fpath.ppp);existsletdir_exists~dry_runp=ifnotdry_runthenOS.Dir.existspelseletaction=matchOS.Dir.existspwithOktrue->`Done|_->`Skipinlet_=show~action"exists %a"Fpath.pppinOktrueletwith_dir~dry_rundirfx=ifnotdry_runthenOS.Dir.with_currentdirfxelsematchOS.Dir.existsdirwith|Oktrue->let_=show~action:`Done"chdir %a"Fpath.ppdirinOS.Dir.with_currentdirfx|_->let_=show"chdir %a"Fpath.ppdirinOk(fx)letmkdir~dry_rundir=ifnotdry_runthenOS.Dir.createdirelsematchOS.Dir.existsdirwith|Oktrue->Oktrue|_->let_=show"mkdir %a"Fpath.ppdirinOkfalseletfile_must_exist~dry_runf=ifnotdry_runthenOS.File.must_existfelselet_=matchOS.File.existsfwith|Oktrue->show~action:`Done"must exists %a"Fpath.ppf|_->show"must exists %a"Fpath.ppfinOkfletouty=matchOS.Cmd.run_outCmd.(v"true")|>OS.Cmd.out_stringwith|Ok(_,x)->(y,x)|Error_->assertfalseletcp~dry_run~rec_~force~src~dst=letcmd=Cmd.(v"cp"%%onrec_(v"-r")%%onforce(v"-f")%psrc%pdst)inrun~dry_runcmdletrelativize~src~dst=R.of_option~none:(fun()->R.error_msgf"Could define path from %a to %a"Fpath.ppsrcFpath.ppdst)(Fpath.relativize~root:srcdst)