123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458(* This file is part of the Catala build system, a specification language for
tax and social benefits computation rules. Copyright (C) 2020-2025 Inria,
contributors: Denis Merigoux <denis.merigoux@inria.fr>, Emile Rolley
<emile.rolley@tuta.io>, Louis Gesbert <louis.gesbert@inria.fr>
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. *)openCmdlineropenCatala_utils(** {1 Command line interface} *)letcatala_exe=Arg.(value&opt(somestring)None&info["e";"exe"]~docv:"EXE"~doc:"Catala compiler executable.")letcatala_opts=Arg.(value&opt_allstring[]&info["c";"catala-opts"]~docv:"FLAG"~doc:"Option to pass to the Catala compiler. Can be repeated. If neither \
this nor $(b,--test-flags) is specified, the flags for the \
different backends default to $(b,-O).")letautotest=Arg.(value&flag&info["autotest"]~doc:"When compiling to the backends, enable the Catala $(i,--autotest) \
option that runs an evaluation of test scopes (understood as scopes \
that need no input) and instruments their compiled version with \
assertions that the results match. This shouldn't be specified \
directly using $(i,--catala-opts=--autotest) because that wouldn't \
guarantee that the necessary artifacts for interpretation are \
present.")letprepare_only=Arg.(value&flag&info["prepare-only"]~doc:"Compile dependencies of the target(s) but do not run it.")letbuild_dir=Arg.(value&opt(somestring)None&info["build-dir"]~docv:"DIR"~env:(Cmd.Env.info"CLERK_BUILD_DIR")~doc:"Directory where intermediate compilation artifacts should be \
written. Defaults to '_build'.")lettarget_dir=Arg.(value&opt(somestring)None&info["target-dir"]~docv:"DIR"~env:(Cmd.Env.info"CLERK_TARGET_DIR")~doc:"Directory where final compilation targets should be written. \
Defaults to '_target'.")letinclude_dirs=letarg=Arg.(value&opt_all(list~sep:':'string)[]&info["I";"include"]~docv:"DIR"~env:(Cmd.Env.info"CATALA_INCLUDE")~doc:"Make modules from the given directory available from everywhere. \
Several dirs can be specified by repeating the flag or separating \
them with '$(b,:)'.")inTerm.(constList.flatten$arg)lettest_flags=Arg.(value&opt~vopt:[""](liststring)[]&info["test-flags"]~docv:"FLAGS"~env:(Cmd.Env.info"CATALA_TEST_FLAGS")~doc:"Flags to pass to the catala interpreter on $(b,catala test-scope) \
tests. Comma-separated list. A subset may also be applied to the \
compilation of modules, as needed.\n\
WARNING: flag shortcuts are not allowed here (i.e. don't use \
non-ambiguous prefixes such as $(b,--closure) for \
$(b,--closure-conversion))\n\
NOTE: if this is set, all cli tests that are $(i,not) $(b,catala \
test-scope) are skipped to avoid redundant testing.")letruntest_report=Arg.(value&opt(somestring)None&info["report"]~docv:"FILE"~doc:"If set, $(i,clerk runtest) will output a tests result summary in \
binary format to the given $(b,FILE)")letruntest_out=Arg.(value&pos1(somestring)None&info[]~docv:"OUTFILE"~doc:"Write the test outcome to file $(b,OUTFILE) instead of stdout.")letbackend=Arg.(value&opt(enum["interpret",`Interpret;"ocaml",`OCaml;"c",`C;"python",`Python;"java",`Java;])`Interpret&info["backend"]~docv:"BACKEND"~doc:"Run the program using the given backend. $(docv) must be one of \
$(b,interpret), $(b,ocaml), $(b,c), $(b,python), $(b,java)")letrun_command=Arg.(value&optstring"interpret"&info["command"]~docv:"CMD"~doc:"The catala command to run on the input files. Normally \
$(b,interpret), this flag can be used to run $(b,typecheck) or a \
custom plugin instead. This is ignored if $(i,--backend) isn't \
$(b,interpret).")letvars_override=Arg.(value&opt_all(pair~sep:'='stringstring)[]&info["vars"]~docv:"VAR=VALUE"~doc:"Override the given build variable with the given value. Use \
$(i,clerk list-vars) to list the available variables.")letconfig_file=Arg.(value&opt(somefile)None&info["config"]~docv:"FILE"~doc:"Clerk configuration file to use, instead of looking up \
\"clerk.toml\" in parent directories.")letcolor=Arg.(value&opt~vopt:Global.AlwaysCli.when_optAuto&info["color"]~env:(Cmd.Env.info"CATALA_COLOR")~doc:"Allow output of colored and styled text. Use $(i,auto), to enable \
when the standard output is to a terminal, $(i,never) to disable.")letdebug=Arg.(value&flag&info["debug";"d"]~doc:"Prints debug information")letninja_output=Arg.(value&opt(somestring)None&info["o";"output"]~docv:"FILE"~doc:"$(i,FILE) is the file that will contain the build.ninja file \
output. If not specified, the build.ninja file is set to \
$(i,<builddir>/clerk.ninja) in debug mode, and a temporary file \
otherwise")letfiles_or_folders=Arg.(value&pos_allstring[]&info[]~docv:"FILE"~doc:"File(s) or folder(s) to process")letfiles=Arg.(value&pos_allfile[]&info[]~docv:"FILE"~doc:"File(s) to process")lettargets=Arg.(value&pos_allstring[]&info[]~docv:"TARGET"~doc:"Clerk targets to build")letsingle_file=Arg.(required&pos0(somefile)None&info[]~docv:"FILE"~doc:"File to process")letreset_test_outputs=Arg.(value&flag&info["r";"reset"]~doc:"Used with the `test` command, resets the test output to whatever is \
output by the Catala compiler.")letscope=Arg.(value&opt(somestring)None&info["s";"scope"]~docv:"SCOPE"~doc:"Used with the `run` command, selects which scope of a given Catala \
file to run.")letclerk_targets_or_files=Arg.(value&pos_allstring[]&info[]~docv:"TARGET(S)"~doc:"Clerk target(s) or individual file(s) to process")letclerk_targets_or_files_or_folders=Arg.(value&pos_allstring[]&info[]~docv:"TARGET(S)"~doc:"Clerk target(s), individual file(s) or folder(s) to process")letreport_verbosity=Arg.(value&vflag`Failures[(`Summary,info["summary"]~doc:"Only display a summary of the test results");(`Short,info["short"]~doc:"Don't display detailed test failures diff");(`Failures,info["failures"]~doc:"Show details of files with failed tests only");(`Verbose,info["verbose";"v"]~doc:"Display the full list of tests that have been run");])letreport_xml=Arg.(value&flag&info["xml"]~env:(Cmd.Env.info"CATALA_XML_REPORT")~doc:"Output the test report in JUnit-compatible XML format")letdiff_command=Arg.(value&opt~vopt:(SomeNone)(some(somestring))None&info["diff"]~env:(Cmd.Env.info"CATALA_DIFF_COMMAND")~doc:"Use a standard $(i,diff) command instead of the default \
side-by-side view. If no argument is supplied, the command will be \
$(b,patdiff) if available or $(b,diff) otherwise. A supplied \
argument will be used as diff command with arguments pointing to \
the reference file and the output file")letninja_flags=letenv=Cmd.Env.info~doc:"make-compatible flags handling. Currently recognizes the -i and -j \
options and forwards them through to Ninja.""MAKEFLAGS"inletmakeflags=Arg.(value&opt(somestring)None&info["makeflags"]~env~docv:"FLAG"~doc:"Provides the contents of a $(i, MAKEFLAGS) variable to pass on to \
Ninja. Currently recognizes the -i and -j options.")inletmakeflags_to_ninja_flags(makeflags:stringoption)=matchmakeflagswith|None->["-k0"]|Somemakeflags->letignore_rex=Re.(compile@@word(char'i'))inlethas_ignore=Re.execpignore_rexmakeflagsinletjobs_rex=Re.(compile@@seq[str"-j";group(repdigit)])inletnumber_of_jobs=try["-j"^Re.Group.get(Re.execjobs_rexmakeflags)1]with_->[]in(ifhas_ignorethen["-k0"]else[])@number_of_jobsinTerm.(constmakeflags_to_ninja_flags$makeflags)letinfo=letdoc="Build system for Catala, a specification language for tax and social \
benefits computation rules."inletman=[`SManpage.s_description;`P"$(b,clerk) is a build system for Catala, a specification language for \
tax and social benefits computation rules";`SManpage.s_authors;`P"Denis Merigoux <denis.merigoux@inria.fr>";`P"Emile Rolley <emile.rolley@tuta.io>";`P"Louis Gesbert <louis.gesbert@inria.fr>";`SManpage.s_examples;`P"Typical usage:";`Pre"clerk test file.catala_en";`SManpage.s_bugs;`P"Please file bug reports at https://github.com/CatalaLang/catala/issues";]inletexits=Cmd.Exit.defaults@[Cmd.Exit.info~doc:"on error."1]inCmd.info"clerk"~version:Catala_utils.Cli.version~doc~exits~man(** {2 Initialisation of options} *)typeconfig={options:Clerk_config.t;fix_path:File.t->File.t;ninja_file:File.toption;test_flags:stringlist;}letinittest_flagsconfig_fileninja_filecatala_execatala_optsbuild_dirtarget_dirinclude_dirscolordebug=let_options=Catala_utils.Global.enforce_options~debug~color()inletdefault_config_file="clerk.toml"inletset_root_dirdir=Message.debug"Entering directory %a"File.formatdir;Sys.chdirdirin(* fix_path adjusts paths specified from the command-line relative to the user
cwd to be instead relative to the project root *)letfix_path,config=letfrom_dir=Sys.getcwd()inmatchconfig_filewith|None->(matchFile.(find_in_parents(fundir->exists(dir/default_config_file)))with|Some(root,rel)->set_root_dirroot;(Catala_utils.File.reverse_path~from_dir~to_dir:rel,Clerk_config.readdefault_config_file)|None->(matchFile.(find_in_parents(functiondir->exists(dir/"catala.opam")||exists(dir/".git")))with|Some(root,rel)->set_root_dirroot;(Catala_utils.File.reverse_path~from_dir~to_dir:rel,Clerk_config.default_config)|None->Fun.id,Clerk_config.default_config))|Somef->letroot=Filename.dirnamefinletconfig=Clerk_config.readfinset_root_dirroot;(fund->Catala_utils.File.reverse_path~from_dir~to_dir:rootd),configinletbuild_dir=letdir=matchbuild_dirwithNone->config.global.build_dir|Somedir->dirinletdir=matchtest_flagswith|[]->dir|flags->File.((dir/"test")^String.concat""flags)inletdir=File.clean_pathdirinFile.ensure_dirdir;dir(* Note: it could be safer here to use File.(Sys.getcwd () / "_build") by
default, but Ninja treats relative and absolute paths separately so that
you wouldn't then be able to build target _build/foo.ml but would have to
write the full path every time *)inlettarget_dir=letdir=matchtarget_dirwithNone->config.global.target_dir|Somedir->dirinletdir=File.clean_pathdirinFile.ensure_dirdir;dirin{options={configwithglobal={config.globalwithbuild_dir;target_dir;catala_exe;catala_opts=config.global.catala_opts@catala_opts;include_dirs=config.global.include_dirs@include_dirs;};};fix_path;ninja_file;test_flags;}letinit_term?(allow_test_flags=false)()=lettest_flags=ifallow_test_flagsthentest_flagselseTerm.const[]inTerm.(constinit$test_flags$config_file$ninja_output$catala_exe$catala_opts$build_dir$target_dir$include_dirs$color$debug)