123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224(**************************************************************************)(* *)(* Copyright 2012-2015 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. *)(* *)(**************************************************************************)openOpamTypesopenOpamTypesBaseopenOpamProcess.Job.Opletlogfmt=OpamConsole.log"RSYNC"fmtletrsync_arg="-rLptgoDrvc"(* if rsync -arv return 4 lines, this means that no files have changed *)letrsync_trim=function|[]->[]|_::t->matchList.revtwith|_::_::_::l->List.filter((<>)"./")l|_->[]letcall_rsynccheckargs=OpamSystem.make_command"rsync"args@@>funr->matchr.OpamProcess.r_codewith|0->Done(Some(rsync_trimr.OpamProcess.r_stdout))|3|5|10|11|12->(* protocol or file errors *)DoneNone|20->(* signal *)raiseSys.Break|23|24->(* partial, mostly mode, link or perm errors. But may also be a
complete error so we do an additional check *)ifcheck()then(OpamConsole.warning"Rsync partially failed:\n%s"(OpamStd.Format.itemize~bullet:""(funx->x)r.OpamProcess.r_stderr);Done(Some(rsync_trimr.OpamProcess.r_stdout)))elseDoneNone|30|35->(* timeouts *)DoneNone|_->OpamSystem.process_errorrletrsync?(args=[])?(exclude_vcdirs=true)srcdst=log"rsync: src=%s dst=%s"srcdst;letremote=String.containssrc':'inletoverlapsrcdst=letnormd=Filename.concatd""inOpamStd.String.starts_with~prefix:(normsrc)(normdst)&¬(OpamStd.String.starts_with~prefix:(norm(Filename.concatsrcOpamSwitch.external_dirname))(normdst))||OpamStd.String.starts_with~prefix:(normdst)(normsrc)inletexclude_args=ifexclude_vcdirsthen["--exclude";".git";"--exclude";"_darcs";"--exclude";".hg";"--exclude";".#*";"--exclude";OpamSwitch.external_dirname^"*";]else["--exclude";".#*";"--exclude";OpamSwitch.external_dirname^"*";]inifnot(remote||Sys.file_existssrc)thenDone(Not_available(None,src))elseifsrc=dstthenDone(Up_to_date[])elseifoverlapsrcdstthen(OpamConsole.error"Cannot sync %s into %s: they overlap"srcdst;Done(Not_available(None,src)))else(OpamSystem.mkdirdst;call_rsync(fun()->not(OpamSystem.dir_is_emptydst))(rsync_arg::args@exclude_args@["--delete";"--delete-excluded";src;dst;])@@|function|None->Not_available(None,src)|Some[]->Up_to_date[]|Somelines->Resultlines)letis_remoteurl=url.OpamUrl.transport<>"file"letrsync_dirs?args?exclude_vcdirsurldst=letsrc_s=OpamUrl.(Op.(url/"").path)in(* Ensure trailing '/' *)letdst_s=OpamFilename.Dir.to_stringdstinifnot(is_remoteurl)&¬(OpamFilename.exists_dir(OpamFilename.Dir.of_stringsrc_s))thenDone(Not_available(None,Printf.sprintf"Directory %s does not exist"src_s))elsersync?args?exclude_vcdirssrc_sdst_s@@|function|Not_available_asna->na|Result_->ifOpamFilename.exists_dirdstthenResultdstelseNot_available(None,dst_s)|Up_to_date_->Up_to_datedstletrsync_file?(args=[])urldst=letsrc_s=url.OpamUrl.pathinletdst_s=OpamFilename.to_stringdstinlog"rsync_file src=%s dst=%s"src_sdst_s;ifnot(is_remoteurl||OpamFilename.(exists(of_stringsrc_s)))thenDone(Not_available(None,src_s))elseifsrc_s=dst_sthenDone(Up_to_datedst)else(OpamFilename.mkdir(OpamFilename.dirnamedst);call_rsync(fun()->Sys.file_existsdst_s)(rsync_arg::args@[src_s;dst_s])@@|function|None->Not_available(None,src_s)|Some[]->Up_to_datedst|Some[_]->ifOpamFilename.existsdstthenResultdstelseNot_available(None,src_s)|Somel->OpamSystem.internal_error"unknown rsync output: {%s}"(String.concat", "l))moduleB=structletname=`rsyncletpull_dir_quietlocal_dirnameurl=rsync_dirsurllocal_dirnameletfetch_repo_updaterepo_name?cache_dir:_repo_rooturl=log"pull-repo-update";letquarantine=OpamFilename.Dir.(of_string(to_stringrepo_root^".new"))inletfinalise()=OpamFilename.rmdirquarantineinOpamProcess.Job.catch(fune->finalise();Done(OpamRepositoryBackend.Update_erre))@@fun()->OpamRepositoryBackend.job_textrepo_name"sync"(matchOpamUrl.local_dirurlwith|Somedir->OpamFilename.copy_dir~src:dir~dst:quarantine;(* fixme: Would be best to symlink, but at the moment our filename api
isn't able to cope properly with the symlinks afterwards
OpamFilename.link_dir ~target:dir ~link:quarantine; *)Done(Resultquarantine)|None->ifOpamFilename.exists_dirrepo_rootthenOpamFilename.copy_dir~src:repo_root~dst:quarantineelseOpamFilename.mkdirquarantine;pull_dir_quietquarantineurl)@@+function|Not_available_->finalise();Done(OpamRepositoryBackend.Update_err(Failure"rsync failed"))|Up_to_date_->finalise();DoneOpamRepositoryBackend.Update_empty|Result_->ifnot(OpamFilename.exists_dirrepo_root)||OpamFilename.dir_is_emptyrepo_rootthenDone(OpamRepositoryBackend.Update_fullquarantine)elseOpamProcess.Job.finallyfinalise@@fun()->OpamRepositoryBackend.job_textrepo_name"diff"@@OpamRepositoryBackend.get_diff(OpamFilename.dirname_dirrepo_root)(OpamFilename.basename_dirrepo_root)(OpamFilename.basename_dirquarantine)@@|function|None->OpamRepositoryBackend.Update_empty|Somep->OpamRepositoryBackend.Update_patchpletrepo_update_complete__=Done()letpull_url?cache_dir:_local_dirname_checksumremote_url=OpamFilename.mkdirlocal_dirname;letdir=OpamFilename.Dir.to_stringlocal_dirnameinletremote_url=matchOpamUrl.local_dirremote_urlwith|Some_->(* ensure that rsync doesn't recreate a subdir: add trailing '/' *)OpamUrl.Op.(remote_url/"")|None->remote_urlinrsyncremote_url.OpamUrl.pathdir@@|function|Not_available_asna->na|(Result_|Up_to_date_)asr->letresx=matchrwith|Result_->Resultx|Up_to_date_->Up_to_datex|_->assertfalseinifOpamUrl.has_trailing_slashremote_urlthenresNoneelseletfilename=OpamFilename.Op.(local_dirname//OpamUrl.basenameremote_url)inifOpamFilename.existsfilenamethenres(Somefilename)elseNot_available(None,Printf.sprintf"Could not find target file %s after rsync with %s. \
Perhaps you meant %s/ ?"(OpamUrl.basenameremote_url)(OpamUrl.to_stringremote_url)(OpamUrl.to_stringremote_url))letrevision_=DoneNoneletsync_dirtydirurl=pull_urldirNoneurlend