123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207(********************************************************************************)(* crs - A tool for managing code review comments embedded in source code *)(* Copyright (C) 2024-2025 Mathieu Barbin <mathieu.barbin@gmail.com> *)(* *)(* This file is part of crs. *)(* *)(* crs is free software; you can redistribute it and/or modify it under the *)(* terms of the GNU Lesser General Public License as published by the Free *)(* Software Foundation either version 3 of the License, or any later version, *)(* with the LGPL-3.0 Linking Exception. *)(* *)(* crs is distributed in the hope that it will be useful, but WITHOUT ANY *)(* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS *)(* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License and *)(* the file `NOTICE.md` at the root of this repository for more details. *)(* *)(* You should have received a copy of the GNU Lesser General Public License *)(* and the LGPL-3.0 Linking Exception along with this library. If not, see *)(* <http://www.gnu.org/licenses/> and <https://spdx.org>, respectively. *)(********************************************************************************)(* This module is derived from Iron (v0.9.114.44+47), file
* [./hg/cr_comment.ml], which is released under Apache 2.0:
*
* Copyright (c) 2016-2017 Jane Street Group, LLC <opensource-contacts@janestreet.com>
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* See the file `NOTICE.md` at the root of this repository for more details.
*
* Changes:
*
* - Migrate to this file only the part that relates to grepping versioned files.
* - Remove dependency to [Core] - make small adjustments to use [Base] instead.
* - Remove dependency to [Async] - replace by [Spawn] and [Stdio].
* - Use [Vcs] instead of [Hg].
*)moduleUnix=UnixLabelsletcr_pattern_egrep=File_parser.cr_pattern_egrepletparse_file=File_parser.parse_fileletrecwaitpid_non_intrpid=tryUnix.waitpid~mode:[]pidwith|Unix.Unix_error(EINTR,_,_)->waitpid_non_intrpid[@coverageoff];;letread_all_from_fdfd=letout=In_channel.input_all(Unix.in_channel_of_descrfd)inUnix.closefd;out;;letfind_executable~path~executable_basename=letrecloop=function|[]->(None[@coverageoff])|path::rest->letfn=Stdlib.Filename.concatpathexecutable_basenameinifStdlib.Sys.file_existsfnthenSomefnelselooprestinloop(String.splitpath~on:':');;letfind_xargs=lazy(matchStdlib.Sys.getenv_opt"PATH"with|None->None[@coverageoff]|Somepath->find_executable~path~executable_basename:"xargs");;moduleExit_status=struct[@@@coverageoff]typet=[`Exitedofint|`Signaledofint|`Stoppedofint][@@derivingsexp_of]endletnull_separator=String.make1(Char.of_int_exn0)let()=(* Something similar is done when you link with [Core_unix] however it is
preferable to make the rendering of errors deterministic based on code
present in this module here rather than purely from dependencies, since
dependencies may change. *)Sexplib0.Sexp_conv.Exn_converter.add[%extension_constructorUnix.Unix_error](function|Unix.Unix_error(error,fn,param)->Sexp.List[Atom"Unix.Unix_error";Atom(Unix.error_messageerror);Atomfn;Atomparam]|_->assertfalse);;letgrep~vcs~repo_root~below=letfiles_to_grep=matchVcs.ls_filesvcs~repo_root~belowwith|[]->[]|_::_asfiles_to_grep->letstdin_text=files_to_grep|>List.map~f:Vcs.Path_in_repo.to_string|>String.concat~sep:null_separatorinletstdout_ref=ref"<Unknown>"inletstderr_ref=ref"<Unknown>"in(matchletprog=matchLazy.forcefind_xargswith|Someprog->prog|None->failwith"Cannot find xargs in PATH"[@coverageoff]inletstdin_reader,stdin_writer=Spawn.safe_pipe()inletstdout_reader,stdout_writer=Spawn.safe_pipe()inletstderr_reader,stderr_writer=Spawn.safe_pipe()inletpid=Spawn.spawn~cwd:(Path(Vcs.Repo_root.to_stringrepo_root))~prog~argv:["xargs";"-0";"grep";"--no-messages";"-E";"-l";"--binary-files=without-match";cr_pattern_egrep]~stdin:stdin_reader~stdout:stdout_writer~stderr:stderr_writer()inUnix.closestdin_reader;Unix.closestdout_writer;Unix.closestderr_writer;let()=letstdin_oc=Unix.out_channel_of_descrstdin_writerinOut_channel.output_stringstdin_ocstdin_text;Out_channel.flushstdin_oc;Unix.closestdin_writerinletstdout=read_all_from_fdstdout_readerinstdout_ref:=stdout;letstderr=read_all_from_fdstderr_readerinstderr_ref:=stderr;letpid',process_status=waitpid_non_intrpidinassert(pid=pid');matchprocess_statuswith|Unix.WEXITEDn->(* The exit code of [xargs] is not consistent on all of the platforms
that we'd like to support. While it always returns [0] in case of
a match, when the inner [grep] doesn't find a match and returns
[1], the outer call to [xargs] may return [1] or [123] depending
on things like the OS. *)ifInt.equaln0then(letfiles=stdout|>String.split_lines|>List.map~f:Vcs.Path_in_repo.vin`Filesfiles)elseif(Int.equaln123||Int.equaln1(* On MacOS *))&&String.is_emptystdout&&String.is_emptystderrthen`Files[]else`Error(`Exitedn)|Unix.WSIGNALEDn->`Error(`Signaledn)[@coverageoff]|Unix.WSTOPPEDn->`Error(`Stoppedn)[@coverageoff]with|`Filesfiles->files|`Errorexit_status->letstdout=!stdout_refinletstderr=!stderr_refinraise(Err.E(Err.create[Pp.text"Process xargs exited abnormally.";Err.sexp[%sexp{exit_status:Exit_status.t;stdout:string;stderr:string}]]))|exceptionexn->raise(Err.E(Err.create[Pp.text"Error while running xargs process.";Err.exnexn]))[@coverageoff])inList.concat_mapfiles_to_grep~f:(funpath_in_repo->letfile_contents=In_channel.read_all(Vcs.Repo_root.appendrepo_rootpath_in_repo|>Absolute_path.to_string)|>Vcs.File_contents.createinparse_file~path:path_in_repo~file_contents);;