diff options
author | Nicolas Pouillard <np@nicolaspouillard.fr> | 2007-02-07 08:59:16 +0000 |
---|---|---|
committer | Nicolas Pouillard <np@nicolaspouillard.fr> | 2007-02-07 08:59:16 +0000 |
commit | 381e325c0f7c9f4188c2a4e6421b46d41c0c007c (patch) | |
tree | 194fbc6442deb3d79b6c595f30f356ed58f063cb | |
parent | 2d26308ad4d34ea0c00e44db62c4c24c7031c78c (diff) | |
download | ocaml-381e325c0f7c9f4188c2a4e6421b46d41c0c007c.tar.gz |
Add the ocamlbuild directory
git-svn-id: http://caml.inria.fr/svn/ocaml/trunk@7823 f963ae5c-01c2-4b8c-9fe0-0dff7051ff02
183 files changed, 14243 insertions, 0 deletions
diff --git a/ocamlbuild/AUTHORS b/ocamlbuild/AUTHORS new file mode 100644 index 0000000000..0b27a5b1a8 --- /dev/null +++ b/ocamlbuild/AUTHORS @@ -0,0 +1,2 @@ +Nicolas Pouillard +Berke Durak diff --git a/ocamlbuild/ChangeLog b/ocamlbuild/ChangeLog new file mode 100644 index 0000000000..3d02e2607f --- /dev/null +++ b/ocamlbuild/ChangeLog @@ -0,0 +1,3147 @@ +2007-02-06 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Make -a more static, to avoid some complications. + + * ocaml_utils.ml, + * ocaml_compiler.ml, + * ocaml_compiler.ml, + * ocaml_specific.ml: Ditto. + +2007-02-06 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + A fix. + + * ocaml_compiler.ml: Don't use these refs too early. + * ocamlbuild-presentation.rslide: . + +2007-02-05 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Plugin signature. + + Somewhat a big patch, but that's just moving things around. + + * signatures.mli: Add TAGS, OUTCOME, MISC, OPTIONS, ARCH and PLUGIN. + * ocamlbuild_plugin.mli: New. + * ocamlbuild_plugin.ml: Conform to the sig. + + * command.ml, + * command.mli: Add a tags type. + * main.ml: Quit early if no targets. + * my_std.ml, + * my_std.mli: More things are in signatures. + * resource.ml, + * resource.mli: Remove the type t that was an Pathname.t alias. + * options.ml, + * options.mli: Add ext_lib, ext_obj, ext_dll. + * ocaml_compiler.ml: Update. + * ocaml_tools.ml: Update to Outcome. + * ocaml_specific.ml: Update. + * ocaml_utils.mli: Remove *ext_*. + * ocaml_arch.mli: Now in signatures. + * pathname.ml: Add readdir. + * slurp.ml: open Outcome. + * rule.ml, + * rule.mli, + * solver.ml, + * solver.mli: Update to Resource.t and Outcome.t. + * tags.mli: Now in Signatures. + * test/good-output: Update. + * test/test8/test.sh, + * test/test3/test.sh, + * test/test4/test.sh, + * test/test5/test.sh, + * test/test6/test.sh, + * test/test7/test.sh, + * test/test2/test.sh: Update to -verbose 0. + +2007-02-05 Berke Durak <berke.durak@inria.fr> + + Continuing doc. + + * manual/manual.tex: . + * .: . + +2007-02-05 Berke Durak <berke.durak@inria.fr> + + Described display line. + + * manual/manual.tex: . + +2007-02-05 Berke Durak <berke.durak@inria.fr> + + Renamed -debug as -verbose. Authorized spaces etc. in flags. Continuing documentation. + + * lexers.mll: . + * manual/manual.tex: . + * options.ml: . + +2007-02-05 Berke Durak <berke.durak@inria.fr> + + Added man page. + + * main.ml: . + * man: New. + * man/ocamlbuild.1: New. + * manual/manual.tex: . + * TODO: . + +2007-02-05 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Update start.sh. + + * start.sh: Update. + +2007-02-05 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Typo s/Orignal/Original/g. + +2007-02-05 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Make signatures and std_signatures mliS. + + * signatures.ml: Remove. + * std_signatures.ml: Remove. + * signatures.mli: New. + * std_signatures.mli: New. + * Makefile: Update. + * lexers.mll: Allow any prefix: for tags. + +2007-02-04 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + The beginning of a presentation. + + * ocamlbuild-presentation.rslide: New. + +2007-02-04 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Also add who is the original author of the file. + + * ocamlbuild.ml, + * ocamlbuild_plugin.ml, + * ocamlbuildlight.ml, + * ocamlbuild_where.mli, + * ocamlbuild.mli, + * ocamlbuildlight.mli, + * bool.ml, + * bool.mli, + * configuration.ml, + * configuration.mli, + * command.ml, + * command.mli, + * display.ml, + * discard_printf.ml, + * display.mli, + * discard_printf.mli, + * executor.ml, + * executor.mli, + * flags.ml, + * fda.ml, + * flags.mli, + * fda.mli, + * glob.ml, + * glob_ast.ml, + * glob_ast.mli, + * glob.mli, + * glob_lexer.mli, + * glob_lexer.mll, + * hygiene.ml, + * hooks.ml, + * hygiene.mli, + * hooks.mli, + * log.ml, + * lexers.mli, + * log.mli, + * lexers.mll, + * my_unix_with_unix.ml, + * main.ml, + * my_unix.ml, + * my_std.ml, + * my_unix_with_unix.mli, + * my_std.mli, + * my_unix.mli, + * main.mli, + * ocaml_utils.ml, + * ocaml_tools.ml, + * ocaml_arch.ml, + * ocaml_specific.ml, + * ocaml_compiler.ml, + * ocaml_dependencies.ml, + * ocaml_utils.mli, + * ocaml_specific.mli, + * ocaml_dependencies.mli, + * ocaml_tools.mli, + * ocaml_arch.mli, + * ocaml_compiler.mli, + * options.ml, + * options.mli, + * ocamldep.ml, + * ocamldep.mli, + * plugin.ml, + * ppcache.ml, + * pathname.ml, + * ppcache.mli, + * plugin.mli, + * pathname.mli, + * resource.ml, + * resource.mli, + * rule.ml, + * rule.mli, + * report.ml, + * report.mli, + * signatures.ml, + * slurp.ml, + * std_signatures.ml, + * solver.ml, + * shell.ml, + * shell.mli, + * slurp.mli, + * solver.mli, + * tags.ml, + * tools.ml, + * tags.mli, + * tools.mli: Ditto. + +2007-02-04 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add the header license. + + * ocamlbuildlight.ml, + * ocamlbuild.ml, + * ocamlbuild_plugin.ml, + * ocamlbuild_where.mli, + * ocamlbuild.mli, + * ocamlbuildlight.mli, + * bool.ml, + * bool.mli, + * configuration.ml, + * configuration.mli, + * command.ml, + * command.mli, + * discard_printf.ml, + * display.ml, + * display.mli, + * discard_printf.mli, + * executor.ml, + * executor.mli, + * fda.ml, + * flags.ml, + * flags.mli, + * fda.mli, + * glob.ml, + * glob_ast.ml, + * glob.mli, + * glob_ast.mli, + * glob_lexer.mli, + * glob_lexer.mll, + * hygiene.ml, + * hooks.ml, + * hygiene.mli, + * hooks.mli, + * log.ml, + * lexers.mli, + * log.mli, + * lexers.mll, + * my_unix.ml, + * main.ml, + * my_std.ml, + * my_unix_with_unix.ml, + * misc/opentracer.ml, + * my_std.mli, + * main.mli, + * my_unix.mli, + * my_unix_with_unix.mli, + * ocaml_arch.ml, + * ocaml_compiler.ml, + * ocaml_specific.ml, + * ocaml_tools.ml, + * ocaml_utils.ml, + * ocaml_dependencies.ml, + * ocaml_utils.mli, + * ocaml_tools.mli, + * ocaml_dependencies.mli, + * ocaml_compiler.mli, + * ocaml_specific.mli, + * ocaml_arch.mli, + * options.ml, + * options.mli, + * ocamldep.ml, + * ocamldep.mli, + * plugin.ml, + * pathname.ml, + * ppcache.ml, + * plugin.mli, + * ppcache.mli, + * pathname.mli, + * resource.ml, + * resource.mli, + * rule.ml, + * rule.mli, + * report.ml, + * report.mli, + * slurp.ml, + * std_signatures.ml, + * signatures.ml, + * solver.ml, + * shell.ml, + * shell.mli, + * slurp.mli, + * solver.mli, + * tools.ml, + * tags.ml, + * tags.mli, + * tools.mli: Ditto. + +2007-02-03 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix a bug in copy_file. + + * my_std.ml: Also use the binary mode for the output channel. + +2007-02-03 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add nopervasives, and nolabels. + + * ocaml_specific.ml: Ditto. + +2007-02-03 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Windows cannot use executor. + + * shell.ml: As in command.ml use executor only in non-windows and + non-degraded mode. + * rule.ml: Update two error messages. + +2007-02-02 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Some minor things for the ocaml myocamlbuild for instance. + + * configuration.ml, + * configuration.mli: Add has_tag. + * my_std.ml, + * my_std.mli: Add getenv and copy_chan. + * ocaml_utils.ml, + * ocaml_utils.mli: Move some commands to rule. + * ocaml_specific.ml: Improve the menhir switching. + * options.ml, + * options.mli: Add -use-menhir and -menhir options. + * rule.ml, + * rule.mli: Add copy_rule and move some commands from ocaml_utils. + * signatures.ml: . + +2007-02-01 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Move main sigs in signatures.ml and std_signatures.ml. + + * ocamlbuild_pack.mlpack: . + * ocamlbuildlib.mllib: . + * ocamlbuildlightlib.mllib: . + * ocamlbuild.odocl: . + * command.mli: . + * glob.mli: . + * log.mli: . + * my_unix_with_unix.ml: . + * my_std.ml: . + * my_std.mli: . + * ocaml_specific.ml: . + * pathname.mli: . + * std_signatures.ml: New. + * signatures.ml: New. + * start.sh: . + * tags.ml: . + * tags.mli: . + * test/test5/_tags: . + * Makefile: . + * _tags: . + +2007-02-01 Berke Durak <berke.durak@inria.fr> + + Shell.run doesn't use execute_many in degraded mode. + + * shell.ml: . + +2007-02-01 Berke Durak <berke.durak@inria.fr> + + cp, rm -rf and mv-like commands use Executor to better play with display. + + * display.ml: . + * my_unix.ml: . + * main.ml: . + * my_std.ml: . + * my_unix.mli: . + * shell.ml: . + * shell.mli: . + * start.sh: . + * _tags: . + +2007-02-01 Berke Durak <berke.durak@inria.fr> + + Systematizing exit codes. + + * executor.ml: . + * main.ml: . + +2007-02-01 Berke Durak <berke.durak@inria.fr> + + Added automatic file: tag, changed flag syntax. + + * lexers.mli: . + * lexers.mll: . + * main.ml: . + * ocamldep.ml: . + * ocamldep.mli: . + * tools.ml: . + +2007-01-31 Berke Durak <berke.durak@inria.fr> + + Cleans up links to the _build directory. + + * main.ml: . + * options.ml: . + * options.mli: . + * pathname.ml: . + * pathname.mli: . + +2007-01-31 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Restore the link to binary targets functionality. + + * main.ml: Make it separate from target running. + +2007-01-31 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add an hygiene hook pair. + + * ocamlbuild_plugin.ml, + * hooks.ml, + * hooks.mli, + * main.ml: Ditto. + +2007-01-31 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + The Killer feature about a fine grained dependency injection control. + + * rule.ml, + * rule.mli: Add build_deps_of_tags and call it automatically before + * ocaml_compiler.ml, + * ocaml_compiler.mli: Rework tags, to have them when callinng + build_deps_of_tags. + executing a command. + * ocaml_specific.ml, + * ocaml_specific.mli: Move the exception Exit_build_error to main and + remove the old dep function. + * ocamlbuild_plugin.ml: Export some new functions. + * test/test7/myocamlbuild.ml: Add a dep declaration. + * test/test7/cool_plugin.ml: New. + * test/test7/_tags: New. + * test/good-output: Update. + * flags.ml, + * command.ml, + * command.mli: Rename flags_of_tags as tag_handler. + * main.ml: Update error handling. + * TODO: Done. + +2007-01-30 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix and improve the new link/deps system. + + * ocaml_dependencies.ml, + * ocaml_dependencies.mli: Some fixes and improvements. + * pathname.ml, + * pathname.mli: Add check_extension. + * ocaml_compiler.ml, + * ocaml_compiler.mli: Add support for hidden_packages and update. + +2007-01-30 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Reverse the last 2 patches, since there is fact no name clash. + +2007-01-30 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Shell -> Oshell second part. + + * ocamlbuild.odocl: Ditto. + * test/good-output: Update. + +2007-01-30 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Rename the Shell module as Oshell to avoid a name clash with labltk. + + * shell.ml: Remove. + * shell.mli: Remove. + * oshell.ml: New. + * oshell.mli: New. + * ocamlbuild_pack.mlpack, + * command.ml, + * display.ml, + * main.ml, + * options.ml, + * ppcache.ml, + * pathname.ml, + * plugin.ml, + * resource.ml, + * start.sh: Update. + +2007-01-30 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix 2 bugs. + + * test/test9/testglob.ml: More tests. + * glob_lexer.mll: Fix "/**". + * _tags: Restore my warnings. + * executor.ml: Use the unused variable. + +2007-01-30 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Improve dprintf and update. + + * log.ml, log.mli: dprintf now wraps the message between "@[<2>" and "@]@.". + * command.ml, + * display.ml, + * fda.ml, + * main.ml, + * ocaml_dependencies.ml, + * ocaml_compiler.ml, + * ocaml_utils.ml, + * ocamldep.ml, + * pathname.ml, + * resource.ml, + * rule.ml, + * solver.ml: Update the dprintf usage. + +2007-01-30 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add the new dependency linking system (plz test it !). + + * ocamlbuild_pack.mlpack: Add a brand new module. + * ocaml_dependencies.ml: New. + * ocaml_dependencies.mli: New. + * ocaml_compiler.ml, + * ocaml_compiler.mli: Use this new module. + * resource.ml, + * resource.mli: Export a folding function on dependencies. + * TODO: Add something to do. + * start.sh: . + * main.ml: Update. + +2007-01-29 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Executor exit codes. + + * executor.ml: Use the standard exit. + * main.ml: Some exit codes are reserved for Executor. + +2007-01-29 Berke Durak <berke.durak@inria.fr> + + Executor returns finer-grained results. + + * executor.ml: . + * manual/manual.tex: . + +2007-01-29 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Toward a working command execute feature :). + + * executor.ml, + * executor.mli: FIXME. + * command.ml, + * command.mli: Update to the new signature and merge the degraded mode + to avoid duplication. + * my_unix.ml, + * my_unix.mli, + * ocaml_utils.ml, + * ocamldep.ml, + * plugin.ml, + * resource.ml, + * rule.ml, + * solver.ml, + * test/good-output: Update. + +2007-01-29 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Revert almost all of the 2 last patches. + + * command.ml: . + * command.mli: . + * executor.ml: . + * executor.mli: . + * my_unix.ml: . + * my_unix.mli: . + * ocaml_utils.ml: . + * ocaml_specific.ml: . + * ocamldep.ml: . + * plugin.ml: . + * resource.ml: . + * rule.ml: . + * solver.ml: . + +2007-01-29 Berke Durak <berke.durak@inria.fr> + + Fixing before/after thunks. + + * command.ml: . + * command.mli: . + * ocaml_utils.ml: . + * ocamldep.ml: . + * plugin.ml: . + * resource.ml: . + * rule.ml: . + * solver.ml: . + * TODO: . + +2007-01-29 Berke Durak <berke.durak@inria.fr> + + Adding before and after handlers to Executor. + + * command.ml: . + * executor.ml: . + * executor.mli: . + * my_unix.ml: . + * my_unix.mli: . + * manual/manual.tex: . + +2007-01-29 Berke Durak <berke.durak@inria.fr> + + Fixed multi-dir globbing. + + * glob_lexer.mll: . + * manual/manual.tex: . + +2007-01-29 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add Rule.custom_rule and cleanup the ocamldep meta rule. + + * ocamldep.ml, + * ocamldep.mli: Make it a meta rule (or a rule generator). + * rule.ml, + * rule.mli: Add custom_rule. + * ocaml_specific.ml: Update to Ocamldep. + * test/good-output: Minor update. + +2007-01-29 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + MakefileS... + + * manual/Makefile: More things to remove (sometimes). + * Makefile: Use $(BUILDDIR) instead of _build. + +2007-01-26 Berke Durak <berke.durak@inria.fr> + + Documenting glob expressions. + + * glob_lexer.mll: Added negative character classes. + * manual/manual.tex: . + +2007-01-26 Berke Durak <berke.durak@inria.fr> + + Started documenting glob syntax. + + * manual/manual.tex: . + +2007-01-25 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + One other include dir fix. + + * main.ml: Ditto. + * test/test9/testglob.ml: Add a failing test (request for feature). + * test/good-output: Update. + +2007-01-25 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Include dirs and Backtrace. + + * main.ml: Fix -I, and restore the backtrace. + * report.ml, + * report.mli: Fix the backtrace and rename analyze to + print_backtrace_analyze. + +2007-01-25 Berke Durak <berke.durak@inria.fr> + + Added cross-directory globbing. + + * glob_ast.ml: . + * glob.ml: . + * glob_ast.mli: . + * glob_lexer.mll: . + * test/test9/testglob.ml: . + +2007-01-25 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Inlcude dirs trought tags. + + * main.ml: Ditto. + * my_unix_with_unix.ml: Imrpove stat errors. + * my_std.ml, + * my_std.mli: . + * pathname.ml: bmla. + * slurp.ml, + * slurp.mli: Add force, fix bugs. + +2007-01-25 Berke Durak <berke.durak@inria.fr> + + Fixed double display of error status. + + * command.ml: . + * display.ml: . + * display.mli: . + * log.ml: . + * log.mli: . + * main.ml: . + * my_std.ml: . + * my_std.mli: . + * plugin.ml: . + +2007-01-25 Berke Durak <berke.durak@inria.fr> + + Stupid bug. + + * log.ml: . + * main.ml: . + * options.ml: . + +2007-01-25 Berke Durak <berke.durak@inria.fr> + + Fixed interface, handling of -- with no argument. + + * ocamlbuild_plugin.mli: Remove. + * manual/manual.tex: . + * options.ml: . + +2007-01-25 Berke Durak <berke.durak@inria.fr> + + Updated start.sh. + + * start.sh: . + +2007-01-25 Berke Durak <berke.durak@inria.fr> + + Added .mlis. + + * ocamlbuild_plugin.mli: New. + * fda.mli: New. + * main.ml: . + * ocaml_specific.ml: . + * plugin.ml: . + * plugin.mli: New. + +2007-01-25 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Cut down ocaml_specific in pieces. + + * ocaml_specific.ml, + * ocaml_specific.mli: Split. + * ocamlbuild_plugin.ml: Update. + * ocamlbuild_pack.mlpack: Add new modules. + * my_std.ml, + * my_std.mli: Add good_outcome. + * ocaml_utils.ml: New. + * ocaml_tools.ml: New. + * ocaml_compiler.ml: New. + * ocaml_utils.mli: New. + * ocaml_compiler.mli: New. + * ocaml_tools.mli: New. + * ocamldep.ml: New. + * ocamldep.mli: New. + * start.sh: Update. + * TODO: Move things done. + +2007-01-25 Berke Durak <berke.durak@inria.fr> + + Fixer return codes and error message flushing issues. + + * display.ml: . + * log.ml: . + * log.mli: . + * main.ml: . + * report.ml: . + * report.mli: . + +2007-01-25 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add a warning. + + * ocaml_specific.ml: In -debug 1 mode there is a now a warning when + ocamlbuild skip a seliently a module, supposing that's an error of + ocamldep. + +2007-01-24 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + More hooks. + + * ocamlbuild_plugin.ml, + * hooks.ml, + * hooks.mli, + * main.ml: Add {Before,After}_rules. + +2007-01-24 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Call these hooks. + + * main.ml: Call these hooks. + +2007-01-24 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add a first version of dispatch. + + * ocamlbuild_plugin.ml: Export dispatch and the hooks type. + * ocamlbuild_pack.mlpack: Add Hooks. + * hooks.ml: New. + * hooks.mli: New. + * ocaml_specific.mli: New line. + +2007-01-24 Berke Durak <berke.durak@inria.fr> + + Mini slurp bug. + + * slurp.ml: . + * TODO: . + +2007-01-24 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix few more things. + + * ocamlbuildlight.ml: . + * ocamlbuild_version.ml: Remove. + * ocamlbuild.ml: . + * ocamlbuild_pack.mlpack: . + * main.ml: . + * ocaml_specific.ml: . + * ocaml_specific.mli: . + * start.sh: . + * test/test2/toto.ml: . + * test/good-output: . + +2007-01-24 Berke Durak <berke.durak@inria.fr> + + Read directories before files in Slurp. + + * slurp.ml: . + * TODO: . + +2007-01-24 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix some bugs. + + * ocamlbuild_version.ml: Remove. + * ocamlbuild.ml, + * ocamlbuildlight.ml: Main is now in the pack. + * ocamlbuild_pack.mlpack: more things. + * ocaml_specific.ml: One fix and one comment. + * start.sh: Update. + +2007-01-24 Berke Durak <berke.durak@inria.fr> + + Splitting ocaml_specific into multiple files. + + * ocamlbuildlight.ml: . + * ocamlbuild.ml: . + * ocamlbuild_version.ml: New. + * ocamlbuild.mli: . + * ocamlbuildlight.mli: . + * ocamlbuild_pack.mlpack: . + * command.ml: . + * fda.ml: New. + * hygiene.ml: . + * main.ml: New. + * my_std.ml: . + * my_std.mli: . + * main.mli: New. + * manual/manual.tex: . + * ocaml_specific.ml: . + * ocaml_specific.mli: . + * options.ml: . + * options.mli: . + * plugin.ml: New. + * rule.ml: . + * report.ml: . + * tools.ml: New. + * tools.mli: New. + * TODO: . + * _tags: . + +2007-01-24 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Minor changes. + + * manual/manual.tex: Typo s/the the/the/g. + * ocaml_specific.ml, + * ocaml_specific.mli: Add some function to deal with linking of a + module list. Add a better lib declaration function. + * TODO: Update. + +2007-01-17 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + A new pathname operator and a bug fix. + + * pathname.ml, + * pathname.mli: add the ( -.- ) operator to add an extension to a + pathname. + * ocaml_specific.ml: Use that new operator. + * resource.ml: Fix a bug. + +2007-01-17 Berke Durak <berke.durak@inria.fr> + + More examples. + + * examples/example3/epoch.ml: . + * examples/example3/make.sh: New. + * manual/manual.tex: . + * TODO: . + +2007-01-17 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Infered mli's, and bug fixes. + + * my_unix.ml: Fix a bug. + * my_std.mli: Doc. + * manual/manual.tex: Use \verb. + * ocaml_arch.ml: Don't always overide the forpack_flags_of_pathname + function reference. + * ocaml_arch.mli: Remove the reference. + * ocaml_specific.ml: Update for forpack and add infered mli's. + * pathname.ml, + * pathname.mli: Add is_directory. + +2007-01-17 Berke Durak <berke.durak@inria.fr> + + More examples. + + * examples/example3/epoch.ml: New. + * examples/example2/hello.ml: . + * examples/example2/greet.ml: New. + * examples/example3: New. + * examples/example2: New. + * manual/manual.tex: . + * TODO: . + +2007-01-17 Berke Durak <berke.durak@inria.fr> + + Started examples. + + * examples/example1/hello.ml: New. + * examples/example1: New. + * examples: New. + * manual/manual.tex: . + * .vcs: . + * TODO: . + +2007-01-17 Berke Durak <berke.durak@inria.fr> + + Wrote limitations and features. + + * manual/manual.tex: . + +2007-01-17 Berke Durak <berke.durak@inria.fr> + + Wrote motivations. + + * manual/manual.tex: . + * _tags: . + +2007-01-17 Berke Durak <berke.durak@inria.fr> + + Started manual. + + * manual/Makefile: New. + * manual/manual.tex: New. + * manual: New. + +2007-01-17 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Bugs, menhir, path variables. + + * display.ml: Fix a bug. + * glob.mli: Fix a typo. + * lexers.mli, + * lexers.mll: Extend ocamldep_output lexer and meta_path lexer. + * my_std.ml, + * my_std.mli: Add memo and String.rev. + * ocaml_specific.ml, + * ocaml_specific.mli: Better rules for C lib linking and menhir rules. + * resource.ml, + * resource.mli: Handle naively some multiple variables. + * rule.ml, + * rule.mli: Update. + * start.sh: Update. + +2007-01-11 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Integrate dprintf to the display. + + * display.ml, + * display.mli: Add dprintf and log_level. + * log.ml, + * log.mli: Add dprintf and level. + * debug.ml: Remove. + * debug.mli: Remove. + * options.ml: Update. + * command.ml, ocaml_specific.ml, my_std.ml, + * pathname.ml, ppcache.ml, resource.ml, + * rule.ml, report.ml, slurp.ml, solver.ml, + * configuration.ml, tags.ml: Update to Log. + * ocamlbuild.odocl: Add Log, remove Debug. + * ocamlbuild_pack.mlpack: Remove Debug. + * bool.ml: Remove the debug dependency. + +2007-01-10 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Execute and windows... + + * command.ml: Test windows here. + * my_unix_with_unix.ml: Revert a little. + +2007-01-10 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Don't use executor on windows. + + * my_unix_with_unix.ml: Since at least set_nonblock does not works on + windows. + +2007-01-10 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add the -no-log option and fix a log bug. + + * log.mli, + * log.ml: Log is now a lazy to have the good setup order. + * options.ml: Add the -no-log option. + +2007-01-10 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix a bug with quoting of the nil string. + + * shell.ml: Quote the nil string. + +2007-01-09 Berke Durak <berke.durak@inria.fr> + + Documented the interface of the glob module. + + * glob.mli: . + +2007-01-09 Berke Durak <berke.durak@inria.fr> + + Continuing to document interfaces. + + * bool.mli: . + * debug.mli: . + * discard_printf.mli: . + * executor.mli: . + * hygiene.mli: . + * my_std.mli: . + * slurp.mli: . + * Makefile: . + +2007-01-09 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix a bug with directory links to build dir. + + * ocaml_specific.ml, + * options.ml, + * options.mli: Keep the Slurp.entry instead of a set. + * pathname.ml: Clean the entry instead of the set, that more precise. + * Makefile: Add doc phonny rules. + +2007-01-09 Berke Durak <berke.durak@inria.fr> + + Doc for Configuration. + + * ocamlbuild_plugin.ml: . + * bool.mli: . + * configuration.ml: . + * configuration.mli: . + * command.mli: . + * doc: New. + * glob.ml: . + * ocaml_specific.ml: . + * Makefile: . + +2007-01-09 Berke Durak <berke.durak@inria.fr> + + Started documentation. + + * bool.mli: . + * command.ml: . + * command.mli: . + +2007-01-09 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Export the doc. + + * Makefile: Use a link. + * _tags: Don't spend times in that dir. + +2007-01-09 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Put the log file in the source dir and not when building plugin. + + * log.ml, + * log.mli: Use an optional. + * options.ml: Update. + * pathname.mli: Export in_source_dir. + * .vcs: Add _log. + +2007-01-09 Berke Durak <berke.durak@inria.fr> + + Added doc target. + + * report.ml: . + * Makefile: . + * TODO: . + +2007-01-09 Berke Durak <berke.durak@inria.fr> + + Writes tags to log file. + + * display.ml: . + +2007-01-09 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add the Log module. + + * ocamlbuild_pack.mlpack: Add Log. + * command.ml, + * command.mli: Use Log. + * log.ml: New. + * log.mli: New. + * options.ml: Use Log. + * start.sh: Update. + +2007-01-09 Berke Durak <berke.durak@inria.fr> + + Added -log option. + + * command.ml: . + * command.mli: . + * display.ml: . + * display.mli: . + * executor.mli: . + * options.ml: . + * _tags: . + +2007-01-09 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Make usable the ocamldoc support. + + * ocaml_specific.ml: Add rules for ocamldoc. + * ocamlbuild.odocl: New. + * test/test3/proj.odocl: New. + * test/good-output: Update. + * test/test3/test.sh: Add a odoc test. + +2007-01-09 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Some cleanups. + + * ocamlbuild_plugin.ml: Add tag_file that simule one simple line in the + _tags file. + * ocaml_specific.ml, + * ocaml_specific.mli: Add ln_s, touch, chmod. + * pathname.ml, + * pathname.mli: Remove map_extension*, split_extension* and compiled + files hack skipping. + * rule.ml: Improve logging. + * solver.ml: Use another level. + +2007-01-07 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix a bug with debug rules. + + * ocaml_specific.ml: Move %.cmi from prods to deps. + * test/good-output: Update. + +2007-01-07 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add debugging rules. + To get a ocamlbuild with debugging info you can + call `make debug' that will produce ocamlbuild.d.byte + and x.d.cmo files. + + * ocaml_specific.ml, + * ocaml_specific.mli: Add debugging rules, reorder warnings flag to + have 'A' and 'a' before others. + * Makefile: Add the debug target. + * _tags: Cleanup (remove the debug tag that was set by default). + +2007-01-07 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add profiling support directly in rules. + This means that you can now request for building a target such as + my_main.p.native or my_lib.p.cmxa, that will create %.p.cmx + intermediate files that do not interfer with non-profiling ones. + + * ocaml_specific.ml, + * ocaml_specific.mli: Add rules and functions for native link and + comilation in profiling mode. + * Makefile: Add a profile target (require a fixed ocamlopt w.r.t pack). + * _tags: Take care also of .p.cmx files. + * glob.ml: IS.print is equivalent to print_is. + * my_std.ml: Fix a bug. + +2007-01-07 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add some functions... + + * glob.ml: Extract is_suffix and is_prefix. + * my_std.ml, + * my_std.mli: Add String.{is_suffix,is_prefix,first_chars,last_chars} + and List.union. + * pathname.ml, + * pathname.mli: Add get_extensions, remove_extensions, + update_extensions, map_extensions that treat all extensions instead of + just the last. + * tags.ml, + * tags.mli: Add +++ and --- that treat optional tags. + +2007-01-06 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Change the default display in degraded mode. + + * command.ml: Ditto. + +2007-01-06 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Cleanup Makefile options. + + * Makefile: Ditto. + +2007-01-06 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add a simple opened files tracer. + + * misc/opentracer.ml: New. + Just support ktrace for now. A strace one will be appreciated the + interface to follow is quite simple anyway. + * misc: New. + +2007-01-06 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Handle better commands without Px atom. + + * command.ml: Display the whole command if no Px is found. + * display.mli: No longer export these strings. + +2007-01-06 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Handle myocamlbuild_config.mli. + + * ocaml_specific.ml: Add support for an interface to the config. + +2007-01-06 Berke Durak <berke.durak@inria.fr> + + Improved language of explanations in Report. + + * report.ml: . + +2007-01-06 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Factor and fix the plugin building. + + * ocamlbuildlight.mli: New. + * executor.ml: Call cleanup, add a fixme. + * ocaml_specific.ml: Factor and fix plugin stuffs. + * start.sh: Update. + * Makefile: Update. + * TODO: Update. + * _tags: No longer do favors to some modules. + +2007-01-05 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix plugins. + + * ocamlbuildlib.mllib: Add missing modules. + * ocamlbuildlightlib.mllib: New. + * Makefile: Update. + +2007-01-05 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Change the my_unix system. + + * ocamlbuildlight.ml: Just call the main. + * ocamlbuild.ml: Setup my_unix_with_unix. + * ocamlbuildlib.mllib: Remove executor and exit_codes for the lib. + * ocamlbuild_pack.mlpack: Remove my_std and my_unix. + * exit_codes.ml: Remove. Put them directly in executor. + * executor.ml: Add exitcodes. + * my_unix.ml: New. Default implem. + * my_unix_with_unix.ml: Extend the default implem. + * my_unix_without_unix.ml: Remove. + * my_unix.mli: Add the implem type and val. + * my_unix_with_unix.mli: New. + * ocaml_specific.ml, + * pathname.ml, + * slurp.ml, + * Makefile, + * command.ml, + * _tags: Update. + +2007-01-05 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Don't use executor for the myocamlbuild call. + + * ocaml_specific.ml: Use sys_command directly. + +2007-01-05 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix a stupid bug. + + * command.ml: That cause to have reversed sequences. + +2007-01-05 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Some libs and ocamldoc changes. + + * ocaml_specific.ml, + * ocaml_specific.mli: Improve ocaml_lib_flag, add fews libs. + Fix ocamldoc support update tags, and use Px only once. + +2007-01-05 Berke Durak <berke.durak@inria.fr> + + Started ocamldoc support. + + * ocaml_specific.ml: . + * options.ml: . + * options.mli: . + +2007-01-05 Berke Durak <berke.durak@inria.fr> + + Pretend option didn't work. + + * command.ml: . + +2007-01-05 Berke Durak <berke.durak@inria.fr> + + TODO + typo. + + * options.ml: . + * TODO: . + +2007-01-05 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Really call executor all time. + + * command.ml, + * command.mli: Remove normalization. + And execute_many, it's now execute that do all the job. + In degraded mode it's execute_degraded. + * my_unix_without_unix.ml: Update. + * ocaml_specific.ml, + * resource.ml, + * rule.ml, + * solver.ml: Update to Command.execute type. + +2007-01-05 Berke Durak <berke.durak@inria.fr> + + Isatty detection logic. + + * command.ml: . + * executor.ml: . + * my_unix_with_unix.ml: . + * my_unix_without_unix.ml: . + * my_unix.mli: . + +2007-01-05 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Always call executor. + + * command.ml: Unless in degraded mode. + +2007-01-05 Berke Durak <berke.durak@inria.fr> + + Removed debugging output, added period argument for ticker. + + * display.ml: . + * executor.ml: . + * executor.mli: . + * my_unix.mli: . + +2007-01-05 Berke Durak <berke.durak@inria.fr> + + Somewhat slow but executor seems to work. + + * executor.ml: . + +2007-01-05 Berke Durak <berke.durak@inria.fr> + + Added an Exit_codes module. Fixing Executor... + + * ocamlbuild.ml: . + * ocamlbuildlib.mllib: . + * executor.ml: . + * exit_codes.ml: New. + * solver.ml: . + +2007-01-05 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix the max_jobs argument passing. + + * command.ml: Use an optional argument. + +2007-01-05 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Subway changes... + + * my_unix_without_unix.ml, + * my_unix_with_unix.ml, + * my_unix.mli, + * command.ml: Call the new execute_many. + * executor.ml, + * executor.mli: Handle command sequences. + +2007-01-04 Berke Durak <berke.durak@inria.fr> + + Added Display.update. + + * display.ml: . + * display.mli: . + * executor.ml: . + +2007-01-04 Berke Durak <berke.durak@inria.fr> + + Added display function, indentation, language. + + * display.ml: . + * display.mli: . + * hygiene.ml: . + +2007-01-04 Berke Durak <berke.durak@inria.fr> + + Fixing interface of Executor. + + * executor.ml: . + * executor.mli: . + * my_unix_with_unix.ml: . + +2007-01-04 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add attributes to entries. Add the -byte-plugin option. + + * slurp.ml, + * slurp.mli: Add an attribute field, add map, rename fold_pathnames to + fold and filter_on_names to filter. + * hygiene.ml, + * hygiene.mli: Perform hygiene only on entries with a true attribute. + * options.ml, + * options.mli: Add the native_plugin reference and the -byte-plugin + option. + * ocaml_specific.ml, + * ocaml_specific.mli: Exclude files tagged not_hygienic or precious + from hygiene. + +2007-01-04 Berke Durak <berke.durak@inria.fr> + + Fixed pack issues. + + * ocamlbuild.ml: . + * executor.ml: . + * executor.mli: New. + * _tags: . + +2007-01-04 Berke Durak <berke.durak@inria.fr> + + Started executor module. + + * executor.ml: New. + * hygiene.ml: . + * my_unix_with_unix.ml: . + +2007-01-04 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add virtual commands. + + * command.ml, + * command.mli: Add the V constructor for virtual commands that + will query a virtual command solver to use the best implementation + of that virtual command. + +2007-01-04 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Mainly, prepare for parallel display. + + * ocamlbuild_plugin.ml: Export file_rule. + * command.ml, + * command.mli: Some cleanup and preparation. + * lexers.mll: Remove the dirty hack. + * my_std.ml, + * my_std.mli: Move search_in_path to Command and add ( @:= ). + * my_unix_with_unix.ml, + * my_unix_without_unix.ml, + * my_unix.mli: Change the execute_many_using_fork type. + * ocaml_specific.ml: Use the nopervasives tag for + pervasives dependencies. + * start.sh: Update. + * test/test8/myocamlbuild.ml: Update. + * test/good-output: Update. + * Makefile: Update. + +2007-01-03 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + I don't like microbes. + + * hygiene.ml: Reverse the bool. + +2007-01-03 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix the stat problem. + + * ocaml_specific.ml: Use the filtered entry for source_dir_path_set. + +2007-01-03 Berke Durak <berke.durak@inria.fr> + + Hygiene filters cleaned out microbes. + + * hygiene.ml: . + * hygiene.mli: . + * ocaml_specific.ml: . + * ocaml_specific.mli: . + * slurp.ml: . + * slurp.mli: . + +2007-01-03 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Filename concat cleanup. + + * my_std.ml, + * my_std.mli: Add filename_concat. + * glob.ml, + * hygiene.ml, + * lexers.mll, + * pathname.ml, + * resource.ml, + * report.ml, + * solver.ml, + * slurp.ml, + * solver.mli: + Use filename_concat. + * flags.ml: FIXME. + +2007-01-03 Berke Durak <berke.durak@inria.fr> + + Revert to old. + + * slurp.ml: . + +2007-01-03 Berke Durak <berke.durak@inria.fr> + + Debugging tags for myocamlbuild.ml. + + * ocaml_specific.ml: . + * slurp.ml: . + +2007-01-02 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Another atempt to fix the slurp bug and lazy. + + * slurp.ml: Ditto. + +2007-01-02 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix slurp w.r.t lazyness: keep the cwd. + + * slurp.ml: Ditto. + +2007-01-02 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + My_unix, slurp in degraded mode, _tags in subdirs, fix the bug with -j... + + * ocamlbuild_version.mli: Remove. + * ocamlbuild_where.mli: New. + * display.mli: New. + * shell.ml: New. + * shell.mli: New. + * glob.ml, + * glob.mli, + * configuration.ml, + * lexers.mli, + * lexers.mll, + * configuration.mli: Honor _tags files in subdirs. + * my_unix_with_unix.ml, + * command.ml, + * command.mli, + * resource.ml, + * resource.mli, + * solver.ml: Fix the bug with the -j option. + * slurp.ml, + * slurp.mli: New degraded mode using the find command. + Use lazy values to avoid computing useless directories. + * options.ml, + * options.mli: Update -version and -where. + * pathname.ml, + * pathname.mli: Remove the init section. + * rule.ml, + * rule.mli: Add file_rule useful for rules that don't run a command but + just write a file. + * ocaml_specific.ml: Fix some plugin bugs. Remove -I to ocamldep. + Handle msvc .obj,.lib instead of .o,.a. + * my_unix_without_unix.ml: Make works link stuffs running the readlink + command. + * display.ml, + * hygiene.ml, + * my_std.ml, + * my_unix.mli, + * my_std.mli, + * start.sh, + * test/test5/test.sh, + * test/good-output, + * test/test6/test.sh, + * test/test7/test.sh, + * test/test4/test.sh, + * test/test8/test.sh, + * test/test3/test.sh, + * test/test2/test.sh, + * Makefile, + * _tags, + * ocamlbuild_pack.mlpack: Update. + +2007-01-02 Berke Durak <berke.durak@inria.fr> + + Fixed ticker. + + * display.ml: . + +2006-12-21 Berke Durak <berke.durak@inria.fr> + + Cosmetic. + + * command.ml: . + * display.ml: . + +2006-12-21 Berke Durak <berke.durak@inria.fr> + + Computing display length. + + * display.ml: . + +2006-12-21 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add -classic-display. + + * command.ml, + * command.mli: Provide a way to use the classic display. + * options.ml: Add the -classic-display option. + * Makefile: Remove ppcache form the default. + +2006-12-21 Berke Durak <berke.durak@inria.fr> + + Finish display only once ; display number of jobs cached. + + * command.ml: . + * display.ml: . + +2006-12-21 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Oops fix a bug. + + * command.ml: Add begin .. end. + +2006-12-21 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Some display fixes. + + * command.ml: Select the display mode and remove the assert false. + * display.ml: Change the print function to have a more compact one. + * start.sh: Update. + +2006-12-21 Berke Durak <berke.durak@inria.fr> + + Error support in Display.finish. + + * display.ml: . + +2006-12-21 Berke Durak <berke.durak@inria.fr> + + Support for cache. + + * display.ml: . + +2006-12-21 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Integrate display mode. + + * ocamlbuild_pack.mlpack: Add display. + * command.mli: Add Px to indicate to highligth this pathname. + * command.ml: Support Px and call Display. + * display.ml: Fix minor bugs. + * ocaml_specific.ml: Declare some Px, and quiet ocamlyacc, ocamllex. + * options.ml: Add quiet to default tags. + * ppcache.ml: Detect more accuratly ocamlrun. + * pathname.ml: Improve concat. + * _tags: No profile. + +2006-12-21 Berke Durak <berke.durak@inria.fr> + + Added pretend. + + * display.ml: . + +2006-12-21 Berke Durak <berke.durak@inria.fr> + + Added ticker. + + * display.ml: . + +2006-12-21 Berke Durak <berke.durak@inria.fr> + + Display module. + + * display.ml: . + * my_unix_with_unix.ml: . + * my_unix_without_unix.ml: . + * my_unix.mli: . + * test/test10/test.sh: New. + * test/test10: New. + * test/test10/dbdi: New. + +2006-12-21 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Use a better init order, and fix a Filename.concat usage. + + * ocaml_specific.ml: The plugin should act before any initialization. + * ocaml_arch.ml: Use Pathname.(/). + +2006-12-21 Berke Durak <berke.durak@inria.fr> + + Started user-friendly display module. + + * display.ml: New. + +2006-12-21 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix init order. + + * ocaml_specific.ml: Config must be available for plugin building. + +2006-12-21 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Some fixes. + + * command.ml: Quote if needed. + * my_std.mli: Comment String.contains_string. + * resource.ml: Remove a useless separator. + * test/good-output: Update. + +2006-12-21 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Plugin config file and profile mode. + + * ocaml_specific.ml: Fix a bug due to the lazyness of &&. + * ocaml_specific.mli: Move some functions. + +2006-12-21 Berke Durak <berke.durak@inria.fr> + + Now compiles patterns for fast matching. Removed regexp support. + + * glob_ast.ml: . + * glob.ml: . + * glob_lexer.mli: . + * glob_ast.mli: . + * glob_lexer.mll: . + * test/test9/testglob.ml: . + * test/test9/dbgl: New. + +2006-12-20 Berke Durak <berke.durak@inria.fr> + + Pattern matching seems to start to work. + + * glob.ml: . + +2006-12-20 Berke Durak <berke.durak@inria.fr> + + Started faster pattern matching code. + + * ocaml_specific.ml: . + * _tags: . + +2006-12-20 Berke Durak <berke.durak@inria.fr> + + myocamlbuild is rebuilt only as needed. + + * hygiene.ml: . + * ocaml_specific.ml: . + * pathname.ml: . + * pathname.mli: . + * resource.ml: . + +2006-12-20 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Some changes mainly for windows support. + + * command.ml, + * command.mli: Add the Quote constructor to help quoting building in + commands. + * my_unix_with_unix.ml, + * my_unix_without_unix.ml, + * glob.ml: Commented reslash mode. + * my_std.ml, + * my_std.mli: Some new functions. + * my_unix.mli: Export sys_command. + * ocaml_specific.ml, + * ocaml_specific.mli: Update and windows support. + * options.ml, + * options.mli: Remove the ocamlmklib option. + * ppcache.ml: Fix a bug. + * pathname.ml: Add more dirseps. Use a custom Filename.concat (for now). + * resource.ml, + * rule.ml, + * Makefile, + * _tags: Update. + +2006-12-15 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Update start order. + + * start.sh: Ditto. + +2006-12-11 Berke Durak <berke.durak@inria.fr> + + Added -custom, fixed paths for installation. + + * ocaml_specific.ml: . + * Makefile: . + +2006-12-11 Berke Durak <berke.durak@inria.fr> + + Typo. + + * report.ml: . + +2006-12-08 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add a basic ocamlmklib support. + + * ocaml_specific.ml, + * ocaml_specific.mli: Use ocamlmklib to make libraries if enabled. + * options.ml, + * options.mli: Add -ocamlmklib and -use-ocamlmklib. + +2006-12-08 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Export more references of options. + + * command.ml, + * command.mli: Add ?quiet to execute. + * ocaml_specific.ml, + * ocaml_specific.mli: Update to options. + * options.ml, + * options.mli: Move ocamlc, ocamlopt... to references on command specs. + * solver.ml: Update. + * Makefile: Use _ocamldistr to avoid hygiene. + * .vcs: Use _ocamldistr. + +2006-12-08 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Ocaml distrib stuffs. + + * command.ml, + * command.mli: Add a normalization callback. + * ocaml_specific.ml, + * ocaml_specific.mli: Add a more complete interface. + * options.ml, + * options.mli: Add nostdlib. + * pathname.ml: Add mkdir -p to import in build. + * rule.ml, + * rule.mli: Call normalization of commands for digest. + * report.ml: Add ignore. + * start.sh: Add report.ml*. + * Makefile: Add distrib exportation (make a link). + * .vcs: Unmask ocamldistrib link. + +2006-12-07 Berke Durak <berke.durak@inria.fr> + + Added TODO item. + + * .vcs: . + * TODO: . + +2006-12-07 Berke Durak <berke.durak@inria.fr> + + Added TODO file. + + * TODO: New. + +2006-12-07 Berke Durak <berke.durak@inria.fr> + + Very rudimentary report analysis. + + * report.ml: . + * _tags: . + +2006-12-07 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Update tests to run ocamlbuild correctly. + + * test/test2/test.sh, + * test/test3/test.sh, + * test/test4/test.sh, + * test/test5/test.sh, + * test/test6/test.sh, + * test/test7/test.sh, + * test/test8/test.sh, + * test/test9/test.sh: Ditto. + * test/good-output: Update. + +2006-12-07 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Make test9 independant. + + * test/test9/test.sh: Ditto. + +2006-12-07 Berke Durak <berke.durak@inria.fr> + + Rewrote globbing engine, adding {,} ; moved reporting functions to Report. + + * ocamlbuild_pack.mlpack: . + * command.ml: . + * glob_ast.ml: . + * glob.ml: . + * glob_ast.mli: . + * glob_lexer.mll: . + * ocaml_specific.ml: . + * report.ml: New. + * report.mli: New. + * solver.ml: . + * solver.mli: . + * start.sh: . + * test/test9/testglob.ml: . + * test/test9/test.sh: . + * test/test3/test.sh: . + * _tags: . + +2006-12-07 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Degraded mode... + + * ocamlbuildlight.ml: New. + * ocamlbuild_pack.mlpack: Include new modules. + * bool.ml: Fake dependency. + * configuration.ml: Adapt to the glob parser. + * command.ml: Export the fork usage. + * glob.ml: Use Str through My_unix. + * glob_lexer.mli: New. + * glob_lexer.mll: Add slashs to valid character patterns. + * lexers.mli, + * lexers.mll: Use the glob parser. + * my_std.ml: Use My_unix. + * my_unix_with_unix.ml: New. + * my_unix_without_unix.ml: New. + * my_unix.mli: New. + * my_std.mli: Add search_in_path and change lazy force to ( !* ). + * ocaml_specific.ml: Some updates. + * options.ml, + * options.mli: Add -ocamlrun. + * pathname.ml: Adapt to an optional slurp. + * ppcache.ml: Use search_in_path of my_std. + * resource.ml: Update to ( !* ). + * solver.ml: Export Unix errors reporting. + * slurp.ml, + * slurp.mli: Use My_unix. + * start.sh: Update. + * test/test9/testglob.ml: Test a constant. + * test/test5/_tags, + * test/test3/_tags, + * test/test4/_tags: Don't use regexp. + * test/good-output: Add test9. + * test/test9/test.sh: Remove the parent usage. + * Makefile: Add the light mode. + * .vcs: Update. + * _tags: Update. + +2006-12-06 Berke Durak <berke.durak@inria.fr> + + Extra tests for globbing. + + * test/test9/testglob.ml: . + +2006-12-06 Berke Durak <berke.durak@inria.fr> + + First draft of pattern matching. + + * glob_ast.ml: . + * glob.ml: . + * glob_ast.mli: . + * glob_lexer.mll: . + +2006-12-06 Berke Durak <berke.durak@inria.fr> + + More hard-wired but common cases for globbing. + + * glob.ml: . + * test/test9/testglob.ml: . + +2006-12-06 Berke Durak <berke.durak@inria.fr> + + Hidden interface in globber. + + * glob.mli: . + * test/test9/testglob.ml: . + +2006-12-06 Berke Durak <berke.durak@inria.fr> + + Basic globbing works. + + * glob.ml: . + * glob.mli: . + * test/test9/testglob.ml: . + +2006-12-06 Berke Durak <berke.durak@inria.fr> + + Improved interface. + + * glob.ml: . + * glob_ast.ml: New. + * glob_ast.mli: New. + * glob.mli: New. + * glob_lexer.mll: . + * test/test9/testglob.ml: . + * _tags: . + +2006-12-06 Berke Durak <berke.durak@inria.fr> + + Added test9. + + * test/test9/testglob.ml: New. + * test/test9/parent: New. + * test/runtest.sh: . + * test/test9: New. + * test/test9/test.sh: New. + +2006-12-06 Berke Durak <berke.durak@inria.fr> + + Parser seems to work. + + * glob.ml: . + * glob_lexer.mll: . + +2006-12-06 Berke Durak <berke.durak@inria.fr> + + Removed eof_char. + + * glob.ml: . + * glob_lexer.mll: . + +2006-12-06 Berke Durak <berke.durak@inria.fr> + + Interface seems to be OK. + + * glob.ml: . + * glob_lexer.mll: . + +2006-12-06 Berke Durak <berke.durak@inria.fr> + + Adding files for the globbing module. + + * bool.ml: New. + * bool.mli: New. + * glob.ml: New. + * glob_lexer.mll: New. + * _tags: . + +2006-12-06 Berke Durak <berke.durak@inria.fr> + + Replaced numeric escapes. + + * lexers.mll: . + +2006-12-05 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Remove most of the Str usage by using ocamllex. + + * ocamlbuild_pack.mlpack: Remove Re, add Lexers. + * configuration.ml: Use Lexers. + * command.ml: Don't use Re. + * lexers.mli: New. + * lexers.mll: New. + * my_std.ml, + * my_std.mli: Add String.before and String.after. + * ocaml_specific.ml, + * ocaml_specific.mli: Use Lexers but also provide tags for warnings. + * resource.ml, + * rule.ml, + * options.ml, + * ppcache.ml, + * pathname.ml: Use Lexers. + * re.ml: Remove. + * re.mli: Remove. + * start.sh: Update. + * Makefile: Igonre _build... and gives -ml to ocamllex. + * _tags: Warnings for lexers. + +2006-12-05 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Use Sys instead of Unix for readdir. + + * my_std.ml, + * my_std.mli: Supress a Unix usage. + +2006-12-05 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add an option to disable the link creation. + + * ocaml_specific.ml: Honor this option. + * options.ml: Declare it. + * options.mli: Define it. + +2006-12-05 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Don't import compiled files... + + * pathname.ml: For the OCaml compilation itself I need to exclude some + dirs that contains compiled files but I want to use some of them with + ocamlbuild. + +2006-12-05 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Support flags for ocamlyacc and ocamllex. + + * ocaml_specific.ml, + * options.ml, + * options.mli: Add these options. + +2006-12-04 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Two fixes (hygiene and libraries)... + + * hygiene.ml: Exit 0 if sterilize removes some files (since source + files are cached in a rather persistent data structure I prefer let the + user start on a fresh setup). + * ocaml_specific.ml: Use the dirname if there is no directory named by + removing the extension. + +2006-12-04 Berke Durak <berke.durak@inria.fr> + + Small bug in hygiene. + + * hygiene.ml: . + +2006-12-04 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add postition specifications to rules. + + * rule.ml, + * rule.mli: Add a way to specifie where to put a new rule + (top,bottom,before another,after another). + * flags.ml: Reorder. + * my_std.ml, + * my_std.mli: Add mv, fix an error handling. + * ocaml_specific.ml: Better error message for circular dependencies. + * ppcache.ml: Handle errors better. + +2006-11-29 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add a working multiple job support. + + * command.ml, + * command.mli: Add different versions of execute_many including a + version that use forks. + * options.ml, + * options.mli: Restore the -j option. + * solver.ml: Call Command.execute_many. + * test/runtest.sh: Pass $@ to sub tests. + * test/good-output: Update. + +2006-11-28 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix the link order. + + * start.sh: Fix the link order. + +2006-11-28 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + One step toward multiple jobs: Add the support for suspended building. + + * resource.ml, + * resource.mli: Add the notion of suspended building. + This represent a resource that is fully ready for evaluation, it's just + a command and a function to apply after. + * rule.ml: Do not really execute rules that can be safely suspended. + * solver.ml: Play with suspended rules to collect as many as possible + to get closer to a pararllel execution. + +2006-11-27 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix the makefile. + + * Makefile: Fix deps. + +2006-11-27 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Activates more warnings, and prepare the -j feature. + + * hygiene.ml: Consolidates fragile patterns. + * my_std.ml: Likewise. + * ocaml_specific.ml: Mainly update to the new builder prototype. + * pathname.ml, + * pathname.mli: Kick a useless parameter. + * resource.ml: Remove dead code and update. + * rule.ml, + * rule.mli: The bulider now takes a list of resource lists, it will + try to make in parallel the first level of commands. + * solver.ml: Update to builder without parallelism. + * test/good-output: Update. + * Makefile: Warnings are now set to -w A -warn-error A. + +2006-11-26 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix packages... again. + + * ocaml_specific.ml: Ditto. + +2006-11-26 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix packages. + + * ocaml_specific.ml: Try to handle better packages during link. + * Makefile: Add the try_bootstrap rule. + +2006-11-26 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add -tag, -tags to options. + + * ocaml_specific.ml: Append default tags from options. + * options.ml, + * options.mli: Add -tag and -tags. + * tags.mli: Indent. + +2006-11-26 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix a bug and update tests. + + * resource.ml: Use Hashtbl.replace of course instead of Hashtbl.add to + avoid a nasty bug. + * test/test7/test.sh, + * test/test8/test.sh, + * test/test2/test.sh, + * test/test6/test.sh, + * test/test4/test.sh, + * test/test5/test.sh, + * test/test3/test.sh: Extract program options to be sure that + the -nothing-should-be-rebuilt option is before the -- one. + * test/good-output: Update. + +2006-11-26 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Use a hashtbl for digests. + + * resource.ml: Ditto. + * ocaml_specific.ml: Remove dead code. + +2006-11-26 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Use lists instead of sets for rule deps & prods. + + * ocaml_specific.ml: Move the mli dep first. + * resource.ml, + * resource.mli: No more provide digest_resources but digest_resource. + * rule.ml, + * rule.mli: Use list instead of sets for deps and prods, since they are + not heavily updated and the order matter. + * solver.ml: Adapt. + * test/good-output: Yeah! + +2006-11-26 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + One more fix for libraries. + + * ocaml_specific.ml: Improve the link_exception handling. + * test/good-output: Update. + +2006-11-25 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix the library linking. + + * ocaml_specific.ml: The test7 is specially made to check that feature. + +2006-11-25 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Remove list_set. + + * ocamlbuild_pack.mlpack: Remove list_set + * list_set.ml: Remove. + * list_set.mli: Remove. + * start.sh: Remove list_set. + * test/good-output: Regen. + +2006-11-25 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix the C rule when dirname = '.'. + + * ocaml_specific.ml: Don't move the output when it's useless. + +2006-11-25 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Ignore ocamlbuild_version.ml. + +2006-11-25 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + New transitive closure. + + * ocamlbuild_version.ml: Remove. + * my_std.ml, + * my_std.mli: Add a debug mode for digests and run_and_read. + * ocaml_specific.ml: New transitive closure. + * pathname.ml, + * pathname.mli: Export also parent_dir_name and fix same_contents. + * resource.ml, + * resource.mli: Add dependencies. + * rule.ml: Adapt. + * test/good-output: Regen. + * Makefile: Improve install. + * .vcs: Ignore other _build dirs. + +2006-11-20 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Rule definition shortcut and C files. + + * rule.ml, + * rule.mli: Allow to pass ~prod and ~dep when there is just one file. + * ocaml_specific.ml: Add a rule for C files and use the previous + shortcut. + +2006-11-18 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + No more extend Format. + + * command.ml, + * my_std.ml, + * my_std.mli: Put directly ksbprintf and sbprintf in My_std. + +2006-11-18 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Clean up and consistent use of Pathname instead of Filename. + + * command.ml, + * my_std.ml, + * my_std.mli, + * ocaml_specific.ml, + * pathname.ml, + * ppcache.ml, + * pathname.mli, + * resource.ml: That's it. + +2006-11-18 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Restore List_set. + + * ocamlbuild_pack.mlpack, + * list_set.ml, + * list_set.mli, + * resource.ml, + * start.sh: Ditto. + +2006-11-18 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Remove List_set and List_map. + + * ocamlbuild_pack.mlpack: No more in the pack. + * list_set.ml: Remove. + * list_map.ml: Remove. + * list_map.mli: Remove. + * list_set.mli: Remove. + * resource.ml: Use a Set. + * start.sh: Adapt. + +2006-11-18 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Huge speed up, worth updating. + + * resource.ml, + * resource.mli: Use a hash instead of map, remove the percent type. + * rule.ml, + * rule.mli: Remove the function for rule names. Use an exception to + choose matching rules. + +2006-11-18 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Speedup rule calling. + + * rule.ml, + * rule.mli: No more call the code rule twice to compute the digest. + * ocaml_specific.ml, + * ocaml_specific.mli: Adapt to Rule. + * test/test8/myocamlbuild.ml: Use the exception. + * test/good-output: Update. + * boot: Update svn:ignore. + +2006-11-16 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Remove phony resources and include dependencies. + + * ocaml_specific.ml, + * options.ml, + * options.mli, + * pathname.ml, + * pathname.mli, + * resource.ml, + * resource.mli, + * rule.ml, + * rule.mli, + * solver.ml, + * test/test8/myocamlbuild.ml: Simplify a lot the code. + +2006-11-16 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Some improvements... + + * ocamlbuild.ml: . + * ocamlbuild_version.ml: New. + * ocamlbuild_plugin.ml: New. + * ocamlbuild_version.mli: New. + * ocamlbuildlib.mllib: . + * ocamlbuild.sh: Remove. + * ocamlbuild_pack.mlpack: New. + * boot: . + * ocaml_specific.ml: . + * ocaml_specific.mli: . + * options.ml: . + * options.mli: . + * rule.ml: . + * rule.mli: . + * start.sh: . + * test/test8/a.ml: New. + * test/test7/a2.ml: . + * test/test7/a3.ml: New. + * test/test8/myocamlbuild.ml: New. + * test/test7/myocamlbuild.ml: New. + * test/test8: New. + * test/test8/test.sh: New. + * test/runtest.sh: . + * test/test7/test.sh: . + * test/good-output: . + * Makefile: . + * _tags: . + +2006-11-15 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add support for libraries. + + * ocamlbuildlib.ml: Remove. + * ocamlbuildlib.mllib: New. + * ocaml_specific.ml: Rules and actions for libraries. + * rule.ml: Improve explanations. + * start.sh: Don't make ocamlbuildlib. + * test/test7/a.mli: New. + * test/runtest.sh: Add test7. + * test/test7/test.sh: Add reverts for a.ml. + * test/good-output: Update. + * Makefile: Remove junk lines. + +2006-11-14 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add a tests for libraries. + + * test/test7/e.ml: New. + * test/test7/d.ml: New. + * test/test7/a.ml: New. + * test/test7/b.ml: New. + * test/test7/a2.ml: New. + * test/test7/c.ml: New. + * test/test7/test.sh: New. + * test/test7/ablib.mllib: New. + * test/test7: New. + +2006-11-14 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Simplify dependency rules. + + * ocaml_specific.ml: No more use bytelinkdeps... + * rule.ml, + * rule.mli: Add a dyndeps set. + * ocamlbuild.sh, + * pathname.ml, + * Makefile: Update. + +2006-11-14 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Update tests... + + * test/test2/vivi3.ml: . + * test/good-output: . + +2006-11-10 Berke Durak <berke.durak@inria.fr> + + Added -sterilize option. + + * hygiene.ml: ditto + * hygiene.mli: ditto + * ocaml_specific.ml: ditto + * options.ml: ditto + * options.mli: ditto + +2006-11-10 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + View the context dir in first. + + * pathname.ml: Ditto. + +2006-11-10 Berke Durak <berke.durak@inria.fr> + + Added thread and profile tags. + + * ocaml_specific.ml: ditto. + +2006-11-10 Berke Durak <berke.durak@inria.fr> + + Added law for leftover dependency files. + + * ocaml_specific.ml: ditto. + +2006-11-10 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Reverse the ignore_auto default value. + + * options.ml: Add -no-skip, remove -ignore-auto, add -Is and -Xs. + * test/test2/test.sh, + * test/test5/test.sh, + * test/test6/test.sh, + * test/test4/test.sh, + * test/test3/test.sh, + * Makefile: Revert flags. + +2006-11-10 Berke Durak <berke.durak@inria.fr> + + Added install target to Makefile. + + * Makefile: . + +2006-11-10 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Deal with for-pack flags... + + * ocaml_arch.ml: Define a hook. + * ocaml_arch.mli: Declare it. + * ocaml_specific.ml: Use it. + * test/test6: Ignore main.byte. + +2006-11-09 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix start.sh and remove dead code. + + * ocaml_specific.ml: Remove dead code about ignore_auto. + * start.sh: Swap two modules. + * test/test6/main.byte: Remove. + +2006-11-09 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Pack now works great... + + * ocamlbuild.sh: Use ocamlopt. + * command.ml: Reset filesys cache. + * my_std.ml, + * my_std.mli: Add a filesys cache for + case sensitive file_exists and digest over files. + * ocaml_specific.ml: Work on link and packs. + * ppcache.ml: Exit 2 is for unix. + * pathname.ml, + * resource.ml, + * rule.ml, + * rule.mli, + * slurp.ml, + * solver.ml, + * solver.mli, + * test/test5: Update. + +2006-11-07 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Too lazy to fill this up :). + + * ocamlbuild.sh, + * configuration.ml, + * command.ml, + * debug.ml, + * debug.mli, + * my_std.ml, + * my_std.mli, + * ocaml_specific.ml, + * ocaml_specific.mli, + * options.ml, + * options.mli, + * pathname.ml, + * ppcache.ml,ew. + * ppcache.mli,ew. + * pathname.mli, + * resource.ml, + * resource.mli, + * rule.ml, + * rule.mli, + * slurp.ml, + * solver.ml, + * solver.mli, + * slurp.mli, + * start.sh, + * tags.ml, + * test/test5/test.sh, + * test/test4/test.sh, + * test/test3/test.sh, + * test/good-output, + * test/test2/test.sh, + * test/test6/test.sh, + * Makefile, + * _tags: This too. + +2006-11-04 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Some pack,dirs stuffs. + + * ocamlbuild.ml, + * ocamlbuildlib.ml,ew. + * ocamlbuild.sh,ew. + * configuration.ml, + * my_std.ml, + * my_std.mli, + * ocaml_arch.ml,ew. + * ocaml_specific.ml, + * ocaml_specific.mli, + * ocaml_arch.mli,ew. + * options.ml, + * options.mli, + * pathname.ml, + * pathname.mli, + * resource.ml, + * resource.mli, + * rule.ml, + * solver.ml, + * test/good-output, + * Makefile, + * _tags: That's it. + +2006-10-31 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Remove the dirty thing about cmi's. + + * ocaml_specific.ml, + * ocaml_specific.mli: Moves of files are no more needed. + * test/good-output: Update. + +2006-10-31 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Some renaming and cleanup... + + * ocamlbuild.ml, + * configuration.ml, + * configuration.mli, + * list_set.ml, + * ocaml_specific.ml, + * resource.ml, + * test/good-output, + * test/test6/test.sh: Do that. + +2006-10-31 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Use the nothing-should-be-rebuilt for tests and update the output. + + * test/test2, + * test/test2/test.sh, + * test/test3/test.sh, + * test/test4/test.sh, + * test/test5/test.sh, + * test/test6/test.sh, + * test/good-output: Do that. + +2006-10-31 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add a mode usefull for tests. + + * options.ml, options.mli, rule.ml: + This new mode fails when something needs to be rebuilt. + +2006-10-31 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Improve the ocaml rule set. + + * ocaml_specific.ml: Yipee! + +2006-10-31 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add scripts to run tests. + + * test/test2/vivi1.ml: New. + * test/test2/vivi2.ml: New. + * test/test2/vivi3.ml: New. + * test/test2/vivi.ml: . + * test/test4/test.sh: New. + * test/test5/test.sh: New. + * test/test2/test.sh: New. + * test/test6/test.sh: . + * test/good-output: New. + * test/test3/test.sh: New. + * test/runtest.sh: New. + +2006-10-31 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Restore some recursivity for includes. + + * resource.ml, + * resource.mli: Remove the digest field. + * rule.ml: . + * test/test6/test.sh: . + +2006-10-30 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Remove the arbitrary deep dependencies. + + * ocaml_specific.ml, + * ocaml_specific.mli: No more implicit transitives deps. + * resource.ml, + * resource.mli: Remove as many things as possible. + * rule.ml, + * rule.mli, + * solver.ml: Simplify. + * command.ml: Fix newlines and flush. + +2006-10-30 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Separated preprocessing, total order over rules... + + * ocamlbuild.ml, + * my_std.ml, + * my_std.mli, + * ocaml_specific.ml, + * ocaml_specific.mli, + * options.ml, + * options.mli, + * pathname.ml, + * pathname.mli, + * resource.ml, + * resource.mli, + * rule.ml, + * rule.mli, + * solver.ml, + * test/test2/tutu.ml, + * tags.ml, + * test/test2/tyty.mli,ew. + * test/test6/test.sh, + * test/test6, + * test/test5/_tags, + * test/test5: Update. + +2006-10-27 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add options: -ocamlc,-ocamlopt,-ocamldep,-ocamlyacc,-ocamllex. + + * options.ml, + * options.mli: Declare them. + * ocaml_specific.ml: Use them. + +2006-10-27 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix start.sh. + + * start.sh: Fix the output. + +2006-10-27 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Regen start.sh. + + * start.sh: Regen. + +2006-10-27 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Use the list based implems and fix many bugs. + + * ocamlbuild.ml: Rename some dependency files. + * boot: Ignore boot/ocamlbuild.byte.save.* files. + * command.ml: Fix command printing. + * my_std.ml, + * my_std.mli: Add List.equal, use the cp command in Shell.cp. + * ocaml_specific.ml, + * ocaml_specific.mli: Many things. + * pathname.ml, pathname.mli: Make compare obselete prefer equal. + * resource.ml, resource.mli: Add print_cache and use list based + sets and maps. + * Makefile: Add the bootstrap rule. + +2006-10-27 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add a test for fine-grained dependencies. + + * test/test6/main.ml: New. + * test/test6/d.ml: New. + * test/test6/b.ml: New. + * test/test6/a.ml: New. + * test/test6/main.mli: New. + * test/test6/a.mli: New. + * test/test6/d.mli: New. + * test/test6/b.mli: New. + * test/test6/b.mli.v2: New. + * test/test6/main.byte: New. + * test/test6/d.mli.v1: New. + * test/test6/test.sh: New. + * test/test6/d.mli.v2: New. + * test/test6/b.mli.v1: New. + * test/test6: New. + +2006-10-26 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Dummy implementations for set and map using lists. + The main advantage is to only rely on the equal function that is simpler + to maintain correct in an imperative setting. + + * list_map.ml: New. + * list_set.ml: New. + * list_map.mli: New. + * list_set.mli: New. + +2006-10-24 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fixes and improvment. + + * ocamlbuild.ml, + * my_std.ml, + * my_std.mli, + * ocaml_specific.ml, + * ocaml_specific.mli, + * pathname.ml, + * resource.ml, + * rule.ml, + * rule.mli, + * solver.ml, + * solver.mli: The previous version was somwhat unstable. + +2006-10-24 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Many things... + + * ocamlbuild.ml, + * command.ml, + * command.mli, + * ocaml_specific.ml, + * ocaml_specific.mli, + * options.ml, + * options.mli, + * resource.ml, + * resource.mli, + * rule.ml, + * rule.mli, + * solver.ml, + * solver.mli: + Simplify the whole solver by removing the value type. + Rule code now returns a resource set, that is injected as dependencies. + So rule code always returns unit. But can raise exceptions. + Add -ignore, and -ignore-auto options to workaround ocamldep + approximations without igonring errors. + * Makefile: Add backup and restore targets. + +2006-10-23 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Compute digests of dependencies recursively. + + * ocamlbuild.ml, + * ocaml_specific.ml, + * options.ml, + * options.mli, + * resource.ml, + * resource.mli, + * rule.ml: Ditto. + +2006-10-23 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + One step toward a parallelisable system. + + * boot, + * command.ml, + * ocaml_specific.ml, + * ocaml_specific.mli, + * options.ml, + * options.mli, + * rule.ml, + * rule.mli, + * solver.ml: Update. + * value.ml: Remove. + * value.mli: Remove. + +2006-10-20 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add vcs config file to setup a good default setup. + + * .vcs: New. + +2006-10-20 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Simplify the bootstrap by introducing a shell script. + + * boot/ocamlbuild.byte: Remove. Useless in distribution mode + but will be created, the first time. So the devel is not + disturbed. + * start.sh: New. + * Makefile: Remove the old one to use start.sh. + +2006-10-20 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Take command line in account for the digest computation. + + * command.ml, + * command.mli, + * debug.ml, + * ocaml_specific.ml, + * ocaml_specific.mli, + * resource.ml, + * resource.mli, + * rule.ml, + * rule.mli, + * solver.ml, + * value.ml, + * value.mli: Update. + +2006-10-19 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + New pathname representation. + + * pathname.ml, pathname.mli: This new representation should + avoids "fix" problems. + +2006-10-17 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Change the cache implem -> now really fast at link time. + + * ocamlbuild.ml, + * boot/ocamlbuild.byte, + * my_std.ml, + * ocaml_specific.ml, + * pathname.ml, + * resource.ml, + * resource.mli, + * solver.ml: By replacing various sets by a map of records and + remember that something has not changed, or cannot be built; + there is a real speedup. In particular to detect that the link is + not necessary to do. + +2006-10-17 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add a basic support for a digest based cache verification. + + * resource.ml, resource.mli: Add have_digest and store_digest. + * rule.ml: Use these digests but don't include the command for + now. + * test/test2/vivi.ml, test/test2/tata.mli: Dummy updates. + +2006-10-16 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Split in many files. + + * ocamlbuild.ml: Splitted. + * boot/ocamlbuild.byte: Updated. + * configuration.ml: New. + * configuration.mli: New. + * command.ml: New. + * command.mli: New. + * debug.ml: New. + * debug.mli: New. + * flags.ml: New. + * flags.mli: New. + * my_std.ml: New. + * my_std.mli: New. + * ocaml_specific.ml: New. + * ocaml_specific.mli: New. + * options.ml: New. + * options.mli: New. + * pathname.ml: New. + * pathname.mli: New. + * re.ml: New. + * re.mli: New. + * resource.ml: New. + * resource.mli: New. + * rule.ml: New. + * rule.mli: New. + * solver.ml: New. + * solver.mli: New. + * test/test5/d.ml: New. + * tags.ml: New. + * test/test5/b.ml: New. + * test/test5/a.ml: New. + * tags.mli: New. + * test/test5/a.mli: New. + * test/test5/c.mlpack: New. + * test/test5/_tags: New. + * test/test5: New. + * value.ml: New. + * value.mli: New. + * Makefile: . + +2006-10-16 Berke Durak <berke.durak@inria.fr> + + Various useful changes. + + * ocamlbuild.ml: Hygiene to true. + * slurp.ml: Remove debugging + * Makefile: Clean annot and object files. + +2006-10-15 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Bootstrap it ;). + + * ocamlbuild.ml: Add support for -g, -dtypes, and -rectypes in + four lines. + * _tags: New. Specify how to build ocamlbuild itself. + * boot: New. + * boot/ocamlbuild.byte: New. A bytecode version needed to + bootstrap + * Makefile: By default make it a wrapper over ocamlbuild in boot. + +2006-10-15 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Little fix... + + * ocamlbuild.ml: Don't assoc over pathnames since the default + compare is wrong and slow use the string repr. + +2006-10-15 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Allow to control flags, and libraries by tags. + + * ocamlbuild.ml: In the _tags file you can add or remove flags + using a colon flag_name:flag_value. + * test/test2/vivi.ml, + * test/test3/f.ml, + * test/test4/b/bb.ml: Dummy updates. + * test/test3/_tags: New. + * test/test4/_tags: New. + +2006-10-15 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add a tag based flag system. + + * test/test2/vivi.ml: An example. + * test/test2/_tags: New. + * ocamlbuild.ml: Now a command can request for flags by giving a + set of tags these tags include file specific tags this allow to + tweak flags by just providing a _tags file. + +2006-10-15 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add -lib,-libs options remove -P. + + * ocamlbuild.ml: -P Is useless due to the fact that we now + have the same directory structure in the _build directory. + Add -lib,-libs that allows one to specify -lib unix without + its extension in order to request for native and byte + compilations. + +2006-10-15 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Multi directories now works ;). + + * ocamlbuild.ml: Solve the whole problem by improving the + Pathname module. Pathnames are now symbolic values that + can include variable names. These variable names represent + still ambiguous pathnames /a/b/(c|d as x1)/e.ml but variables + can be shared, so discovering that /a/b/(c|d as x1)/e.ml is in + fact /a/b/c/e.ml will make /a/b/(c|d as x1)/e.cmo automatically + take this value /a/b/c/e.cmo cause it shares the x1 variable. + +2006-10-13 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + I prefer capitalized names. + + * AUTHORS + +2006-10-13 Berke Durak <berke.durak@inria.fr> + + Added an AUTHORS file. + + * AUTHORS: New. + +2006-10-13 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add the vcs dir. + + * vcs: New. + * vcs/ocamlbuild.rb: New. + +2006-10-13 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + * ocamlbuild.ml: Restore dependencies. + +2006-10-13 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix the makefile. + + * Makefile, discard_printf.ml: Ditto. + +2006-10-13 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Improve the directory handling. + + * ocamlbuild.ml: Ditto, but there is still a problem with native. + * Makefile: Update. + +2006-10-11 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix native dependencies. + + * ocamlbuild.ml: By default due to inlining the cmx dependencies + are needed to build a cmx. + * Makefile: Add native support. + +2006-10-11 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Use phony for linkdeps. + + * ocamlbuild.ml: Ditto. + +2006-10-11 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix exit on multiple targets. + + * ocamlbuild.ml: Ditto. + +2006-10-11 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + More flags -lflags,-lflag... + + * ocamlbuild.ml: Add plrual form options for those that use + comma separated lists. + +2006-10-11 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Use phony resources for .cmo.linkdeps. + + * ocamlbuild.ml: Also restore the command running if "--" + is specified + +2006-10-11 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Remove Include_string_list resources, add Phony resources. + + * ocamlbuild.ml: Also fix some rules. + +2006-10-11 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Shift debug levels. + + * ocamlbuild.ml: Add -quiet. + +2006-10-11 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Use str more intensively. + + * ocamlbuild.ml: Also clean up useless functions. + +2006-10-11 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Fix link dependencies. + + * ocamlbuild.ml: Force to consider recursivly Include_ tagged + resources for their full contents. Alas it takes more time to + know if we need to recompute the link. + * test/test2/vivi.ml: Update. + +2006-10-10 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Support multiple directories, it can compile the OCaml compiler :). + + * ocamlbuild.ml: Add directory handling but also start + the tags config files handling. + * Makefile: Use str.cma. + +2006-10-08 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + Add library support. + + * ocamlbuild.ml: Deduce basic set of tags form the target + extension. + +2006-10-08 Nicolas Pouillard <nicolas.pouillard@gmail.com> + + More customisable flags, and cycle detection. + + * ocamlbuild.ml: Add some flags -lflag, -ppflag, -cflag, --. + Also add a detection mechanism for dependencies. + * discard_printf.ml, Makefile: Update. + diff --git a/ocamlbuild/Makefile b/ocamlbuild/Makefile new file mode 100644 index 0000000000..34bd5dc95e --- /dev/null +++ b/ocamlbuild/Makefile @@ -0,0 +1,95 @@ +.PHONY: all byte native profile debug ppcache doc + +ifndef INSTALL_PREFIX +INSTALL_PREFIX := /usr/local +endif + +ifndef INSTALL_LIB +INSTALL_LIB := $(INSTALL_PREFIX)/lib/ocamlbuild +endif + +ifndef INSTALL_BIN +INSTALL_BIN := $(INSTALL_PREFIX)/bin +endif + +ifndef BUILDDIR +BUILDDIR := "_build" +endif + +ifndef OCAMLBUILDCMD +OCAMLBUILDCMD := ./boot/ocamlbuild +endif + +ifdef O +OCAMLBUILD_OPTIONS := $(OCAMLBUILD_OPTIONS) $(O) +endif + +ifeq ($(wildcard ./boot/oc*build),./boot/ocamlbuild) +OCAMLBUILD=INSTALL_LIB=$(INSTALL_LIB) $(OCAMLBUILDCMD) -build-dir $(BUILDDIR) -no-links $(OCAMLBUILD_OPTIONS) +LIBS=ocamlbuildlib ocamlbuildlightlib +PROGRAMS=ocamlbuild ocamlbuildlight +BYTE=$(LIBS:=.cma) $(PROGRAMS:=.byte) +NATIVE=$(LIBS:=.cmxa) $(PROGRAMS:=.native) + +all: + $(OCAMLBUILD) $(BYTE) $(NATIVE) +byte: + $(OCAMLBUILD) $(BYTE) +profile: + $(OCAMLBUILD) $(LIBS:=.p.cmxa) $(PROGRAMS:=.p.native) +debug: + $(OCAMLBUILD) $(LIBS:=.d.cma) $(PROGRAMS:=.d.byte) +ppcache: + $(OCAMLBUILD) ppcache.byte ppcache.native +doc: + $(OCAMLBUILD) ocamlbuild.docdir/index.html + ln -sf $(BUILDDIR)/ocamlbuild.docdir doc +else +all byte native: ocamlbuild.byte.start + cp ocamlbuild.byte.start boot/ocamlbuild + $(MAKE) $(MFLAGS) $(MAKECMDGOALS) + cp $(BUILDDIR)/ocamlbuild.native boot/ocamlbuild + $(MAKE) $(MFLAGS) $(MAKECMDGOALS) OCAMLBUILD_OPTIONS="-nothing-should-be-rebuilt -debug -1" +endif + +ocamlbuild.byte.start: + ./start.sh + +promote: + cp $(BUILDDIR)/ocamlbuild.native boot/ocamlbuild + +clean: + rm -rf $(BUILDDIR) + +distclean: clean + rm -rf _start ocamlbuild.byte.start boot/ocamlbuild + +install: all + mkdir -p $(INSTALL_BIN) + mkdir -p $(INSTALL_LIB) + install $(BUILDDIR)/ocamlbuild.byte \ + $(BUILDDIR)/ocamlbuild.native \ + $(BUILDDIR)/ocamlbuildlight.byte \ + $(BUILDDIR)/ocamlbuildlight.native \ + $(INSTALL_BIN) + install $(BUILDDIR)/ocamlbuild.native $(INSTALL_BIN)/ocamlbuild + install $(BUILDDIR)/ocamlbuildlight.byte $(INSTALL_BIN)/ocamlbuildlight + install -m 644 \ + $(BUILDDIR)/ocamlbuildlib.cmxa \ + $(BUILDDIR)/ocamlbuildlib.a \ + $(BUILDDIR)/ocamlbuildlib.cma \ + $(BUILDDIR)/ocamlbuildlightlib.cmxa \ + $(BUILDDIR)/ocamlbuildlightlib.a \ + $(BUILDDIR)/ocamlbuildlightlib.cma \ + $(BUILDDIR)/ocamlbuild_pack.cmi \ + $(BUILDDIR)/ocamlbuild_pack.cmx \ + $(BUILDDIR)/ocamlbuild.cmi \ + $(BUILDDIR)/ocamlbuild_plugin.cmi \ + $(BUILDDIR)/ocamlbuild.cmx \ + $(BUILDDIR)/ocamlbuild.o \ + $(BUILDDIR)/ocamlbuild.cmo \ + $(BUILDDIR)/ocamlbuildlight.cmx \ + $(BUILDDIR)/ocamlbuildlight.o \ + $(BUILDDIR)/ocamlbuildlight.cmo $(INSTALL_LIB) + ranlib $(INSTALL_LIB)/ocamlbuildlib.a + ranlib $(INSTALL_LIB)/ocamlbuildlightlib.a diff --git a/ocamlbuild/TODO b/ocamlbuild/TODO new file mode 100644 index 0000000000..f48333c002 --- /dev/null +++ b/ocamlbuild/TODO @@ -0,0 +1,30 @@ +To do: +* Ensure that _build and _log are not created if not needed (with -help for + instance) +* Fix report +* Design a nice, friendly, future-proof plugin (myocamlbuild) API +* Display: should display nothing (even when finish is called) when no real + event as occured. + +Being done: +* Write doc + +Almost done: +* Fine control for hygiene using a glob pattern (command line option + tag) + => the command line option is todo. + -tag "<glob1> or <glob2> ..." "tag1, -tag2, ..." + +Won't fix: +* Config file for options => no since myocamlbuild is sufficent +* Optimize MD5 (Daemon ? Dnotify ?) : too much hassle for little gain + +Done: +* Fix uncaught exception handler to play well with the Display module +* Finish display before executing target +* Slurp: in a directory read files, before subdirs (to have _tags before foo/_tags) +* Add a -clean option +* Add ocamldoc rules (use .odoc extension) +* Add .inferred.mli target rules +* -- with no args does not call the executable +* Complain when used with -- and no target +* dep ["ocaml"; "link"; "use_foo"] ["foo/foo.o"] tags for adding targets diff --git a/ocamlbuild/_tags b/ocamlbuild/_tags new file mode 100644 index 0000000000..8a42d27ecb --- /dev/null +++ b/ocamlbuild/_tags @@ -0,0 +1,12 @@ +# OCamlbuild tags file +<*.ml> or <*.mli>: warn_A, warn_error_A, dtypes +"discard_printf.ml": rectypes +"ocamlbuildlib.cma" or "ocamlbuildlightlib.cma": linkall +<*.byte> or <*.native>: use_unix +"ocamlbuildlight.byte": -use_unix +<*.cmx>: for-pack(Ocamlbuild_pack) +<{ocamlbuild_{pack,plugin},my_unix_with_unix,ppcache,executor}{,.p}.cmx>: -for-pack(Ocamlbuild_pack) +"lexers.ml" or "glob_lexer.ml": -warn_A, -warn_error_A +"glob.ml": -warn_E, -warn_error_E, -warn_A, -warn_error_A +"doc": not_hygienic +"resource.ml": warn_error_e diff --git a/ocamlbuild/bool.ml b/ocamlbuild/bool.ml new file mode 100644 index 0000000000..4fa734e921 --- /dev/null +++ b/ocamlbuild/bool.ml @@ -0,0 +1,38 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Berke Durak *) +(* Bool *) + +type 'a boolean = And of 'a boolean list | Or of 'a boolean list | Not of 'a boolean | Atom of 'a | True | False;; + +let rec eval f = function + | And l -> List.for_all (eval f) l + | Or l -> List.exists (eval f) l + | Not x -> not (eval f x) + | Atom a -> f a + | True -> true + | False -> false +;; +let rec iter f = function + | (And l|Or l) -> List.iter (iter f) l + | Not x -> iter f x + | Atom a -> f a + | True|False -> () +;; +let rec map f = function + | And l -> And(List.map (map f) l) + | Or l -> Or(List.map (map f) l) + | Not x -> Not(map f x) + | Atom a -> Atom(f a) + | (True|False) as b -> b +;; diff --git a/ocamlbuild/bool.mli b/ocamlbuild/bool.mli new file mode 100644 index 0000000000..4b3ea3f669 --- /dev/null +++ b/ocamlbuild/bool.mli @@ -0,0 +1,34 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Berke Durak *) +(* Bool *) + +(** Provides a datatype for representing boolean formulas and evaluation, + iteration and map functions. *) + +(** Public type for generic boolean formulas. An empty conjunction [And[]] is true and + an empty disjunction [Or[]] is false. *) +type 'a boolean = + And of 'a boolean list + | Or of 'a boolean list + | Not of 'a boolean + | Atom of 'a + | True + | False + +val eval : ('a -> bool) -> 'a boolean -> bool +(** [eval g f] evaluates the boolean formula [f] using the values returned by [g] for the atoms. *) +val iter : ('a -> unit) -> 'a boolean -> unit +(** [iter g f] calls [g] over every atom of [f]. *) +val map : ('a -> 'b) -> 'a boolean -> 'b boolean +(** [map g f] replaces every atom of [f] by its image by [g]. *) diff --git a/ocamlbuild/command.ml b/ocamlbuild/command.ml new file mode 100644 index 0000000000..593b1f8d08 --- /dev/null +++ b/ocamlbuild/command.ml @@ -0,0 +1,295 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +(* Command *) + +open My_std +open Log + +type tags = Tags.t + +let jobs = ref 1 + +type t = +| Seq of t list +| Cmd of spec +| Nop +and spec = +| N (* nop or nil *) +| S of spec list +| A of string +| P of string (* Pathname.t *) +| Px of string (* Pathname.t *) +| Sh of string +| T of Tags.t +| V of string +| Quote of spec + +(*type v = [ `Seq of v list | `Cmd of vspec | `Nop ] +and vspec = + [ `N + | `S of vspec list + | `A of string + | `P of string (* Pathname.t *) + | `Px of string (* Pathname.t *) + | `Sh of string + | `Quote of vspec ] + +let rec spec_of_vspec = + function + | `N -> N + | `S vspecs -> S (List.map spec_of_vspec vspecs) + | `A s -> A s + | `P s -> P s + | `Px s -> Px s + | `Sh s -> Sh s + | `Quote vspec -> Quote (spec_of_vspec vspec) + +let rec vspec_of_spec = + function + | N -> `N + | S specs -> `S (List.map vspec_of_spec specs) + | A s -> `A s + | P s -> `P s + | Px s -> `Px s + | Sh s -> `Sh s + | T _ -> invalid_arg "vspec_of_spec: T not supported" + | Quote spec -> `Quote (vspec_of_spec spec) + +let rec t_of_v = + function + | `Nop -> Nop + | `Cmd vspec -> Cmd (spec_of_vspec vspec) + | `Seq cmds -> Seq (List.map t_of_v cmds) + +let rec v_of_t = + function + | Nop -> `Nop + | Cmd spec -> `Cmd (vspec_of_spec spec) + | Seq cmds -> `Seq (List.map v_of_t cmds)*) + +let no_tag_handler _ = failwith "no_tag_handler" + +let tag_handler = ref no_tag_handler + +(*** atomize *) +let atomize l = S(List.map (fun x -> A x) l) +let atomize_paths l = S(List.map (fun x -> P x) l) +(* ***) + +let env_path = lazy begin + let path_var = Sys.getenv "PATH" in + Lexers.colon_sep_strings (Lexing.from_string path_var) +end + +let virtual_solvers = Hashtbl.create 32 +let setup_virtual_command_solver virtual_command solver = + Hashtbl.replace virtual_solvers virtual_command solver +let virtual_solver virtual_command = + let solver = + try + Hashtbl.find virtual_solvers virtual_command + with Not_found -> + failwith (sbprintf "no solver for the virtual command %S \ + (setup one with Command.setup_virtual_command_solver)" + virtual_command) + in + try solver () + with Not_found -> + failwith (Printf.sprintf "the solver for the virtual command %S \ + has failed finding a valid command" virtual_command) + + +(* FIXME windows *) +let search_in_path cmd = + if Filename.is_implicit cmd then + let path = List.find begin fun path -> + if path = Filename.current_dir_name then sys_file_exists cmd + else sys_file_exists (filename_concat path cmd) + end !*env_path in + filename_concat path cmd + else cmd + +(*** string_of_command_spec{,_with_calls *) +let rec string_of_command_spec_with_calls call_with_tags call_with_target resolve_virtuals spec = + let self = string_of_command_spec_with_calls call_with_tags call_with_target resolve_virtuals in + let b = Buffer.create 256 in + let first = ref true in + let put_space () = + if !first then + first := false + else + Buffer.add_char b ' ' + in + let put_filename p = + Buffer.add_string b (Shell.quote_filename_if_needed p) + in + let rec do_spec = function + | N -> () + | A u -> put_space (); put_filename u + | Sh u -> put_space (); Buffer.add_string b u + | P p -> put_space (); put_filename p + | Px u -> put_space (); put_filename u; call_with_target u + | V v -> if resolve_virtuals then do_spec (virtual_solver v) + else (put_space (); Printf.bprintf b "<virtual %s>" (Shell.quote_filename_if_needed v)) + | S l -> List.iter do_spec l + | T tags -> call_with_tags tags; do_spec (!tag_handler tags) + | Quote s -> put_space (); put_filename (self s) + in + do_spec spec; + Buffer.contents b + +let string_of_command_spec x = string_of_command_spec_with_calls ignore ignore false x + +let string_print_of_command_spec spec = + let rtags = ref Tags.empty in + let rtarget = ref "" in + let s = string_of_command_spec_with_calls ((:=) rtags) ((:=) rtarget) true spec in + let target = if !rtarget = "" then s else !rtarget in + (s, (fun quiet pretend () -> if not quiet then Log.event ~pretend s target !rtags)) +(* ***) + +let rec print f = + function + | Cmd spec -> Format.pp_print_string f (string_of_command_spec spec) + | Seq seq -> List.print print f seq + | Nop -> Format.pp_print_string f "nop" + +let to_string x = sbprintf "%a" print x + +let rec list_rev_iter f = + function + | [] -> () + | x :: xs -> list_rev_iter f xs; f x + +let spec_list_of_cmd cmd = + let rec loop acc = + function + | [] -> acc + | Nop :: xs -> loop acc xs + | Cmd spec :: xs -> loop (string_print_of_command_spec spec :: acc) xs + | Seq l :: xs -> loop (loop acc l) xs + in List.rev (loop [] [cmd]) + +let execute_many ?(quiet=false) ?(pretend=false) cmds = + let degraded = !*My_unix.is_degraded || Sys.os_type = "Win32" in + let jobs = !jobs in + if jobs < 0 then invalid_arg "jobs < 0"; + let max_jobs = if jobs = 0 then None else Some jobs in + + let ticker = Log.update in + let display = Log.display in + + if cmds = [] then + None + else + begin + let konts = + List.map + begin fun cmd -> + let specs = spec_list_of_cmd cmd in + List.map + begin fun (cmd, print) -> + (cmd, (print quiet pretend)) + end + specs + end + cmds + in + if pretend then + begin + List.iter + begin fun l -> + List.iter + begin fun (_, f) -> f () end + l + end + konts; + None + end + else + begin + reset_filesys_cache (); + if degraded then + let res, opt_exn = + List.fold_left begin fun (acc_res, acc_exn) cmds -> + match acc_exn with + | None -> + begin try + List.iter begin fun (cmd, print) -> + print (); + let rc = sys_command cmd in + if rc <> 0 then begin + if not quiet then + eprintf "Exit code %d while executing this \ + command:@\n%s" rc cmd; + raise (Exit_with_code rc) + end + end cmds; + true :: acc_res, None + with e -> false :: acc_res, Some e + end + | Some _ -> false :: acc_res, acc_exn + end ([], None) konts + in match opt_exn with + | Some(exn) -> Some(res, exn) + | None -> None + else + My_unix.execute_many ~ticker ?max_jobs ~display konts + end + end +;; + +let execute ?quiet ?pretend cmd = + match execute_many ?quiet ?pretend [cmd] with + | Some(_, exn) -> raise exn + | _ -> () + +let rec reduce x = + let rec self x acc = + match x with + | N -> acc + | A _ | Sh _ | P _ | Px _ | V _ -> x :: acc + | S l -> List.fold_right self l acc + | T tags -> self (!tag_handler tags) acc + | Quote s -> Quote (reduce s) :: acc in + match self x [] with + | [] -> N + | [x] -> x + | xs -> S xs + +let to_string_for_digest = to_string +(* +let to_string_for_digest x = + let rec cmd_of_spec = + function + | [] -> None + | N :: xs -> cmd_of_spec xs + | (A x | P x | P x) :: _ -> Some x + | Sh x :: _ -> + if Shell.is_simple_filename x then Some x + else None (* Sh"ocamlfind ocamlc" for example will not be digested. *) + | S specs1 :: specs2 -> cmd_of_spec (specs1 @ specs2) + | (T _ | Quote _) :: _ -> assert false in + let rec cmd_of_cmds = + function + | Nop | Seq [] -> None + | Cmd spec -> cmd_of_spec [spec] + | Seq (cmd :: _) -> cmd_of_cmds cmd in + let s = to_string x in + match cmd_of_cmds x with + | Some x -> + if sys_file_exists x then sprintf "(%S,%S)" s (Digest.file x) + else s + | None -> s +*) diff --git a/ocamlbuild/command.mli b/ocamlbuild/command.mli new file mode 100644 index 0000000000..5ad5302dd4 --- /dev/null +++ b/ocamlbuild/command.mli @@ -0,0 +1,30 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +(* Command *) + +(** Provides an abstract type for easily building complex shell commands without making + quotation mistakes. *) +include Signatures.COMMAND with type tags = Tags.t + +(** {6 For system use only, not for the casual user} *) + +(** Same as [to_string]. *) +val to_string_for_digest : t -> string + +(** Maximum number of parallel jobs. *) +val jobs : int ref + +(** Hook here the function that maps a set of tags to appropriate command + options. It also build the dependencies that matches the tags. *) +val tag_handler : (Tags.t -> spec) ref diff --git a/ocamlbuild/configuration.ml b/ocamlbuild/configuration.ml new file mode 100644 index 0000000000..6f1a7f3a5c --- /dev/null +++ b/ocamlbuild/configuration.ml @@ -0,0 +1,63 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +open My_std +open Log +open Lexers + +type flag_list = (string * string) list + +type t = Lexers.conf + +let cache = Hashtbl.create 107 +let (configs, add_config) = + let configs = ref [] in + (fun () -> !configs), + (fun config -> configs := config :: !configs; Hashtbl.clear cache) + +let parse_string s = + let conf = Lexers.conf_lines None 1 (Printf.sprintf "string: %S" s) (Lexing.from_string s) in + add_config conf + +let parse_file ?dir file = + with_input_file file begin fun ic -> + let conf = Lexers.conf_lines dir 1 (Printf.sprintf "file: %S" file) (Lexing.from_channel ic) in + add_config conf + end + +let key_match = Glob.eval + +let apply_config s (config : t) init = + List.fold_left begin fun (tags, flags as acc) (key, v) -> + if key_match key s then + (List.fold_right Tags.add v.plus_tags (List.fold_right Tags.remove v.minus_tags tags), + List.fold_right Flags.add v.plus_flags (List.fold_right Flags.remove v.minus_flags flags)) + else acc + end init config + +let apply_configs s = + let (tags, flags) = + List.fold_right (apply_config s) (configs ()) (Tags.empty, []) + in (tags, Flags.to_spec flags) + +let tags_and_flags_of_filename s = + try Hashtbl.find cache s + with Not_found -> + let res = apply_configs s in + let () = Hashtbl.replace cache s res in + res + +let tags_of_filename x = fst (tags_and_flags_of_filename x) +let flags_of_filename x = snd (tags_and_flags_of_filename x) + +let has_tag tag = Tags.mem tag (tags_of_filename "") diff --git a/ocamlbuild/configuration.mli b/ocamlbuild/configuration.mli new file mode 100644 index 0000000000..a8578bf768 --- /dev/null +++ b/ocamlbuild/configuration.mli @@ -0,0 +1,34 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +(* Configuration *) + +(** Handles the "_tags" file mechanism. *) + +type flag_list = (string * string) list + +(** Incorporate a newline-separated configuration string into the current configuration. + Will usually raising an [Invalid_arg] with an appropriately explicit message in case of error. *) +val parse_string : string -> unit + +(** [parse_file ?dir fn] incorporates the configuration file named [fn], prefixing its glob patterns + with [dir] if given. *) +val parse_file : ?dir:string -> string -> unit + +(** Return the set of tags that apply to a given filename under the current configuration. *) +val tags_of_filename : string -> Tags.t + +(** Return the set of flags that apply to a given filename under the current configuration. *) +val flags_of_filename : string -> Command.spec + +val has_tag : string -> bool diff --git a/ocamlbuild/discard_printf.ml b/ocamlbuild/discard_printf.ml new file mode 100644 index 0000000000..d77ecd3ddb --- /dev/null +++ b/ocamlbuild/discard_printf.ml @@ -0,0 +1,16 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +let rec greedy _ = greedy + +let discard_printf _fmt = Obj.magic greedy diff --git a/ocamlbuild/discard_printf.mli b/ocamlbuild/discard_printf.mli new file mode 100644 index 0000000000..363e33d773 --- /dev/null +++ b/ocamlbuild/discard_printf.mli @@ -0,0 +1,19 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +(* Discard_printf *) + +(** This module compiled with [-rectypes] allows one to write functions + taking formatters as arguments. *) +open Format +val discard_printf: ('a, formatter, unit) format -> 'a diff --git a/ocamlbuild/display.ml b/ocamlbuild/display.ml new file mode 100644 index 0000000000..b1f13ee393 --- /dev/null +++ b/ocamlbuild/display.ml @@ -0,0 +1,385 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Berke Durak *) +(* Display *) +open My_std;; + +open My_unix;; + +let fp = Printf.fprintf;; + +(*** ANSI *) +module ANSI = + struct + let up oc n = fp oc "\027[%dA" n;; + let clear_to_eol oc () = fp oc "\027[K";; + let bol oc () = fp oc "\r";; + let get_columns () = + try + int_of_string (String.chomp (My_unix.run_and_read "tput cols")) + with + | Failure _ -> 80 + end +;; +(* ***) +(*** tagline_description *) +type tagline_description = (string * char) list;; +(* ***) +(*** sophisticated_display *) +type sophisticated_display = { + ds_channel : out_channel; (** Channel for writing *) + ds_start_time : float; (** When was compilation started *) + mutable ds_last_update : float; (** When was the display last updated *) + mutable ds_last_target : string; (** Last target built *) + mutable ds_last_cached : bool; (** Was the last target cached or really built ? *) + mutable ds_last_tags : Tags.t; (** Tags of the last command *) + mutable ds_changed : bool; (** Does the tag line need recomputing ? *) + ds_update_interval : float; (** Minimum interval between updates *) + ds_columns : int; (** Number of columns in dssplay *) + mutable ds_jobs : int; (** Number of jobs launched or cached *) + mutable ds_jobs_cached : int; (** Number of jobs cached *) + ds_tagline : string; (** Current tagline *) + mutable ds_seen_tags : Tags.t; (** Tags that we have encountered *) + ds_pathname_length : int; (** How much space for displaying pathnames ? *) + ds_tld : tagline_description; (** Description for the tagline *) +};; +(* ***) +(*** display_line, display *) +type display_line = +| Classic +| Sophisticated of sophisticated_display + +type display = { + di_log_level : int; + di_log_channel : (Format.formatter * out_channel) option; + di_channel : out_channel; + di_formatter : Format.formatter; + di_display_line : display_line; + mutable di_finished : bool; +} +;; +(* ***) +(*** various defaults *) +let default_update_interval = 0.05;; +let default_tagline_description = [ + "ocaml", 'O'; + "native", 'N'; + "byte", 'B'; + "program", 'P'; + "pp", 'R'; + "debug", 'D'; + "interf", 'I'; + "link", 'L'; +];; + +(* NOT including spaces *) +let countdown_chars = 8;; +let jobs_chars = 3;; +let jobs_cached_chars = 5;; +let dots = "...";; +let start_target = "STARTING";; +let finish_target = "FINISHED";; +let ticker_chars = 3;; +let ticker_period = 0.25;; +let ticker_animation = [| + "\\"; + "|"; + "/"; + "-"; +|];; +let cached = "*";; +let uncached = " ";; +let cache_chars = 1;; +(* ***) +(*** create_tagline *) +let create_tagline description = String.make (List.length description) '-';; +(* ***) +(*** create *) +let create + ?(channel=stdout) + ?(mode:[`Classic|`Sophisticated] = `Sophisticated) + ?columns:(_columns=75) + ?(description = default_tagline_description) + ?log_file + ?(log_level=1) + () + = + let log_channel = + match log_file with + | None -> None + | Some fn -> + let oc = open_out_gen [Open_text; Open_wronly; Open_creat; Open_trunc] 0o644 fn in + let f = Format.formatter_of_out_channel oc in + Format.fprintf f "*** Starting build.\n"; + Some (f, oc) + in + + let display_line = + match mode with + | `Classic -> Classic + | `Sophisticated -> + (* We assume Unix is not degraded. *) + let n = ANSI.get_columns () in + let tag_chars = List.length description in + Sophisticated + { ds_channel = stdout; + ds_start_time = gettimeofday (); + ds_last_update = 0.0; + ds_last_target = start_target; + ds_last_tags = Tags.empty; + ds_last_cached = false; + ds_changed = false; + ds_update_interval = default_update_interval; + ds_columns = n; + ds_jobs = 0; + ds_jobs_cached = 0; + ds_tagline = create_tagline description; + ds_seen_tags = Tags.empty; + ds_pathname_length = n - + (countdown_chars + 1 + jobs_chars + 1 + jobs_cached_chars + 1 + + cache_chars + 1 + tag_chars + 1 + ticker_chars + 2); + ds_tld = description } + in + { di_log_level = log_level; + di_log_channel = log_channel; + di_channel = channel; + di_formatter = Format.formatter_of_out_channel channel; + di_display_line = display_line; + di_finished = false } +;; +(* ***) +(*** print_time *) +let print_time oc t = + let t = int_of_float t in + let s = t mod 60 in + let m = (t / 60) mod 60 in + let h = t / 3600 in + fp oc "%02d:%02d:%02d" h m s +;; +(* ***) +(*** print_shortened_pathname *) +let print_shortened_pathname length oc u = + assert(length >= 3); + let m = String.length u in + if m <= length then + begin + output_string oc u; + fp oc "%*s" (length - m) "" + end + else + begin + let n = String.length dots in + let k = length - n in + output_string oc dots; + output oc u (m - k) k; + end +(* ***) +(*** Layout + +00000000001111111111222222222233333333334444444444555555555566666666667777777777 +01234567890123456789012345678901234567890123456789012345678901234567890123456789 +HH MM SS XXXX PATHNAME +00:12:31 32 ( 26) ...lp4Filters/Camlp4LocationStripper.cmo * OBn------------- +| | | | | \ tags +| | | \ last target built \ cached ? +| | | +| | \ number of jobs cached +| \ number of jobs +\ elapsed time +cmo mllib +***) +(*** redraw_sophisticated *) +let redraw_sophisticated ds = + let t = gettimeofday () in + let oc = ds.ds_channel in + let dt = t -. ds.ds_start_time in + ds.ds_last_update <- t; + fp oc "%a" ANSI.bol (); + let ticker_phase = (abs (int_of_float (ceil (dt /. ticker_period)))) mod (Array.length ticker_animation) in + let ticker = ticker_animation.(ticker_phase) in + fp oc "%a %-4d (%-4d) %a %s %s %s" + print_time dt + ds.ds_jobs + ds.ds_jobs_cached + (print_shortened_pathname ds.ds_pathname_length) ds.ds_last_target + (if ds.ds_last_cached then cached else uncached) + ds.ds_tagline + ticker; + fp oc "%a%!" ANSI.clear_to_eol () +;; +(* ***) +(*** redraw *) +let redraw = function + | Classic -> () + | Sophisticated ds -> redraw_sophisticated ds +;; +(* ***) +(*** finish_sophisticated *) +let finish_sophisticated ?(how=`Success) ds = + let t = gettimeofday () in + let oc = ds.ds_channel in + let dt = t -. ds.ds_start_time in + match how with + | `Success|`Error -> + fp oc "%a" ANSI.bol (); + fp oc "%s %d target%s (%d cached) in %a." + (if how = `Error then + "Compilation unsuccessful after building" + else + "Finished,") + ds.ds_jobs + (if ds.ds_jobs = 1 then "" else "s") + ds.ds_jobs_cached + print_time dt; + fp oc "%a\n%!" ANSI.clear_to_eol () + | `Quiet -> + fp oc "%a%a%!" ANSI.bol () ANSI.clear_to_eol (); +;; +(* ***) +(*** sophisticated_display *) +let sophisticated_display ds f = + fp ds.ds_channel "%a%a%!" ANSI.bol () ANSI.clear_to_eol (); + f ds.ds_channel +;; +(* ***) +(*** call_if *) +let call_if log_channel f = + match log_channel with + | None -> () + | Some x -> f x +;; +(* ***) +(*** display *) +let display di f = + call_if di.di_log_channel (fun (_, oc) -> f oc); + match di.di_display_line with + | Classic -> f di.di_channel + | Sophisticated ds -> sophisticated_display ds f +;; +(* ***) +(*** finish *) +let finish ?(how=`Success) di = + if not di.di_finished then begin + di.di_finished <- true; + call_if di.di_log_channel + begin fun (fmt, oc) -> + Format.fprintf fmt "# Compilation %ssuccessful.@." (if how = `Error then "un" else ""); + close_out oc + end; + match di.di_display_line with + | Classic -> () + | Sophisticated ds -> finish_sophisticated ~how ds + end +;; +(* ***) +(*** update_tagline_from_tags *) +let update_tagline_from_tags ds = + let tagline = ds.ds_tagline in + let tags = ds.ds_last_tags in + let rec loop i = function + | [] -> + for j = i to String.length tagline - 1 do + tagline.[j] <- '-' + done + | (tag, c) :: rest -> + if Tags.mem tag tags then + tagline.[i] <- Char.uppercase c + else + if Tags.mem tag ds.ds_seen_tags then + tagline.[i] <- Char.lowercase c + else + tagline.[i] <- '-'; + loop (i + 1) rest + in + loop 0 ds.ds_tld; +;; +(* ***) +(*** update_sophisticated *) +let update_sophisticated ds = + let t = gettimeofday () in + let dt = t -. ds.ds_last_update in + if dt > ds.ds_update_interval then + begin + if ds.ds_changed then + begin + update_tagline_from_tags ds; + ds.ds_changed <- false + end; + redraw_sophisticated ds + end + else + () +;; +(* ***) +(*** set_target_sophisticated *) +let set_target_sophisticated ds target tags cached = + ds.ds_changed <- true; + ds.ds_last_target <- target; + ds.ds_last_tags <- tags; + ds.ds_jobs <- 1 + ds.ds_jobs; + if cached then ds.ds_jobs_cached <- 1 + ds.ds_jobs_cached; + ds.ds_last_cached <- cached; + ds.ds_seen_tags <- Tags.union ds.ds_seen_tags ds.ds_last_tags; + update_sophisticated ds +;; + +let print_tags f tags = + let first = ref true in + Tags.iter begin fun tag -> + if !first then begin + first := false; + Format.fprintf f "%s" tag + end else Format.fprintf f ", %s" tag + end tags +;; +(* ***) +(*** update *) +let update di = + match di.di_display_line with + | Classic -> () + | Sophisticated ds -> update_sophisticated ds +;; +(* ***) +(*** event *) +let event di ?(pretend=false) command target tags = + call_if di.di_log_channel + (fun (fmt, _) -> + Format.fprintf fmt "# Target: %s, tags: { %a }\n" target print_tags tags; + Format.fprintf fmt "%s%s@." command (if pretend then " # cached" else "")); + match di.di_display_line with + | Classic -> + if pretend then + (if di.di_log_level >= 2 then Format.fprintf di.di_formatter "[cache hit] %s\n%!" command) + else + (if di.di_log_level >= 1 then Format.fprintf di.di_formatter "%s\n%!" command) + | Sophisticated ds -> + set_target_sophisticated ds target tags pretend; + update_sophisticated ds +;; +(* ***) +(*** dprintf *) +let dprintf ?(log_level=1) di fmt = + if log_level > di.di_log_level then Discard_printf.discard_printf fmt else + match di.di_display_line with + | Classic -> Format.fprintf di.di_formatter fmt + | Sophisticated _ -> + if log_level < 0 then + begin + display di ignore; + Format.fprintf di.di_formatter fmt + end + else + match di.di_log_channel with + | Some (f, _) -> Format.fprintf f fmt + | None -> Discard_printf.discard_printf fmt +(* ***) diff --git a/ocamlbuild/display.mli b/ocamlbuild/display.mli new file mode 100644 index 0000000000..d184774b7a --- /dev/null +++ b/ocamlbuild/display.mli @@ -0,0 +1,33 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Berke Durak *) +(* Display *) + +type display +type tagline_description = (string * char) list + +val create : + ?channel:out_channel -> + ?mode:[ `Classic | `Sophisticated ] -> + ?columns:int -> + ?description:tagline_description -> + ?log_file:string -> + ?log_level:int -> + unit -> + display + +val finish : ?how:[`Success|`Error|`Quiet] -> display -> unit +val event : display -> ?pretend:bool -> string -> string -> Tags.t -> unit +val display : display -> (out_channel -> unit) -> unit +val update : display -> unit +val dprintf : ?log_level:int -> display -> ('a, Format.formatter, unit) format -> 'a diff --git a/ocamlbuild/examples/example1/hello.ml b/ocamlbuild/examples/example1/hello.ml new file mode 100644 index 0000000000..c85cb66b87 --- /dev/null +++ b/ocamlbuild/examples/example1/hello.ml @@ -0,0 +1,5 @@ +let _ = + Printf.printf "Hello, %s ! My name is %s\n" + (if Array.length Sys.argv > 1 then Sys.argv.(1) else "stranger") + Sys.argv.(0) +;; diff --git a/ocamlbuild/examples/example2/greet.ml b/ocamlbuild/examples/example2/greet.ml new file mode 100644 index 0000000000..ec8088916e --- /dev/null +++ b/ocamlbuild/examples/example2/greet.ml @@ -0,0 +1,6 @@ +type how = Nicely | Badly;; + +let greet how who = + match how with Nicely -> Printf.printf "Hello, %s !\n" who + | Badly -> Printf.printf "Oh, here is that %s again.\n" who +;; diff --git a/ocamlbuild/examples/example2/hello.ml b/ocamlbuild/examples/example2/hello.ml new file mode 100644 index 0000000000..b48806a3db --- /dev/null +++ b/ocamlbuild/examples/example2/hello.ml @@ -0,0 +1,14 @@ +open Greet + +let _ = + let name = + if Array.length Sys.argv > 1 then + Sys.argv.(1) + else + "stranger" + in + greet + (if name = "Caesar" then Nicely else Badly) + name; + Printf.printf "My name is %s\n" Sys.argv.(0) +;; diff --git a/ocamlbuild/examples/example3/epoch.ml b/ocamlbuild/examples/example3/epoch.ml new file mode 100644 index 0000000000..ad95a0394f --- /dev/null +++ b/ocamlbuild/examples/example3/epoch.ml @@ -0,0 +1,6 @@ +let _ = + let s = Num.num_of_string (Printf.sprintf "%.0f" (Unix.gettimeofday ())) in + let ps = Num.mult_num (Num.num_of_string "1000000000000") s in + Printf.printf "%s picoseconds have passed since January 1st, 1970.\n" + (Num.string_of_num ps) +;; diff --git a/ocamlbuild/examples/example3/make.sh b/ocamlbuild/examples/example3/make.sh new file mode 100755 index 0000000000..3588a713fc --- /dev/null +++ b/ocamlbuild/examples/example3/make.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +set -e + +TARGET=epoch +FLAGS="-libs unix,nums" +OCAMLBUILD=ocamlbuild + +ocb() +{ + $OCAMLBUILD $FLAGS $* +} + +rule() { + case $1 in + clean) ocb -clean;; + native) ocb $TARGET.native;; + byte) ocb $TARGET.byte;; + all) ocb $TARGET.native $TARGET.byte;; + depend) echo "Not needed.";; + *) echo "Unknown action $1";; + esac; +} + +if [ $# -eq 0 ]; then + rule all +else + while [ $# -gt 0 ]; do + rule $1; + shift + done +fi diff --git a/ocamlbuild/executor.ml b/ocamlbuild/executor.ml new file mode 100644 index 0000000000..2a05ca96b8 --- /dev/null +++ b/ocamlbuild/executor.ml @@ -0,0 +1,315 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Berke Durak *) +(* Executor *) + +open Unix;; + +module Exit_codes = struct + let rc_subcommand_failed = 10 + let rc_subcommand_got_signal = 11 + let rc_io_error = 12 + let rc_exceptional_condition = 13 +end;; + +type task = (string * (unit -> unit));; + +type job = { + job_id : int * int; + job_command : string; + job_next : (string * (unit -> unit)) list; + job_result : bool ref; (* Result of this sequence group *) + job_stdout : in_channel; + job_stdin : out_channel; + job_stderr : in_channel; + job_buffer : Buffer.t; + mutable job_dying : bool; +};; + +module JS = Set.Make(struct type t = job let compare = compare end);; +module FDM = Map.Make(struct type t = file_descr let compare = compare end);; + +let sf = Printf.sprintf;; +let fp = Printf.fprintf;; + +(*** print_unix_status *) +(* FIXME never called *) +let print_unix_status oc = function + | WEXITED x -> fp oc "exit %d" x + | WSIGNALED i -> fp oc "signal %d" i + | WSTOPPED i -> fp oc "stop %d" i +;; +(* ***) +(*** print_job_id *) +let print_job_id oc (x,y) = fp oc "%d.%d" x y;; +(* ***) +(*** output_lines *) +let output_lines prefix oc buffer = + let u = Buffer.contents buffer in + let m = String.length u in + let output_line i j = + output_string oc prefix; + output oc u i (j - i); + output_char oc '\n' + in + let rec loop i = + if i = m then + () + else + begin + try + let j = String.index_from u i '\n' in + output_line i j; + loop (j + 1) + with + | Not_found -> + output_line i m + end + in + loop 0 +;; +(* ***) +(*** execute *) +(* XXX: Add test for non reentrancy *) +let execute + ?(max_jobs=max_int) + ?(ticker=ignore) + ?(period=0.1) + ?(display=(fun f -> f Pervasives.stdout)) + (commands : task list list) + = + let batch_id = ref 0 in + let env = environment () in + let jobs = ref JS.empty in + let jobs_active = ref 0 in + let jobs_to_terminate = Queue.create () in + let commands_to_execute = Queue.create () in + let all_ok = ref true in + let results = + List.map (fun tasks -> + let result = ref false in + Queue.add (tasks, result) commands_to_execute; + result) + commands + in + let outputs = ref FDM.empty in + let doi = descr_of_in_channel in + let doo = descr_of_out_channel in + (*** compute_fds *) + let compute_fds = + let fds = ref ([], [], []) in + let prev_jobs = ref JS.empty in + fun () -> + if not (!prev_jobs == !jobs) then + begin + prev_jobs := !jobs; + fds := + JS.fold + begin fun job (rfds, wfds, xfds) -> + let ofd = doi job.job_stdout + and ifd = doo job.job_stdin + and efd = doi job.job_stderr + in + (ofd :: efd :: rfds, wfds, ofd :: ifd :: efd :: xfds) + end + !jobs + ([], [], []) + end; + !fds + in + (* ***) + (*** add_job *) + let add_job (cmd, action) rest result id = + (*display begin fun oc -> fp oc "Job %a is %s\n%!" print_job_id id cmd; end;*) + action (); + let (stdout', stdin', stderr') = open_process_full cmd env in + incr jobs_active; + set_nonblock (doi stdout'); + set_nonblock (doi stderr'); + let job = + { job_id = id; + job_command = cmd; + job_next = rest; + job_result = result; + job_stdout = stdout'; + job_stdin = stdin'; + job_stderr = stderr'; + job_buffer = Buffer.create 1024; + job_dying = false } + in + outputs := FDM.add (doi stdout') job (FDM.add (doi stderr') job !outputs); + jobs := JS.add job !jobs; + in + (* ***) + (*** add_some_jobs *) + let add_some_jobs () = + let (tasks, result) = Queue.take commands_to_execute in + match tasks with + | [] -> result := false + | task :: rest -> + let b_id = !batch_id in + incr batch_id; + add_job task rest result (b_id, 0) + in + (* ***) + (*** terminate *) + let terminate ?(continue=true) job = + if not job.job_dying then + begin + job.job_dying <- true; + Queue.add (job, continue) jobs_to_terminate + end + else + () + in + (* ***) + (*** add_more_jobs_if_possible *) + let add_more_jobs_if_possible () = + while !jobs_active < max_jobs && not (Queue.is_empty commands_to_execute) do + add_some_jobs () + done + in + (* ***) + (*** process_jobs_to_terminate *) + let process_jobs_to_terminate () = + while not (Queue.is_empty jobs_to_terminate) do + ticker (); + let (job, continue) = Queue.take jobs_to_terminate in + + (*display begin fun oc -> fp oc "Terminating job %a\n%!" print_job_id job.job_id; end;*) + + decr jobs_active; + outputs := FDM.remove (doi job.job_stdout) (FDM.remove (doi job.job_stderr) !outputs); + jobs := JS.remove job !jobs; + let status = close_process_full (job.job_stdout, job.job_stdin, job.job_stderr) in + + let shown = ref false in + + let show_command () = + if !shown then + () + else + display + begin fun oc -> + shown := true; + fp oc "+ %s\n" job.job_command; + output_lines "" oc job.job_buffer + end + in + if Buffer.length job.job_buffer > 0 then show_command (); + begin + match status with + | Unix.WEXITED 0 -> + begin + if continue then + begin + match job.job_next with + | [] -> job.job_result := true + | task :: rest -> + let (b_id, s_id) = job.job_id in + add_job task rest job.job_result (b_id, s_id + 1) + end + else + all_ok := false; + end + | Unix.WEXITED rc -> + show_command (); + display (fun oc -> fp oc "Command exited with code %d.\n" rc); + all_ok := false; + exit Exit_codes.rc_subcommand_failed + | Unix.WSTOPPED s | Unix.WSIGNALED s -> + show_command (); + all_ok := false; + display (fun oc -> fp oc "Command got signal %d.\n" s); + exit Exit_codes.rc_subcommand_got_signal + end + done + in + (* ***) + (*** do_read *) + let do_read = + let u = String.create 4096 in + fun fd job -> + if job.job_dying then + () + else + try + let m = read fd u 0 (String.length u) in + if m = 0 then + terminate job + else + begin + Buffer.add_substring job.job_buffer u 0 m + end + with + | x -> + display + begin fun oc -> + fp oc "Exception %s while reading output of command %S\n%!" job.job_command + (Printexc.to_string x); + end; + exit Exit_codes.rc_io_error + in + (* ***) + (*** terminate_all_jobs *) + let terminate_all_jobs () = + JS.iter (terminate ~continue:false) !jobs + in + (* ***) + (*** loop *) + let rec loop () = + (*display (fun oc -> fp oc "Total %d jobs\n" !jobs_active);*) + process_jobs_to_terminate (); + add_more_jobs_if_possible (); + if JS.is_empty !jobs then + () + else + begin + let (rfds, wfds, xfds) = compute_fds () in + ticker (); + let (chrfds, chwfds, chxfds) = select rfds wfds xfds period in + List.iter + begin fun (fdlist, hook) -> + List.iter + begin fun fd -> + let job = FDM.find fd !outputs in + ticker (); + hook fd job + end + fdlist + end + [chrfds, do_read; + chwfds, (fun _ _ -> ()); + chxfds, + begin fun _ job -> + display (fun oc -> fp oc "Exceptional condition on command %S\n%!" job.job_command); + exit Exit_codes.rc_exceptional_condition + end]; + loop () + end + in + try + loop (); + None + with + | x -> + begin + try + terminate_all_jobs () + with + | x' -> + display (fun oc -> fp oc "Extra exception %s\n%!" (Printexc.to_string x')) + end; + Some(List.map (!) results, x) +;; +(* ***) diff --git a/ocamlbuild/executor.mli b/ocamlbuild/executor.mli new file mode 100644 index 0000000000..76fc6879fd --- /dev/null +++ b/ocamlbuild/executor.mli @@ -0,0 +1,35 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Berke Durak *) +(* Executor *) + +(** UNIX-specific module for running tasks in parallel and properly multiplexing their outputs. *) + +(** [execute ~ticker ~period ~display commands] will execute the commands in [commands] + in parallel, correctly multiplexing their outputs. A command is a pair [(cmd, action)] + where [cmd] is a shell command string, and [action] is a thunk that is to be called just + before [cmd] is about to be executed. If specified, it will call [ticker] at least every [period] + seconds. If specified, it will call [display f] when it wishes to print something; + [display] should then call [f] with then channel on which [f] should print. + Note that [f] must be idempotent as it may well be called twice, once for the log file, + once for the actual output. + If one of the commands fails, it will exit with an appropriate error code, + calling [cleanup] before. +*) +val execute : + ?max_jobs:int -> + ?ticker:(unit -> unit) -> + ?period:float -> + ?display:((out_channel -> unit) -> unit) -> + ((string * (unit -> unit)) list list) -> + (bool list * exn) option diff --git a/ocamlbuild/fda.ml b/ocamlbuild/fda.ml new file mode 100644 index 0000000000..2a2ce85449 --- /dev/null +++ b/ocamlbuild/fda.ml @@ -0,0 +1,76 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Berke Durak *) +(* FDA *) + +open Log +open Hygiene +;; + +exception Exit_hygiene_failed +;; + +let laws = + [ + { law_name = "Leftover Ocaml compilation files"; + law_rules = [Not ".cmo"; Not ".cmi"; Not ".cmx"; Not ".cma"; Not ".cmxa"]; + law_penalty = Fail }; + { law_name = "Leftover Ocaml type annotation files"; + law_rules = [Not ".annot"]; + law_penalty = Warn }; + { law_name = "Leftover object files"; + law_rules = [Not ".o"; Not ".a"; Not ".so"; Not ".obj"; Not ".lib"; Not ".dll"]; + law_penalty = Fail }; + { law_name = "Leftover ocamlyacc-generated files"; + law_rules = [Implies_not(".mly",".ml"); Implies_not(".mly",".mli")]; + law_penalty = Fail }; + { law_name = "Leftover ocamllex-generated files"; + law_rules = [Implies_not(".mll",".ml")]; + law_penalty = Fail }; + { law_name = "Leftover dependency files"; + law_rules = [Not ".ml.depends"; Not ".mli.depends"]; + law_penalty = Fail } + ] + +let inspect entry = + dprintf 5 "Doing sanity checks"; + let evil = ref false in + match Hygiene.check ~sterilize:!Options.sterilize laws entry with + | (entry, []) -> entry + | (entry, stuff) -> + List.iter + begin fun (law, msgs) -> + Printf.printf "%s: %s:\n" + (match law.law_penalty with + | Warn -> "Warning" + | Fail -> + if not !evil then + begin + Printf.printf "IMPORTANT:\n\ + \ I cannot work with leftover compiled files: Please remove them.\n\ + \ The directory should contain only source files.\n\ + \ By default I compile in the %s/ directory.\n%!" !Options.build_dir; + evil := true + end; + "ERROR") + law.law_name; + List.iter + begin fun msg -> + Printf.printf " %s\n" msg + end + msgs + end + stuff; + if !evil then raise Exit_hygiene_failed; + entry +;; diff --git a/ocamlbuild/fda.mli b/ocamlbuild/fda.mli new file mode 100644 index 0000000000..ac1c824812 --- /dev/null +++ b/ocamlbuild/fda.mli @@ -0,0 +1,18 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Berke Durak *) +(* Fda *) + +exception Exit_hygiene_failed + +val inspect : bool Slurp.entry -> bool Slurp.entry diff --git a/ocamlbuild/flags.ml b/ocamlbuild/flags.ml new file mode 100644 index 0000000000..ddecba5646 --- /dev/null +++ b/ocamlbuild/flags.ml @@ -0,0 +1,43 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +open Command +open Bool (* FIXME remove me *) +open Tags.Operators +let all_flags = ref [] + +let of_tags tags = + S begin + List.fold_left begin fun acc (xtags, xflags) -> + if Tags.does_match tags xtags then xflags :: acc + else acc + end [] !all_flags + end + +let () = Command.tag_handler := of_tags + +let of_tag_list x = of_tags (Tags.of_list x) + +let set_flags tags flags = + all_flags := (tags, flags) :: !all_flags + +let flag tags flags = set_flags (Tags.of_list tags) flags + +let add x xs = x :: xs +let remove me = List.filter (fun x -> me <> x) +let to_spec l = + S begin + List.fold_right begin fun (x, y) acc -> + A ("-"^x) :: A y :: acc + end l [] + end diff --git a/ocamlbuild/flags.mli b/ocamlbuild/flags.mli new file mode 100644 index 0000000000..d0e8c98caf --- /dev/null +++ b/ocamlbuild/flags.mli @@ -0,0 +1,19 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +val of_tags : Tags.t -> Command.spec +val of_tag_list : Tags.elt list -> Command.spec +val flag : Tags.elt list -> Command.spec -> unit +val add : 'a -> 'a list -> 'a list +val remove : 'a -> 'a list -> 'a list +val to_spec : (string * string) list -> Command.spec diff --git a/ocamlbuild/glob.ml b/ocamlbuild/glob.ml new file mode 100644 index 0000000000..8003dbbb8f --- /dev/null +++ b/ocamlbuild/glob.ml @@ -0,0 +1,398 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Berke Durak *) +(* Glob *) +open My_std;; +open Bool;; +include Glob_ast;; +open Glob_lexer;; + +let sf = Printf.sprintf;; + +let brute_limit = 10;; + +(*** string_of_token *) +let string_of_token = function +| ATOM _ -> "ATOM" +| AND -> "AND" +| OR -> "OR" +| NOT -> "NOT" +| LPAR -> "LPAR" +| RPAR -> "RPAR" +| TRUE -> "TRUE" +| FALSE -> "FALSE" +| EOF -> "EOF" +;; +(* ***) +(*** match_character_class *) +let match_character_class cl c = + Bool.eval + begin function (c1,c2) -> + c1 <= c && c <= c2 + end + cl +;; +(* ***) +(*** NFA *) +module NFA = + struct + type transition = + | QCLASS of character_class + | QEPSILON + ;; + + module IS = Set.Make(struct type t = int let compare = compare let print = Format.pp_print_int end);; + module ISM = Map.Make(struct type t = IS.t let compare = IS.compare let print = IS.print end);; + + type machine = { + mc_qi : IS.t; + mc_table : (character_class * IS.t) list array; + mc_qf : int; + mc_power_table : (char, IS.t ISM.t) Hashtbl.t + } + + (*** build' *) + let build' p = + let count = ref 0 in + let transitions = ref [] in + let epsilons : (int * int) list ref = ref [] in + let state () = let id = !count in incr count; id in + let ( --> ) q1 t q2 = + match t with + | QEPSILON -> epsilons := (q1,q2) :: !epsilons; q1 + | QCLASS cl -> transitions := (q1,cl,q2) :: !transitions; q1 + in + (* Construit les transitions correspondant au motif donné et arrivant + * sur l'état qf. Retourne l'état d'origine. *) + let rec loop qf = function + | Epsilon -> qf + | Word u -> + let m = String.length u in + let q0 = state () in + let rec loop q i = + if i = m then + q0 + else + begin + let q' = + if i = m - 1 then + qf + else + state () + in + let _ = (q --> QCLASS(Atom(u.[i], u.[i]))) q' in + loop q' (i + 1) + end + in + loop q0 0 + | Class cl -> + let q1 = state () in + (q1 --> QCLASS cl) qf + | Star p -> + (* The fucking Kleene star *) + let q2 = state () in + let q1 = loop q2 p in (* q1 -{p}-> q2 *) + let _ = (q1 --> QEPSILON) qf in + let _ = (q2 --> QEPSILON) q1 in + let _ = (q2 --> QEPSILON) q1 in + q1 + | Concat(p1,p2) -> + let q12 = state () in + let q1 = loop q12 p1 in (* q1 -{p1}-> q12 *) + let q2 = loop qf p2 in (* q2 -{p2}-> qf *) + let _ = (q12 --> QEPSILON) q2 in + q1 + | Union pl -> + let qi = state () in + List.iter + begin fun p -> + let q = loop qf p in (* q -{p2}-> qf *) + let _ = (qi --> QEPSILON) q in (* qi -{}---> q *) + () + end + pl; + qi + in + let qf = state () in + let qi = loop qf p in + let m = !count in + + (* Compute epsilon closure *) + let graph = Array.make m IS.empty in + List.iter + begin fun (q,q') -> + graph.(q) <- IS.add q' graph.(q) + end + !epsilons; + + let closure = Array.make m IS.empty in + let rec transitive past = function + | [] -> past + | q :: future -> + let past' = IS.add q past in + let future' = + IS.fold + begin fun q' future' -> + (* q -{}--> q' *) + if IS.mem q' past' then + future' + else + q' :: future' + end + graph.(q) + future + in + transitive past' future' + in + for i = 0 to m - 1 do + closure.(i) <- transitive IS.empty [i] (* O(n^2), I know *) + done; + + (* Finally, build the table *) + let table = Array.make m [] in + List.iter + begin fun (q,t,q') -> + table.(q) <- (t, closure.(q')) :: table.(q) + end + !transitions; + + (graph, closure, + { mc_qi = closure.(qi); + mc_table = table; + mc_qf = qf; + mc_power_table = Hashtbl.create 37 }) + ;; + let build x = let (_,_, machine) = build' x in machine;; + (* ***) + (*** run *) + let run ?(trace=false) machine u = + let m = String.length u in + let apply qs c = + try + let t = Hashtbl.find machine.mc_power_table c in + ISM.find qs t + with + | Not_found -> + let qs' = + IS.fold + begin fun q qs' -> + List.fold_left + begin fun qs' (cl,qs'') -> + if match_character_class cl c then + IS.union qs' qs'' + else + qs' + end + qs' + machine.mc_table.(q) + end + qs + IS.empty + in + let t = + try + Hashtbl.find machine.mc_power_table c + with + | Not_found -> ISM.empty + in + Hashtbl.replace machine.mc_power_table c (ISM.add qs qs' t); + qs' + in + let rec loop qs i = + if IS.is_empty qs then + false + else + begin + if i = m then + IS.mem machine.mc_qf qs + else + begin + let c = u.[i] in + if trace then + begin + Printf.printf "%d %C {" i c; + IS.iter (fun q -> Printf.printf " %d" q) qs; + Printf.printf " }\n%!" + end; + let qs' = apply qs c in + loop qs' (i + 1) + end + end + in + loop machine.mc_qi 0 + ;; + (* ***) + end +;; +(* ***) +(*** Brute *) +module Brute = + struct + exception Succeed;; + exception Fail;; + exception Too_hard;; + + (*** match_pattern *) + let match_pattern counter p u = + let m = String.length u in + (** [loop i n p] returns [true] iff the word [u.(i .. i + n - 1)] is in the + ** language generated by the pattern [p]. + ** We must have 0 <= i and i + n <= m *) + let rec loop (i,n,p) = + assert (0 <= i && 0 <= n && i + n <= m); + incr counter; + if !counter >= brute_limit then raise Too_hard; + match p with + | Word v -> + String.length v = n && + begin + let rec check j = j = n or (v.[j] = u.[i + j] && check (j + 1)) + in + check 0 + end + | Epsilon -> n = 0 + | Star(Class True) -> true + | Star(Class cl) -> + let rec check k = + if k = n then + true + else + (match_character_class cl u.[i + k]) && check (k + 1) + in + check 0 + | Star p -> raise Too_hard + | Class cl -> n = 1 && match_character_class cl u.[i] + | Concat(p1,p2) -> + let rec scan j = + j <= n && ((loop (i,j,p1) && loop (i+j, n - j,p2)) || scan (j + 1)) + in + scan 0 + | Union pl -> List.exists (fun p' -> loop (i,n,p')) pl + in + loop (0,m,p) + ;; + (* ***) +end +;; +(* ***) +(*** fast_pattern, globber *) +type fast_pattern = +| Brute of int ref * pattern +| Machine of NFA.machine +;; + +type globber = fast_pattern ref atom Bool.boolean;; +(* ***) +(*** add_dir *) +let add_dir dir x = + match dir with + | None -> x + | Some(dir) -> + match x with + | Constant(s) -> + Constant(My_std.filename_concat dir s) + | Pattern(p) -> + Pattern(Concat(Word(My_std.filename_concat dir ""), p)) +;; +(* ***) +(*** parse *) +let parse ?dir u = + let l = Lexing.from_string u in + let tok = ref None in + let f = + fun () -> + match !tok with + | None -> token l + | Some x -> + tok := None; + x + in + let g t = + match !tok with + | None -> tok := Some t + | Some t' -> + raise (Parse_error(sf "Trying to unput token %s while %s is active" (string_of_token t) (string_of_token t'))) + in + let read x = + let y = f () in + if x = y then + () + else + raise (Parse_error(sf "Unexpected token, expecting %s, got %s" (string_of_token x) (string_of_token y))) + in + let rec atomizer continuation = match f () with + | NOT -> atomizer (fun x -> continuation (Not x)) + | ATOM x -> + begin + let a = + match add_dir dir x with + | Constant u -> Constant u + | Pattern p -> Pattern(ref (Brute(ref 0, p))) + in + continuation (Atom a) + end + | TRUE -> continuation True + | FALSE -> continuation False + | LPAR -> + let y = parse_s () in + read RPAR; + continuation y + | t -> raise (Parse_error(sf "Unexpected token %s in atomizer" (string_of_token t))) + and parse_s1 x = match f () with + | OR -> let y = parse_s () in Or[x; y] + | AND -> parse_t x + | t -> g t; x + and parse_t1 x y = match f () with + | OR -> let z = parse_s () in Or[And[x;y]; z] + | AND -> parse_t (And[x;y]) + | t -> g t; And[x;y] + and parse_s () = atomizer parse_s1 + and parse_t x = atomizer (parse_t1 x) + in + let x = parse_s () in + read EOF; + x +;; +(* ***) +(*** eval *) +let eval g u = + Bool.eval + begin function + | Constant v -> u = v + | Pattern kind -> + match !kind with + | Brute(count, p) -> + begin + let do_nfa () = + let m = NFA.build p in + kind := Machine m; + NFA.run m u + in + if !count >= brute_limit then + do_nfa () + else + try + Brute.match_pattern count p u + with + | Brute.Too_hard -> do_nfa () + end + | Machine m -> NFA.run m u + end + g +(* ***) +(*** Debug *) +(*let (Atom(Pattern x)) = parse "<{a,b}>";; +#install_printer IS.print;; +#install_printer ISM.print;; +let (graph, closure, machine) = build' x;;*) +(* ***) diff --git a/ocamlbuild/glob.mli b/ocamlbuild/glob.mli new file mode 100644 index 0000000000..46dee1528d --- /dev/null +++ b/ocamlbuild/glob.mli @@ -0,0 +1,18 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Berke Durak *) +(* Glob *) + +(** A self-contained module implementing extended shell glob patterns who have an expressive power + equal to boolean combinations of regular expressions. *) +include Signatures.GLOB diff --git a/ocamlbuild/glob_ast.ml b/ocamlbuild/glob_ast.ml new file mode 100644 index 0000000000..2173702ecb --- /dev/null +++ b/ocamlbuild/glob_ast.ml @@ -0,0 +1,31 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Berke Durak *) +(* Glob_ast *) + +exception Parse_error of string;; + +type pattern = +| Epsilon +| Star of pattern (* The fucking Kleene star *) +| Class of character_class +| Concat of pattern * pattern +| Union of pattern list +| Word of string +and character_class = (char * char) Bool.boolean +;; + +type 'pattern atom = +| Constant of string +| Pattern of 'pattern +;; diff --git a/ocamlbuild/glob_ast.mli b/ocamlbuild/glob_ast.mli new file mode 100644 index 0000000000..a4c5a52dd8 --- /dev/null +++ b/ocamlbuild/glob_ast.mli @@ -0,0 +1,25 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Berke Durak *) +(* Glob_ast *) + +exception Parse_error of string +type pattern = +| Epsilon +| Star of pattern +| Class of character_class +| Concat of pattern * pattern +| Union of pattern list +| Word of string +and character_class = (char * char) Bool.boolean +type 'pattern atom = Constant of string | Pattern of 'pattern diff --git a/ocamlbuild/glob_lexer.mli b/ocamlbuild/glob_lexer.mli new file mode 100644 index 0000000000..aa818dda58 --- /dev/null +++ b/ocamlbuild/glob_lexer.mli @@ -0,0 +1,27 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Berke Durak *) +open Glob_ast + +type token = +| ATOM of pattern atom +| AND +| OR +| NOT +| LPAR +| RPAR +| TRUE +| FALSE +| EOF + +val token : Lexing.lexbuf -> token diff --git a/ocamlbuild/glob_lexer.mll b/ocamlbuild/glob_lexer.mll new file mode 100644 index 0000000000..8bf49900da --- /dev/null +++ b/ocamlbuild/glob_lexer.mll @@ -0,0 +1,114 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Berke Durak *) +(* Glob *) +{ +open Bool;; +open Glob_ast;; + +type token = +| ATOM of pattern atom +| AND +| OR +| NOT +| LPAR +| RPAR +| TRUE +| FALSE +| EOF +;; + +let sf = Printf.sprintf;; + +let concat_patterns p1 p2 = + match (p1,p2) with + | (Epsilon,_) -> p2 + | (_,Epsilon) -> p1 + | (_,_) -> Concat(p1,p2) +;; + +let slash = Class(Atom('/','/'));; +let not_slash = Class(Not(Atom('/','/')));; +let any = Class True;; +} + +let pattern_chars = ['a'-'z']|['A'-'Z']|'_'|'-'|['0'-'9']|'.' +let space_chars = [' ' '\t' '\n' '\r' '\012'] + +rule token = parse +| '<' { ATOM(Pattern(let (p,_) = parse_pattern ['>'] Epsilon lexbuf in p)) } +| '"' { ATOM(Constant(parse_string (Buffer.create 32) lexbuf)) } +| "and"|"AND"|"&" { AND } +| "or"|"OR"|"|" { OR } +| "not"|"NOT"|"~" { NOT } +| "true"|"1" { TRUE } +| "false"|"0" { FALSE } +| "(" { LPAR } +| ")" { RPAR } +| space_chars+ { token lexbuf } +| eof { EOF } + +and parse_pattern eof_chars p = parse +| (pattern_chars+ as u) { parse_pattern eof_chars (concat_patterns p (Word u)) lexbuf } +| '{' + { + let rec loop pl = + let (p',c) = parse_pattern ['}';','] Epsilon lexbuf in + let pl = p' :: pl in + if c = ',' then + loop pl + else + parse_pattern eof_chars (concat_patterns p (Union pl)) lexbuf + in + loop [] + } +| "[^" + { + let cl = Not(Or(parse_class [] lexbuf)) in + parse_pattern eof_chars (concat_patterns p (Class cl)) lexbuf + } +| '[' + { + let cl = Or(parse_class [] lexbuf) in + parse_pattern eof_chars (concat_patterns p (Class cl)) lexbuf + } +| "/**/" (* / | /\Sigma^*/ *) + { let q = Union[slash; Concat(slash, Concat(Star any, slash)) ] in + parse_pattern eof_chars (concat_patterns p q) lexbuf } +| "/**" (* \varepsilon | /\Sigma^* *) + { let q = Union[Epsilon; Concat(slash, Star any)] in + parse_pattern eof_chars (concat_patterns p q) lexbuf } +| "**/" (* \varepsilon | \Sigma^*/ *) + { let q = Union[Epsilon; Concat(Star any, slash)] in + parse_pattern eof_chars (concat_patterns p q) lexbuf } +| "**" { raise (Parse_error("Ambiguous ** pattern not allowed unless surrounded by one or more slashes")) } +| '*' { parse_pattern eof_chars (concat_patterns p (Star not_slash)) lexbuf } +| '/' { parse_pattern eof_chars (concat_patterns p slash) lexbuf } +| '?' { parse_pattern eof_chars (concat_patterns p (Class True)) lexbuf } +| _ as c + { if List.mem c eof_chars then + (p,c) + else + raise (Parse_error(sf "Unexpected character %C in glob pattern" c)) + } + +and parse_string b = parse +| "\"" { Buffer.contents b } +| "\\\"" { Buffer.add_char b '"'; parse_string b lexbuf } +| [^'"' '\\']+ as u { Buffer.add_string b u; parse_string b lexbuf } + +and parse_class cl = parse +| ']' { cl } +| "-]" { ((Atom('-','-'))::cl) } +| (_ as c1) '-' (_ as c2) { parse_class ((Atom(c1,c2))::cl) lexbuf } +| _ as c { parse_class ((Atom(c,c))::cl) lexbuf } diff --git a/ocamlbuild/hooks.ml b/ocamlbuild/hooks.ml new file mode 100644 index 0000000000..d1d36998e3 --- /dev/null +++ b/ocamlbuild/hooks.ml @@ -0,0 +1,26 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +type message = + | Before_hygiene + | After_hygiene + | Before_options + | After_options + | Before_rules + | After_rules + +let hooks = ref ignore + +let setup_hooks f = hooks := f + +let call_hook m = !hooks m diff --git a/ocamlbuild/hooks.mli b/ocamlbuild/hooks.mli new file mode 100644 index 0000000000..0c55e9f1a9 --- /dev/null +++ b/ocamlbuild/hooks.mli @@ -0,0 +1,23 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +type message = + | Before_hygiene + | After_hygiene + | Before_options + | After_options + | Before_rules + | After_rules + +val setup_hooks : (message -> unit) -> unit +val call_hook : message -> unit diff --git a/ocamlbuild/hygiene.ml b/ocamlbuild/hygiene.ml new file mode 100644 index 0000000000..7b6a788fc9 --- /dev/null +++ b/ocamlbuild/hygiene.ml @@ -0,0 +1,171 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Berke Durak *) +(* Hygiene *) +open My_std +open Slurp + +exception Exit_hygiene_violations + +type rule = +| Implies_not of pattern * pattern +| Not of pattern +and pattern = suffix +and suffix = string + +type penalty = Warn | Fail + +type law = { + law_name : string; + law_rules : rule list; + law_penalty : penalty +} + +let list_collect f l = + let rec loop result = function + | [] -> List.rev result + | x :: rest -> + match f x with + | None -> loop result rest + | Some y -> loop (y :: result) rest + in + loop [] l + +let list_none_for_all f l = + let rec loop = function + | [] -> None + | x :: rest -> + match f x with + | None -> loop rest + | y -> y + in + loop l + +let sf = Printf.sprintf + +module SS = Set.Make(String);; + +let check ?(sterilize=false) laws entry = + let penalties = ref [] in + let microbes = ref SS.empty in + let remove path name = + if sterilize then + microbes := SS.add (filename_concat path name) !microbes + in + let check_rule = fun entries -> function + | Not suffix -> + list_collect + begin function + | File(path, name, _, true) -> + if Filename.check_suffix name suffix then + begin + remove path name; + Some(sf "File %s in %s has suffix %s" name path suffix) + end + else + None + | File _ | Dir _| Error _ | Nothing -> None + end + entries + | Implies_not(suffix1, suffix2) -> + list_collect + begin function + | File(path, name, _, true) -> + if Filename.check_suffix name suffix1 then + begin + let base = Filename.chop_suffix name suffix1 in + let name' = base ^ suffix2 in + if List.exists + begin function + | File(_, name'', _, true) -> name' = name'' + | File _ | Dir _ | Error _ | Nothing -> false + end + entries + then + begin + remove path name'; + Some(sf "Files %s and %s should not be together in %s" name name' path) + end + else + None + end + else + None + | File _ | Dir _ | Error _ | Nothing -> None + end + entries + in + let rec check_entry = function + | Dir(_,_,_,true,entries) -> + List.iter + begin fun law -> + match List.concat (List.map (check_rule !*entries) law.law_rules) with + | [] -> () + | explanations -> + penalties := (law, explanations) :: !penalties + end + laws; + List.iter check_entry !*entries + | Dir _ | File _ | Error _ | Nothing -> () + in + check_entry entry; + begin + let microbes = !microbes in + if SS.is_empty microbes then + (entry, !penalties) + else + begin + Printf.printf "STERILIZE: do you want me to remove the following files?\n "; + SS.iter + begin fun fn -> + Printf.printf " %s" fn + end + microbes; + Printf.printf "\n"; + let rec answer () = + Printf.printf "Type YES (in uppercase) for removal, something else for no action: %!"; + let u = input_line stdin in + if u <> "" then + u + else + answer () + in + if answer () = "YES" then + begin + let success = ref true in + SS.iter + begin fun fn -> + try + Sys.remove fn + with + | x -> + success := false; + Printf.printf "ERROR removing %s: %s\n%!" fn (Printexc.to_string x) + end + microbes; + if !success then + let entry' = + Slurp.filter (fun path name _ -> not (SS.mem (My_std.filename_concat path name) microbes)) entry + in + (entry', []) (* XXX: No penalties, is this correct ? *) + else + raise Exit_hygiene_violations + end + else + begin + Printf.printf "I am taking your answer for a NO.\n"; + (entry, !penalties) + end + end + end +;; diff --git a/ocamlbuild/hygiene.mli b/ocamlbuild/hygiene.mli new file mode 100644 index 0000000000..6bed8a3c23 --- /dev/null +++ b/ocamlbuild/hygiene.mli @@ -0,0 +1,47 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Berke Durak *) +(* Hygiene *) + +(** Module for checking that the source tree is not polluted by object files. *) + +(** Sanity rules to abide. Not to be confused with compilation rules. *) +type rule = + Implies_not of pattern * pattern (** The rule [Implies_not(".mll",".ml")] is broken if there is a file [foo.mll] + together with a file [foo.ml] int the same directory. The second file can + get sterilized. *) +| Not of pattern (* No files with suffix [pattern] will be tolerated. *) + +(** Suffix matching is enough for the purpose of this module. *) +and pattern = suffix + +(** And a suffix is a string. *) +and suffix = string + +(** A warning is simply displayed. A failures stops the compilation. *) +type penalty = Warn | Fail + +(** This type is used to encode laws that will be checked by this module. *) +type law = { + law_name : string; (** The name of the law that will be printed when it is violated. *) + law_rules : rule list; (** Breaking any of these rules is breaking this law. *) + law_penalty : penalty; (** Breaking the law gives you either a warning or a failure. *) +} + +(** [check ~sterilize laws entry] will scan the directory tree [entry] for violation to the given [laws]. + Any warnings or errors will be printed on the [stdout]. If [sterilize] is true, the user will be + given the option to delete the offending files by interaction on [stdin/stdout]. This function + will return a pair [(entry', penalties)] where [entry'] is the updated directory tree (if the user + has agreed to remove offending files, for instance) and [penalties] is a list of laws and messages + describing the offenses. *) +val check : ?sterilize:bool -> law list -> bool Slurp.entry -> bool Slurp.entry * (law * string list) list diff --git a/ocamlbuild/lexers.mli b/ocamlbuild/lexers.mli new file mode 100644 index 0000000000..3d74cad93e --- /dev/null +++ b/ocamlbuild/lexers.mli @@ -0,0 +1,30 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +exception Error of string + +type conf_values = + { plus_tags : string list; + minus_tags : string list; + plus_flags : (string * string) list; + minus_flags : (string * string) list } + +type conf = (Glob.globber * conf_values) list + +val ocamldep_output : Lexing.lexbuf -> (string * string list) list +val space_sep_strings : Lexing.lexbuf -> string list +val blank_sep_strings : Lexing.lexbuf -> string list +val comma_sep_strings : Lexing.lexbuf -> string list +val colon_sep_strings : Lexing.lexbuf -> string list +val conf_lines : string option -> int -> string -> Lexing.lexbuf -> conf +val meta_path : Lexing.lexbuf -> (string * bool) list diff --git a/ocamlbuild/lexers.mll b/ocamlbuild/lexers.mll new file mode 100644 index 0000000000..1d6ae65d5a --- /dev/null +++ b/ocamlbuild/lexers.mll @@ -0,0 +1,116 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +{ +exception Error of string + +type conf_values = + { plus_tags : string list; + minus_tags : string list; + plus_flags : (string * string) list; + minus_flags : (string * string) list } + +type conf = (Glob.globber * conf_values) list + +let empty = { plus_flags = []; minus_flags = []; plus_tags = []; minus_tags = [] } + +} + +let newline = ('\n' | '\r' | "\r\n") +let space = [' ' '\t' '\012'] +let blank = newline | space +let not_blank = [^' ' '\t' '\012' '\n' '\r'] +let not_space_nor_comma = [^' ' '\t' '\012' ','] +let not_newline = [^ '\n' '\r' ] +let not_newline_nor_colon = [^ '\n' '\r' ':' ] +let normal_flag_value = [^ '(' ')' '\n' '\r'] +let normal = [^ ':' ',' '(' ')' ''' ' ' '\n' '\r'] +let tag = normal+ | ( normal+ ':' normal+ ) +let flag_name = normal+ +let flag_value = normal_flag_value+ + +rule ocamldep_output = parse + | ([^ ':' '\n' '\r' ]+ as k) ':' { let x = (k, space_sep_strings_nl lexbuf) in x :: ocamldep_output lexbuf } + | eof { [] } + | _ { raise (Error "Expecting colon followed by space-separated module name list") } + +and space_sep_strings_nl = parse + | space* (not_blank+ as word) { word :: space_sep_strings_nl lexbuf } + | space* newline { [] } + | _ { raise (Error "Expecting space-separated strings terminated with newline") } + +and space_sep_strings = parse + | space* (not_blank+ as word) { word :: space_sep_strings lexbuf } + | space* newline? eof { [] } + | _ { raise (Error "Expecting space-separated strings") } + +and blank_sep_strings = parse + | blank* '#' not_newline* newline { blank_sep_strings lexbuf } + | blank* (not_blank+ as word) { word :: blank_sep_strings lexbuf } + | blank* eof { [] } + | _ { raise (Error "Expecting blank-separated strings") } + +and comma_sep_strings = parse + | space* (not_space_nor_comma+ as word) space* eof { [word] } + | space* (not_space_nor_comma+ as word) { word :: comma_sep_strings_aux lexbuf } + | space* eof { [] } + | _ { raise (Error "Expecting comma-separated strings (1)") } +and comma_sep_strings_aux = parse + | space* ',' space* (not_space_nor_comma+ as word) { word :: comma_sep_strings_aux lexbuf } + | space* eof { [] } + | _ { raise (Error "Expecting comma-separated strings (2)") } + +and colon_sep_strings = parse + | ([^ ':']+ as word) eof { [word] } + | ([^ ':']+ as word) { word :: colon_sep_strings_aux lexbuf } + | eof { [] } + | _ { raise (Error "Expecting colon-separated strings (1)") } +and colon_sep_strings_aux = parse + | ':' ([^ ':']+ as word) { word :: colon_sep_strings_aux lexbuf } + | eof { [] } + | _ { raise (Error "Expecting colon-separated strings (2)") } + +and conf_lines dir pos err = parse + | space* '#' not_newline* newline { conf_lines dir (pos + 1) err lexbuf } + | space* newline { conf_lines dir (pos + 1) err lexbuf } + | space* eof { [] } + | space* (not_newline_nor_colon+ as k) space* ':' space* + { + let bexpr = Glob.parse ?dir k in + let v1 = conf_value pos err empty lexbuf in + let v2 = conf_values pos err v1 lexbuf in + let rest = conf_lines dir (pos + 1) err lexbuf in (bexpr, v2) :: rest + } + | _ { raise (Error(Printf.sprintf "Bad key in configuration line at line %d (from %s)" pos err)) } + +and conf_value pos err x = parse + | '-' (flag_name as t1) '(' (flag_value as t2) ')' { { (x) with minus_flags = (t1, t2) :: x.minus_flags } } + | '+'? (flag_name as t1) '(' (flag_value as t2) ')' { { (x) with plus_flags = (t1, t2) :: x.plus_flags } } + | '-' (tag as tag) { { (x) with minus_tags = tag :: x.minus_tags } } + | '+'? (tag as tag) { { (x) with plus_tags = tag :: x.plus_tags } } + | (_ | eof) { raise (Error(Printf.sprintf "Bad value in configuration line at line %d (from %s)" pos err)) } + +and conf_values pos err x = parse + | space* ',' space* { conf_values pos err (conf_value pos err x lexbuf) lexbuf } + | (newline | eof) { x } + | (_ | eof) { raise (Error(Printf.sprintf "Bad values in configuration line at line %d (from %s)" pos err)) } + +and meta_path = parse + | ([^ '%' ]+ as prefix) + { (prefix, false) :: meta_path lexbuf } + | "%(" ([ 'a'-'z' 'A'-'Z' '_' '-' '0'-'9' ]* as var) ')' + { (var, true) :: meta_path lexbuf } + | '%' + { ("", true) :: meta_path lexbuf } + | eof + { [] } diff --git a/ocamlbuild/log.ml b/ocamlbuild/log.ml new file mode 100644 index 0000000000..c0ed1f68ea --- /dev/null +++ b/ocamlbuild/log.ml @@ -0,0 +1,49 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +open My_std + +module Debug = struct +let mode _ = true +end +include Debug + +let level = ref 1 + +let classic_display = ref false +let log_file = ref (lazy None) + +let internal_display = lazy begin + let mode = + if !classic_display || !*My_unix.is_degraded || !level <= 0 || not (My_unix.stdout_isatty ()) then + `Classic + else + `Sophisticated + in + Display.create ~mode ?log_file:!*(!log_file) ~log_level:!level () +end + +let raw_dprintf log_level = Display.dprintf ~log_level !*internal_display + +let dprintf log_level fmt = raw_dprintf log_level ("@[<2>"^^fmt^^"@]@.") +let eprintf fmt = dprintf (-1) fmt + +let update () = Display.update !*internal_display +let event ?pretend x = Display.event !*internal_display ?pretend x +let display x = Display.display !*internal_display x + +let finish ?how () = + if Lazy.lazy_is_val internal_display then + Display.finish ?how !*internal_display + +(*let () = My_unix.at_exit_once finish*) diff --git a/ocamlbuild/log.mli b/ocamlbuild/log.mli new file mode 100644 index 0000000000..8fbb1da7c3 --- /dev/null +++ b/ocamlbuild/log.mli @@ -0,0 +1,34 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +(* Log *) + +(** Module for modulating the logging output with the logging level. *) +include Signatures.LOG + +(** Turn it to true to have a classic display of commands. *) +val classic_display : bool ref + +(** The optional log file. *) +val log_file : string option Lazy.t ref + +(** See {Display.event}. *) +val event : ?pretend:bool -> string -> string -> Tags.t -> unit + +(**/**) + +val internal_display : Display.display Lazy.t +val finish : ?how:[`Success|`Error|`Quiet] -> unit -> unit +val display : (out_channel -> unit) -> unit +val update : unit -> unit +val mode : string -> bool diff --git a/ocamlbuild/main.ml b/ocamlbuild/main.ml new file mode 100644 index 0000000000..59033274d3 --- /dev/null +++ b/ocamlbuild/main.ml @@ -0,0 +1,266 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Berke Durak *) +open My_std +open Log +open Pathname.Operators +open Command +open Tools +open Ocaml_specific +open Format +;; + +exception Exit_build_error of string +exception Exit_silently + +let clean () = + Shell.rm_rf !Options.build_dir; + begin + match !Options.internal_log_file with + | None -> () + | Some fn -> Shell.rm_f fn + end; + let entry = + Slurp.map (fun _ _ _ -> true) + (Slurp.slurp Filename.current_dir_name) + in + Slurp.force (Pathname.clean_up_links entry); + raise Exit_silently +;; + +let proceed () = + Hooks.call_hook Hooks.Before_options; + Options.init (); + if !Options.must_clean then clean (); + Hooks.call_hook Hooks.After_options; + Tools.default_tags := Tags.of_list !Options.tags; + Plugin.execute_plugin_if_needed (); + + if !Options.targets = [] then raise Exit_silently; + + let target_dirs = List.union [] (List.map Pathname.dirname !Options.targets) in + + let newpwd = Sys.getcwd () in + Sys.chdir Pathname.pwd; + let entry_include_dirs = ref [] in + let entry = + Slurp.filter + begin fun path name _ -> + let dir = + if path = Filename.current_dir_name then + None + else + Some path + in + let path_name = path/name in + if name = "_tags" then + ignore (Configuration.parse_file ?dir path_name); + + (String.length name > 0 && name.[0] <> '_' && not (List.mem name !Options.exclude_dirs)) + && begin + if path_name <> Filename.current_dir_name && Pathname.is_directory path_name then + let tags = tags_of_pathname path_name in + if Tags.mem "include" tags + || List.mem path_name !Options.include_dirs then + (entry_include_dirs := path_name :: !entry_include_dirs; true) + else + Tags.mem "traverse" tags + || List.exists (Pathname.is_prefix path_name) !Options.include_dirs + || List.exists (Pathname.is_prefix path_name) target_dirs + else true + end + end + (Slurp.slurp Filename.current_dir_name) + in + let hygiene_entry = + Slurp.map begin fun path name () -> + let tags = tags_of_pathname (path/name) in + not (Tags.mem "not_hygienic" tags) && not (Tags.mem "precious" tags) + end entry in + Hooks.call_hook Hooks.Before_hygiene; + let entry = + if !Options.hygiene then + Fda.inspect hygiene_entry + else + (Slurp.force hygiene_entry; hygiene_entry) + in + Hooks.call_hook Hooks.After_hygiene; + Options.include_dirs := Pathname.current_dir_name :: List.rev !entry_include_dirs; + dprintf 3 "include directories are:@ %a" print_string_list !Options.include_dirs; + Options.entry := Some entry; + + Hooks.call_hook Hooks.Before_rules; + Ocaml_specific.init (); + Hooks.call_hook Hooks.After_rules; + + Sys.chdir newpwd; + (*let () = dprintf 0 "source_dir_path_set:@ %a" StringSet.print source_dir_path_set*) + + dprintf 8 "Rules are:@ %a" (List.print Rule.print) (Rule.get_rules ()); + Resource.Cache.init (); + + Configuration.parse_string + "<**/*.ml> or <**/*.mli> or <**/*.mlpack> or <**/*.ml.depends>: ocaml + <**/*.byte>: ocaml, byte, program + <**/*.odoc>: ocaml, doc + <**/*.native>: ocaml, native, program + <**/*.cma>: ocaml, byte, library + <**/*.cmxa>: ocaml, native, library + <**/*.cmo>: ocaml, byte + <**/*.cmi>: ocaml, byte, native + <**/*.cmx>: ocaml, native + "; + + Sys.catch_break true; + + let targets = + List.map begin fun starget -> + let target = path_and_context_of_string starget in + let ext = Pathname.get_extension starget in + (target, starget, ext) + end !Options.targets in + + try + let targets = + List.map begin fun (target, starget, ext) -> + Shell.mkdir_p (Pathname.dirname starget); + let target = Solver.solve_target starget target in + (target, ext) + end targets in + + Log.finish (); + + Shell.chdir Pathname.pwd; + + let call spec = sys_command (Command.string_of_command_spec spec) in + + let cmds = + List.fold_right begin fun (target, ext) acc -> + let cmd = !Options.build_dir/target in + if ext = "byte" || ext = "native" then begin + if !Options.make_links then ignore (call (S [A"ln"; A"-sf"; P cmd; A Pathname.current_dir_name])); + cmd :: acc + end else begin + if !Options.program_to_execute then + eprintf "Warning: Won't execute %s whose extension is neither .byte nor .native" cmd; + acc + end + end targets [] in + + if !Options.program_to_execute then + begin + match List.rev cmds with + | [] -> raise (Exit_usage "Using -- requires one target"); + | cmd :: rest -> + if rest <> [] then dprintf 0 "Warning: Using -- only run the last target"; + let cmd_spec = S [P cmd; atomize !Options.program_args] in + dprintf 3 "Running the user command:@ %a" Pathname.print cmd; + raise (Exit_with_code (call cmd_spec)) (* Exit with the exit code of the called command *) + end + else + () + with + | Ocaml_dependencies.Circular_dependencies(seen, p) -> + raise + (Exit_build_error + (sbprintf "@[<2>Circular dependencies: %S already seen in@ %a@]@." p pp_l seen)) +;; + +module Exit_codes = + struct + let rc_ok = 0 + let rc_usage = 1 + let rc_failure = 2 + let rc_invalid_argument = 3 + let rc_system_error = 4 + let rc_hygiene = 1 + let rc_circularity = 5 + let rc_solver_failed = 6 + let rc_ocamldep_error = 7 + let rc_lexing_error = 8 + let rc_build_error = 9 + let rc_executor_reserved_1 = 10 (* Redefined in Executor *) + let rc_executor_reserved_2 = 11 + let rc_executor_reserved_3 = 12 + let rc_executor_reserved_4 = 13 + end + +open Exit_codes;; + +let main () = + let exit rc = + Log.finish ~how:(if rc <> 0 then `Error else `Success) (); + Pervasives.exit rc + in + try + proceed () + with + | Exit_OK -> exit rc_ok + | Fda.Exit_hygiene_failed -> + Log.eprintf "Exiting due to hygiene violations (try -sterilize)."; + exit rc_hygiene + | Exit_usage u -> + Log.eprintf "Usage:@ %s." u; + exit rc_usage + | Exit_system_error msg -> + Log.eprintf "System error:@ %s." msg; + exit rc_system_error + | Exit_with_code rc -> + exit rc + | Exit_silently -> + Log.finish ~how:`Quiet (); + Pervasives.exit rc_ok + | Exit_silently_with_code rc -> + Log.finish ~how:`Quiet (); + Pervasives.exit rc + | Solver.Failed backtrace -> + Log.raw_dprintf (-1) "@[<v0>@[<2>Solver failed:@ %a@]@\n@[<v2>Backtrace:%a@]@]@." + Report.print_backtrace_analyze backtrace Report.print_backtrace backtrace; + exit rc_solver_failed + | Failure s -> + Log.eprintf "Failure:@ %s." s; + exit rc_failure + | Solver.Circular(r, rs) -> + Log.eprintf "Circular build detected@ (%a already seen in %a)" + Resource.print r (List.print Resource.print) rs; + exit rc_circularity + | Invalid_argument s -> + Log.eprintf + "INTERNAL ERROR: Invalid argument %s\n\ + This is likely to be a bug, please report this to the ocamlbuild\n\ + developers." s; + exit rc_invalid_argument + | Ocamldep.Error msg -> + Log.eprintf "Ocamldep error: %s" msg; + exit rc_ocamldep_error + | Lexers.Error msg -> + Log.eprintf "Lexical analysis error: %s" msg; + exit rc_lexing_error + | Arg.Bad msg -> + Log.eprintf "%s" msg; + exit rc_usage + | Exit_build_error msg -> + Log.eprintf "%s" msg; + exit rc_build_error + | Arg.Help msg -> + Log.eprintf "%s" msg; + exit rc_ok + | e -> + try + Log.eprintf "%a" My_unix.report_error e; + exit 100 + with + | e -> + Log.eprintf "Exception@ %s." (Printexc.to_string e); + exit 100 +;; diff --git a/ocamlbuild/main.mli b/ocamlbuild/main.mli new file mode 100644 index 0000000000..a7a816ea79 --- /dev/null +++ b/ocamlbuild/main.mli @@ -0,0 +1,14 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +val main : unit -> unit diff --git a/ocamlbuild/man/ocamlbuild.1 b/ocamlbuild/man/ocamlbuild.1 new file mode 100644 index 0000000000..aa75159466 --- /dev/null +++ b/ocamlbuild/man/ocamlbuild.1 @@ -0,0 +1,253 @@ +.TH OCAMLBUILD 1 + +.SH NAME +ocamlbuild \- The Objective Caml project compilation tool + + +.SH SYNOPSIS +.B ocamlbuild +[ +.B \-Is \ dir1,... +] +[ +.BI \-libs \ lib1,... +] +[ +.BI \-lflags \ flag1,... +] +[ +.BI \-pp \ flags +] +[ +.BI \-tags \ tag1,... +] +[ +.B \-j \ parallel-jobs +] +.I target.native +[ +.B \-\- arg1 arg2 ... +] + +.I (same options) + +.SH DESCRIPTION + +.BR ocamlbuild (1) +orchestrates the compilation process of your OCaml project. It is similar +in function to +.BR make (1) +except that it is tailor-made to automatically compile most OCaml projects +with very little user input. + +.BR ocamlbuild +should be invoked in the root of a clean project tree (e.g., with no leftover +compilation files). Given one or more targets to compile, it scans the required +subdirectories to gather information about the various files present, running +tools such as +.BR ocamldep (1) +to extract dependency information, and gathering optional files that fine-tune +its behaviour. +Target names are very significant. + +.SH TARGET NAMES +.BR ocamlbuild +uses a set of target naming conventions to select the kind of objects to +produce. Target names are of the form +.BR base.extension +where +.BR base +is usually the name of the underlying Ocaml module and +.BR extension +denotes the kind of object to produce from that file -- a byte code executable, +a native executable, documentation... +Of course extensions such as +.BR .cmo, +.BR .cma, +.BR .cmi... +map to their usual counterparts. Here is a list of the most important +.BR ocamlbuild \&-specific +extensions: + +.TP 2i +.B .native +Native code executable + +.TP 2i +.B .byte +Byte code executable + +.TP 2i +.B .inferred.mli +Interface inferred with +.BR ocamlc -i + +.TP 2i +.B .docdir/index.html +HTML documentation generated with +.BR ocamldoc + +.PP + +.SH OPTIONS + +The following command-line options are recognized by +.BR ocamlbuild (1). + +.TP +\fB\-version\fR +Display the version +.TP +\fB\-verbose\fR +Turn on the verbose mode +.TP +\fB\-quiet\fR +Make as quiet as possible +.TP +\fB\-debug\fR <level> +Set the debug level +.TP +\fB\-log\fR <file> +Set log file +.TP +\fB\-no\-log\fR +No log file +.TP +\fB\-clean\fR +Remove build directory and other files, then exit +.TP +\fB\-I\fR <path> +Add to include directories +.TP +\fB\-Is\fR <path,...> +(same as above, but accepts a comma\-separated list) +.TP +\fB\-X\fR <path> +Directory to ignore +.TP +\fB\-Xs\fR <path,...> +(idem) +.TP +\fB\-lib\fR <flag> +Link to this ocaml library +.TP +\fB\-libs\fR <flag,...> +(idem) +.TP +\fB\-lflag\fR <flag> +Add to ocamlc link flags +.TP +\fB\-lflags\fR <flag,...> +(idem) +.TP +\fB\-cflag\fR <flag> +Add to ocamlc compile flags +.TP +\fB\-cflags\fR <flag,...> +(idem) +.TP +\fB\-yaccflag\fR <flag> +Add to ocamlyacc flags +.TP +\fB\-yaccflags\fR <flag,...> +(idem) +.TP +\fB\-lexflag\fR <flag> +Add to ocamllex flags +.TP +\fB\-lexflags\fR <flag,...> +(idem) +.TP +\fB\-ppflag\fR <flag> +Add to ocaml preprocessing flags +.TP +\fB\-pp\fR <flag,...> +(idem) +.TP +\fB\-tag\fR <tag> +Add to default tags +.TP +\fB\-tags\fR <tag,...> +(idem) +.TP +\fB\-ignore\fR <module,...> +Don't try to build these modules +.TP +\fB\-no\-links\fR +Don't make links of produced final targets +.TP +\fB\-no\-skip\fR +Don't skip modules that are requested by ocamldep but cannot be built +.TP +\fB\-no\-hygiene\fR +Don't apply sanity\-check rules +.TP +\fB\-no\-plugin\fR +Don't build myocamlbuild.ml +.TP +\fB\-no\-stdlib\fR +Don't ignore stdlib modules +.TP +\fB\-just\-plugin\fR +Just build myocamlbuild.ml +.TP +\fB\-byte\-plugin\fR +Don't use a native plugin but bytecode +.TP +\fB\-sterilize\fR +Enforce sanity\-check rules by removing leftover files (DANGER) +.TP +\fB\-nothing\-should\-be\-rebuilt\fR +Fail if something needs to be rebuilt +.TP +\fB\-classic\-display\fR +Display executed commands the old\-fashioned way +.TP +\fB\-j\fR <N> +Allow N jobs at once (0 for unlimited) +.TP +\fB\-build\-dir\fR <path> +Set build directory +.TP +\fB\-install\-dir\fR <path> +Set the install directory +.TP +\fB\-where\fR +Display the install directory +.TP +\fB\-ocamlc\fR <command> +Set the OCaml bytecode compiler +.TP +\fB\-ocamlopt\fR <command> +Set the OCaml native compiler +.TP +\fB\-ocamldep\fR <command> +Set the OCaml dependency tool +.TP +\fB\-ocamlyacc\fR <command> +Set the ocamlyacc tool +.TP +\fB\-ocamllex\fR <command> +Set the ocamllex tool +.TP +\fB\-ocamlrun\fR <command> +Set the ocamlrun tool +.TP +\fB\-\-\fR +Stop argument processing, remaining arguments are given to the user program +.TP +\fB\-help\fR +Display the list of options +.TP +\fB\-\-help\fR +Display the list of options +.PP + +.SH SEE ALSO +The +.BR ocamlbuild +manual, +.BR ocaml (1), +.BR make (1). +.br +.I The Objective Caml user's manual, chapter "Batch compilation". diff --git a/ocamlbuild/manual/Makefile b/ocamlbuild/manual/Makefile new file mode 100644 index 0000000000..4c2ef88b2c --- /dev/null +++ b/ocamlbuild/manual/Makefile @@ -0,0 +1,11 @@ +# Makefile + +manual.pdf: manual.tex + +%.pdf: %.tex + pdflatex $< + +.PHONY: clean + +clean: + rm -f *.pdf *.log *.aux *.ps *.dvi diff --git a/ocamlbuild/manual/manual.tex b/ocamlbuild/manual/manual.tex new file mode 100644 index 0000000000..c255e9a220 --- /dev/null +++ b/ocamlbuild/manual/manual.tex @@ -0,0 +1,943 @@ +%(*** preamble +\documentclass[12pt]{article} +\usepackage[utf8]{inputenc} +\usepackage{palatino} +\usepackage{mathrsfs} +\usepackage[T1]{fontenc} +\usepackage[english]{babel} +\usepackage[a4paper,lmargin=1cm,rmargin=1cm,tmargin=1cm,bmargin=1cm]{geometry} +%***) +%(*** title +\begin{document} +\newcommand{\ocb}{\texttt{ocamlbuild}~} +\newcommand{\tags}{\texttt{\_tags}~} +\title{\ocb, a tool for \\ automatic compilation \\ of OCaml projects} +\author{Nicolas \textsc{Pouillard}, Berke \textsc{Durak}} +\date{January 2007} +\maketitle +%***) +%(*** abstract +\abstract{ + +} +%***) +%(*** Motivations +\section{Motivations} +{\em This section describes the frustation that led us to write \ocb.} + +Many people have painfully found that the utilities of the \texttt{make} +family, namely GNU Make, BSD Make, and their derivatives, fail to scale to +large projects, especially when using multi-stage compilation rules, such as +custom pre-processors, unless dependencies are hand-defined. But as your +project gets larger, more modular, and uses more diverse pre-processing tools, +it becomes increasingly difficult to correctly define dependencies by hand. +Hence people tend to use language-specific tools that attempt to extract +dependencies. However another problem then appears: \texttt{make} was designed +with the idea of a static dependency graph. Dependency extracting tools, +however, are typically run by a rule in \texttt{make} itself; this means that +make has to reload the dependency information. This is the origin of the +\texttt{make clean; make depend; make} mantra. This approach tends to work +quite well as long as all the files sit in a single directory and there is only +one stage of pre-processing. If there are two or more stages, then dependency +extracting tools must be run two or more times - and this means multiple +invocations of \texttt{make}. Also, if one distributes the modules of a large +project into multiple subdirectories, it becomes difficult to distribute the +makefiles themselves, because the language of \texttt{make} was not conceived +to be modular; the only two mechanisms permitted, inclusion of makefile +fragments, and invocation of other make instances, must be skillfully +coordinated with phony target names (\texttt{depend1, depend2...}) to insure +inclusion of generated dependencies with multi-stage programming; changes in +the structure of the project must be reflected by hand and the order of +variable definitions must be well-thought ahead to avoid long afternoons spent +combinatorially fiddling makefiles until it works but no one understands why. + +These problems become especially apparent with OCaml: to ensure type safety and +to allow a small amount of cross-unit optimization when compiling native code, +interface and object files include cryptographical digests of interfaces they +are to be linked with. This means that linking is safer, but that makefile sloppiness +leads to messages such as: +\begin{verbatim} +Files foo.cmo and bar.cmo +make inconsistent assumptions over interface Bar +\end{verbatim} + +The typical reaction is then to issue the mantra \texttt{make clean; make +depend; make} and everything compiles just fine... from the beginning. Hence +on medium projects, the programmer often has to wait for minutes instead of the +few seconds that would be taken if \texttt{make} could correctly guess the +small number of files that really had to be recompiled. + +It is not surprising that hacking a build tool such as \texttt{make} to include +a programming language while retaining the original syntax and semantics gives +an improvised and cumbersome macro language of dubious expressive power. For +example, using GNU make, suppose you have a list of \texttt{.ml}s that you want +to convert into a list including both \texttt{.cmo}s and \texttt{.cmi}s, that +is you want to transform \texttt{a.ml b.ml c.ml} into \texttt{a.cmi a.cmo b.cmi +b.cmo c.cmi c.cmo} while preserving the dependency order which must be hand +specified for linking \footnote{By the way, what's the point of having a +declarative language if \texttt{make} can't sort the dependencies in +topological order for giving them to \texttt{gcc} or whatever ?}. +Unfortunately \texttt{\$patsubst \%.ml, \%.cmi \%.cmo, a.ml b.ml c.ml} won't +work since the \%-sign in the right-hand of a \texttt{patsubst} gets +substituted only once. You then have to delve into something that is hardly +lambda calculus: an intricate network of \texttt{foreach}, \texttt{eval}, +\texttt{call} and \texttt{define}s may get you the job done, unless you chicken +out and opt for an external \texttt{awk}, \texttt{sed} or \texttt{perl} call. +People who at this point have not lost their temper or sanity usually resort to +metaprogramming by writing Makefile generators using a mixture of shell and m4. +One such an attempt gave something that is the nightmare of wannabe package +maintainers: it's called \texttt{autotools}. + +Note that it is also difficult to write \texttt{Makefiles} to build object +files in a separate directory. It is not impossible since the language of +\texttt{make} is Turing-complete, a proof of which is left as an exercise. +Note that building things in a separate directory is not necessarily a young +enthousiast's way of giving a different look and feel to his projects -- it may +be a good way of telling the computer that \texttt{foo.mli} is generated by +\texttt{ocamlyacc} using \texttt{foo.mly} and can thus be removed. +%***) +%(*** Features of ocamlbuild +\section{Features of \ocb} +{\em This section is intended to read like a sales brochure or a datasheet.} + +\begin{itemize} +\item Built-in compilation rules for OCaml projects handle all the nasty cases : native and bytecode, +missing \texttt{.mli} files, preprocessor rules, debugging and profiling flags, C stubs. +\item Plugin mechanism for writing compilation rules and actions in a real programming language, +OCaml itself. +\item Automatic inference of dependencies +\item Correct handling of dynamically discovered dependencies +\item Object files and other temporary files are created in a specific directory, leaving your main directory uncluttered. +\item Sanity checks ensure that object files are where they are supposed to be: in the build directory. +\item Simple projects are built using a single command with no extra files. +\item Parallel compilation to speed up things on multi-core systems. +\item Sophisticated display mode to keep your screen free of boring and repetitive compilation message +while giving you important progress information in a glimpse, and correctly multiplexing the error messages. +\item Tags and flags provide a concise and convenient mechanism for automatic selection of compilation, preprocessing and +other options +\item Extended shell-like glob patterns, that can be combined using boolean operators, +allow you to concisely define the tags that apply to a given file. +\item Mechanisms for defining the mutual visibility of subdirectories. +\item Cache mechanism avoiding unnecessary compilations where reasonably computable. +\end{itemize} +%***) +%(*** Limitations +\section{Limitations} +{\em Not perfect nor complete yet, but already pretty damn useful.} + +We were not expecting to write the ultimate compilation tool in a few man-months, however we believe we have +a tool that solves many compilation problems, especially our own, in a satisfactory way. Hence there are a +lot of missing features, incomplete options and hideous bugs lurking in \ocb, and we hope that the OCaml community +will find our first try at \ocb useful and hopefully help it grow into a tool that satisfies most needs of most users +by providing feedback, bug reports and patches. + +The plugin API is lacking in maturity, as it has only been tested by the +authors. We believe a good API can only evolve under pressure from many peers +and the courage to rewrite things cleanly when time is ripe by the developers. + +Nonetheless the parts of \ocb that do not rely on the plugin API, namely, the command-line +options, the \texttt{\_tags} file, the target names, names of the tags, and so +on, are not expected to change in incompatible ways. We intend to keep a project that compiles +without a plugin compilable without modifications in the future. + +As for plugins, we recommend that users isolate calls to the \ocb library from their logic +to be able to keep the later when incompatible API changes arise. + +\begin{itemize} +\item Plugin API not stable +\item No built-in rules for creating toplevels or librairies +\end{itemize} +%***) +%(*** Using ocamlbuild +\section{Using \ocb} +{\em Learn how to use \ocb with short, specific, straight-to-the-point examples.} + +The amount of time and effort spent on the compilation process of a project +should be proportionate to that spent on the project itself. It should be easy +to set up a small project, maybe a little harder for a medium-sized project, +and it may take some more time, but not too much, for a big project. Ideally +setting up a big project would be as easy as setting up a small project. However, +as projects grow, modularization techniques start to be used, and the probability +of using meta programming or multiple programming languages increases, thus making +the compilation process more delicate. + +\ocb is intended to be very easy to use for projects, large or small, with a simple +compilation process: typing +\texttt{ocamlbuild foo.native} should be enough to compile the native version +of a program whose top module is \texttt{foo.ml} and whose dependencies are in +the same directory. As your project gets more complex, you will gradually +start to use command-line options to specify libraries to link with, then +configuration files, ultimately culminating in a custom OCaml plugin for +complex projects with arbitrary dependencies and actions. + +%(*** Hygiene *) +\subsection{Hygiene \& where is my code ?} +Your code is in the \texttt{\_build} directory, but \ocb automatically creates +a symbolic link to the executables it produces in the current directory. +\ocb copies the source files and compiles them in a separate directory +which is \texttt{\_build} by default. + +For \ocb, any file that is not in the build directory is a source file. +It is not unreasonable to think that some users may have bought binary object files +they keep in their project directory. Usually binary files cluttering the project +directory are due to previous builds using other systems. \ocb has so-called +``hygiene'' rules that state that object files (\texttt{.cmo}, \texttt{.cmi}, +or \texttt{.o} files, for instance) must not appear outside of the build +directory. These rules are enforced at startup; any violations will be reported +and \ocb will exit. You must then remove these files by hand or use, with caution, +the \texttt{-sterilize} option which will remove them for you, after prompting +you for confirmation. + +To disable these checks, you can use the \texttt{-no-hygiene} flag. If you have +files that must elude the hygiene squad, just tag them with \texttt{precious} +or \texttt{not\_hygienic}. +%***) +%(*** Hello, world ! +\subsection{Hello, world !} +Assuming we are in a directory named \texttt{example1} containing one file \texttt{hello.ml} +whose contents are +\begin{verbatim} +let _ = + Printf.printf "Hello, %s ! My name is %s\n" + (if Array.length Sys.argv > 1 then Sys.argv.(1) else "stranger") + Sys.argv.(0) +;; +\end{verbatim} +we can compile and link it into a native executable by invoking \texttt{ocamlbuild hello.native}. +Here, \texttt{hello} is the basename of the top-level module and \texttt{native} is an extension used +by \ocb to denote native code executables. +\begin{verbatim} +% ls +hello.ml +% ocamlbuild hello.native +Finished, 4 targets (0 cached) in 00:00:00. +% ls -l +total 12 +drwxrwx--- 2 linus gallium 4096 2007-01-17 16:24 _build/ +-rw-rw---- 1 linus gallium 43 2007-01-17 16:23 hello.ml +lrwxrwxrwx 1 linus gallium 19 2007-01-17 16:24 hello.native -> _build/hello.native* +-rw-r----- 1 linus gallium 460 2007-01-17 16:24 _log +\end{verbatim} +What's this funny \texttt{\_build} directory ? Well that's where \ocb does its dirty work +of compiling. You usually won't have to look very often into this directory. Source files are be copied +into \texttt{\_build} and this is where the compilers will be run. Various cache files are also stored +there. Its contents may look like this: +\begin{verbatim} +% ls -l _build +total 208 +-rw-rw---- 1 linus gallium 337 2007-01-17 16:24 _digests +-rw-rw---- 1 linus gallium 191 2007-01-17 16:24 hello.cmi +-rw-rw---- 1 linus gallium 262 2007-01-17 16:24 hello.cmo +-rw-rw---- 1 linus gallium 225 2007-01-17 16:24 hello.cmx +-rw-rw---- 1 linus gallium 43 2007-01-17 16:23 hello.ml +-rw-rw---- 1 linus gallium 17 2007-01-17 16:24 hello.ml.depends +-rwxrwx--- 1 linus gallium 173528 2007-01-17 16:24 hello.native* +-rw-rw---- 1 linus gallium 936 2007-01-17 16:24 hello.o +-rw-rw---- 1 linus gallium 22 2007-01-17 16:24 ocamlc.where +\end{verbatim} +%***) +%(*** Executing my code +\subsection{Executing my code} +You can execute your code the old-fashioned way (\texttt{./hello.native}). +You may also type +\begin{verbatim} +ocamlbuild main.native -- Caesar +\end{verbatim} +and it will compile and then run \texttt{main.native} with the arguments following \texttt{-{}-}, +which should display: +\begin{verbatim} +% ocamlbuild hello.native -- Caesar +Finished, 4 targets (0 cached) in 00:00:00. +Hello, Caesar ! My name is _build/hello.native +\end{verbatim} +%***) +%(*** The log file, verbosity and debugging +\subsection{The log file, verbosity and debugging} +By default, if you run \ocb on a terminal, it will use some ANSI escape sequences +to display a nice, one-line progress indicator. To see what commands \ocb has actually run, +you can check the contents of the \texttt{\_log} file. To change the name of the +log file or to disable logging, use the \texttt{-log <file>} or \texttt{-no-log} options. +Note that the log file is truncated at each execution of \ocb. + +The log file contains all the external commands that \ocb ran or intended to +run along with the target name and the computed tags. With the +\texttt{-verbose <level>} option, \ocb will also write more or less useful +debugging information; a verbosity level of $1$ (which can also be specified +using the \texttt{-verbose} switch) prints generally useful information; higher +levels produce much more output. +%***) +%(*** Cleaning +\subsection{Cleaning} +\ocb may leave a \texttt{\_build} directory, symbolic links to executables in +that directory, and a \texttt{\_log} file. All of these can be removed safely +by hand, or by invoking \ocb with the \texttt{-clean} flag. +%***) +%(*** Where and how to run \ocb +\subsection{Where and how to run \ocb ?} +An important point is that \ocb must be invoked from the root of the project, +even if this project has multiple, nested subdirectories. This is because \ocb +likes to store the object files in a single \texttt{\_build} directory. You +can change the name of that directory with the \texttt{-build-dir} option. + +\ocb can be either invoked manually from the UNIX or Windows shell, or +automatically from a build script or a Makefile. Unless run with the +\texttt{-no-hygiene} option, there is the possibility that \ocb will prompt the +user for a response. By default, on UNIX systems, if \ocb senses that the +standard output is a terminal, it will use a nice progress indicator using ANSI +codes, instrumenting the output of the processes it spawns to have a consistent +display. Under non-UNIX systems, or if the standard output is not a terminal, +it will run in classic mode where it will echo the executed commands on its +standard output. This selection can be overridden with the \texttt{-classic-display} option. +%***) +%(*** Dependencies +\subsection{Dependencies} +{\em Dependencies are automatically discovered.} + +Most of the value of \ocb lies in the fact that it often needs no extra +information to compile a project besides the name of the top-level module. +\ocb calls \texttt{ocamldep} to automatically find the dependencies of any +modules it wants to compile. These dependencies are dynamically incorporated +in the depdendency graph, something \texttt{make} cannot do. +For instance, let's add a module \texttt{Greet} that implements various ways of +greeting people. +\begin{verbatim} +% cat greet.ml +type how = Nicely | Badly;; + +let greet how who = + match how with Nicely -> Printf.printf "Hello, %s !\n" who + | Badly -> Printf.printf "Oh, here is that %s again.\n" who +;; +% cat hello.ml +open Greet + +let _ = + let name = + if Array.length Sys.argv > 1 then + Sys.argv.(1) + else + "stranger" + in + greet + (if name = "Caesar" then Nicely else Badly) + name; + Printf.printf "My name is %s\n" Sys.argv.(0) +;; +\end{verbatim} +Then the module \texttt{Main} depends on the module \texttt{Greet} and \ocb can +figure this out for himself -- we still only have to invoke \texttt{\ocb +main.native}. Needless to say, this works for any number of modules. +%***) +%(*** Native and byte code +\subsection{Native and bytecode} +If we want to compile bytecode instead of native, we just a target name of +\texttt{main.byte} instead of \texttt{main.native}, i.e., we type +\texttt{\ocb hello.byte}. +%***) +%(*** Compile flags +\subsection{Compile flags} +To pass a flag to the compiler, such as the \texttt{-rectypes} option, +use the \texttt{-cflag} option as in : +\begin{verbatim} +ocamlbuild -cflag -rectypes hello.native +\end{verbatim} +You can put multiple \texttt{-cflag} options, they will be passed to the compiler +in the same order. You can also given them in a comma-separated list with the +\texttt{-cflags} option (notice the plural): +\begin{verbatim} +ocamlbuild -cflags -I,+lablgtk,-rectypes hello.native +\end{verbatim} +These flags apply when compiling, that is, when producing \texttt{.cmi}, +\texttt{.cmo},\texttt{.cmx} and \texttt{.o} files from \texttt{.ml} or +\texttt{.mli} files. +%***) +%(*** Link flags +\subsection{Link flags} +Link flags apply when the various object files are collected and linked into +one executable. These will typically be include directories for libraries. +They are given using the \texttt{-lflag} and \texttt{-lflags} options, which +work in the same way as the \texttt{-cflag} and \texttt{-cflags} options. +%***) +%(*** Linking with external libraries +\subsection{Linking with external libraries} +In our third example, we use one Unix system call and functions from the \texttt{num} +library: +\begin{verbatim} +% cat epoch.ml +let _ = + let s = Num.num_of_string (Printf.sprintf "%.0f" (Unix.gettimeofday ())) in + let ps = Num.mult_num (Num.num_of_string "1000000000000") s in + Printf.printf "%s picoseconds have passed since January 1st, 1970.\n" + (Num.string_of_num ps) +;; +\end{verbatim} +This requires linking with the \texttt{unix} and \texttt{num} modules, which is accomplished +by using the \texttt{-lib unix} and \texttt{-lib num} flags, or, alternatively, \texttt{-libs unix,num}: +\begin{verbatim} +% ocamlbuild -libs nums,unix epoch.native -- a +Finished, 4 targets (4 cached) in 00:00:00. +1169051647000000000000 picoseconds have passed since January 1st, 1970. +\end{verbatim} +You may need to add options such as \texttt{-cflags -I,/usr/local/lib/ocaml/} +and \texttt{-lflags -I,/usr/local/lib/ocaml/} if the librairies you wish to +link with are not in OCaml's default search path. +%***) +%(*** The _tags files +\subsection{The \tags files} +Finer control over the compiler flags applied to each source file, such as +preprocessing, debugging, profiling and linking options, can be gained using +\ocb's tagging mechanism. + +Every source file has a set of tags which tells \ocb what kind of file it is +and what to do with it. A tag is simply a string, usually lowercase, for +example \texttt{ocaml} or \texttt{native}. The set of tags attached to a file +is computed by applying the tagging rules to the filename. Tagging rules are +defined in \tags files in any parent directory of a file, up to the main +project directory. + +Each line in the \tags file is made of a glob pattern (see subsection +\ref{subsec:glob}) and a list of tags. More than one rule can apply to a file +and rules are applied in the order in which they appear in a file. +By preceding a tag with a minus sign, one may remove tags from one or more files. + +\subsubsection{Example: the built-in \tags file} +\begin{verbatim} + "<**/*.ml> or <**/*.mli> or <**/*.mlpack> or <**/*.ml.depends>: ocaml + <**/*.byte>: ocaml, byte, program + <**/*.odoc>: ocaml, doc + <**/*.native>: ocaml, native, program + <**/*.cma>: ocaml, byte, library + <**/*.cmxa>: ocaml, native, library + <**/*.cmo>: ocaml, byte + <**/*.cmi>: ocaml, byte, native + <**/*.cmx>: ocaml, native +\end{verbatim} + +A special tag made from the path name of the file relative to the toplevel +of the project is automatically defined for each file. For a file +\texttt{foo/bar.ml} this tag wil be \texttt{file:foo/bar.ml}. + +If you do not have subdirectories, you can put \texttt{*.ml} instead of +\texttt{**/*.ml}. +%***) +%(*** Glob patterns and expressions +\subsection{Glob patterns and expressions} +\label{subsec:glob} +Glob patterns have a syntax similar to those used by UNIX shells to select path +names (like \texttt{foo\_\*\.ba?}). They are used in \ocb to define the files +and directories to which tags apply. Glob expressions are glob patterns +enclosed in brackets \texttt{<} and \texttt{>} combined using the standard +boolean operators \texttt{and}, \texttt{or}, \texttt{not}. This allows one to +describe sets of path names in more concise and more readable ways. + +Please note that file and directory names are supposed to be made of the +following characters: $\texttt{a}$, $\dots$, $\texttt{z}$, $\texttt{A}$, +$\dots$, $\texttt{Z}$, $\texttt{0}$, $\dots$, $\texttt{9}$, $\texttt{\_}$, +$\texttt{-}$ and $\texttt{.}$. This is called the pathname alphabet $P$. + +\begin{table}[h] + \begin{center} + \small + \begin{tabular}{|p{3cm}|l|p{3cm}|p{3cm}|p{5cm}|} + \hline + {\em Formal syntax} & + {\em Example} & {\em Matches} & {\em Doesn't match} & + {\em Meaning (formal meaning)} \\ + \hline + \hline +%% + {$u$ \vskip 0.5em A string of pathname characters} & + \texttt{foo.ml} & + \texttt{foo.ml} & + \texttt{fo.ml}, \texttt{bar/foo.ml} & + The exact string $u$ + ($\{ u \}$, where $u \in P^*$) \\ + \hline +%% + {\texttt{*} \vskip 0.5em The wildcard star}& + \texttt{*}& + $\varepsilon$, \texttt{foo}, \texttt{bar} & + \texttt{foo/bar}, \texttt{/bar} & + Any string not containing a slash + ($P^*$) \\ + \hline +%% + {\texttt{?} \vskip 0.5em The joker}& + \texttt{?}& + \texttt{a}, \texttt{b}, \texttt{z} & + \texttt{/}, \texttt{bar} & + Any one-letter string, excluding the slash \\ + \hline +%% + {\texttt{**/} \vskip 0.5em The prefix interdirectory star}& + \texttt{**/foo.ml}& + \texttt{foo.ml}, \texttt{bar/foo.ml}, \texttt{bar/baz/foo.ml} & + \texttt{foo/bar}, \texttt{/bar} & + The empty string, or any string ending with a slash + ($\varepsilon \cup P^*\mathtt{/}$) \\ + \hline +%% + {\texttt{/**} \vskip 0.5em The suffix interdirectory star}& + \texttt{foo/**}& + \texttt{foo}, \texttt{foo/bar} & + \texttt{bar/foo} & + Any string starting with a slash, or the empty string. + ($\varepsilon \cup \mathtt{/}P^*$) \\ + \hline +%% + {\texttt{/**/} \vskip 0.5em The infix interdirectory star}& + \texttt{bar/**/foo.ml}& + \texttt{bar/foo.ml}, \texttt{bar/baz/foo.ml} & + \texttt{foo.ml} & + Any string starting and ending with a slash + ($\varepsilon \cup \mathtt{/}P^*\mathtt{/}$) \\ + \hline +%% + {$\mathtt{[} r_1 r_2 \cdots r_k \mathtt{]}$ + where $r_i$ is either $c$ or $c_1-c_2$ $(1 \leq i \leq k)$ + \vskip 0.5em The positive character class}& + \texttt{[a-fA-F0-9\_.]}& + \texttt{3}, \texttt{F}, \texttt{.} & + \texttt{z}, \texttt{bar} & + Any one-letter string made of characters from one of the ranges + $r_i$ ($1 \leq i \leq n$). + ($\mathscr L(r_1) \cup \cdots \cup \mathscr L(r_n)$) \\ + \hline +%% + {\texttt{[\char`\^}$r_1 r_2 \cdots r_k \mathtt{]}$ + where $r_i$ is either $c$ or $c_1-c_2$ $(1 \leq i \leq k)$ + \vskip 0.5em The negative character class}& + \texttt{[\char`\^a-fA-F0-9\_.]}& + \texttt{z}, \texttt{bar} & + \texttt{3}, \texttt{F}, \texttt{.} & + Any one-letter string NOT made of characters from one of the ranges + $r_i$ ($1 \leq i \leq n$). + ($\Sigma^* \setminus \left(\mathscr L(r_1) \cup \cdots \cup \mathscr L(r_n)\right)$) \\ + \hline +%% + {$p_1 p_2$ \vskip 0.5em A concatenation of patterns}& + \texttt{foo*}& + \texttt{foo}, \texttt{foob}, \texttt{foobar} & + \texttt{fo}, \texttt{bar} & + Any string with a prefix matching $p_1$ and the corresponding suffix + matching $p_2$, + ($\{ uv \mid u \in \mathscr L(p_1), v \in \mathscr L(p_2) \}$) \\ + \hline +%% + {$\mathtt{\{} p_1 \mathtt{,} p_2 \mathtt{,} \cdots \mathtt{,} p_k \mathtt{\}}$ \vskip 0.5em A union of patterns}& + \texttt{toto.\{ml,mli\}}& + \texttt{toto.ml}, \texttt{toto.mli} & + \texttt{toto.} & + Any string matching one of the patterns $p_i$ for $1 \leq i \leq k$. + ($\mathscr L(p_1) \cup \cdots \cup \mathscr L(p_k)$) \\ + \hline +%% + \end{tabular} + \end{center} + \caption{ + Syntax and semantics of glob patterns. + } +\end{table} +\begin{table} + \begin{center} + \small + \begin{tabular}{|p{2cm}|l|p{7cm}|} + \hline + {\em Formal syntax} & + {\em Example} & + {\em Meaning (formal meaning)} \\ + \hline + \hline + {$\mathtt{<}p\mathtt{>}$} & + \texttt{<foo.ml>} & + Pathnames matching the pattern $p$ \\ + \hline + {$e_1 \; \mathtt{or} \; e_2$} & + \texttt{<*.ml> or <foo/bar.ml>} & + Pathnames matching at least one of the expressions $e_1$ and $e_2$ \\ + \hline + {$e_1 \; \mathtt{and} \; e_2$} & + \texttt{<*.ml> and <foo\_*>} & + Pathnames matching both expressions $e_1$ and $e_2$ \\ + \hline + {$\mathtt{not} \; e$} & + \texttt{not <*.mli>} & + Pathnames not matching the expression $e$ \\ + \hline + {$\mathtt{true}$} & + \texttt{true} & + All pathnames \\ + \hline + {$\mathtt{false}$} & + \texttt{false} & + No pathnames \\ + \hline + \end{tabular} + \end{center} + \caption{ + Syntax and semantics of glob expressions. + } +\end{table} +%***) +%(*** Subdirectories +\subsection{Subdirectories} +If the files of your project are held in one or more subdirectories, +\ocb must be made aware of that fact using the \texttt{-I} or \texttt{-Is} options +or by adding an \texttt{include} tag. For instance, assume your project is made +of three subdirectories, \texttt{foo}, \texttt{bar} and \texttt{baz} containing +various \texttt{.ml} files, the main file being \texttt{foo/main.ml}. Then you can +either type: +\begin{verbatim} +% ocamlbuild -Is foo,bar,baz foo/main.native +\end{verbatim} +or add the following line in the \texttt{\_tags} file +\begin{verbatim} +<foo> or <bar> or <baz>: include +\end{verbatim} +and call +\begin{verbatim} +% ocamlbuild foo/main.native +\end{verbatim} + +There are then two cases. If no other modules named \texttt{Bar} or +\texttt{Baz} exist elsewhere in the project, then you are done. Just use +\texttt{Foo}, \texttt{Foo.Bar} and \texttt{Foo.Baz} in your code. +Otherwise, you will need to use the plugin mechanism and define the mutual +visibility of the subdirectories using the XXX function. +%***) +%(*** Grouping targets +\subsection{Grouping targets with \texttt{.itarget}} +You can create a file named \texttt{foo.itarget} containing +a list of targets, one per line, such as +\begin{verbatim} +main.native +main.byte +stuff.docdir/index.html +\end{verbatim} +Requesting the target \texttt{foo.otarget} will then build every target +listed in the file \texttt{foo.itarget}. Blank lines and dashes to comment +out lines are accepted. +%***) +%(*** Packing subdirectories into modules +\subsection{Packing subdirectories into modules} +OCaml's \texttt{-pack} option allows you to structure the contents of a +module in a subdirectory. For instance, assume you have a directory +\texttt{foo} containing two modules \texttt{bar.ml} and \texttt{baz.ml}. +You want from these to build a module \texttt{Foo} containing \texttt{Bar} +and \texttt{Baz} as submodules. In the case where no modules named +\texttt{Bar} or \texttt{Baz} exist outside of \texttt{Foo}, To do this you +must write a file \texttt{foo.mlpack}, preferably sitting in the same +directory as the directory \texttt{Foo} and containing the list of modules +(one per line) it must contain: +\begin{verbatim} +Bar +Baz +\end{verbatim} +%***) +%(*** Preprocessor options +\subsection{Preprocessor options and tags} +You can specify preprocessor options with \texttt{-pp} followed by the +preprocessor string, for instance \texttt{ocamlbuild -pp "camlp4o.opt -unsafe"} +would run your sources thru CamlP4 with the \texttt{-unsafe} option. +Another way is to use the tags file. +\begin{center} + \begin{tabular}{|l|l|l|} + \hline + \textbf{Tag} & \textbf{Preprocessor command} & \textbf{Remark} \\ + \hline + \hline + \texttt{pp(cmd...)} & \texttt{cmd...} & Arbitrary + preprocessor command\footnote{The command must not contain newlines or parentheses.} \\ + \hline + \texttt{camlp4o} & \texttt{camlp4o} & Original OCaml syntax \\ + \hline + \texttt{camlp4r} & \texttt{camlp4r} & Revised OCaml syntax \\ + \hline + \texttt{camlp4of} & \texttt{camlp4of} & Original OCaml syntax with extensions \\ + \hline + \texttt{camlp4rf} & \texttt{camlp4rf} & Revised OCaml syntax with extensions \\ + \hline + \end{tabular} +\end{center} +%***) +%(*** Debugging and profiling +\subsection{Debugging byte code and profiling native code} +The preferred way of compiling code suitable for debugging with \texttt{ocamldebug} or +profiling native code with \texttt{ocamlprof} is to use the appropriate target +extensions, \texttt{.d.byte} for debugging or \texttt{.p.native}. + +Another way is to add use the \texttt{debug} or \texttt{profile} tags. +Note that these tags must be applied at the compilation and linking stages. +Hence you must either use \texttt{-tag debug} or \texttt{-tag profile} +on the command line, or add a +\begin{verbatim} +true: debug +\end{verbatim} +line to your \texttt{\_tags} file. +Please note that the bytecode profiler works in a wholly different way +and is not supported by \ocb. +%***) +%(*** The display line +\subsection{The display line} +Provided \ocb runs in a terminal under a POSIX environment, it will +display a sophisticated progress-indicator line that graciously interacts +with the output of subcommands. This line looks like this: +\begin{verbatim} +00:00:02 210 (180 ) main.cmx ONbp--il / +\end{verbatim} +Here, 00:00:02 is the elapsed time in hour:minute:second format since \ocb has +been invoked; 210 is the number of external commands, typically calls to the +compiler or the like, that may or may not have been invoked; 180 is the number +of external commands that have not been invoked since their result is already +in the build directory; \texttt{main.cmx} is the name of the last target built; +\texttt{ONbp--il} is a short string that describes the tags that have been +encountered and the slash at the end is a frame from a rotating ticker. Hence, +the display line has the following structure: +\begin{verbatim} +HH:MM:SS JOBS (CACHED) PATHNAME TAGS TICKER +\end{verbatim} + +The tag string is made of 8 indicators which each monitor a tag. These tags +are \texttt{ocaml}, \texttt{native}, \texttt{byte}, \texttt{program}, +\texttt{pp}, \texttt{debug}, \texttt{interf} and \texttt{link}. Initially, +each indicator displays a dash \texttt{-}. If the current target has the +monitored tag, then the indicator displays the corresponding character +(see table \ref{tab:tag-chars}) in uppercase. Otherwise, it displays that +character in lowercase. This allows you to see the set of tags that have +been applied to files in your project during the current invocation of \ocb. + +Hence the tag string \texttt{ONbp--il} means that the current target +\texttt{main.cmx} has the tags \texttt{ocaml} and \texttt{native}, and that +the tags \texttt{ocaml}, \texttt{native}, \texttt{byte}, \texttt{program}, +\texttt{interf} and \texttt{link} have already been seen. + +\begin{table} + \begin{center} + \begin{tabular}{|l|c|} + \hline + \textbf{Tag} & \textbf{Display character} \\ + \hline + \hline + ocaml & O \\ + \hline + native & N \\ + \hline + byte & B \\ + \hline + program & P \\ + \hline + pp & R \\ + \hline + debug & D \\ + \hline + interf & I \\ + \hline + link & L \\ + \hline + \end{tabular} + \end{center} + \caption{\label{tab:tag-chars} Relation between the characters displayed in + the tag string and the tags.} +\end{table} +%***) +%(*** ocamllex, ocamlyacc and menhir +\subsection{\texttt{ocamllex}, \texttt{ocamlyacc} and \texttt{menhir}} +\ocb knows how to run the standard lexer and parser generator tools +\texttt{ocamllex} and \texttt{ocamlyacc} when your files have the +standard \texttt{.mll} and \texttt{.mly} extensions. If you want to +use \texttt{menhir} instead of \texttt{ocamlyacc}, you can either +launch \ocb with the \texttt{-use-menhir} option or add a +\begin{verbatim} +true: use_menhir +\end{verbatim} +line to your \texttt{\_tags} file. Note that there is currently no way +of using \texttt{menhir} and \texttt{ocamlyacc} in the same execution +of \ocb. +%***) +%(*** Changing the compilers +\subsection{Changing the compilers or tools} +As \ocb is part of your OCaml distribution, it knows if it can call the +native compilers and tools (\texttt{ocamlc.opt}, \texttt{ocamlopt.opt}...) +or not. However you may want \ocb to use another \texttt{ocaml} compiler +for different reasons (such as cross-compiling or using a wrapper such as +\texttt{ocamlfind}). Here is the list of relevant options: +\begin{itemize} + \item \texttt{-ocamlc <command>} + \item \texttt{-ocamlopt <command>} + \item \texttt{-ocamldep <command>} + \item \texttt{-ocamlyacc <command>} + \item \texttt{-menhir <command>} + \item \texttt{-ocamllex <command>} + \item \texttt{-ocamlrun <command>} +\end{itemize} + +%***) +\subsection{Writing a \texttt{myocamlbuild.ml} plugin} +%(*** Interaction with version control systems +\subsection{Interaction with version control systems} +Here are tips for configuring your version control system to ignore the files +and directories generated by \ocb. + +The directory \texttt{\_build}, the file \texttt{\_log} and any symbolic links +pointing into \texttt{\_build} should be ignored. +To do this, you must add the following ignore patterns to your version +control system's ignore set: +\begin{verbatim} +_log +_build +*.native +*.byte +*.d.native +*.p.byte +\end{verbatim} + +For CVS, add the above lines to the \texttt{.cvsignore} file. +For Subversion (SVN), type \texttt{svn propedit svn:ignore .} and add the +above lines. +%***) +%(*** A shell script for driving it all? +\subsection{A shell script for driving it all?} +{\em To shell or to make ?} +Traditionally, makefiles have two major functions. The first one +is the dependency-ordering, rule-matching logic used for compiling. +The second one is as a dispatcher for various actions defined using +phony targets with shell script actions. These actions include cleaning, +cleaning really well, archiving, uploading and so on. Their characteristic +is that they rely little or not on the building process -- they either need +the building to have been completed, or they don't need anything. +As \texttt{/bin/sh} scripts have been here for three to four decades and are +not going anywhere, why not replace that functionality of makefiles with a +shell script ? We have thought of three bad reasons: +\begin{itemize} + \item Typing \texttt{make} to compile is now an automatism, + \item We need to share variable definitions between rules and actions, + \item Escaping already way too special-character-sensitive shell code with + invisible tabs and backslashes is a dangerously fun game. +\end{itemize} +We also have bad reasons for not using an OCaml script to drive everything: +\begin{itemize} + \item \texttt{Sys.command} calls the \texttt{/bin/sh} anyway, + \item Shell scripts can execute partial commands or commands with badly formed arguments. + \item Shell scripts are more concise for expressing... shell scripts. +\end{itemize} +Anyway you are of course free to use a makefile or an OCaml script to call ocamlbuild. +Here is an example shell driver script: +\begin{verbatim} +#!/bin/sh + +set -e + +TARGET=epoch +FLAGS="-libs unix,nums" +OCAMLBUILD=ocamlbuild + +ocb() +{ + $OCAMLBUILD $FLAGS $* +} + +rule() { + case $1 in + clean) ocb -clean;; + native) ocb $TARGET.native;; + byte) ocb $TARGET.byte;; + all) ocb $TARGET.native $TARGET.byte;; + depend) echo "Not needed.";; + *) echo "Unknown action $1";; + esac; +} + +if [ $# -eq 0 ]; then + rule all +else + while [ $# -gt 0 ]; do + rule $1; + shift + done +fi +\end{verbatim} +%***) +\subsection{Common errors} +%***) +\appendix +%(*** Default rules +\section{Default rules} +\begin{center} +\small +\begin{tabular}{|l|l|p{5cm}|} + \hline + \textbf{Tags} & \textbf{Dependencies} & \textbf{Targets} \\ + \hline + \hline + & \%.itarget & \%.otarget \\ + \hline + ocaml & \%.mli \%.mli.depends & \%.cmi \\ + \hline + byte, debug, ocaml & \%.mlpack \%.cmi & \%.d.cmo \\ + \hline + byte, ocaml & \%.mlpack & \%.cmo \%.cmi \\ + \hline + byte, ocaml & \%.mli \%.ml \%.ml.depends \%.cmi & \%.d.cmo \\ + \hline + byte, ocaml & \%.mli \%.ml \%.ml.depends \%.cmi & \%.cmo \\ + \hline + native, ocaml, profile & \%.mlpack \%.cmi & \%.p.cmx \%.p.o \\ + \hline + native, ocaml & \%.mlpack \%.cmi & \%.cmx \%.o \\ + \hline + native, ocaml, profile & \%.ml \%.ml.depends \%.cmi & \%.p.cmx \%.p.o \\ + \hline + native, ocaml & \%.ml \%.ml.depends \%.cmi & \%.cmx \%.o \\ + \hline + debug, ocaml & \%.ml \%.ml.depends \%.cmi & \%.d.cmo \\ + \hline + ocaml & \%.ml \%.ml.depends & \%.cmo \%.cmi \\ + \hline + byte, debug, ocaml, program & \%.d.cmo & \%.d.byte \\ + \hline + byte, ocaml, program & \%.cmo & \%.byte \\ + \hline + native, ocaml, profile, program & \%.p.cmx \%.p.o & \%.p.native \\ + \hline + native, ocaml, program & \%.cmx \%.o & \%.native \\ + \hline + byte, debug, library, ocaml & \%.mllib & \%.d.cma \\ + \hline + byte, library, ocaml & \%.mllib & \%.cma \\ + \hline + byte, debug, library, ocaml & \%.d.cmo & \%.d.cma \\ + \hline + byte, library, ocaml & \%.cmo & \%.cma \\ + \hline + & lib\%(libname).clib & lib\%(libname).a dll\%(libname).so \\ + \hline + & \%(path)/lib\%(libname).clib & \%(path)/lib\%(libname).a \%(path)/dll\%(libname).so \\ + \hline + library, native, ocaml, profile & \%.mllib & \%.p.cmxa \%.p.a \\ + \hline + library, native, ocaml & \%.mllib & \%.cmxa \%.a \\ + \hline + library, native, ocaml, profile & \%.p.cmx \%.p.o & \%.p.cmxa \%.p.a \\ + \hline + library, native, ocaml & \%.cmx \%.o & \%.cmxa \%.a \\ + \hline + & \%.ml & \%.ml.depends \\ + \hline + & \%.mli & \%.mli.depends \\ + \hline + ocaml & \%.mll & \%.ml \\ + \hline + doc, ocaml & \%.mli \%.mli.depends & \%.odoc \\ + \hline + & \%.odocl & \%.docdir/index.html \\ + \hline + ocaml & \%.mly & \%.ml \%.mli \\ + \hline + & \%.c & \%.o \\ + \hline + & \%.ml \%.ml.depends & \%.inferred.mli \\ + \hline +\end{tabular} +\end{center} +%***) +\end{document} diff --git a/ocamlbuild/misc/opentracer.ml b/ocamlbuild/misc/opentracer.ml new file mode 100644 index 0000000000..1aa62b98ca --- /dev/null +++ b/ocamlbuild/misc/opentracer.ml @@ -0,0 +1,101 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +open My_std + +module type TRACER = sig + (** Call the given command using the tracer, it returns the exit status. *) + val call : string -> string list -> StringSet.t * Unix.process_status +end + +module Ktrace = struct + let process_line line (wait_a_string, set) = + let strings = Lexers.space_sep_strings (Lexing.from_string line) in + if wait_a_string then + match strings with + | [_; _; "NAMI"; file] -> false, StringSet.add file set + | _ -> failwith (Printf.sprintf "unexpected ktrace output line (%S)" line) + else + match strings with + | [_; _; "CALL"; fct] -> + (String.length fct > 5 && String.sub fct 0 5 = "open("), set + | _ -> false, set + + let call cmd args = + let tmp = Filename.temp_file "ktrace" "out" in + match Unix.fork () with + | 0 -> Unix.execvp "ktrace" (Array.of_list("-d"::"-i"::"-t"::"nc"::"-f"::tmp::cmd::args)) + | pid -> + let _, st = Unix.waitpid [] pid in + let ic = Unix.open_process_in (Printf.sprintf "kdump -f %s" (Filename.quote tmp)) in + let close () = ignore (Unix.close_process_in ic); Sys.remove tmp in + let set = + try + let rec loop acc = + match try Some (input_line ic) with End_of_file -> None with + | Some line -> loop (process_line line acc) + | None -> acc in + let _, set = loop (false, StringSet.empty) in + close (); + set + with e -> (close (); raise e) + in set, st + +end + +module Driver (T : TRACER) = struct + let usage () = + Printf.eprintf "Usage: %s [-a <authorized_file>]* <cmd> <args>*\n%!" Sys.argv.(0); + exit 2 + + let main () = + let log = "opentracer.log" in + let oc = + if sys_file_exists log then + open_out_gen [Open_wronly;Open_append;Open_text] 0 log + else + let oc = open_out log in + let () = output_string oc "---\n" in + oc in + let rec loop acc = + function + | "-a" :: file :: rest -> loop (StringSet.add file acc) rest + | "-a" :: _ -> usage () + | "--" :: cmd :: args -> acc, cmd, args + | cmd :: args -> acc, cmd, args + | [] -> usage () in + let authorized_files, cmd, args = + loop StringSet.empty (List.tl (Array.to_list Sys.argv)) in + let opened_files, st = T.call cmd args in + let forbidden_files = StringSet.diff opened_files authorized_files in + + if not (StringSet.is_empty forbidden_files) then begin + Printf.fprintf oc "- cmd: %s\n args:\n%!" cmd; + let pp = Printf.fprintf oc " - %s\n%!" in + List.iter pp args; + Printf.fprintf oc " forbidden_files:\n%!"; + StringSet.iter pp forbidden_files; + end; + close_out oc; + match st with + | Unix.WEXITED st -> exit st + | Unix.WSIGNALED s | Unix.WSTOPPED s -> Unix.kill (Unix.getpid ()) s +end + +let main = + (* match os with *) + (* | "macos" -> *) + let module M = Driver(Ktrace) in M.main + (* | "linux" -> *) + (* let module M = Driver(Strace) in M.main *) + +let () = main () diff --git a/ocamlbuild/my_std.ml b/ocamlbuild/my_std.ml new file mode 100644 index 0000000000..9d3c0a97ce --- /dev/null +++ b/ocamlbuild/my_std.ml @@ -0,0 +1,359 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +open Format + +exception Exit_OK +exception Exit_usage of string +exception Exit_system_error of string +exception Exit_with_code of int +exception Exit_silently_with_code of int + +module Outcome = struct + type ('a,'b) t = + | Good of 'a + | Bad of 'b + + let ignore_good = + function + | Good _ -> () + | Bad e -> raise e + + let good = + function + | Good x -> x + | Bad exn -> raise exn + + let wrap f x = + try Good (f x) with e -> Bad e + +end + +let opt_print elt ppf = + function + | Some x -> fprintf ppf "@[<2>Some@ %a@]" elt x + | None -> pp_print_string ppf "None" + +open Format +let ksbprintf g fmt = + let buff = Buffer.create 42 in + let f = formatter_of_buffer buff in + kfprintf (fun f -> (pp_print_flush f (); g (Buffer.contents buff))) f fmt +let sbprintf fmt = ksbprintf (fun x -> x) fmt + +(** Some extensions of the standard library *) +module Set = struct + + module type OrderedTypePrintable = sig + include Set.OrderedType + val print : formatter -> t -> unit + end + + module type S = sig + include Set.S + val find : (elt -> bool) -> t -> elt + val map : (elt -> elt) -> t -> t + val of_list : elt list -> t + val print : formatter -> t -> unit + end + + module Make (M : OrderedTypePrintable) : S with type elt = M.t = struct + include Set.Make(M) + exception Found of elt + let find p set = + try + iter begin fun elt -> + if p elt then raise (Found elt) + end set; raise Not_found + with Found elt -> elt + let map f set = fold (fun x -> add (f x)) set empty + let of_list l = List.fold_right add l empty + let print f s = + let () = fprintf f "@[<hv0>@[<hv2>{.@ " in + let _ = + fold begin fun elt first -> + if not first then fprintf f ",@ "; + M.print f elt; + false + end s true in + fprintf f "@]@ .}@]" + end +end + +module List = struct + include List + let print pp_elt f ls = + fprintf f "@[<2>[@ "; + let _ = + fold_left begin fun first elt -> + if not first then fprintf f ";@ "; + pp_elt f elt; + false + end true ls in + fprintf f "@ ]@]" + + let filter_opt f xs = + List.fold_right begin fun x acc -> + match f x with + | Some x -> x :: acc + | None -> acc + end xs [] + + let union a b = + let rec self a b = + if a = [] then b else + match b with + | [] -> a + | x :: xs -> + if mem x a then self a xs + else self (x :: a) xs + in rev (self (rev a) b) +end + +module String = struct + include String + + let print f s = fprintf f "%S" s + + let chomp s = + let ls = length s in + if ls = 0 then s + else if s.[ls-1] = '\n' then sub s 0 (ls - 1) + else s + + let before s pos = sub s 0 pos + let after s pos = sub s pos (length s - pos) + let first_chars s n = sub s 0 n + let last_chars s n = sub s (length s - n) n + + let rec eq_sub_strings s1 p1 s2 p2 len = + if len > 0 then s1.[p1] = s2.[p2] && eq_sub_strings s1 (p1+1) s2 (p2+1) (len-1) + else true + + let rec contains_string s1 p1 s2 = + let ls1 = length s1 in + let ls2 = length s2 in + try let pos = index_from s1 p1 s2.[0] in + if ls1 - pos < ls2 then None + else if eq_sub_strings s1 pos s2 0 ls2 then + Some pos else contains_string s1 (pos + 1) s2 + with Not_found -> None + + let subst patt repl s = + let lpatt = length patt in + let lrepl = length repl in + let rec loop s from = + match contains_string s from patt with + | Some pos -> + loop (before s pos ^ repl ^ after s (pos + lpatt)) (pos + lrepl) + | None -> s + in loop s 0 + + let tr patt subst text = + let len = length text in + let text = copy text in + let rec loop pos = + if pos < len then begin + (if text.[pos] = patt then text.[pos] <- subst); + loop (pos + 1) + end + in loop 0; text + + (*** is_prefix : is v a prefix of u ? *) + let is_prefix u v = + let m = String.length u + and n = String.length v + in + m <= n && + let rec loop i = i = m or u.[i] = v.[i] && loop (i + 1) in + loop 0 + (* ***) + + (*** is_suffix : is v a suffix of u ? *) + let is_suffix u v = + let m = String.length u + and n = String.length v + in + n <= m && + let rec loop i = i = n or u.[m - 1 - i] = v.[n - 1 - i] && loop (i + 1) in + loop 0 + (* ***) + + let rev s = + let sl = String.length s in + let s' = String.create sl in + for i = 0 to sl - 1 do + s'.[i] <- s.[sl - i - 1] + done; + s';; +end + +module StringSet = Set.Make(String) + +let sys_readdir, reset_readdir_cache, reset_readdir_cache_for = + let cache = Hashtbl.create 103 in + let sys_readdir dir = + try Hashtbl.find cache dir with Not_found -> + let res = Outcome.wrap Sys.readdir dir in + (Hashtbl.add cache dir res; res) + and reset_readdir_cache () = + Hashtbl.clear cache + and reset_readdir_cache_for dir = + Hashtbl.remove cache dir in + (sys_readdir, reset_readdir_cache, reset_readdir_cache_for) + +let sys_file_exists x = + let dirname = Filename.dirname x in + let basename = Filename.basename x in + if basename = Filename.current_dir_name then true else + match sys_readdir dirname with + | Outcome.Bad _ -> false + | Outcome.Good a -> try Array.iter (fun x -> if x = basename then raise Exit) a; false with Exit -> true + +let sys_command = + match Sys.os_type with + | "Win32" -> fun cmd -> + let cmd = "bash -c "^Filename.quote cmd in + (* FIXME fix Filename.quote for windows *) + let cmd = String.subst "\"&\"\"&\"" "&&" cmd in + Sys.command cmd + | _ -> Sys.command + +(* FIXME warning fix and use Filename.concat *) +let filename_concat x y = + if x = Filename.current_dir_name || x = "" then y else + if y = "" && x.[String.length x - 1] = '/' then x + else x ^ "/" ^ y + +(* let reslash = + match Sys.os_type with + | "Win32" -> tr '\\' '/' + | _ -> (fun x -> x) *) + +open Format + +let invalid_arg' fmt = ksbprintf invalid_arg fmt + +let the = function Some x -> x | None -> invalid_arg "the: expect Some not None" + +let getenv ?default var = + try Sys.getenv var + with Not_found -> + match default with + | Some x -> x + | None -> failwith (sprintf "This command must have %S in his environment" var);; + +let with_input_file ?(bin=false) x f = + let ic = (if bin then open_in_bin else open_in) x in + try let res = f ic in close_in ic; res with e -> (close_in ic; raise e) + +let with_output_file ?(bin=false) x f = + reset_readdir_cache_for (Filename.dirname x); + let oc = (if bin then open_out_bin else open_out) x in + try let res = f oc in close_out oc; res with e -> (close_out oc; raise e) + +let read_file x = + with_input_file ~bin:true x begin fun ic -> + let len = in_channel_length ic in + let buf = String.create len in + let () = really_input ic buf 0 len in + buf + end + +let copy_chan ic oc = + let m = in_channel_length ic in + let m = (m lsr 12) lsl 12 in + let m = max 16384 (min 16777216 m) in + let buf = String.create m in + let rec loop () = + let len = input ic buf 0 m in + if len > 0 then begin + output oc buf 0 len; + loop () + end + in loop () + +let copy_file src dest = + reset_readdir_cache_for (Filename.dirname dest); + with_input_file ~bin:true src begin fun ic -> + with_output_file ~bin:true dest begin fun oc -> + copy_chan ic oc + end + end + +let ( !* ) = Lazy.force + +let ( @:= ) ref list = ref := !ref @ list + +let print_string_list = List.print String.print + +module Digest = struct + include Digest +(* USEFUL FOR DIGEST DEBUGING + let digest_log_hash = Hashtbl.create 103;; + let digest_log = "digest.log";; + let digest_log_oc = open_out_gen [Open_append;Open_wronly;Open_text;Open_creat] 0o644 digest_log;; + let my_to_hex x = to_hex x ^ ";";; + if sys_file_exists digest_log then + with_input_file digest_log begin fun ic -> + try while true do + let l = input_line ic in + Scanf.sscanf l "%S: %S" (Hashtbl.replace digest_log_hash) + done with End_of_file -> () + end;; + let string s = + let res = my_to_hex (string s) in + if try let x = Hashtbl.find digest_log_hash res in s <> x with Not_found -> true then begin + Hashtbl.replace digest_log_hash res s; + Printf.fprintf digest_log_oc "%S: %S\n%!" res s + end; + res + let file f = my_to_hex (file f) + let to_hex x = x +*) + + let digest_cache = Hashtbl.create 103 + let reset_digest_cache () = Hashtbl.clear digest_cache + let reset_digest_cache_for file = Hashtbl.remove digest_cache file + let file f = + try Hashtbl.find digest_cache f + with Not_found -> + let res = file f in + (Hashtbl.add digest_cache f res; res) +end + +let reset_filesys_cache () = + Digest.reset_digest_cache (); + reset_readdir_cache () + +let reset_filesys_cache_for_file file = + Digest.reset_digest_cache_for file; + reset_readdir_cache_for (Filename.dirname file) + +let sys_remove x = + reset_filesys_cache_for_file x; + Sys.remove x + +let with_temp_file pre suf fct = + let tmp = Filename.temp_file pre suf in + (* Sys.remove is used instead of sys_remove since we know that the tempfile is not that important *) + try let res = fct tmp in Sys.remove tmp; res + with e -> (Sys.remove tmp; raise e) + +let memo f = + let cache = Hashtbl.create 103 in + fun x -> + try Hashtbl.find cache x + with Not_found -> + let res = f x in + (Hashtbl.add cache x res; res) diff --git a/ocamlbuild/my_std.mli b/ocamlbuild/my_std.mli new file mode 100644 index 0000000000..9281f471e9 --- /dev/null +++ b/ocamlbuild/my_std.mli @@ -0,0 +1,65 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +(* My_std *) + +(** Generic utility functions, and system-independent glue. *) + +exception Exit_OK +exception Exit_usage of string +exception Exit_system_error of string +exception Exit_with_code of int +exception Exit_silently_with_code of int + +module Outcome : Signatures.OUTCOME + +open Format + +val ksbprintf : (string -> 'a) -> ('b, formatter, unit, 'a) format4 -> 'b +val sbprintf : ('a, formatter, unit, string) format4 -> 'a + +module Set : sig + module type OrderedTypePrintable = Signatures.OrderedTypePrintable + module type S = Signatures.SET + module Make (M : OrderedTypePrintable) : S with type elt = M.t +end + +module List : Signatures.LIST + +module String : Signatures.STRING + +module Digest : sig + type t = string + val string : string -> t + val substring : string -> int -> int -> t + external channel : in_channel -> int -> t = "caml_md5_chan" + val file : string -> t + val output : out_channel -> t -> unit + val input : in_channel -> t + val to_hex : t -> string +end + +module StringSet : Set.S with type elt = String.t + +val sys_readdir : string -> (string array, exn) Outcome.t +val sys_remove : string -> unit +val reset_readdir_cache : unit -> unit +val reset_filesys_cache : unit -> unit +val reset_filesys_cache_for_file : string -> unit +val sys_file_exists : string -> bool +val sys_command : string -> int +val filename_concat : string -> string -> string + +val invalid_arg' : ('a, formatter, unit, 'b) format4 -> 'a + +include Signatures.MISC diff --git a/ocamlbuild/my_unix.ml b/ocamlbuild/my_unix.ml new file mode 100644 index 0000000000..0e9514444d --- /dev/null +++ b/ocamlbuild/my_unix.ml @@ -0,0 +1,139 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +open My_std +open Format + +type file_kind = +| FK_dir +| FK_file +| FK_link +| FK_other + +type stats = + { + stat_file_kind : file_kind; + stat_key : string + } + +type implem = + { + mutable is_degraded : bool; + mutable is_link : string -> bool; + mutable run_and_open : 'a . string -> (in_channel -> 'a) -> 'a; + mutable readlink : string -> string; + mutable execute_many : ?max_jobs:int -> + ?ticker:(unit -> unit) -> + ?period:float -> + ?display:((out_channel -> unit) -> unit) -> + ((string * (unit -> unit)) list list) -> + (bool list * exn) option; + mutable report_error : Format.formatter -> exn -> unit; + mutable at_exit_once : (unit -> unit) -> unit; + mutable gettimeofday : unit -> float; + mutable stdout_isatty : unit -> bool; + mutable stat : string -> stats; + mutable lstat : string -> stats; + } + +let is_degraded = true + +let stat f = + { stat_key = f; + stat_file_kind = + try let _ = with_input_file f input_char in FK_file + with + | Sys_error "Is a directory" -> FK_dir + | End_of_file -> FK_file } + +let run_and_open s kont = + with_temp_file "ocamlbuild" "out" begin fun tmp -> + let s = sprintf "%s > '%s'" s tmp in + let st = sys_command s in + if st <> 0 then failwith (Printf.sprintf "Error while running: %s" s); + with_input_file tmp kont + end + +exception Not_a_link +exception No_such_file +exception Link_to_directories_not_supported + +let readlinkcmd = + let cache = Hashtbl.create 32 in + fun x -> + try Hashtbl.find cache x + with Not_found -> + run_and_open (Printf.sprintf "readlink %s" (Filename.quote x)) begin fun ic -> + let y = String.chomp (input_line ic) in + Hashtbl.replace cache x y; y + end + +let rec readlink x = + if sys_file_exists x then + try + let y = readlinkcmd x in + if (lstat y).stat_file_kind = FK_dir then raise Link_to_directories_not_supported else y + with Failure(_) -> raise Not_a_link + else raise No_such_file + +and is_link x = + try ignore(readlink x); true with + | No_such_file | Not_a_link -> false + +and lstat x = + if is_link x then { stat_key = x; stat_file_kind = FK_link } else stat x + +let implem = + { + is_degraded = true; + + stat = stat; + lstat = lstat; + readlink = readlink; + is_link = is_link; + run_and_open = run_and_open; + + (* at_exit_once is at_exit in the degraded mode since fork is not accessible in this mode *) + at_exit_once = at_exit; + report_error = (fun _ -> raise); + gettimeofday = (fun () -> assert false); + stdout_isatty = (fun () -> false); + execute_many = (fun ?max_jobs:(_) ?ticker:(_) ?period:(_) ?display:(_) _ -> assert false) + } + +let is_degraded = lazy implem.is_degraded +let stat x = implem.stat x +let lstat x = implem.lstat x +let readlink x = implem.readlink x +let is_link x = implem.is_link x +let run_and_open x = implem.run_and_open x +let at_exit_once x = implem.at_exit_once x +let report_error x = implem.report_error x +let gettimeofday x = implem.gettimeofday x +let stdout_isatty x = implem.stdout_isatty x +let execute_many ?max_jobs = implem.execute_many ?max_jobs + +let run_and_read cmd = + let bufsiz = 2048 in + let buf = String.create bufsiz in + let totalbuf = Buffer.create 4096 in + implem.run_and_open cmd begin fun ic -> + let rec loop pos = + let len = input ic buf 0 bufsiz in + if len > 0 then begin + Buffer.add_substring totalbuf buf 0 len; + loop (pos + len) + end + in loop 0; Buffer.contents totalbuf + end + diff --git a/ocamlbuild/my_unix.mli b/ocamlbuild/my_unix.mli new file mode 100644 index 0000000000..5233415d42 --- /dev/null +++ b/ocamlbuild/my_unix.mli @@ -0,0 +1,73 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +type file_kind = +| FK_dir +| FK_file +| FK_link +| FK_other + +type stats = + { + stat_file_kind : file_kind; + stat_key : string + } + +val is_degraded : bool Lazy.t + +val is_link : string -> bool +val run_and_open : string -> (in_channel -> 'a) -> 'a +val readlink : string -> string +val run_and_read : string -> string + +(** See [Executor.execute] *) +val execute_many : + ?max_jobs:int -> + ?ticker:(unit -> unit) -> + ?period:float -> + ?display:((out_channel -> unit) -> unit) -> + ((string * (unit -> unit)) list list) -> + (bool list * exn) option + +val report_error : Format.formatter -> exn -> unit +val at_exit_once : (unit -> unit) -> unit + +val gettimeofday : unit -> float + +val stdout_isatty : unit -> bool + +val stat : string -> stats +val lstat : string -> stats + +(** internal usage only *) +type implem = + { + mutable is_degraded : bool; + mutable is_link : string -> bool; + mutable run_and_open : 'a . string -> (in_channel -> 'a) -> 'a; + mutable readlink : string -> string; + mutable execute_many : ?max_jobs:int -> + ?ticker:(unit -> unit) -> + ?period:float -> + ?display:((out_channel -> unit) -> unit) -> + ((string * (unit -> unit)) list list) -> + (bool list * exn) option; + mutable report_error : Format.formatter -> exn -> unit; + mutable at_exit_once : (unit -> unit) -> unit; + mutable gettimeofday : unit -> float; + mutable stdout_isatty : unit -> bool; + mutable stat : string -> stats; + mutable lstat : string -> stats; + } + +val implem : implem diff --git a/ocamlbuild/my_unix_with_unix.ml b/ocamlbuild/my_unix_with_unix.ml new file mode 100644 index 0000000000..1c0dd1e6fa --- /dev/null +++ b/ocamlbuild/my_unix_with_unix.ml @@ -0,0 +1,75 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +open Format +open Ocamlbuild_pack +open My_unix + +let report_error f = + function + | Unix.Unix_error(err, fun_name, arg) -> + fprintf f "%s: %S failed" Sys.argv.(0) fun_name; + if String.length arg > 0 then + fprintf f " on %S" arg; + fprintf f ": %s" (Unix.error_message err) + | exn -> raise exn + +let mkstat unix_stat x = + let st = + try unix_stat x + with Unix.Unix_error _ as e -> raise (Sys_error (My_std.sbprintf "%a" report_error e)) + in + { stat_key = sprintf "(%d,%d)" st.Unix.st_dev st.Unix.st_ino; + stat_file_kind = + match st.Unix.st_kind with + | Unix.S_LNK -> FK_link + | Unix.S_DIR -> FK_dir + | Unix.S_CHR | Unix.S_BLK | Unix.S_FIFO | Unix.S_SOCK -> FK_other + | Unix.S_REG -> FK_file } + +let is_link s = (Unix.lstat s).Unix.st_kind = Unix.S_LNK + +let at_exit_once callback = + let pid = Unix.getpid () in + at_exit begin fun () -> + if pid = Unix.getpid () then callback () + end + +let run_and_open s kont = + let ic = Unix.open_process_in s in + let close () = + match Unix.close_process_in ic with + | Unix.WEXITED 0 -> () + | Unix.WEXITED _ | Unix.WSIGNALED _ | Unix.WSTOPPED _ -> + failwith (Printf.sprintf "Error while running: %s" s) in + try + let res = kont ic in close (); res + with e -> (close (); raise e) + +let stdout_isatty () = + (* 3.10 + Unix.isatty Unix.stdout *) + true + +let setup () = + implem.is_degraded <- false; + implem.stdout_isatty <- stdout_isatty; + implem.gettimeofday <- Unix.gettimeofday; + implem.report_error <- report_error; + implem.execute_many <- Executor.execute; + implem.readlink <- Unix.readlink; + implem.run_and_open <- run_and_open; + implem.at_exit_once <- at_exit_once; + implem.is_link <- is_link; + implem.stat <- mkstat Unix.stat; + implem.lstat <- mkstat Unix.lstat; diff --git a/ocamlbuild/my_unix_with_unix.mli b/ocamlbuild/my_unix_with_unix.mli new file mode 100644 index 0000000000..1d87e4deed --- /dev/null +++ b/ocamlbuild/my_unix_with_unix.mli @@ -0,0 +1,14 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +val setup : unit -> unit diff --git a/ocamlbuild/ocaml_arch.ml b/ocamlbuild/ocaml_arch.ml new file mode 100644 index 0000000000..c7627383f8 --- /dev/null +++ b/ocamlbuild/ocaml_arch.ml @@ -0,0 +1,135 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +open My_std +open Command +open Pathname.Operators + +type 'a arch = + | Arch_dir of string * 'a * 'a arch list + | Arch_dir_pack of string * 'a * 'a arch list + | Arch_file of string * 'a + +let dir name contents = Arch_dir(name, (), contents) +let dir_pack name contents = Arch_dir_pack(name, (), contents) +let file name = Arch_file(name, ()) + +type info = +{ + current_path : string; + include_dirs : string list; + for_pack : string; +} + +let join_pack parent base = + if parent = "" then base else parent ^ "." ^ base + +let annotate arch = + let rec self arch acc = + match arch with + | Arch_dir_pack(name, _, contents) -> + let acc = { (acc) with for_pack = join_pack acc.for_pack name } in + let (_, _, i, new_contents) = self_contents name contents acc in + ([], Arch_dir_pack(name, i, List.rev new_contents)) + | Arch_dir(name, _, contents) -> + let (current_path, include_dirs, i, new_contents) = self_contents name contents acc in + (current_path :: include_dirs, Arch_dir(name, i, List.rev new_contents)) + | Arch_file(name, _) -> + ([], Arch_file(name, acc)) + and self_contents name contents acc = + let current_path = acc.current_path/name in + let include_dirs = if current_path = "" then acc.include_dirs else current_path :: acc.include_dirs in + let i = { (acc) with current_path = current_path; include_dirs = include_dirs } in + let (include_dirs, new_contents) = + List.fold_left begin fun (include_dirs, new_contents) x -> + let j = { (i) with include_dirs = include_dirs @ i.include_dirs } in + let (include_dirs', x') = self x j in + (include_dirs @ include_dirs', x' :: new_contents) + end ([], []) contents in + (current_path, include_dirs, i, new_contents) in + let init = { current_path = ""; include_dirs = []; for_pack = "" } in + snd (self arch init) + +let rec print print_info f = + let rec print_contents f = + function + | [] -> () + | x :: xs -> Format.fprintf f "@ %a%a" (print print_info) x print_contents xs in + function + | Arch_dir(name, info, contents) -> + Format.fprintf f "@[<v2>dir %S%a%a@]" name print_info info print_contents contents + | Arch_dir_pack(name, info, contents) -> + Format.fprintf f "@[<v2>dir_pack %S%a%a@]" name print_info info print_contents contents + | Arch_file(name, info) -> + Format.fprintf f "@[<2>file %S%a@]" name print_info info + +let print_include_dirs = List.print String.print + +let print_info f i = + Format.fprintf f "@ @[<v2>{ @[<2>current_path =@ %S@];@\ + \ @[<2>include_dirs =@ %a@];@\ + \ @[<2>for_pack =@ %S@] }@]" + i.current_path print_include_dirs i.include_dirs i.for_pack + +let rec iter_info f = + function + | Arch_dir_pack(_, i, xs) | Arch_dir(_, i, xs) -> + f i; List.iter (iter_info f) xs + | Arch_file(_, i) -> f i + +let rec fold_info f arch acc = + match arch with + | Arch_dir_pack(_, i, xs) | Arch_dir(_, i, xs) -> + List.fold_right (fold_info f) xs (f i acc) + | Arch_file(_, i) -> f i acc + +module SS = Set.Make(String) + +let iter_include_dirs arch = + let set = fold_info (fun i -> List.fold_right SS.add i.include_dirs) arch SS.empty in + fun f -> SS.iter f set + +let forpack_flags_of_pathname = ref (fun _ -> N) + +let print_table print_value f table = + Format.fprintf f "@[<hv0>{:@[<hv0>"; + Hashtbl.iter begin fun k v -> + if k <> "" then + Format.fprintf f "@ @[<2>%S =>@ %a@];" k print_value v; + end table; + Format.fprintf f "@]@ :}@]" + +let print_tables f (include_dirs_table, for_pack_table) = + Format.fprintf f "@[<2>@[<2>include_dirs_table:@ %a@];@ @[<2>for_pack_table: %a@]@]" + (print_table (List.print String.print)) include_dirs_table + (print_table String.print) for_pack_table + +let mk_tables arch = + let include_dirs_table = Hashtbl.create 17 + and for_pack_table = Hashtbl.create 17 in + iter_info begin fun i -> + Hashtbl.replace include_dirs_table i.current_path i.include_dirs; + Hashtbl.replace for_pack_table i.current_path i.for_pack + end arch; + let previous_forpack_flags_of_pathname = !forpack_flags_of_pathname in + forpack_flags_of_pathname := begin fun m -> + let m' = Pathname.dirname m in + try + let for_pack = Hashtbl.find for_pack_table m' in + if for_pack = "" then N else S[A"-for-pack"; A for_pack] + with Not_found -> previous_forpack_flags_of_pathname m + end; + (* Format.eprintf "@[<2>%a@]@." print_tables (include_dirs_table, for_pack_table); *) + (include_dirs_table, for_pack_table) + +let forpack_flags_of_pathname m = !forpack_flags_of_pathname m diff --git a/ocamlbuild/ocaml_arch.mli b/ocamlbuild/ocaml_arch.mli new file mode 100644 index 0000000000..f72783567c --- /dev/null +++ b/ocamlbuild/ocaml_arch.mli @@ -0,0 +1,16 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) + +include Signatures.ARCH +val forpack_flags_of_pathname : string -> Command.spec diff --git a/ocamlbuild/ocaml_compiler.ml b/ocamlbuild/ocaml_compiler.ml new file mode 100644 index 0000000000..f536af8d11 --- /dev/null +++ b/ocamlbuild/ocaml_compiler.ml @@ -0,0 +1,315 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +open My_std +open Format +open Log +open Pathname.Operators +open Tools +open Command +open Rule +open Tags.Operators +open Ocaml_utils +open Rule.Common_commands +open Outcome + +let forpack_flags arg tags = + if Tags.mem "pack" tags then + Ocaml_arch.forpack_flags_of_pathname arg + else N + +let ocamlc_c tags arg out = + let tags = tags++"ocaml"++"byte" in + Cmd (S [!Options.ocamlc; A"-c"; T(tags++"compile"); + ocaml_ppflags tags; flags_of_pathname arg; + ocaml_include_flags arg; A"-o"; Px out; P arg]) + +let ocamlc_link flag tags deps out = + Cmd (S [!Options.ocamlc; flag; T tags; + atomize_paths deps; flags_of_pathname out; A"-o"; Px out]) + +let ocamlc_link_lib = ocamlc_link (A"-a") +let ocamlc_link_prog = ocamlc_link N + +let ocamlmklib tags deps out = + Cmd (S [!Options.ocamlmklib; T tags; + atomize_paths deps; flags_of_pathname out; A"-o"; Px (Pathname.remove_extensions out)]) + +let byte_lib_linker tags = + if Tags.mem "ocamlmklib" tags then + ocamlmklib tags + else + ocamlc_link_lib tags + +let byte_lib_linker_tags tags = tags++"ocaml"++"link"++"byte"++"library" + +let ocamlc_p tags deps out = + Cmd (S [!Options.ocamlc; A"-pack"; T tags; + atomize_paths deps; flags_of_pathname out; A"-o"; Px out]) + +let ocamlopt_c tags arg out = + let tags = tags++"ocaml"++"native" in + Cmd (S [!Options.ocamlopt; A"-c"; Ocaml_arch.forpack_flags_of_pathname arg; + T(tags++"compile"); ocaml_ppflags tags; flags_of_pathname arg; + flags_of_pathname out; ocaml_include_flags arg; + A"-o"; Px out (* FIXME ocamlopt bug -o cannot be after the input file *); P arg]) + +let ocamlopt_link flag tags deps out = + Cmd (S [!Options.ocamlopt; flag; forpack_flags out tags; T tags; + atomize_paths deps; flags_of_pathname out; A"-o"; Px out]) + +let ocamlopt_link_lib = ocamlopt_link (A"-a") +let ocamlopt_link_prog = ocamlopt_link N + +let ocamlopt_p tags deps out = + let include_flags = List.fold_right begin fun dep -> + ocaml_add_include_flag (Pathname.dirname dep) + end deps [] in + let cmi = cmi_of out and cmitmp = Pathname.update_extensions "cmitmp" out in + Seq[mv cmi cmitmp; + Cmd (S [!Options.ocamlopt; A"-pack"; forpack_flags out tags; T tags; S include_flags; + atomize_paths deps; flags_of_pathname out; (* FIXME: P (cmi_of out);*) A"-o"; Px out]); + cmp cmitmp cmi] + +let native_lib_linker tags = + if Tags.mem "ocamlmklib" tags then + ocamlmklib tags + else + ocamlopt_link_lib tags + +let native_lib_linker_tags tags = tags++"ocaml"++"link"++"native"++"library" + +let prepare_compile build ml = + let dir = Pathname.dirname ml in + let include_dirs = Pathname.include_dirs_of dir in + let modules = Ocamldep.module_dependencies_of ml in + let results = + build (List.map (fun x -> expand_module include_dirs x ["cmi"]) modules) in + List.iter2 begin fun name res -> + match res with + | Good _ -> () + | Bad exn -> + if !Options.ignore_auto then + dprintf 3 "Warning: Failed to build the module \ + %s requested by ocamldep" name + else raise exn + end modules results + +let byte_compile_ocaml_interf mli cmi env build = + let mli = env mli and cmi = env cmi in + prepare_compile build mli; + ocamlc_c (tags_of_pathname mli++"interf") mli cmi + +let byte_compile_ocaml_implem ?tag ml cmo env build = + let ml = env ml and cmo = env cmo in + prepare_compile build ml; + ocamlc_c (tags_of_pathname ml++"implem"+++tag) ml cmo + +let cache_prepare_link = Hashtbl.create 107 +let rec prepare_link tag cmx extensions build = + let key = (tag, cmx, extensions) in + let dir = Pathname.dirname cmx in + let include_dirs = Pathname.include_dirs_of dir in + if Hashtbl.mem cache_prepare_link key then () else + let () = Hashtbl.add cache_prepare_link key true in + let modules = List.map (fun x -> expand_module include_dirs x extensions) + (Ocamldep.module_dependencies_of (Pathname.update_extensions "ml" cmx)) in + List.iter begin function + | Good p -> prepare_link tag p extensions build + | Bad exn -> if not !Options.ignore_auto then raise exn + end (build modules) + +let native_compile_ocaml_implem ?tag ?(cmx_ext="cmx") ml env build = + let ml = env ml in + let cmi = Pathname.update_extensions "cmi" ml in + let cmx = Pathname.update_extensions cmx_ext ml in + prepare_link cmx cmi [cmx_ext; "cmi"] build; + ocamlopt_c (tags_of_pathname ml++"implem"+++tag) ml cmx + +let libs_of_use_lib tags = + Tags.fold begin fun tag acc -> + if String.is_prefix "use_" tag then + let lib = String.after tag 4 in + try let libpath, extern = Hashtbl.find info_libraries lib in + if extern then acc else libpath :: acc + with Not_found -> acc + else acc + end tags [] + +let prepare_libs cma_ext a_ext out build = + let out_no_ext = Pathname.remove_extension out in + let libs1 = List.union (libraries_of out_no_ext) (libs_of_use_lib (tags_of_pathname out)) in + let () = dprintf 10 "prepare_libs: %S -> %a" out pp_l libs1 in + let libs = List.map (fun x -> x-.-cma_ext) libs1 in + let libs2 = List.map (fun lib -> [lib-.-a_ext]) libs1 in + List.iter ignore_good (build libs2); libs + +let library_index = Hashtbl.create 32 +let package_index = Hashtbl.create 32 +let hidden_packages = ref [] + +let hide_package_contents package = hidden_packages := package :: !hidden_packages + +module Ocaml_dependencies_input = struct + let fold_dependencies = Resource.Cache.fold_dependencies + let fold_libraries f = Hashtbl.fold f library_index + let fold_packages f = Hashtbl.fold f package_index +end +module Ocaml_dependencies = Ocaml_dependencies.Make(Ocaml_dependencies_input) + +let caml_transitive_closure = Ocaml_dependencies.caml_transitive_closure + +let link_gen cmX_ext cma_ext a_ext extensions linker tagger cmX out env build = + let cmX = env cmX and out = env out in + let tags = tagger (tags_of_pathname out) in + let dyndeps = Rule.build_deps_of_tags build tags in + let cmi = Pathname.update_extensions "cmi" cmX in + prepare_link cmX cmi extensions build; + let libs = prepare_libs cma_ext a_ext out build in + let hidden_packages = List.map (fun x -> x-.-cmX_ext) !hidden_packages in + let deps = + caml_transitive_closure + ~caml_obj_ext:cmX_ext ~caml_lib_ext:cma_ext + ~used_libraries:libs ~hidden_packages (cmX :: dyndeps) in + let deps = (List.filter (fun l -> not (List.mem l deps)) libs) @ deps in + if deps = [] then failwith "Link list cannot be empty"; + let () = dprintf 6 "link: %a -o %a" print_string_list deps Pathname.print out in + linker tags deps out + +let byte_link_gen = link_gen "cmo" "cma" "cma" ["cmo"; "cmi"] + +let byte_link = byte_link_gen ocamlc_link_prog + (fun tags -> tags++"ocaml"++"link"++"byte"++"program") + +let byte_library_link = byte_link_gen byte_lib_linker byte_lib_linker_tags + +let byte_debug_link_gen = + link_gen "d.cmo" "d.cma" "d.cma" ["d.cmo"; "cmi"] + +let byte_debug_link = byte_debug_link_gen ocamlc_link_prog + (fun tags -> tags++"ocaml"++"link"++"byte"++"debug"++"program") + +let byte_debug_library_link = byte_debug_link_gen byte_lib_linker + (fun tags -> byte_lib_linker_tags tags++"debug") + +let native_link_gen linker = + link_gen "cmx" "cmxa" !Options.ext_lib [!Options.ext_obj; "cmi"] linker + +let native_link x = native_link_gen ocamlopt_link_prog + (fun tags -> tags++"ocaml"++"link"++"native"++"program") x + +let native_library_link x = + native_link_gen native_lib_linker native_lib_linker_tags x + +let native_profile_link_gen linker = + link_gen "p.cmx" "p.cmxa" ("p" -.- !Options.ext_lib) ["p" -.- !Options.ext_obj; "cmi"] linker + +let native_profile_link x = native_profile_link_gen ocamlopt_link_prog + (fun tags -> tags++"ocaml"++"link"++"native"++"profile"++"program") x + +let native_profile_library_link x = native_profile_link_gen native_lib_linker + (fun tags -> native_lib_linker_tags tags++"profile") x + +let link_units table extensions cmX_ext cma_ext a_ext linker tagger contents_list cmX env build = + let cmX = env cmX in + let tags = tagger (tags_of_pathname cmX) in + let _ = Rule.build_deps_of_tags build tags in + let dir = + let dir1 = Pathname.remove_extensions cmX in + if Pathname.exists_in_source_dir dir1 then dir1 + else Pathname.dirname cmX in + let include_dirs = Pathname.include_dirs_of dir in + let extension_keys = List.map fst extensions in + let libs = prepare_libs cma_ext a_ext cmX build in + let results = + build begin + List.map begin fun module_name -> + expand_module include_dirs module_name extension_keys + end contents_list + end in + let module_paths = + List.map begin function + | Good p -> + let extension_values = List.assoc (Pathname.get_extensions p) extensions in + List.iter begin fun ext -> + List.iter ignore_good (build [[Pathname.update_extensions ext p]]) + end extension_values; p + | Bad exn -> raise exn + end results in + Hashtbl.replace table cmX module_paths; + let hidden_packages = List.map (fun x -> x-.-cmX_ext) !hidden_packages in + let deps = + caml_transitive_closure + ~caml_obj_ext:cmX_ext ~caml_lib_ext:cma_ext + ~hidden_packages ~pack_mode:true module_paths in + let full_contents = libs @ module_paths in + let deps = List.filter (fun x -> List.mem x full_contents) deps in + let deps = (List.filter (fun l -> not (List.mem l deps)) libs) @ deps in + linker tags deps cmX + +let link_modules = link_units library_index +let pack_modules = link_units package_index + +let link_from_file link modules_file cmX env build = + let modules_file = env modules_file in + let contents_list = string_list_of_file modules_file in + link contents_list cmX env build + +let byte_library_link_modules = + link_modules [("cmo",[]); ("cmi",[])] "cmo" "cma" "cma" byte_lib_linker byte_lib_linker_tags + +let byte_library_link_mllib = link_from_file byte_library_link_modules + +let byte_debug_library_link_modules = + link_modules [("d.cmo",[]); ("cmi",[])] "d.cmo" "d.cma" "d.cma" byte_lib_linker + (fun tags -> byte_lib_linker_tags tags++"debug") + +let byte_debug_library_link_mllib = link_from_file byte_debug_library_link_modules + +let byte_pack_modules = + pack_modules [("cmo",["cmi"]); ("cmi",[])] "cmo" "cma" "cma" ocamlc_p + (fun tags -> tags++"ocaml"++"pack"++"byte") + +let byte_pack_mlpack = link_from_file byte_pack_modules + +let byte_debug_pack_modules = + pack_modules [("d.cmo",["cmi"]); ("cmi",[])] "d.cmo" "d.cma" "d.cma" ocamlc_p + (fun tags -> tags++"ocaml"++"pack"++"byte"++"debug") + +let byte_debug_pack_mlpack = link_from_file byte_debug_pack_modules + +let native_pack_modules x = + pack_modules [("cmx",["cmi"; !Options.ext_obj]); ("cmi",[])] "cmx" "cmxa" !Options.ext_lib ocamlopt_p + (fun tags -> tags++"ocaml"++"pack"++"native") x + +let native_pack_mlpack = link_from_file native_pack_modules + +let native_profile_pack_modules x = + pack_modules [("p.cmx",["cmi"; "p" -.- !Options.ext_obj]); ("cmi",[])] "p.cmx" "p.cmxa" + ("p" -.- !Options.ext_lib) ocamlopt_p + (fun tags -> tags++"ocaml"++"pack"++"native"++"profile") x + +let native_profile_pack_mlpack = link_from_file native_profile_pack_modules + +let native_library_link_modules x = + link_modules [("cmx",[!Options.ext_obj]); ("cmi",[])] "cmx" "cmxa" + !Options.ext_lib native_lib_linker native_lib_linker_tags x + +let native_library_link_mllib = link_from_file native_library_link_modules + +let native_profile_library_link_modules x = + link_modules [("p.cmx",["p" -.- !Options.ext_obj]); ("cmi",[])] "p.cmx" "p.cmxa" + ("p" -.- !Options.ext_lib) native_lib_linker + (fun tags -> native_lib_linker_tags tags++"profile") x + +let native_profile_library_link_mllib = link_from_file native_profile_library_link_modules diff --git a/ocamlbuild/ocaml_compiler.mli b/ocamlbuild/ocaml_compiler.mli new file mode 100644 index 0000000000..fdf127d45d --- /dev/null +++ b/ocamlbuild/ocaml_compiler.mli @@ -0,0 +1,81 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) + +val forpack_flags : string -> Tags.t -> Command.spec +val ocamlc_c : Tags.t -> Pathname.t -> Pathname.t -> Command.t +val ocamlc_link_lib : Tags.t -> Pathname.t list -> Pathname.t -> Command.t +val ocamlc_link_prog : Tags.t -> Pathname.t list -> Pathname.t -> Command.t +val ocamlc_p : Tags.t -> Pathname.t list -> Pathname.t -> Command.t +val ocamlopt_c : Tags.t -> Pathname.t -> Pathname.t -> Command.t +val ocamlopt_link_lib : Tags.t -> Pathname.t list -> Pathname.t -> Command.t +val ocamlopt_link_prog : Tags.t -> Pathname.t list -> Pathname.t -> Command.t +val ocamlopt_p : Tags.t -> Pathname.t list -> Pathname.t -> Command.t +val ocamlmklib : Tags.t -> Pathname.t list -> Pathname.t -> Command.t +val prepare_compile : Rule.builder -> Pathname.t -> unit +val byte_compile_ocaml_interf : string -> string -> Rule.action +val byte_compile_ocaml_implem : ?tag:string -> string -> string -> Rule.action +val prepare_link : + Pathname.t -> Pathname.t -> + string list -> Rule.builder -> unit +val native_compile_ocaml_implem : ?tag:string -> ?cmx_ext:string -> string -> Rule.action +val prepare_libs : + string -> string -> Pathname.t -> + Rule.builder -> Pathname.t list +val link_gen : + string -> string -> string -> string list -> + (Tags.t -> Pathname.t list -> Pathname.t -> Command.t) -> + (Tags.t -> Tags.t) -> + string -> string -> Rule.action +val byte_link : string -> string -> Rule.action +val byte_library_link : string -> string -> Rule.action +val byte_debug_link : string -> string -> Rule.action +val byte_debug_library_link : string -> string -> Rule.action +val native_link : string -> string -> Rule.action +val native_library_link : string -> string -> Rule.action +val native_profile_link : string -> string -> Rule.action +val native_profile_library_link : string -> string -> Rule.action +val link_modules : + (Pathname.t * string list) list -> + string -> string -> + string -> (Tags.t -> Pathname.t list -> Pathname.t -> Command.t) -> + (Tags.t -> Tags.t) -> + string list -> string -> Rule.action +val pack_modules : + (Pathname.t * string list) list -> + string -> string -> + string -> (Tags.t -> Pathname.t list -> Pathname.t -> Command.t) -> + (Tags.t -> Tags.t) -> + string list -> string -> Rule.action +val byte_library_link_modules : string list -> string -> Rule.action +val byte_library_link_mllib : string -> string -> Rule.action +val byte_debug_library_link_modules : string list -> string -> Rule.action +val byte_debug_library_link_mllib : string -> string -> Rule.action +val byte_pack_modules : string list -> string -> Rule.action +val byte_pack_mlpack : string -> string -> Rule.action +val byte_debug_pack_modules : string list -> string -> Rule.action +val byte_debug_pack_mlpack : string -> string -> Rule.action +val native_pack_modules : string list -> string -> Rule.action +val native_pack_mlpack : string -> string -> Rule.action +val native_library_link_modules : string list -> string -> Rule.action +val native_library_link_mllib : string -> string -> Rule.action +val native_profile_pack_modules : string list -> string -> Rule.action +val native_profile_pack_mlpack : string -> string -> Rule.action +val native_profile_library_link_modules : string list -> string -> Rule.action +val native_profile_library_link_mllib : string -> string -> Rule.action + +(** [hide_package_contents pack_name] + Don't treat the given package as an open package. + So a module will not be replaced during linking by + this package even if it contains that module. *) +val hide_package_contents : string -> unit diff --git a/ocamlbuild/ocaml_dependencies.ml b/ocamlbuild/ocaml_dependencies.ml new file mode 100644 index 0000000000..4a10bf1dcf --- /dev/null +++ b/ocamlbuild/ocaml_dependencies.ml @@ -0,0 +1,219 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +open My_std +open Log +open Tools +open Ocaml_utils + +let mydprintf fmt = dprintf 10 fmt + +exception Circular_dependencies of string list * string + +module type INPUT = sig + val fold_dependencies : (string -> string -> 'a -> 'a) -> 'a -> 'a + val fold_libraries : (string -> string list -> 'a -> 'a) -> 'a -> 'a + val fold_packages : (string -> string list -> 'a -> 'a) -> 'a -> 'a +end + +module Make (I : INPUT) = struct + open I + + module SMap = Map.Make(String) + + module Resources = Resource.Resources + + module Utils = struct + let add = SMap.add + + let empty = SMap.empty + + let find_all_set x acc = + try SMap.find x acc with Not_found -> Resources.empty + + let smap_add_set src dst acc = + SMap.add src (Resources.add dst (find_all_set src acc)) acc + + let print_smap pp f smap = + Format.fprintf f "@[<hv0>{:@[<hv2>"; + SMap.iter begin fun k v -> + Format.fprintf f "@ @[<2>%S =>@ %a@];" k pp v + end smap; + Format.fprintf f "@]@,:}@]" + + let print_smap_list = print_smap pp_l + + let print_smap_set = print_smap Resources.print + + let print_lazy pp f l = pp f !*l + + let find_all_list x acc = + try SMap.find x acc with Not_found -> [] + + let find_all_rec xs map = + let visited = Hashtbl.create 32 in + let rec self x acc = + try + Hashtbl.find visited x; acc + with Not_found -> + Hashtbl.replace visited x (); + let acc = Resources.add x acc in + try Resources.fold self (SMap.find x map) acc + with Not_found -> acc + in List.fold_right self xs Resources.empty + + let mkindex fold filter = + fold begin fun name contents acc -> + if filter name then + List.fold_right begin fun elt acc -> + add elt (name :: (find_all_list elt acc)) acc + end contents acc + else + acc + end empty + + end + open Utils + + let caml_transitive_closure + ?(caml_obj_ext="cmo") + ?(caml_lib_ext="cma") + ?(pack_mode=false) + ?(used_libraries=[]) + ?(hidden_packages=[]) fns = + + let valid_link_exts = + if pack_mode then [caml_obj_ext; "cmi"] + else [caml_obj_ext; caml_lib_ext] in + + mydprintf "caml_transitive_closure@ ~caml_obj_ext:%S@ ~pack_mode:%b@ ~used_libraries:%a@ %a" + caml_obj_ext pack_mode pp_l used_libraries pp_l fns; + + let packages = fold_packages (fun name _ -> Resources.add name) Resources.empty in + mydprintf "packages:@ %a" Resources.print packages; + + let caml_obj_ext_of_cmi x = + if Filename.check_suffix x ".cmi" then + Pathname.update_extensions caml_obj_ext x + else x in + + let maybe_caml_obj_ext_of_cmi x = + if pack_mode then + if Filename.check_suffix x ".cmi" then + let caml_obj = Pathname.update_extensions caml_obj_ext x in + if Pathname.exists_in_build_dir caml_obj then + caml_obj + else + x + else + x + else + if Filename.check_suffix x ".cmi" then + Pathname.update_extensions caml_obj_ext x + else x in + + let not_linkable x = + not (List.exists (Pathname.check_extension x) valid_link_exts) in + + let dependency_map = + fold_dependencies begin fun x y acc -> + let x = maybe_caml_obj_ext_of_cmi x + and y = maybe_caml_obj_ext_of_cmi y in + if x = y || not_linkable x || not_linkable y then acc + else smap_add_set x y acc + end SMap.empty in + mydprintf "dependency_map:@ %a" print_smap_set dependency_map; + + let used_files = find_all_rec fns dependency_map in + mydprintf "used_files:@ %a" Resources.print used_files; + + let open_packages = + Resources.fold begin fun file acc -> + if Resources.mem file packages && not (List.mem file hidden_packages) + then file :: acc else acc + end used_files [] in + mydprintf "open_packages:@ %a" pp_l open_packages; + + let index_filter ext list x = + Pathname.check_extension x ext && List.mem x list in + + let lib_index = + lazy (mkindex fold_libraries (index_filter caml_lib_ext used_libraries)) in + mydprintf "lib_index:@ %a" (print_lazy print_smap_list) lib_index; + + let package_index = + lazy (mkindex fold_packages (index_filter caml_obj_ext open_packages)) in + + let rec resolve_packages x = + match find_all_list x !*package_index with + | [] -> x + | [x] -> resolve_packages x + | pkgs -> + failwith (sbprintf "the file %S is included in more than one active open package (%a)" + x pp_l pkgs) in + + let libs_of x = find_all_list x !*lib_index in + + let lib_of x = + match libs_of x with + | [] -> None + | [lib] -> Some(lib) + | libs -> + failwith (sbprintf "the file %S is included in more than one active library (%a)" + x pp_l libs) in + + let convert_dependency src dst acc = + let src = resolve_packages src in + let dst = resolve_packages dst in + let add_if_diff x y = if x = y then acc else smap_add_set x y acc in + match (lib_of src, lib_of dst) with + | None, None -> add_if_diff src dst + | Some(liba), Some(libb) -> add_if_diff liba libb + | Some(lib), None -> add_if_diff lib dst + | None, Some(lib) -> add_if_diff src lib in + + let dependencies = lazy begin + SMap.fold begin fun k -> + Resources.fold (convert_dependency k) + end dependency_map empty + end in + + mydprintf "dependencies:@ %a" (print_lazy print_smap_set) dependencies; + + let dependencies_of x = + try SMap.find x !*dependencies with Not_found -> Resources.empty in + + let needed = ref [] in + let seen = ref [] in + let rec aux fn = + if sys_file_exists fn && not (List.mem fn !needed) then begin + if List.mem fn !seen then raise (Circular_dependencies (!seen, fn)); + seen := fn :: !seen; + Resources.iter begin fun f -> + if sys_file_exists f then + if Filename.check_suffix f ".cmi" then + let f' = caml_obj_ext_of_cmi f in + if f' <> fn then + if sys_file_exists f' then aux f' + else if pack_mode then aux f else () + else () + else aux f + end (dependencies_of fn); + needed := fn :: !needed + end + in + List.iter aux fns; + mydprintf "caml_transitive_closure:@ %a ->@ %a" pp_l fns pp_l !needed; + List.rev !needed + +end diff --git a/ocamlbuild/ocaml_dependencies.mli b/ocamlbuild/ocaml_dependencies.mli new file mode 100644 index 0000000000..713de0a04a --- /dev/null +++ b/ocamlbuild/ocaml_dependencies.mli @@ -0,0 +1,43 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +(** Ocaml dependencies *) + +exception Circular_dependencies of string list * string + +(** Give to this module a way to access libraries, packages, + and dependencies between files. *) +module type INPUT = sig + val fold_dependencies : (string -> string -> 'a -> 'a) -> 'a -> 'a + val fold_libraries : (string -> string list -> 'a -> 'a) -> 'a -> 'a + val fold_packages : (string -> string list -> 'a -> 'a) -> 'a -> 'a +end + +(** Wait an [INPUT] module and gives a function to compute the + transitive closure of caml file takeing in account libraries and packages. *) +module Make (I : INPUT) : sig + + (** [caml_transitive_closure] takes a list of root ocaml compiled files and returns + the list of files that must be given to a linker. Optionally you can change the + extension of caml object/library files (cmo/cma by default); use the pack mode + (false by default) to include only root files (just sort them); and gives the + list of used libraries (empty by default). *) + val caml_transitive_closure : + ?caml_obj_ext:string -> + ?caml_lib_ext:string -> + ?pack_mode:bool -> + ?used_libraries:string list -> + ?hidden_packages:string list -> + Pathname.t list -> Pathname.t list + +end diff --git a/ocamlbuild/ocaml_specific.ml b/ocamlbuild/ocaml_specific.ml new file mode 100644 index 0000000000..79532f8c82 --- /dev/null +++ b/ocamlbuild/ocaml_specific.ml @@ -0,0 +1,373 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +open My_std +open Format +open Log +open Pathname.Operators +open Tags.Operators +open Rule +open Tools +open Rule.Common_commands +open Outcome +open Command;; + +open Ocaml_utils + +module C_tools = struct + let link_C_library clib a libname env build = + let clib = env clib and a = env a and libname = env libname in + let objs = string_list_of_file clib in + let include_dirs = Pathname.include_dirs_of (Pathname.dirname a) in + let obj_of_o x = + if Filename.check_suffix x ".o" && !Options.ext_obj <> "o" then + Pathname.update_extension !Options.ext_obj x + else x in + let resluts = build (List.map (fun o -> List.map (fun dir -> dir / obj_of_o o) include_dirs) objs) in + let objs = List.map begin function + | Good o -> o + | Bad exn -> raise exn + end resluts in + Cmd(S[!Options.ocamlmklib; A"-o"; Px libname; T(tags_of_pathname a++"c"++"ocamlmklib"); atomize objs]);; +end + +open Flags +open Command +open Rule + +let nop _env _build = () + +let ocaml_lib ?(extern=false) ?(byte=true) ?(native=true) ?dir libpath = + let add_dir x = + match dir with + | Some dir -> S[A"-I"; P dir; x] + | None -> x + and lib = Pathname.basename libpath in + Hashtbl.replace info_libraries lib (libpath, extern); + if byte then flag ["ocaml"; "use_"^lib; "link"; "byte"] (add_dir (A (libpath^".cma"))); + if native then flag ["ocaml"; "use_"^lib; "link"; "native"] (add_dir (A (libpath^".cmxa")));; + +let init () = let module M = struct + +let ext_lib = !Options.ext_lib;; +let ext_obj = !Options.ext_obj;; +let ext_dll = !Options.ext_dll;; +let x_o = "%"-.-ext_obj;; +let x_a = "%"-.-ext_lib;; +let x_dll = "%"-.-ext_dll;; +let x_p_o = "%.p"-.-ext_obj;; +let x_p_a = "%.p"-.-ext_lib;; + +rule "target files" + ~dep:"%.itarget" + ~prod:"%.otarget" + begin fun env build -> + let itarget = env "%.itarget" and otarget = env "%.otarget" in + let dir = Pathname.dirname itarget in + List.iter ignore_good + (build (List.map (fun x -> [dir/x]) (string_list_of_file itarget))); + touch otarget + end;; + +rule "ocaml: mli -> cmi" + ~tags:["ocaml"] + ~prod:"%.cmi" + ~deps:["%.mli"; "%.mli.depends"] + (Ocaml_compiler.byte_compile_ocaml_interf "%.mli" "%.cmi");; + +rule "ocaml: mlpack & d.cmo* -> d.cmo & cmi" + ~tags:["ocaml"; "debug"; "byte"] + ~prods:["%.d.cmo"] + ~deps:["%.mlpack"; "%.cmi"] + (Ocaml_compiler.byte_debug_pack_mlpack "%.mlpack" "%.d.cmo");; + +rule "ocaml: mlpack & cmo* -> cmo & cmi" + ~tags:["ocaml"; "byte"] + ~prods:["%.cmo"; "%.cmi"] + ~dep:"%.mlpack" + (Ocaml_compiler.byte_pack_mlpack "%.mlpack" "%.cmo");; + +rule "ocaml: ml & cmi -> d.cmo" + ~tags:["ocaml"; "byte"] + ~prod:"%.d.cmo" + ~deps:["%.mli"(* This one is inserted to force this rule to be skiped when + a .ml is provided without a .mli *); "%.ml"; "%.ml.depends"; "%.cmi"] + (Ocaml_compiler.byte_compile_ocaml_implem ~tag:"debug" "%.ml" "%.d.cmo");; + +rule "ocaml: ml & cmi -> cmo" + ~tags:["ocaml"; "byte"] + ~prod:"%.cmo" + ~deps:["%.mli"(* This one is inserted to force this rule to be skiped when + a .ml is provided without a .mli *); "%.ml"; "%.ml.depends"; "%.cmi"] + (Ocaml_compiler.byte_compile_ocaml_implem "%.ml" "%.cmo");; + +rule "ocaml: mlpack & cmi & p.cmx* & p.o* -> p.cmx & p.o" + ~tags:["ocaml"; "profile"; "native"] + ~prods:["%.p.cmx"; x_p_o(* no cmi here you must make the byte version to have it *)] + ~deps:["%.mlpack"; "%.cmi"] + (Ocaml_compiler.native_profile_pack_mlpack "%.mlpack" "%.p.cmx");; + +rule "ocaml: mlpack & cmi & cmx* & o* -> cmx & o" + ~tags:["ocaml"; "native"] + ~prods:["%.cmx"; x_o(* no cmi here you must make the byte version to have it *)] + ~deps:["%.mlpack"; "%.cmi"] + (Ocaml_compiler.native_pack_mlpack "%.mlpack" "%.cmx");; + +rule "ocaml: ml & cmi -> p.cmx & p.o" + ~tags:["ocaml"; "native"; "profile"] + ~prods:["%.p.cmx"; x_p_o] + ~deps:["%.ml"; "%.ml.depends"; "%.cmi"] + (Ocaml_compiler.native_compile_ocaml_implem ~tag:"profile" ~cmx_ext:"p.cmx" "%.ml");; + +rule "ocaml: ml & cmi -> cmx & o" + ~tags:["ocaml"; "native"] + ~prods:["%.cmx"; x_o] + ~deps:["%.ml"; "%.ml.depends"; "%.cmi"] + (Ocaml_compiler.native_compile_ocaml_implem "%.ml");; + +rule "ocaml: ml -> d.cmo & cmi" + ~tags:["ocaml"; "debug"] + ~prods:["%.d.cmo"] + ~deps:["%.ml"; "%.ml.depends"; "%.cmi"] + (Ocaml_compiler.byte_compile_ocaml_implem ~tag:"debug" "%.ml" "%.d.cmo");; + +rule "ocaml: ml -> cmo & cmi" + ~tags:["ocaml"] + ~prods:["%.cmo"; "%.cmi"] + ~deps:["%.ml"; "%.ml.depends"] + (Ocaml_compiler.byte_compile_ocaml_implem "%.ml" "%.cmo");; + +rule "ocaml: d.cmo* -> d.byte" + ~tags:["ocaml"; "byte"; "debug"; "program"] + ~prod:"%.d.byte" + ~dep:"%.d.cmo" + (Ocaml_compiler.byte_debug_link "%.d.cmo" "%.d.byte");; + +rule "ocaml: cmo* -> byte" + ~tags:["ocaml"; "byte"; "program"] + ~prod:"%.byte" + ~dep:"%.cmo" + (Ocaml_compiler.byte_link "%.cmo" "%.byte");; + +rule "ocaml: p.cmx* & p.o* -> p.native" + ~tags:["ocaml"; "native"; "profile"; "program"] + ~prod:"%.p.native" + ~deps:["%.p.cmx"; x_p_o] + (Ocaml_compiler.native_profile_link "%.p.cmx" "%.p.native");; + +rule "ocaml: cmx* & o* -> native" + ~tags:["ocaml"; "native"; "program"] + ~prod:"%.native" + ~deps:["%.cmx"; x_o] + (Ocaml_compiler.native_link "%.cmx" "%.native");; + +rule "ocaml: mllib & d.cmo* -> d.cma" + ~tags:["ocaml"; "byte"; "debug"; "library"] + ~prod:"%.d.cma" + ~dep:"%.mllib" + (Ocaml_compiler.byte_debug_library_link_mllib "%.mllib" "%.d.cma");; + +rule "ocaml: mllib & cmo* -> cma" + ~tags:["ocaml"; "byte"; "library"] + ~prod:"%.cma" + ~dep:"%.mllib" + (Ocaml_compiler.byte_library_link_mllib "%.mllib" "%.cma");; + +rule "ocaml: d.cmo* -> d.cma" + ~tags:["ocaml"; "byte"; "debug"; "library"] + ~prod:"%.d.cma" + ~dep:"%.d.cmo" + (Ocaml_compiler.byte_debug_library_link "%.d.cmo" "%.d.cma");; + +rule "ocaml: cmo* -> cma" + ~tags:["ocaml"; "byte"; "library"] + ~prod:"%.cma" + ~dep:"%.cmo" + (Ocaml_compiler.byte_library_link "%.cmo" "%.cma");; + +rule "ocaml C stubs (short): clib & (o|obj)* -> (a|lib) & (so|dll)" + ~prods:["lib%(libname)"-.-ext_lib; "dll%(libname)"-.-ext_dll] + ~dep:"lib%(libname).clib" + (C_tools.link_C_library "lib%(libname).clib" ("lib%(libname)"-.-ext_lib) "%(libname)");; + +rule "ocaml C stubs: clib & (o|obj)* -> (a|lib) & (so|dll)" + ~prods:["%(path)/lib%(libname)"-.-ext_lib; "%(path)/dll%(libname)"-.-ext_dll] + ~dep:"%(path)/lib%(libname).clib" + (C_tools.link_C_library "%(path)/lib%(libname).clib" ("%(path)/lib%(libname)"-.-ext_lib) "%(path)/%(libname)");; + +rule "ocaml: mllib & p.cmx* & p.o* -> p.cmxa & p.a" + ~tags:["ocaml"; "native"; "profile"; "library"] + ~prods:["%.p.cmxa"; x_p_a] + ~dep:"%.mllib" + (Ocaml_compiler.native_profile_library_link_mllib "%.mllib" "%.p.cmxa");; + +rule "ocaml: mllib & cmx* & o* -> cmxa & a" + ~tags:["ocaml"; "native"; "library"] + ~prods:["%.cmxa"; x_a] + ~dep:"%.mllib" + (Ocaml_compiler.native_library_link_mllib "%.mllib" "%.cmxa");; + +rule "ocaml: p.cmx* & p.o* -> p.cmxa & p.a" + ~tags:["ocaml"; "native"; "profile"; "library"] + ~prods:["%.p.cmxa"; x_p_a] + ~deps:["%.p.cmx"; x_p_o] + (Ocaml_compiler.native_profile_library_link "%.p.cmx" "%.p.cmxa");; + +rule "ocaml: cmx* & o* -> cmxa & a" + ~tags:["ocaml"; "native"; "library"] + ~prods:["%.cmxa"; x_a] + ~deps:["%.cmx"; x_o] + (Ocaml_compiler.native_library_link "%.cmx" "%.cmxa");; + +Ocamldep.depends "ocaml dependencies ml" + ~prod:"%.ml.depends" + ~dep:"%.ml" ();; + +Ocamldep.depends "ocaml dependencies mli" + ~prod:"%.mli.depends" + ~dep:"%.mli" ();; + +rule "ocamllex" + ~tags:["ocaml"] (* FIXME "lexer" *) + ~prod:"%.ml" + ~dep:"%.mll" + (Ocaml_tools.ocamllex "%.mll");; + +rule "ocaml: mli -> odoc" + ~tags:["ocaml"; "doc"] + ~prod:"%.odoc" + ~deps:["%.mli"; "%.mli.depends"] + (Ocaml_tools.document_ocaml_interf "%.mli" "%.odoc");; + +rule "ocamldoc: document ocaml project *odoc -> docdir" + ~prod:"%.docdir/index.html" + ~dep:"%.odocl" + (Ocaml_tools.document_ocaml_project "%.odocl" "%.docdir");; + +(* To use menhir give the -use-menhir option at command line, + Or put true: use_menhir in your tag file. *) +if !Options.use_menhir || Configuration.has_tag "use_menhir" then begin + rule "ocaml: menhir" + ~prods:["%.ml"; "%.mli"] + ~deps:["%.mly"; "%.mly.depends"] + (Ocaml_tools.menhir "%.mly"); + + Ocamldep.depends "ocaml: menhir dependencies" + ~prod:"%.mly.depends" + ~dep:"%.mly" + ~ocamldep_command:Ocamldep.menhir_ocamldep_command (); +end else + rule "ocamlyacc" + ~tags:["ocaml"] (* FIXME "parser" *) + ~prods:["%.ml"; "%.mli"] + ~dep:"%.mly" + (Ocaml_tools.ocamlyacc "%.mly");; + +rule "ocaml C stubs: c -> o" + ~prod:x_o + ~dep:"%.c" + begin fun env _build -> + let c = env "%.c" in + let o = env x_o in + let cc = Cmd(S[!Options.ocamlc; T(tags_of_pathname c++"c"++"compile"); A"-c"; Px c]) in + if Pathname.dirname o = Pathname.current_dir_name then cc + else Seq[cc; mv (Pathname.basename o) o] + end;; + +rule "ocaml: ml & ml.depends & *cmi -> .inferred.mli" + ~prod:"%.inferred.mli" + ~deps:["%.ml"; "%.ml.depends"] + (Ocaml_tools.infer_interface "%.ml" "%.inferred.mli");; + +flag ["ocaml"; "pp"] begin + S (List.fold_right (fun x acc -> Sh x :: acc) !Options.ocaml_ppflags []) +end;; + +flag ["ocaml"; "compile"] begin + atomize !Options.ocaml_cflags +end;; + +flag ["ocaml"; "link"] begin + atomize !Options.ocaml_lflags +end;; + +flag ["ocaml"; "ocamlyacc"] (atomize !Options.ocaml_yaccflags);; + +flag ["ocaml"; "ocamllex"] (atomize !Options.ocaml_lexflags);; + +flag ["ocaml"; "byte"; "link"] begin + S (List.map (fun x -> A (x^".cma")) !Options.ocaml_libs) +end;; + +flag ["ocaml"; "native"; "link"] begin + S (List.map (fun x -> A (x^".cmxa")) !Options.ocaml_libs) +end;; + +let camlp4_flags camlp4s = + List.iter begin fun camlp4 -> + flag ["ocaml"; "pp"; camlp4] (A camlp4) + end camlp4s;; + +camlp4_flags ["camlp4o"; "camlp4r"; "camlp4of"; "camlp4rf"; "camlp4orf"];; + +ocaml_lib ~extern:true ~native:false "dynlink";; +ocaml_lib ~extern:true "unix";; +ocaml_lib ~extern:true "str";; +ocaml_lib ~extern:true "bigarray";; +ocaml_lib ~extern:true "nums";; +ocaml_lib ~extern:true "dbm";; +ocaml_lib ~extern:true "graphics";; +ocaml_lib ~extern:true "labltk";; +ocaml_lib ~extern:true ~dir:"+camlp4" "camlp4";; + +flag ["ocaml"; "debug"; "compile"; "byte"] (A "-g");; +flag ["ocaml"; "debug"; "link"; "byte"] (A "-g");; +flag ["ocaml"; "debug"; "pack"; "byte"] (A "-g");; +flag ["ocaml"; "dtypes"; "compile"] (A "-dtypes");; +flag ["ocaml"; "rectypes"; "compile"] (A "-rectypes");; +flag ["ocaml"; "linkall"; "link"] (A "-linkall");; +flag ["ocaml"; "link"; "profile"; "native"] (A "-p");; +flag ["ocaml"; "link"; "program"; "custom"; "byte"] (A "-custom");; +flag ["ocaml"; "compile"; "profile"; "native"] (A "-p");; +flag ["ocaml"; "compile"; "thread"] (A "-thread");; +flag ["ocaml"; "link"; "thread"] (S[A "threads.cmxa"; A "-thread"]);; +flag ["ocaml"; "compile"; "nopervasives"] (A"-nopervasives");; +flag ["ocaml"; "compile"; "nolabels"] (A"-nolabels");; + +(*flag ["ocaml"; "ocamlyacc"; "quiet"] (A"-q");;*) +flag ["ocaml"; "ocamllex"; "quiet"] (A"-q");; + +let ocaml_warn_flag c = + flag ["ocaml"; "compile"; sprintf "warn_%c" (Char.uppercase c)] + (S[A"-w"; A (sprintf "%c" (Char.uppercase c))]); + flag ["ocaml"; "compile"; sprintf "warn_error_%c" (Char.uppercase c)] + (S[A"-warn-error"; A (sprintf "%c" (Char.uppercase c))]); + flag ["ocaml"; "compile"; sprintf "warn_%c" (Char.lowercase c)] + (S[A"-w"; A (sprintf "%c" (Char.lowercase c))]); + flag ["ocaml"; "compile"; sprintf "warn_error_%c" (Char.lowercase c)] + (S[A"-warn-error"; A (sprintf "%c" (Char.lowercase c))]);; + +List.iter ocaml_warn_flag ['A'; 'C'; 'D'; 'E'; 'F'; 'L'; 'M'; 'P'; 'S'; 'U'; 'V'; 'Y'; 'Z'; 'X'];; + +(** Ocamlbuild plugin for it's own building *) +let install_lib = lazy (try Sys.getenv "INSTALL_LIB" with Not_found -> !*stdlib_dir/"ocamlbuild" (* not My_std.getenv since it's lazy*)) in +file_rule "ocamlbuild_where.ml" + ~prod:"%ocamlbuild_where.ml" + ~cache:(fun _ -> !*install_lib) + begin fun _ oc -> + Printf.fprintf oc "let where = ref %S;;\n" !*install_lib + end;; +ocaml_lib "ocamlbuildlib";; +ocaml_lib "ocamlbuildlightlib";; + +end in () diff --git a/ocamlbuild/ocaml_specific.mli b/ocamlbuild/ocaml_specific.mli new file mode 100644 index 0000000000..1234b0dec2 --- /dev/null +++ b/ocamlbuild/ocaml_specific.mli @@ -0,0 +1,17 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +val nop : 'a -> 'b -> unit +val ocaml_lib : ?extern:bool -> ?byte:bool -> ?native:bool -> ?dir:Pathname.t -> Pathname.t -> unit + +val init : unit -> unit diff --git a/ocamlbuild/ocaml_tools.ml b/ocamlbuild/ocaml_tools.ml new file mode 100644 index 0000000000..3c8253d33c --- /dev/null +++ b/ocamlbuild/ocaml_tools.ml @@ -0,0 +1,73 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +open My_std +open Pathname.Operators +open Tags.Operators +open Tools +open Command +open Ocaml_utils + +let ocamlyacc mly env _build = + let mly = env mly in + Cmd(S[!Options.ocamlyacc; T(tags_of_pathname mly++"ocaml"++"parser"++"ocamlyacc"); + flags_of_pathname mly; Px mly]) + +let ocamllex mll env _build = + let mll = env mll in + Cmd(S[!Options.ocamllex; T(tags_of_pathname mll++"ocaml"++"lexer"++"ocamllex"); + flags_of_pathname mll; Px mll]) + +let infer_interface ml mli env build = + let ml = env ml and mli = env mli in + Ocaml_compiler.prepare_compile build ml; + Cmd(S[!Options.ocamlc; A"-i"; T(tags_of_pathname ml++"ocaml"++"infer_interface"); P ml; Sh">"; Px mli]) + +let menhir mly env build = + let mly = env mly in + Ocaml_compiler.prepare_compile build mly; + Cmd(S[!Options.ocamlyacc; T(tags_of_pathname mly++"ocaml"++"parser"++"menhir"); + A"--infer"; flags_of_pathname mly; Px mly]) + +let ocamldoc_c tags arg odoc = + let tags = tags++"ocaml" in + Cmd (S [!Options.ocamldoc; A"-dump"; Px odoc; T(tags++"doc"); + ocaml_ppflags tags; flags_of_pathname arg; + ocaml_include_flags arg; P arg]) + +let ocamldoc_l tags deps out = + let tags = tags++"ocaml" in + Seq[Cmd (S[A"rm"; A"-rf"; Px out]); + Cmd (S[A"mkdir"; A"-p"; Px out]); + Cmd (S [!Options.ocamldoc; + S(List.map (fun a -> S[A"-load"; P a]) deps); + T(tags++"doc"); + A"-html"; + A"-d"; + Px out])] + +let document_ocaml_interf mli odoc env build = + let mli = env mli and odoc = env odoc in + Ocaml_compiler.prepare_compile build mli; + ocamldoc_c (tags_of_pathname mli++"interf") mli odoc + +let document_ocaml_project odocl docdir env build = + let odocl = env odocl and docdir = env docdir in + let contents = string_list_of_file odocl in + let include_dirs = Pathname.include_dirs_of (Pathname.dirname odocl) in + let to_build = + List.map begin fun module_name -> + expand_module include_dirs module_name ["odoc"] + end contents in + let module_paths = List.map Outcome.good (build to_build) in + ocamldoc_l (tags_of_pathname docdir) module_paths docdir diff --git a/ocamlbuild/ocaml_tools.mli b/ocamlbuild/ocaml_tools.mli new file mode 100644 index 0000000000..c50fb5f5ad --- /dev/null +++ b/ocamlbuild/ocaml_tools.mli @@ -0,0 +1,22 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +val ocamldoc_c : Tags.t -> string -> string -> Command.t +val ocamldoc_l : Tags.t -> string list -> string -> Command.t + +val ocamlyacc : string -> Rule.action +val ocamllex : string -> Rule.action +val menhir : string -> Rule.action +val infer_interface : string -> string -> Rule.action +val document_ocaml_interf : string -> string -> Rule.action +val document_ocaml_project : string -> string -> Rule.action diff --git a/ocamlbuild/ocaml_utils.ml b/ocamlbuild/ocaml_utils.ml new file mode 100644 index 0000000000..f669b3e50e --- /dev/null +++ b/ocamlbuild/ocaml_utils.ml @@ -0,0 +1,87 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +open My_std +open Format +open Log +open Pathname.Operators +open Tags.Operators +open Tools +open Command;; + + +module S = Set.Make(String) + +let stdlib_dir = lazy begin + (* FIXME *) + let ocamlc_where = sprintf "%s/ocamlc.where" (Pathname.pwd / !Options.build_dir) in + let () = Command.execute ~quiet:true (Cmd(S[!Options.ocamlc; A"-where"; Sh">"; P ocamlc_where])) in + String.chomp (read_file ocamlc_where) +end + +let module_name_of_filename f = String.capitalize (Pathname.remove_extensions f) +let module_name_of_pathname x = + module_name_of_filename (Pathname.to_string (Pathname.basename x)) + +let ignore_stdlib x = + if !Options.nostdlib then false + else + let x' = !*stdlib_dir/((String.uncapitalize x)-.-"cmi") in + Pathname.exists x' + +let non_dependencies = ref [] +let non_dependency m1 m2 = non_dependencies := (m1, m2) :: !non_dependencies + +let ignore_this_module modpath x = + List.mem (modpath, x) !non_dependencies + || (List.mem x !Options.ignore_list) || ignore_stdlib x + +let keep_this_module modpath x = + if ignore_this_module modpath x then + let () = dprintf 3 "This module (%s) is ignored by %s" x modpath in false + else true + +let expand_module include_dirs module_name exts = + List.fold_right begin fun include_dir -> + List.fold_right begin fun ext acc -> + let module_name_ext = module_name-.-ext in + include_dir/(String.uncapitalize module_name_ext) :: + include_dir/(String.capitalize module_name_ext) :: acc + end exts + end include_dirs [] + +let string_list_of_file file = + with_input_file file begin fun ic -> + Lexers.blank_sep_strings (Lexing.from_channel ic) + end +let print_path_list = Pathname.print_path_list + +let ocaml_ppflags tags = + let flags = Flags.of_tags (tags++"ocaml"++"pp") in + let reduced = Command.reduce flags in + if reduced = N then N else S[A"-pp"; Quote reduced] + +let ocaml_add_include_flag x acc = + if x = Pathname.current_dir_name then acc else A"-I" :: A x :: acc + +let ocaml_include_flags path = + S (List.fold_right ocaml_add_include_flag (Pathname.include_dirs_of (Pathname.dirname path)) []) + +let info_libraries = Hashtbl.create 103 + +let libraries = Hashtbl.create 103 +let libraries_of m = + try Hashtbl.find libraries m with Not_found -> [] +let use_lib m lib = Hashtbl.replace libraries m (lib :: libraries_of m) + +let cmi_of = Pathname.update_extensions "cmi" diff --git a/ocamlbuild/ocaml_utils.mli b/ocamlbuild/ocaml_utils.mli new file mode 100644 index 0000000000..00ece2c6a6 --- /dev/null +++ b/ocamlbuild/ocaml_utils.mli @@ -0,0 +1,30 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +val stdlib_dir : Pathname.t Lazy.t +val module_name_of_filename : Pathname.t -> string +val module_name_of_pathname : Pathname.t -> string +val ignore_stdlib : string -> bool +val non_dependency : string -> string -> unit +val expand_module : + Pathname.t list -> Pathname.t -> string list -> Pathname.t list +val string_list_of_file : string -> string list +val ocaml_ppflags : Tags.t -> Command.spec +val ocaml_include_flags : Pathname.t -> Command.spec +val libraries_of : Pathname.t -> Pathname.t list +val use_lib : Pathname.t -> Pathname.t -> unit +val cmi_of : Pathname.t -> Pathname.t +val ocaml_add_include_flag : string -> Command.spec list -> Command.spec list +val keep_this_module : string -> string -> bool + +val info_libraries : (string, string * bool) Hashtbl.t diff --git a/ocamlbuild/ocamlbuild-presentation.rslide b/ocamlbuild/ocamlbuild-presentation.rslide new file mode 100644 index 0000000000..6c071b6bca --- /dev/null +++ b/ocamlbuild/ocamlbuild-presentation.rslide @@ -0,0 +1,116 @@ +documentclass :beamer, :t, :compress, :red +usepackage :inputenc, :utf8 + +title "ocamlbuild, a tool for automatic compilation of O'Caml projects" +authors "Berke Durak", "Nicolas Pouillard" +institute do + > @@Berke.Durak@inria.fr@@ + hfill + > @@Nicolas.Pouillard@inria.fr@@ +end + +usetheme :JuanLesPins +usefonttheme :serif +beamer_header '\setbeamercolor*{titlelike}{parent=structure}' +at_begin_subsection do + slide "Outline" do + tableofcontents 'sectionstyle=show/hide', + 'subsectionstyle=show/shaded/hide' + end +end +beamer_footline + +words "**O'Caml**" + +words "??ocamlbuild??" + +maketitle + +slide "Why such a tool?" do + * To make our O'Caml life easier. + * A Makefile is so painful. +end + +slide "What does ocamlbuild handle?" do + + box "Regular O'Caml projects of arbitrary size" do + > Trivially handled using the command line options. + end + + box "Mostly regular O'Caml projects with common exceptions" do + > Requires writing one _tag_ file that declares those exceptions. + end + + box "Almost any project" do + > Accomplished by writing an ocamlbuild plugin. + end + +end + +slide "What kind of services for a project?" do + * Compilation of the whole project. + * Minimal re-compilation after a change. + * Other builds like the documentation. + * Different build directories at a same time. + * Cleaning of what's built. + * Keeping an hygienic source directory. + * Saving time! +end + +slide "What's a regular O'Caml project?" do + * O'Caml compilation units (ml and mli files). + * O'Caml parsers and lexers specifications (mly and mll files). + * Internal packages and libraries (mlpack, mllib). + * Use of external libraries. + * One main O'Caml unit that represents the starting point. +end + +slide "How difficult are regular projects?" do + * +end + +slide "What's an exception?" do + box "Some flags can be different for some files" do + * Enable/disable some warning flags. + * Debugging (-g), profiling (-p), type annotations flags, + recursive types, -linkall, -thread, -custom. + end + * Have to link this binary with that library. + * Include or exclude that directory. + * Some dependencies. +end + +slide "??Make?? and exceptions" do + * The ??make?? tool can handle rules but not exceptions. + * With ??make?? exceptions are encoded as specific rules. + * This generally makes rules and exceptions tightly bound by variables. + * This way totally doesn't scale, and is not *modular*. +end + +slide "The tags, our way to specify exceptions" do + box "Rules produce commands with *tagged holes*" do + : let tagged_hole = tags_for(ml)++"ocaml"++"compile"++"byte" in + Cmd(S[A"ocamlc"; A"-c"; T(tagged_hole); P ml; A"-o"; Px cmo]) + end + box "These holes can be filled by some command pieces (flags)" do + : flag ["ocaml"; "compile"; "byte"; "rectypes"] (A"-rectypes") + end + box "Files can also be tagged and make things happen" do + : "foo/bar.ml": rectypes + end +end + +slide "The tags file (_tags)" do + * Each line is a pattern and a set of tags. + * The pattern is a boolean tree (*and*, *or*, *not*). + * Leafs are either constant strings or glob patterns. + * In the set of tags, they can appear positively or negatively. +end + +slide "" do + +end + +slide "A big thanks to" do + * Alain, Xavier, Michel... +end
\ No newline at end of file diff --git a/ocamlbuild/ocamlbuild.ml b/ocamlbuild/ocamlbuild.ml new file mode 100644 index 0000000000..96d78185b2 --- /dev/null +++ b/ocamlbuild/ocamlbuild.ml @@ -0,0 +1,15 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +My_unix_with_unix.setup (); +Ocamlbuild_pack.Main.main () diff --git a/ocamlbuild/ocamlbuild.mli b/ocamlbuild/ocamlbuild.mli new file mode 100644 index 0000000000..3d643e3e52 --- /dev/null +++ b/ocamlbuild/ocamlbuild.mli @@ -0,0 +1,15 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +(** Nothing to export for now *) + diff --git a/ocamlbuild/ocamlbuild.odocl b/ocamlbuild/ocamlbuild.odocl new file mode 100644 index 0000000000..d4374386af --- /dev/null +++ b/ocamlbuild/ocamlbuild.odocl @@ -0,0 +1,38 @@ +Log +My_unix +My_std +Std_signatures +Signatures +Shell +Display +Command +Configuration +Discard_printf +Flags +Hygiene +Options +Pathname +Report +Resource +Rule +Slurp +Solver +Tags +Tools +Fda +Ocaml_specific +Ocaml_arch +Ocamlbuild_where +Lexers +Glob +Bool +Glob_ast +Glob_lexer +Plugin +Main +Hooks +Ocaml_utils +Ocaml_tools +Ocaml_compiler +Ocamldep +Ocaml_dependencies diff --git a/ocamlbuild/ocamlbuild_pack.mlpack b/ocamlbuild/ocamlbuild_pack.mlpack new file mode 100644 index 0000000000..d4374386af --- /dev/null +++ b/ocamlbuild/ocamlbuild_pack.mlpack @@ -0,0 +1,38 @@ +Log +My_unix +My_std +Std_signatures +Signatures +Shell +Display +Command +Configuration +Discard_printf +Flags +Hygiene +Options +Pathname +Report +Resource +Rule +Slurp +Solver +Tags +Tools +Fda +Ocaml_specific +Ocaml_arch +Ocamlbuild_where +Lexers +Glob +Bool +Glob_ast +Glob_lexer +Plugin +Main +Hooks +Ocaml_utils +Ocaml_tools +Ocaml_compiler +Ocamldep +Ocaml_dependencies diff --git a/ocamlbuild/ocamlbuild_plugin.ml b/ocamlbuild/ocamlbuild_plugin.ml new file mode 100644 index 0000000000..827c93417d --- /dev/null +++ b/ocamlbuild/ocamlbuild_plugin.ml @@ -0,0 +1,53 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) + +open Ocamlbuild_pack +include Ocamlbuild_pack.My_std +module Arch = Ocamlbuild_pack.Ocaml_arch +module Command = Ocamlbuild_pack.Command +module Pathname = Ocamlbuild_pack.Pathname +module Tags = Ocamlbuild_pack.Tags +include Pathname.Operators +include Tags.Operators +module Rule = Ocamlbuild_pack.Rule +module Options = Ocamlbuild_pack.Options +include Rule.Common_commands +type env = Pathname.t -> Pathname.t +type builder = Pathname.t list list -> (Pathname.t, exn) Ocamlbuild_pack.My_std.Outcome.t list +type action = env -> builder -> Command.t +let rule = Rule.rule +let dep = Rule.dep +let file_rule = Rule.file_rule +let copy_rule = Rule.copy_rule +let custom_rule = Rule.custom_rule +let flag = Ocamlbuild_pack.Flags.flag +let non_dependency = Ocamlbuild_pack.Ocaml_utils.non_dependency +let use_lib = Ocamlbuild_pack.Ocaml_utils.use_lib +let module_name_of_pathname = Ocamlbuild_pack.Ocaml_utils.module_name_of_pathname +let string_list_of_file = Ocamlbuild_pack.Ocaml_utils.string_list_of_file +let expand_module = Ocamlbuild_pack.Ocaml_utils.expand_module +let tags_of_pathname = Ocamlbuild_pack.Tools.tags_of_pathname +let hide_package_contents = Ocamlbuild_pack.Ocaml_compiler.hide_package_contents +let tag_file file tags = + Ocamlbuild_pack.Configuration.parse_string (Printf.sprintf "%S: %s" file (String.concat ", " tags));; +let tag_any tags = + Ocamlbuild_pack.Configuration.parse_string (Printf.sprintf "true: %s" (String.concat ", " tags));; +type hook = Ocamlbuild_pack.Hooks.message = + | Before_hygiene + | After_hygiene + | Before_options + | After_options + | Before_rules + | After_rules +let dispatch = Ocamlbuild_pack.Hooks.setup_hooks diff --git a/ocamlbuild/ocamlbuild_plugin.mli b/ocamlbuild/ocamlbuild_plugin.mli new file mode 100644 index 0000000000..8e642f31a2 --- /dev/null +++ b/ocamlbuild/ocamlbuild_plugin.mli @@ -0,0 +1,5 @@ +include Ocamlbuild_pack.Signatures.PLUGIN + with module Pathname = Ocamlbuild_pack.Pathname + and module Outcome = Ocamlbuild_pack.My_std.Outcome + and module Tags = Ocamlbuild_pack.Tags + and module Command = Ocamlbuild_pack.Command diff --git a/ocamlbuild/ocamlbuild_where.mli b/ocamlbuild/ocamlbuild_where.mli new file mode 100644 index 0000000000..ba8b3a720d --- /dev/null +++ b/ocamlbuild/ocamlbuild_where.mli @@ -0,0 +1,14 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +val where : string ref diff --git a/ocamlbuild/ocamlbuildlib.mllib b/ocamlbuild/ocamlbuildlib.mllib new file mode 100644 index 0000000000..eb1598d64a --- /dev/null +++ b/ocamlbuild/ocamlbuildlib.mllib @@ -0,0 +1,4 @@ +Executor +My_unix_with_unix +Ocamlbuild_pack +Ocamlbuild_plugin diff --git a/ocamlbuild/ocamlbuildlight.ml b/ocamlbuild/ocamlbuildlight.ml new file mode 100644 index 0000000000..e0e90041ca --- /dev/null +++ b/ocamlbuild/ocamlbuildlight.ml @@ -0,0 +1,14 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +Ocamlbuild_pack.Main.main ();; diff --git a/ocamlbuild/ocamlbuildlight.mli b/ocamlbuild/ocamlbuildlight.mli new file mode 100644 index 0000000000..274f0c4a23 --- /dev/null +++ b/ocamlbuild/ocamlbuildlight.mli @@ -0,0 +1,14 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +(* Nothing *) diff --git a/ocamlbuild/ocamlbuildlightlib.mllib b/ocamlbuild/ocamlbuildlightlib.mllib new file mode 100644 index 0000000000..dc38da3dc4 --- /dev/null +++ b/ocamlbuild/ocamlbuildlightlib.mllib @@ -0,0 +1,2 @@ +Ocamlbuild_pack +Ocamlbuild_plugin diff --git a/ocamlbuild/ocamldep.ml b/ocamlbuild/ocamldep.ml new file mode 100644 index 0000000000..8bdfcde818 --- /dev/null +++ b/ocamlbuild/ocamldep.ml @@ -0,0 +1,72 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +open My_std +open Log +open Command +open Tags.Operators +open Tools +open Ocaml_utils +open Pathname.Operators + +exception Error of string + +let ocamldep_command arg = + let tags = tags_of_pathname arg++"ocaml"++"ocamldep" in + S [!Options.ocamldep; T tags; ocaml_ppflags tags; + flags_of_pathname arg; A "-modules"] + +let menhir_ocamldep_command arg out = + let tags = tags_of_pathname arg++"ocaml"++"menhir_ocamldep" in + Cmd (S [!Options.ocamlyacc; T tags; A"--raw-depend"; + A"--ocamldep"; Quote (ocamldep_command arg); + P arg; Sh ">"; Px out]) + +let ocamldep_command arg out = + Cmd (S[ocamldep_command arg; P arg; Sh ">"; Px out]) + +let module_dependencies = Hashtbl.create 103 +let module_dependencies_of module_path = + try Hashtbl.find module_dependencies module_path with Not_found -> [] +let register_module_dependencies module_path deps = + Hashtbl.replace module_dependencies module_path + (List.union (module_dependencies_of module_path) (List.filter (keep_this_module module_path) deps)) + +let depends name ?tags ~prod ~dep ?insert ?(ocamldep_command=ocamldep_command) () = + Rule.custom_rule name ?tags ~prod ~dep ?insert + ~cache:(fun env -> Command.to_string (ocamldep_command (env dep) (env prod))) + begin fun env ~cached -> + let arg = env dep in + let out = env prod in + let cmd = ocamldep_command arg out in + let () = dprintf 6 "ocamldep: %a %a" Pathname.print arg Command.print cmd in + if not (Pathname.exists arg) then + raise (Error(sbprintf "Ocamldep.ocamldep: no input file (%a)" Pathname.print arg)) + else begin + Command.execute ~pretend:cached cmd; + with_input_file out begin fun ic -> + let ocamldep_output = + try Lexers.ocamldep_output (Lexing.from_channel ic) + with Lexers.Error msg -> raise (Error(Printf.sprintf "Ocamldep.ocamldep: bad output (%s)" msg)) in + let ocamldep_output = + List.fold_right begin fun (_, deps) acc -> + List.union deps acc + end ocamldep_output [] in + let ocamldep_output = + if !Options.nostdlib && not (Tags.mem "nopervasives" (tags_of_pathname arg)) then + "Pervasives" :: ocamldep_output + else ocamldep_output in + register_module_dependencies arg ocamldep_output + end + end + end diff --git a/ocamlbuild/ocamldep.mli b/ocamlbuild/ocamldep.mli new file mode 100644 index 0000000000..b576616285 --- /dev/null +++ b/ocamlbuild/ocamldep.mli @@ -0,0 +1,26 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +exception Error of string +val ocamldep_command : Pathname.t -> Pathname.t -> Command.t +val menhir_ocamldep_command : Pathname.t -> Pathname.t -> Command.t +val module_dependencies_of : Pathname.t -> string list +val register_module_dependencies : Pathname.t -> string list -> unit +val depends : + string -> + ?tags:string list -> + prod:string -> + dep:string -> + ?insert:[`top | `before of string | `after of string | `bottom] -> + ?ocamldep_command:(Pathname.t -> Pathname.t -> Command.t) -> + unit -> unit diff --git a/ocamlbuild/options.ml b/ocamlbuild/options.ml new file mode 100644 index 0000000000..cc46b9f36a --- /dev/null +++ b/ocamlbuild/options.ml @@ -0,0 +1,194 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) + +let version = "ocamlbuild 0.1";; + +type command_spec = Command.spec + +open My_std +open Arg +open Format +open Command + +let entry = ref None +let build_dir = ref "_build" +let include_dirs = ref [] +let exclude_dirs = ref [] +let nothing_should_be_rebuilt = ref false +let sterilize = ref false +let hygiene = ref true +let ignore_auto = ref true +let plugin = ref true +let just_plugin = ref false +let native_plugin = ref true +let make_links = ref true +let nostdlib = ref false +let use_menhir = ref false +let ocamlc = ref (A"ocamlc.opt") +let ocamlopt = ref (A"ocamlopt.opt") +let ocamldep = ref (A"ocamldep.opt") +let ocamldoc = ref (A"ocamldoc.opt") +let ocamlyacc = ref (A"ocamlyacc") +let ocamllex = ref (A"ocamllex") +let ocamlmklib = ref (A"ocamlmklib") +let ocamlrun = ref N +let program_to_execute = ref false +let must_clean = ref false +let ext_lib = ref "a" +let ext_obj = ref "o" +let ext_dll = ref "so" + +let targets_internal = ref [] +let ocaml_libs_internal = ref [] +let ocaml_lflags_internal = ref [] +let ocaml_cflags_internal = ref [] +let ocaml_ppflags_internal = ref [] +let ocaml_yaccflags_internal = ref [] +let ocaml_lexflags_internal = ref [] +let program_args_internal = ref [] +let ignore_list_internal = ref [] +let tags_internal = ref [["quiet"]] + +let my_include_dirs = ref [[Filename.current_dir_name]] +let my_exclude_dirs = ref [[".svn"; "CVS"]] + +let pwd = Sys.getcwd () + +let internal_log_file = ref None +let set_log_file file = + internal_log_file := Some file; + Log.log_file := lazy begin + if !Log.level <= 0 + || ((!plugin || !just_plugin) + && sys_file_exists (filename_concat pwd "myocamlbuild.ml")) then + None + else Some(filename_concat pwd file) + end + +let () = set_log_file "_log" + +let dummy = "*invalid-dummy-string*";; (* Dummy string for delimiting the latest argument *) + +let add_to rxs x = + let xs = Lexers.comma_sep_strings (Lexing.from_string x) in + rxs := xs :: !rxs +let add_to' rxs x = + if x <> dummy then + rxs := [x] :: !rxs + else + () +let set_cmd rcmd = String (fun s -> rcmd := Sh s) +let spec = + Arg.align + [ + "-version", Unit (fun () -> print_endline version; raise Exit_OK), " Display the version"; + "-quiet", Unit (fun () -> Log.level := 0), " Make as quiet as possible"; + "-verbose", Int (fun i -> Log.level := i + 2), "<level> Set the verbosity level"; + "-log", String set_log_file, "<file> Set log file"; + "-no-log", Unit (fun () -> Log.log_file := lazy None), " No log file"; + "-clean", Set must_clean, " Remove build directory and other files, then exit"; + + "-I", String (add_to' my_include_dirs), "<path> Add to include directories"; + "-Is", String (add_to my_include_dirs), "<path,...> (same as above, but accepts a comma-separated list)"; + "-X", String (add_to' my_exclude_dirs), "<path> Directory to ignore"; + "-Xs", String (add_to my_exclude_dirs), "<path,...> (idem)"; + + "-lib", String (add_to' ocaml_libs_internal), "<flag> Link to this ocaml library"; + "-libs", String (add_to ocaml_libs_internal), "<flag,...> (idem)"; + "-lflag", String (add_to' ocaml_lflags_internal), "<flag> Add to ocamlc link flags"; + "-lflags", String (add_to ocaml_lflags_internal), "<flag,...> (idem)"; + "-cflag", String (add_to' ocaml_cflags_internal), "<flag> Add to ocamlc compile flags"; + "-cflags", String (add_to ocaml_cflags_internal), "<flag,...> (idem)"; + "-yaccflag", String (add_to' ocaml_yaccflags_internal), "<flag> Add to ocamlyacc flags"; + "-yaccflags", String (add_to ocaml_yaccflags_internal), "<flag,...> (idem)"; + "-lexflag", String (add_to' ocaml_lexflags_internal), "<flag> Add to ocamllex flags"; + "-lexflags", String (add_to ocaml_lexflags_internal), "<flag,...> (idem)"; + "-ppflag", String (add_to' ocaml_ppflags_internal), "<flag> Add to ocaml preprocessing flags"; + "-pp", String (add_to ocaml_ppflags_internal), "<flag,...> (idem)"; + "-tag", String (add_to' tags_internal), "<tag> Add to default tags"; + "-tags", String (add_to tags_internal), "<tag,...> (idem)"; + + "-ignore", String (add_to ignore_list_internal), "<module,...> Don't try to build these modules"; + "-no-links", Clear make_links, " Don't make links of produced final targets"; + "-no-skip", Clear ignore_auto, " Don't skip modules that are requested by ocamldep but cannot be built"; + "-no-hygiene", Clear hygiene, " Don't apply sanity-check rules"; + "-no-plugin", Clear plugin, " Don't build myocamlbuild.ml"; + "-no-stdlib", Set nostdlib, " Don't ignore stdlib modules"; + "-just-plugin", Set just_plugin, " Just build myocamlbuild.ml"; + "-byte-plugin", Clear native_plugin, " Don't use a native plugin but bytecode"; + "-sterilize", Set sterilize, " Enforce sanity-check rules by removing leftover files (DANGER)"; + "-nothing-should-be-rebuilt", Set nothing_should_be_rebuilt, " Fail if something needs to be rebuilt"; + "-classic-display", Set Log.classic_display, " Display executed commands the old-fashioned way"; + "-use-menhir", Unit(fun () -> use_menhir := true; ocamlyacc := A"menhir"), + " Use menhir instead of ocamlyacc"; + + "-j", Set_int Command.jobs, "<N> Allow N jobs at once (0 for unlimited)"; + + "-build-dir", Set_string build_dir, "<path> Set build directory"; + "-install-dir", Set_string Ocamlbuild_where.where, "<path> Set the install directory"; + "-where", Unit (fun () -> print_endline !Ocamlbuild_where.where; raise Exit_OK), " Display the install directory"; + + "-ocamlc", set_cmd ocamlc, "<command> Set the OCaml bytecode compiler"; + "-ocamlopt", set_cmd ocamlopt, "<command> Set the OCaml native compiler"; + "-ocamldep", set_cmd ocamldep, "<command> Set the OCaml dependency tool"; + "-ocamlyacc", set_cmd ocamlyacc, "<command> Set the ocamlyacc tool"; + "-menhir", set_cmd ocamlyacc, "<command> Set the menhir tool (use it after -use-menhir)"; + "-ocamllex", set_cmd ocamllex, "<command> Set the ocamllex tool"; + (* Not set since we perhaps want to replace ocamlmklib *) + (* "-ocamlmklib", set_cmd ocamlmklib, "<command> Set the ocamlmklib tool"; *) + "-ocamlrun", set_cmd ocamlrun, "<command> Set the ocamlrun tool"; + + "--", Rest (fun x -> program_to_execute := true; add_to' program_args_internal x), + " Stop argument processing, remaining arguments are given to the user program"; + ] + +let targets = ref [] +let ocaml_libs = ref [] +let ocaml_lflags = ref [] +let ocaml_cflags = ref [] +let ocaml_ppflags = ref [] +let ocaml_yaccflags = ref [] +let ocaml_lexflags = ref [] +let program_args = ref [] +let ignore_list = ref [] +let tags = ref [] + +let init () = + let anon_fun = add_to' targets_internal in + let usage_msg = sprintf "Usage %s [options] <target>" Sys.argv.(0) in + let argv' = Array.concat [Sys.argv; [|dummy|]] in + parse_argv argv' spec anon_fun usage_msg; + Shell.mkdir_p !build_dir; + let reorder x y = x := (List.concat (List.rev !y)) in + reorder targets targets_internal; + reorder ocaml_libs ocaml_libs_internal; + reorder ocaml_cflags ocaml_cflags_internal; + reorder ocaml_lflags ocaml_lflags_internal; + reorder ocaml_ppflags ocaml_ppflags_internal; + reorder ocaml_yaccflags ocaml_yaccflags_internal; + reorder ocaml_lexflags ocaml_lexflags_internal; + reorder program_args program_args_internal; + reorder tags tags_internal; + reorder ignore_list ignore_list_internal; + + let dir_reorder my dir = + let d = !dir in + reorder dir my; + dir := List.filter sys_file_exists (!dir @ d) + in + dir_reorder my_include_dirs include_dirs; + dir_reorder my_exclude_dirs exclude_dirs; + + ignore_list := List.map String.capitalize !ignore_list +;; diff --git a/ocamlbuild/options.mli b/ocamlbuild/options.mli new file mode 100644 index 0000000000..0d94c5d0b2 --- /dev/null +++ b/ocamlbuild/options.mli @@ -0,0 +1,18 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) + +include Signatures.OPTIONS with type command_spec = Command.spec + +val entry : bool Slurp.entry option ref +val init : unit -> unit diff --git a/ocamlbuild/pathname.ml b/ocamlbuild/pathname.ml new file mode 100644 index 0000000000..24793ddfb8 --- /dev/null +++ b/ocamlbuild/pathname.ml @@ -0,0 +1,194 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +open My_std +open Format +open Log + +type t = string + +include Filename + +let print_strings = List.print String.print + +let concat = filename_concat + +let compare = compare + +let print = pp_print_string + +let mk s = s + +let pwd = Sys.getcwd () + +let add_extension ext x = x ^ "." ^ ext + +let check_extension x ext = + let lx = String.length x and lext = String.length ext in + lx > lext + 1 && x.[lx - lext - 1] = '.' && String.is_suffix x ext + +module Operators = struct + let ( / ) = concat + let ( -.- ) file ext = add_extension ext file +end +open Operators + +let in_source_dir p = + if is_implicit p then pwd/p else invalid_arg (sprintf "in_source_dir: %S" p) + +let equal x y = x = y + +let to_string x = x + +let is_link = Shell.is_link +let readlink = Shell.readlink +let is_directory x = + try (My_unix.stat x).My_unix.stat_file_kind = My_unix.FK_dir + with Sys_error _ -> false +let readdir x = Outcome.good (sys_readdir x) + +let dir_seps = ['/';'\\'] (* FIXME add more *) +let parent x = concat parent_dir_name x + +(* [is_prefix x y] is [x] a pathname prefix of [y] *) +let is_prefix x y = + let lx = String.length x and ly = String.length y in + if lx = ly then x = (String.before y lx) + else if lx < ly then x = (String.before y lx) && List.mem y.[lx] dir_seps + else false + +let link_to_dir p dir = is_link p && is_prefix dir (readlink p) + +let remove_extension x = + try chop_extension x + with Invalid_argument _ -> x +let get_extension x = + try + let pos = String.rindex x '.' in + String.after x (pos + 1) + with Not_found -> "" +let update_extension ext x = + add_extension ext (chop_extension x) + +let chop_extensions x = + let dirname = dirname x and basename = basename x in + try + let pos = String.index basename '.' in + dirname / (String.before basename pos) + with Not_found -> invalid_arg "chop_extensions: no extensions" +let remove_extensions x = + try chop_extensions x + with Invalid_argument _ -> x +let get_extensions x = + let basename = basename x in + try + let pos = String.index basename '.' in + String.after basename (pos + 1) + with Not_found -> "" +let update_extensions ext x = + add_extension ext (chop_extensions x) + +let clean_up_links entry = + Slurp.filter begin fun path name _ -> + let pathname = in_source_dir (path/name) in + if link_to_dir pathname !Options.build_dir then + let z = readlink pathname in + (if not (Sys.file_exists z) then + Shell.rm pathname; false) + else true + end entry + +let clean_up_link_to_build () = + Options.entry := Some(clean_up_links (the !Options.entry)) + +let source_dir_path_set_without_links_to_build = + lazy begin + clean_up_link_to_build (); + Slurp.fold (fun path name _ -> StringSet.add (path/name)) + (the !Options.entry) StringSet.empty + end + +let exists_in_source_dir p = + if !*My_unix.is_degraded then sys_file_exists (in_source_dir p) + else StringSet.mem p !*source_dir_path_set_without_links_to_build + +let clean_links () = + if !*My_unix.is_degraded then + () + else + ignore (clean_up_link_to_build ()) + +let exists = sys_file_exists + +let copy = Shell.cp +let remove = Shell.rm +let try_remove x = if exists x then Shell.rm x +let read = read_file + +let with_input_file = with_input_file + +let with_output_file = with_output_file + +let print_path_list = List.print print + +let root = mk "__root__" + +let context_table = Hashtbl.create 107 + +let merge_include_dirs a b = + let rec aux a b = + match a, b with + | [], _ -> b + | _, [] -> a + | _, x::xs -> + if List.mem x a then aux a xs + else aux (x :: a) xs + in List.rev (aux (List.rev a) b) + +let define_context dir context = + let dir = if dir = "" then current_dir_name else dir in + try + let context = merge_include_dirs context (Hashtbl.find context_table dir) in + Hashtbl.replace context_table dir context + with Not_found -> + let context = merge_include_dirs context (!Options.include_dirs) in + Hashtbl.add context_table dir context + +let rec include_dirs_of dir = + try Hashtbl.find context_table dir + with Not_found -> dir :: List.filter (fun dir' -> dir <> dir') !Options.include_dirs + +(* +let include_dirs_of s = + let res = include_dirs_of s in + let () = dprintf 0 "include_dirs_of %S ->@ %a" s (List.print print) res + in res +*) + +let in_build_dir p = + if is_relative p then p + else + root/p (* XXX: Never reached *) + +let exists_in_build_dir p = exists (in_build_dir p) + +let same_contents x y = Digest.file x = Digest.file y + +let is_up_to_date b p = + let x = in_build_dir p in + if b then exists_in_source_dir p && exists x && same_contents x (in_source_dir p) + else not (exists_in_source_dir p) || exists x && same_contents x (in_source_dir p) + +let import_in_build_dir p = + let p_in_build_dir = in_build_dir p in + Shell.mkdir_p (dirname p); copy (in_source_dir p) p_in_build_dir diff --git a/ocamlbuild/pathname.mli b/ocamlbuild/pathname.mli new file mode 100644 index 0000000000..1bd1f09dbe --- /dev/null +++ b/ocamlbuild/pathname.mli @@ -0,0 +1,22 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +include Signatures.PATHNAME + +val is_up_to_date : bool -> t -> bool +val clean_up_links : bool Slurp.entry -> bool Slurp.entry +val exists_in_source_dir : t -> bool +val exists_in_build_dir : t -> bool +val import_in_build_dir : t -> unit +val in_build_dir : t -> t +val in_source_dir : t -> t diff --git a/ocamlbuild/plugin.ml b/ocamlbuild/plugin.ml new file mode 100644 index 0000000000..88516d517f --- /dev/null +++ b/ocamlbuild/plugin.ml @@ -0,0 +1,111 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +open My_std +open Format +open Log +open Pathname.Operators +open Tags.Operators +open Rule +open Tools +open Command +;; + +module Make(U:sig end) = + struct + let plugin = "myocamlbuild" + let plugin_file = plugin^".ml" + let plugin_config_file = plugin^"_config.ml" + let plugin_config_file_interface = plugin^"_config.mli" + + let we_have_a_config_file = sys_file_exists plugin_config_file + let we_need_a_plugin = !Options.plugin && sys_file_exists plugin_file + let we_have_a_plugin = sys_file_exists (!Options.build_dir/plugin) + let we_have_a_config_file_interface = sys_file_exists plugin_config_file_interface + + let up_to_date_or_copy fn = + let fn' = !Options.build_dir/fn in + Pathname.exists fn && + begin + Pathname.exists fn' && Pathname.same_contents fn fn' || + begin + Shell.cp fn fn'; + false + end + end + + let profiling = Tags.mem "profile" (tags_of_pathname plugin_file) + + let debugging = Tags.mem "debug" (tags_of_pathname plugin_file) + + let rebuild_plugin_if_needed () = + let a = up_to_date_or_copy plugin_file in + let b = (not we_have_a_config_file) or up_to_date_or_copy plugin_config_file in + let c = (not we_have_a_config_file_interface) or up_to_date_or_copy plugin_config_file_interface in + if a && b && c && we_have_a_plugin then + () (* Up to date *) + (* FIXME: remove ocamlbuild_config.ml in _build/ if removed in parent *) + else begin + let plugin_config = + if we_have_a_config_file then + if we_have_a_config_file_interface then + S[P plugin_config_file_interface; P plugin_config_file] + else P plugin_config_file + else N in + let cma, cmo, more_options, compiler = + if !Options.native_plugin then + "cmxa", "cmx", (if profiling then A"-p" else N), !Options.ocamlopt + else + "cma", "cmo", (if debugging then A"-g" else N), !Options.ocamlc + in + let ocamlbuildlib, ocamlbuild, libs = + if (not !Options.native_plugin) && !*My_unix.is_degraded then + "ocamlbuildlightlib", "ocamlbuildlight", N + else + "ocamlbuildlib", "ocamlbuild", A("unix"-.-cma) + in + let ocamlbuildlib = ocamlbuildlib-.-cma in + let ocamlbuild = ocamlbuild-.-cmo in + let dir = !Ocamlbuild_where.where in + if not (sys_file_exists (dir/ocamlbuildlib)) then + failwith (sprintf "Cannot found %S in ocamlbuild -where directory" ocamlbuildlib); + let dir = if Pathname.is_implicit dir then Pathname.pwd/dir else dir in + let cmd = + Cmd(S[compiler; A"-I"; P dir; libs; more_options; + P(dir/ocamlbuildlib); plugin_config; P plugin_file; + P(dir/ocamlbuild); A"-o"; Px plugin]) + in + Shell.chdir !Options.build_dir; + Shell.rm_f plugin; + Command.execute cmd + end + + let execute_plugin_if_needed () = + if we_need_a_plugin then + begin + rebuild_plugin_if_needed (); + Shell.chdir Pathname.pwd; + if not !Options.just_plugin then + let spec = S[!Options.ocamlrun; P(!Options.build_dir/plugin); + A"-no-plugin"; atomize (List.tl (Array.to_list Sys.argv))] in + raise (Exit_silently_with_code (sys_command (Command.string_of_command_spec spec))) + end + else + () + end +;; + +let execute_plugin_if_needed () = + let module P = Make(struct end) in + P.execute_plugin_if_needed () +;; diff --git a/ocamlbuild/plugin.mli b/ocamlbuild/plugin.mli new file mode 100644 index 0000000000..a6d85b3404 --- /dev/null +++ b/ocamlbuild/plugin.mli @@ -0,0 +1,16 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +(* Plugin *) + +val execute_plugin_if_needed : unit -> unit diff --git a/ocamlbuild/ppcache.ml b/ocamlbuild/ppcache.ml new file mode 100644 index 0000000000..fe7a7d7442 --- /dev/null +++ b/ocamlbuild/ppcache.ml @@ -0,0 +1,88 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +open My_std +open Command +open Pathname.Operators +let () = Log.level := -1000 + +let usage () = + Format.eprintf "Usage: %s <preprocess-command> <preprocess-args>@." Sys.argv.(0); + exit 4 + +let () = if Array.length Sys.argv < 2 then usage () + +let args = List.tl (Array.to_list Sys.argv) + +let buf = Buffer.create 2048 + +let digest_file file = + Buffer.add_string buf (Digest.file file) +let digest_string string = + Buffer.add_string buf (Digest.string string) + +let search_in_path x = + if Sys.file_exists x then x else + try search_in_path x + with Not_found -> (Format.eprintf "Command not found %s@." x; exit 3) + +let cmd = + match args with + | ocamlrun :: x :: _ when String.contains_string ocamlrun 0 "ocamlrun" <> None -> + digest_file (search_in_path ocamlrun); x + | x :: _ -> x + | _ -> usage () + +let output = ref "" + +let () = digest_file (search_in_path cmd) + +let rec loop = + function + | [] -> Digest.string (Buffer.contents buf) + | ("-impl"|"-intf") :: x :: xs -> + digest_string x; digest_file x; loop xs + | "-o" :: x :: xs -> + output := x; loop xs + | x :: xs -> + let ext = Pathname.get_extension x in + digest_string x; + (match ext with + | "cmo" | "cma" | "ml" | "mli" -> digest_file x + | _ -> ()); + loop xs + +let digest = loop args;; + +let cache_dir = "/tmp/ppcache";; (* FIXME *) + +let () = Shell.mkdir_p cache_dir;; + +let path = cache_dir/(Digest.to_hex digest);; + +if sys_file_exists path then + if !output = "" then + print_string (read_file path) + else + Shell.cp path !output +else + let cmd = atomize args in + if !output = "" then begin + let tmp = path^".tmp" in + Command.execute (Cmd(S[cmd; Sh ">"; A tmp])); + Shell.mv tmp path; + print_string (read_file path) + end else begin + Command.execute (Cmd cmd); + Shell.cp !output path + end diff --git a/ocamlbuild/ppcache.mli b/ocamlbuild/ppcache.mli new file mode 100644 index 0000000000..419a1f5cda --- /dev/null +++ b/ocamlbuild/ppcache.mli @@ -0,0 +1,14 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +(* nothing to export *) diff --git a/ocamlbuild/report.ml b/ocamlbuild/report.ml new file mode 100644 index 0000000000..df9144ceab --- /dev/null +++ b/ocamlbuild/report.ml @@ -0,0 +1,61 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Berke Durak *) +(* Report *) + +open My_std +open Log +open Format +open Solver + +let sources_glob = Glob.parse "<*.ml> or <*.mli> or <*.c> or <*.h>";; + +let rec analyze f bt = + match bt with + | Leaf r -> + fprintf f "Ocamlbuild knows of no rules that apply to a target named %a. \ + This can happen if you ask Ocamlbuild to build a target with the \ + wrong extension (e.g. .opt instead of .native) or if the source \ + files live in directories that have not been specified as \ + include directories." + Resource.print r; + false + | Depth(r, bt) -> + if Glob.eval sources_glob r then + begin + fprintf f "Ocamlbuild cannot find or build %a. A file with such a name would \ + usually be a source file. I suspect you have given a wrong target \ + name to Ocamlbuild." + Resource.print r; + false + end + else + analyze f bt + | Choice bl -> List.for_all (analyze f) bl + | Target(_, bt) -> analyze f bt + +let rec print_backtrace f = + function + | Target (name, backtrace) -> + fprintf f "@\n- @[<2>Failed to build the target %s%a@]" name print_backtrace backtrace + | Leaf r -> + fprintf f "@\n- @[<2>Building %a@]" Resource.print r + | Depth (r, backtrace) -> + fprintf f "@\n- @[<v2>Building %a:%a@]" Resource.print r print_backtrace backtrace + | Choice [backtrace] -> print_backtrace f backtrace + | Choice backtraces -> + fprintf f "@\n- @[<v2>Failed to build all of these:"; + List.iter (print_backtrace f) backtraces; + fprintf f "@]" + +let print_backtrace_analyze f bt = ignore (analyze f bt) diff --git a/ocamlbuild/report.mli b/ocamlbuild/report.mli new file mode 100644 index 0000000000..f95fa7bf02 --- /dev/null +++ b/ocamlbuild/report.mli @@ -0,0 +1,18 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Berke Durak *) +(* Report *) + +val print_backtrace_analyze : Format.formatter -> Solver.backtrace -> unit + +val print_backtrace : Format.formatter -> Solver.backtrace -> unit diff --git a/ocamlbuild/resource.ml b/ocamlbuild/resource.ml new file mode 100644 index 0000000000..3ab416eb1a --- /dev/null +++ b/ocamlbuild/resource.ml @@ -0,0 +1,324 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +open My_std +open Format +open Log + +module Resources = Set.Make(Pathname) + +let print = Pathname.print + +let equal = (=) +let compare = compare + +module Cache = struct + open Pathname.Operators + + let clean () = Shell.chdir Pathname.pwd; Shell.rm_rf !Options.build_dir + + type knowledge = + | Yes + | No + | Unknown + + type suspension = (Command.t * (unit -> unit)) + + type build_status = + | Bbuilt + | Bcannot_be_built + | Bnot_built_yet + | Bsuspension of suspension + + type cache_entry = + { mutable built : build_status; + mutable changed : knowledge; + mutable dependencies : Resources.t } + + let empty () = + { built = Bnot_built_yet; + changed = Unknown; + dependencies = Resources.empty } + + let print_knowledge f = + function + | Yes -> pp_print_string f "Yes" + | No -> pp_print_string f "No" + | Unknown -> pp_print_string f "Unknown" + + let print_build_status f = + function + | Bbuilt -> pp_print_string f "Bbuilt" + | Bnot_built_yet -> pp_print_string f "Bnot_built_yet" + | Bcannot_be_built -> pp_print_string f "Bcannot_be_built" + | Bsuspension(cmd, _) -> + fprintf f "@[<2>Bsuspension(%a,@ (<fun> : unit -> unit))@]" Command.print cmd + + let print_cache_entry f e = + fprintf f "@[<2>{ @[<2>built =@ %a@];@ @[<2>changed =@ %a@];@ @[<2>dependencies =@ %a@]@ }@]" + print_build_status e.built print_knowledge e.changed Resources.print e.dependencies + + let cache = Hashtbl.create 103 + + let get r = + try Hashtbl.find cache r + with Not_found -> + let cache_entry = empty () in + Hashtbl.add cache r cache_entry; cache_entry + + let fold_cache f x = Hashtbl.fold f cache x + + let print_cache f () = + fprintf f "@[<hv0>@[<hv2>{:"; + fold_cache begin fun k v () -> + fprintf f "@ @[<2>%a =>@ %a@];" print k print_cache_entry v + end (); + fprintf f "@]:}@]" + + let print_graph f () = + fprintf f "@[<hv0>@[<hv2>{:"; + fold_cache begin fun k v () -> + if not (Resources.is_empty v.dependencies) then + fprintf f "@ @[<2>%a =>@ %a@];" print k Resources.print v.dependencies + end (); + fprintf f "@]@ :}@]" + + let resource_changed r = + dprintf 10 "resource_changed:@ %a" print r; + (get r).changed <- Yes + + let rec resource_has_changed r = + let cache_entry = get r in + match cache_entry.changed with + | Yes -> true + | No -> false + | Unknown -> + let res = + match cache_entry.built with + | Bbuilt -> false + | Bsuspension _ -> assert false + | Bcannot_be_built -> false + | Bnot_built_yet -> not (Pathname.is_up_to_date false r) in + let () = cache_entry.changed <- if res then Yes else No in res + + let resource_state r = (get r).built + + let resource_is_built r = (get r).built = Bbuilt + + let resource_built r = (get r).built <- Bbuilt + + let resource_is_failed r = (get r).built = Bcannot_be_built + + let resource_failed r = (get r).built <- Bcannot_be_built + + let suspend_resource r cmd kont prods = + let cache_entry = get r in + match cache_entry.built with + | Bsuspension _ -> () + | Bbuilt -> () + | Bcannot_be_built -> assert false + | Bnot_built_yet -> + let kont = begin fun () -> + kont (); + List.iter begin fun prod -> + (get prod).built <- Bbuilt + end prods + end in cache_entry.built <- Bsuspension(cmd, kont) + + let resume_suspension (cmd, kont) = + Command.execute cmd; + kont () + + let resume_resource r = + let cache_entry = get r in + match cache_entry.built with + | Bsuspension(s) -> resume_suspension s + | Bbuilt -> () + | Bcannot_be_built -> () + | Bnot_built_yet -> () + + let get_optional_resource_suspension r = + match (get r).built with + | Bsuspension cmd_kont -> Some cmd_kont + | Bbuilt | Bcannot_be_built | Bnot_built_yet -> None + + let clear_resource_failed r = (get r).built <- Bnot_built_yet + + let dependencies r = (get r).dependencies + + let fold_dependencies f = + fold_cache (fun k v -> Resources.fold (f k) v.dependencies) + + let add_dependency r s = + let cache_entry = get r in + cache_entry.dependencies <- Resources.add s cache_entry.dependencies + + let print_dependencies = print_graph + + let digest_resource p = + let f = Pathname.to_string (Pathname.in_build_dir p) in + let buf = Buffer.create 1024 in + Buffer.add_string buf f; + (if sys_file_exists f then Buffer.add_string buf (Digest.file f)); + Digest.string (Buffer.contents buf) + + let digests = Hashtbl.create 103 + + let get_digest_for name = + try Some (Hashtbl.find digests name) + with Not_found -> None + let store_digest name d = Hashtbl.replace digests name d + + let _digests = lazy (Pathname.pwd / !Options.build_dir / (Pathname.mk "_digests")) + + let finalize () = + with_output_file !*_digests begin fun oc -> + Hashtbl.iter begin fun name digest -> + Printf.fprintf oc "%S: %S\n" name digest + end digests + end + + let init () = + Shell.chdir !Options.build_dir; + if Pathname.exists !*_digests then + with_input_file !*_digests begin fun ic -> + try while true do + let l = input_line ic in + Scanf.sscanf l "%S: %S" store_digest + done with End_of_file -> () + end; + My_unix.at_exit_once finalize + +end + +let clean p = Shell.rm_f p + +(* +type env = string + +let split_percent s = + try + let pos = String.index s '%' in + Some (String.before s pos, String.after s (pos + 1)) + with Not_found -> None + +let extract prefix suffix s = + let lprefix = String.length prefix in + let lsuffix = String.length suffix in + let ls = String.length s in + if lprefix + lsuffix > ls then None else + let s' = String.sub s lprefix (ls - lsuffix - lprefix) in + if equal (prefix ^ s' ^ suffix) s then Some s' else None + +let matchit r1 r2 = + match split_percent r1 with + | Some (x, y) -> extract x y r2 + | _ -> if equal r1 r2 then Some "" else None + +let rec subst percent r = + match split_percent r with + | Some (x, y) -> x ^ percent ^ y + | _ -> r + +let print_env = pp_print_string +*) + +let is_up_to_date path = Pathname.is_up_to_date true path + +let import x = x + +module MetaPath : sig + + type env + + val matchit : string -> string -> env option + val subst : env -> string -> string + val print_env : Format.formatter -> env -> unit + +end = struct + + type atoms = A of string | V of string + type t = atoms list + type env = (string * string) list + + exception No_solution + + let mk s = List.map (fun (s, is_var) -> if is_var then V s else A s) (Lexers.meta_path (Lexing.from_string s)) + + let mk = memo mk + + let match_prefix s pos prefix = + match String.contains_string s pos prefix with + | Some(pos') -> if pos = pos' then pos' + String.length prefix else raise No_solution + | None -> raise No_solution + + let matchit p s = + let sl = String.length s in + let rec loop xs pos acc = + match xs with + | [] -> if pos = sl then acc else raise No_solution + | A prefix :: xs -> loop xs (match_prefix s pos prefix) acc + | V var :: A s2 :: xs -> + begin match String.contains_string s pos s2 with + | Some(pos') -> loop xs (pos' + String.length s2) ((var, String.sub s pos (pos' - pos)) :: acc) + | None -> raise No_solution + end + | [V var] -> (var, String.sub s pos (sl - pos)) :: acc + | V _ :: _ -> assert false + in + try Some (loop (mk p) 0 []) + with No_solution -> None + + let pp_opt pp_elt f = + function + | None -> pp_print_string f "None" + | Some x -> Format.fprintf f "Some(%a)" pp_elt x + + let print_env f env = + List.iter begin fun (k, v) -> + if k = "" then Format.fprintf f "%%=%s " v + else Format.fprintf f "%%(%s)=%s " k v + end env + + (* let matchit p s = + let res = matchit p s in + Format.eprintf "matchit %S %S = %a@." p s (pp_opt print_env) res; + res + + let _ = begin + assert (matchit "%(path)lib%(libname).a" "libfoo.a" <> None); + assert (matchit "%(path)lib%(libname).a" "path/libfoo.a" <> None); + assert (matchit "libfoo.a" "libfoo.a" <> None); + assert (matchit "lib%(libname).a" "libfoo.a" <> None); + assert (matchit "%(path)libfoo.a" "path/libfoo.a" <> None); + assert (matchit "foo%" "foobar" <> None); + exit 42 + end;; *) + + let subst env s = + String.concat "" begin + List.map begin fun x -> + match x with + | A atom -> atom + | V var -> List.assoc var env + end (mk s) + end +end + +type env = MetaPath.env + +let matchit = MetaPath.matchit + +let subst = MetaPath.subst + +let print_env = MetaPath.print_env diff --git a/ocamlbuild/resource.mli b/ocamlbuild/resource.mli new file mode 100644 index 0000000000..a0a6a4b87a --- /dev/null +++ b/ocamlbuild/resource.mli @@ -0,0 +1,63 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +open My_std + +open Pathname +type env + +module Resources : Set.S with type elt = t + +module Cache : + sig + type suspension + + type build_status = + | Bbuilt + | Bcannot_be_built + | Bnot_built_yet + | Bsuspension of suspension + + val clean : unit -> unit + val init : unit -> unit + val resource_state : t -> build_status + val resource_changed : t -> unit + val resource_has_changed : t -> bool + val resource_is_built : t -> bool + val resource_built : t -> unit + val resource_is_failed : t -> bool + val resource_failed : t -> unit + val suspend_resource : t -> Command.t -> (unit -> unit) -> t list -> unit + val resume_resource : t -> unit + val resume_suspension : suspension -> unit + val get_optional_resource_suspension : t -> (Command.t * (unit -> unit)) option + val clear_resource_failed : t -> unit + val dependencies : t -> Resources.t + val add_dependency : t -> t -> unit + val get_digest_for : string -> string option + val store_digest : string -> string -> unit + val digest_resource : t -> string + val print_cache : Format.formatter -> unit -> unit + val print_dependencies : Format.formatter -> unit -> unit + val fold_dependencies : (string -> string -> 'a -> 'a) -> 'a -> 'a + end + +val compare : t -> t -> int +val print : Format.formatter -> t -> unit +val clean : t -> unit +val import : string -> t + +val matchit : t -> t -> env option +val subst : env -> t -> t +val is_up_to_date : t -> bool +val print_env : Format.formatter -> env -> unit diff --git a/ocamlbuild/rule.ml b/ocamlbuild/rule.ml new file mode 100644 index 0000000000..d496f8716e --- /dev/null +++ b/ocamlbuild/rule.ml @@ -0,0 +1,296 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +open My_std +open Format +open Log +open Outcome +module Resources = Resource.Resources + +exception Exit_rule_error of string + +type env = Pathname.t -> Pathname.t +type builder = Pathname.t list list -> (Pathname.t, exn) Outcome.t list +type action = env -> builder -> Command.t + +type t = + { name : string; + tags : Tags.t; + deps : Pathname.t list; + prods : Pathname.t list; + code : env -> builder -> Command.t } + +exception Code_digest of string * (bool -> unit) + +let compare _ _ = assert false + +let print_rule_name f r = pp_print_string f r.name + +let print_resource_list = List.print Resource.print + +let print_rule_contents f r = + fprintf f "@[<v2>{@ @[<2>name =@ %S@];@ @[<2>tags =@ %a@];@ @[<2>deps =@ %a@];@ @[<2>prods = %a@];@ @[<2>code = <fun>@]@]@ }" + r.name Tags.print r.tags print_resource_list r.deps print_resource_list r.prods + +let print = print_rule_name + +let subst env rule = + let subst_resources = List.map (Resource.subst env) in + let finder next_finder p = next_finder (Resource.subst env p) in + { (rule) with name = sbprintf "%s (%a)" rule.name Resource.print_env env; + prods = subst_resources rule.prods; + deps = subst_resources rule.deps; + code = (fun env -> rule.code (finder env)) } + +exception Can_produce of t + +let can_produce target rule = + try + List.iter begin fun resource -> + match Resource.matchit resource target with + | Some env -> raise (Can_produce (subst env rule)) + | None -> () + end rule.prods; None + with Can_produce r -> Some r + +let tags_matches tags r = if Tags.does_match tags r.tags then Some r else None + +let digest_prods r = + List.fold_right begin fun p acc -> + let f = Pathname.to_string (Pathname.in_build_dir p) in + if sys_file_exists f then (f, Digest.file f) :: acc else acc + end r.prods [] + +let digest_rule r dyndeps cmd_or_digest = + let buf = Buffer.create 1024 in + (match cmd_or_digest with + | Good cmd -> Buffer.add_string buf (Command.to_string_for_digest cmd) + | Bad(s, _) -> Buffer.add_string buf s); + let add_resource r = Buffer.add_string buf (Resource.Cache.digest_resource r) in + Buffer.add_string buf "prods:"; + List.iter add_resource r.prods; + Buffer.add_string buf "deps:"; + List.iter add_resource r.deps; + Buffer.add_string buf "dyndeps:"; + Resources.iter add_resource dyndeps; + Digest.string (Buffer.contents buf) + +let print_digest f x = pp_print_string f (Digest.to_hex x) + +let exists2 find p rs = + try Some (find p rs) with Not_found -> None + +let all_deps_of_tags = ref [] + +let cons deps acc = + List.fold_left begin fun acc dep -> + if List.mem dep acc then acc else dep :: acc + end acc deps + +let deps_of_tags tags = + List.fold_left begin fun acc (xtags, xdeps) -> + if Tags.does_match tags xtags then cons xdeps acc + else acc + end [] !all_deps_of_tags + +let set_deps_of_tags tags deps = + all_deps_of_tags := (tags, deps) :: !all_deps_of_tags + +let dep tags deps = set_deps_of_tags (Tags.of_list tags) deps + +let build_deps_of_tags builder tags = + match deps_of_tags tags with + | [] -> [] + | deps -> List.map Outcome.good (builder (List.map (fun x -> [x]) deps)) + +let build_deps_of_tags_on_cmd builder x = + let rec spec x = + match x with + | Command.N | Command.A _ | Command.Sh _ | Command.P _ | Command.Px _ | Command.V _ | Command.Quote _ -> () + | Command.S l -> List.iter spec l + | Command.T tags -> + begin match deps_of_tags tags with + | [] -> () + | deps -> List.iter ignore_good (builder (List.map (fun x -> [x]) deps)) + end in + let rec cmd x = + match x with + | Command.Nop -> () + | Command.Cmd(s) -> spec s + | Command.Seq(s) -> List.iter cmd s in + cmd x + +let call builder r = + let dyndeps = ref Resources.empty in + let builder rs = + let results = builder rs in + List.map begin fun res -> + match res with + | Good res' -> + let () = dprintf 10 "new dyndep for %S(%a): %S" r.name print_resource_list r.prods res' in + dyndeps := Resources.add res' !dyndeps; + List.iter (fun x -> Resource.Cache.add_dependency x res') r.prods; + res + | Bad _ -> res + end results in + let () = dprintf 5 "start rule %a" print r in + let cmd_or_digest = + try + let cmd = r.code (fun x -> x) builder in + build_deps_of_tags_on_cmd builder cmd; + Good cmd + with Code_digest(s, kont) -> Bad(s, kont) in + let dyndeps = !dyndeps in + let () = dprintf 10 "dyndeps: %a" Resources.print dyndeps in + let (reason, cached) = + match exists2 List.find (fun r -> not (Pathname.exists_in_build_dir r)) r.prods with + | Some r -> (`cache_miss_missing_prod r, false) + | _ -> + begin match exists2 List.find Resource.Cache.resource_has_changed r.deps with + | Some r -> (`cache_miss_changed_dep r, false) + | _ -> + begin match exists2 Resources.find Resource.Cache.resource_has_changed dyndeps with + | Some r -> (`cache_miss_changed_dyn_dep r, false) + | _ -> + begin match Resource.Cache.get_digest_for r.name with + | None -> (`cache_miss_no_digest, false) + | Some d -> + begin match cmd_or_digest with + | Bad("", _) -> + (`cache_miss_undigest, false) + | Bad(_, _) | Good(_) -> + let rule_digest = digest_rule r dyndeps cmd_or_digest in + if d = rule_digest then (`cache_hit, true) + else (`cache_miss_digest_changed(d, rule_digest), false) + end + end + end + end + in + let explain_reason l = + raw_dprintf (l+1) "mid rule %a: " print r; + match reason with + | `cache_miss_missing_prod r -> + dprintf l "cache miss: a product is not in build dir (%a)" Resource.print r + | `cache_miss_changed_dep r -> + dprintf l "cache miss: a dependency has changed (%a)" Resource.print r + | `cache_miss_changed_dyn_dep r -> + dprintf l "cache miss: a dynamic dependency has changed (%a)" Resource.print r + | `cache_miss_no_digest -> + dprintf l "cache miss: no digest found for %S (the command, a dependency, or a product)" + r.name + | `cache_hit -> dprintf (l+1) "cache hit" + | `cache_miss_digest_changed(old_d, new_d) -> + dprintf l "cache miss: the digest has changed for %S (the command, a dependency, or a product: %a <> %a)" + r.name print_digest old_d print_digest new_d + | `cache_miss_undigest -> + dprintf l "cache miss: cache not supported for the rule %S" r.name in + let prod_digests = digest_prods r in + (if not cached then List.iter Resource.clean r.prods); + (if !Options.nothing_should_be_rebuilt && not cached then + (explain_reason (-1); + let msg = sbprintf "Need to rebuild %a through the rule `%a'" print_resource_list r.prods print r in + raise (Exit_rule_error msg))); + explain_reason 3; + let kont = begin fun () -> + try + (match cmd_or_digest with + | Good cmd -> if cached then Command.execute ~pretend:true cmd + | Bad (_, kont) -> kont cached); + List.iter Resource.Cache.resource_built r.prods; + (if not cached then + let new_rule_digest = digest_rule r dyndeps cmd_or_digest in + let new_prod_digests = digest_prods r in + let () = Resource.Cache.store_digest r.name new_rule_digest in + List.iter begin fun p -> + let f = Pathname.to_string (Pathname.in_build_dir p) in + (try let digest = List.assoc f prod_digests in + let new_digest = List.assoc f new_prod_digests in + if digest <> new_digest then raise Not_found + with Not_found -> Resource.Cache.resource_changed p) + end r.prods); + dprintf 5 "end rule %a" print r + with exn -> (List.iter Resource.clean r.prods; raise exn) + end in + match cmd_or_digest with + | Good cmd when not cached -> + List.iter (fun x -> Resource.Cache.suspend_resource x cmd kont r.prods) r.prods + | Bad _ | Good _ -> kont () + +let (get_rules, add_rule) = + let rules = ref [] in + (fun () -> !rules), + begin fun pos r -> + try + let _ = List.find (fun x -> x.name = r.name) !rules in + raise (Exit_rule_error (sbprintf "Rule.add_rule: already exists: (%a)" print r)) + with Not_found -> + match pos with + | `bottom -> rules := !rules @ [r] + | `top -> rules := r :: !rules + | `after s -> + rules := + List.fold_right begin fun x acc -> + if x.name = s then x :: r :: acc else x :: acc + end !rules [] + | `before s -> + rules := + List.fold_right begin fun x acc -> + if x.name = s then r :: x :: acc else x :: acc + end !rules [] + end + +let rule name ?(tags=[]) ?(prods=[]) ?(deps=[]) ?prod ?dep ?(insert = `bottom) code = + let res_add x acc = + let x = Resource.import x in + if List.mem x acc then + failwith (sprintf "in rule %s, multiple occurences of the resource %s" name x) + else x :: acc in + let res_of_opt = function None -> [] | Some r -> [Resource.import r] in + if prods = [] && prod = None then raise (Exit_rule_error "Can't make a rule that produce nothing"); + add_rule insert + { name = name; + tags = List.fold_right Tags.add tags Tags.empty; + deps = List.fold_right res_add deps (res_of_opt dep); + prods = List.fold_right res_add prods (res_of_opt prod); + code = code } + +let file_rule name ?tags ~prod ?deps ?dep ?insert ~cache action = + rule name ?tags ~prod ?dep ?deps ?insert begin fun env _ -> + raise (Code_digest (cache env, (fun cached -> + if not cached then + with_output_file (env prod) (action env)))) + end + +let custom_rule name ?tags ?prods ?prod ?deps ?dep ?insert ~cache action = + rule name ?tags ?prods ?prod ?dep ?deps ?insert begin fun env _ -> + raise (Code_digest (cache env, fun cached -> action env ~cached)) + end + +module Common_commands = struct + open Command + let mv src dest = Cmd (S [A"mv"; P src; Px dest]) + let cp src dest = Cmd (S [A"cp"; P src; Px dest]) + let ln_f pointed pointer = Cmd (S [A"ln"; A"-f"; P pointed; Px pointer]) + let ln_s pointed pointer = Cmd (S[A"ln"; A"-s"; P pointed; Px pointer]) + let rm_f x = Cmd (S [A"rm"; A"-f"; Px x]) + let touch file = Cmd (S[A"touch"; Px file]) + let chmod opts file = Cmd (S[A"chmod"; opts; Px file]) + let cmp a b = Cmd (S[A"cmp"; P a; Px b]) +end +open Common_commands + +let copy_rule name ?insert src dest = + rule name ?insert ~prod:dest ~dep:src + (fun env _ -> cp (env src) (env dest)) + diff --git a/ocamlbuild/rule.mli b/ocamlbuild/rule.mli new file mode 100644 index 0000000000..690913a8e5 --- /dev/null +++ b/ocamlbuild/rule.mli @@ -0,0 +1,90 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +open My_std +open Resource + +type env = Pathname.t -> Pathname.t +type builder = Pathname.t list list -> (Pathname.t, exn) Outcome.t list +type action = env -> builder -> Command.t + +type t = private + { name : string; + tags : Tags.t; + deps : Pathname.t list; + prods : Pathname.t list; + code : env -> builder -> Command.t } + +val rule : string -> + ?tags:string list -> + ?prods:string list -> + ?deps:string list -> + ?prod:string -> + ?dep:string -> + ?insert:[`top | `before of string | `after of string | `bottom] -> + action -> unit + +val file_rule : string -> + ?tags:string list -> + prod:string -> + ?deps:string list -> + ?dep:string -> + ?insert:[`top | `before of string | `after of string | `bottom] -> + cache:(env -> string) -> + (env -> out_channel -> unit) -> unit + +val custom_rule : string -> + ?tags:string list -> + ?prods:string list -> + ?prod:string -> + ?deps:string list -> + ?dep:string -> + ?insert:[`top | `before of string | `after of string | `bottom] -> + cache:(env -> string) -> + (env -> cached:bool -> unit) -> unit + +(** [copy_rule name ?insert source destination] *) +val copy_rule : string -> + ?insert:[`top | `before of string | `after of string | `bottom] -> + string -> string -> unit + +(** [dep tags deps] Will build [deps] when [tags] will be activated. *) +val dep : string list -> string list -> unit + +module Common_commands : sig + val mv : Pathname.t -> Pathname.t -> Command.t + val cp : Pathname.t -> Pathname.t -> Command.t + val ln_f : Pathname.t -> Pathname.t -> Command.t + val ln_s : Pathname.t -> Pathname.t -> Command.t + val rm_f : Pathname.t -> Command.t + val touch : Pathname.t -> Command.t + val chmod : Command.spec -> Pathname.t -> Command.t + val cmp : Pathname.t -> Pathname.t -> Command.t +end + +(** For system use only *) + +val subst : Resource.env -> t -> t +val can_produce : Pathname.t -> t -> t option +val tags_matches : Tags.t -> t -> t option +val compare : t -> t -> int + +val print_rule_name : Format.formatter -> t -> unit +val print_rule_contents : Format.formatter -> t -> unit +val print : Format.formatter -> t -> unit + +val get_rules : unit -> t list + +val call : builder -> t -> unit + +val build_deps_of_tags : builder -> Tags.t -> Pathname.t list diff --git a/ocamlbuild/shell.ml b/ocamlbuild/shell.ml new file mode 100644 index 0000000000..9c056ff397 --- /dev/null +++ b/ocamlbuild/shell.ml @@ -0,0 +1,71 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +open My_std + +let is_simple_filename s = + let ls = String.length s in + ls <> 0 && + let rec loop pos = + if pos >= ls then true else + match s.[pos] with + | 'a'..'z' | 'A'..'Z' | '0'..'9' | '.' | '-' | '/' | '_' | ':' | '@' | '+' | ',' -> loop (pos + 1) + | _ -> false in + loop 0 +let quote_filename_if_needed s = + if is_simple_filename s then s else Filename.quote s +let chdir dir = + reset_filesys_cache (); + Sys.chdir dir +let run args = + reset_readdir_cache (); + let cmd = String.concat " " (List.map quote_filename_if_needed args) in + if !*My_unix.is_degraded || Sys.os_type = "Win32" then + begin + let st = sys_command cmd in + if st <> 0 then + failwith (Printf.sprintf "Error during command `%s'.\nExit code %d.\n" cmd st) + else + () + end + else + match My_unix.execute_many ~ticker:Log.update ~display:Log.display [[(cmd, ignore)]] with + | None -> () + | Some(_, x) -> + failwith (Printf.sprintf "Error during command %S: %s" cmd (Printexc.to_string x)) +let rm = sys_remove +let rm_f x = + if sys_file_exists x then rm x +let mkdir dir = + reset_filesys_cache_for_file dir; + (*Sys.mkdir dir (* MISSING in ocaml *) *) + run ["mkdir"; dir] +let try_mkdir dir = if not (sys_file_exists dir) then mkdir dir +let rec mkdir_p dir = + if sys_file_exists dir then () + else (mkdir_p (Filename.dirname dir); mkdir dir) +let cp = copy_file (* Décret du 2007-02-01 *) +(* + let cp src dest = + reset_filesys_cache_for_file dest; + run["cp";"-pf";src;dest]*) +let readlink = My_unix.readlink +let is_link = My_unix.is_link +let rm_rf x = + reset_filesys_cache (); + run["rm";"-Rf";x] +let mv src dest = + reset_filesys_cache_for_file src; + reset_filesys_cache_for_file dest; + run["mv"; src; dest] + (*Sys.rename src dest*) diff --git a/ocamlbuild/shell.mli b/ocamlbuild/shell.mli new file mode 100644 index 0000000000..2eb5e30ba1 --- /dev/null +++ b/ocamlbuild/shell.mli @@ -0,0 +1,26 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +val is_simple_filename : string -> bool +val quote_filename_if_needed : string -> string +val chdir : string -> unit +val rm : string -> unit +val rm_f : string -> unit +val rm_rf : string -> unit +val mkdir : string -> unit +val try_mkdir : string -> unit +val mkdir_p : string -> unit +val cp : string -> string -> unit +val mv : string -> string -> unit +val readlink : string -> string +val is_link : string -> bool diff --git a/ocamlbuild/signatures.mli b/ocamlbuild/signatures.mli new file mode 100644 index 0000000000..9c88aa6b84 --- /dev/null +++ b/ocamlbuild/signatures.mli @@ -0,0 +1,506 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +(** This module contains all module signatures that the user + could use to build an ocamlbuild plugin. *) + +module type OrderedTypePrintable = sig + type t + val compare : t -> t -> int + val print : Format.formatter -> t -> unit +end + +module type SET = sig + include Set.S + val find : (elt -> bool) -> t -> elt + val map : (elt -> elt) -> t -> t + val of_list : elt list -> t + val print : Format.formatter -> t -> unit +end + +module type LIST = sig + (* Added functions *) + val print : (Format.formatter -> 'a -> 'b) -> Format.formatter -> 'a list -> unit + val filter_opt : ('a -> 'b option) -> 'a list -> 'b list + val union : 'a list -> 'a list -> 'a list + + (* Original functions *) + include Std_signatures.LIST +end + +module type STRING = sig + val print : Format.formatter -> string -> unit + val chomp : string -> string + + (** [before s n] returns the substring of all characters of [s] + that precede position [n] (excluding the character at + position [n]). + This is the same function as {!Str.string_before}. *) + val before : string -> int -> string + + (** [after s n] returns the substring of all characters of [s] + that follow position [n] (including the character at + position [n]). + This is the same function as {!Str.string_after}. *) + val after : string -> int -> string + + val first_chars : string -> int -> string + (** [first_chars s n] returns the first [n] characters of [s]. + This is the same function as {!String.before} ant {!Str.first_chars}. *) + + val last_chars : string -> int -> string + (** [last_chars s n] returns the last [n] characters of [s]. + This is the same function as {!Str.last_chars}. *) + + val eq_sub_strings : string -> int -> string -> int -> int -> bool + + (** [is_prefix u v] is v a prefix of u ? *) + val is_prefix : string -> string -> bool + (** [is_suffix u v] : is v a suffix of u ? *) + val is_suffix : string -> string -> bool + + (** [contains_string s1 p2 s2] Search in [s1] starting from [p1] if it + contains the [s2] string. Returns [Some position] where [position] + is the begining of the string [s2] in [s1], [None] otherwise. *) + val contains_string : string -> int -> string -> int option + + (** [subst patt repl text] *) + val subst : string -> string -> string -> string + + (** [tr patt repl text] *) + val tr : char -> char -> string -> string + + val rev : string -> string + + (** The following are original functions from the [String] module. *) + include Std_signatures.STRING +end + +module type TAGS = sig + include Set.S with type elt = string + val of_list : string list -> t + val print : Format.formatter -> t -> unit + val does_match : t -> t -> bool + module Operators : sig + val ( ++ ) : t -> elt -> t + val ( -- ) : t -> elt -> t + val ( +++ ) : t -> elt option -> t + val ( --- ) : t -> elt option -> t + end +end + +module type PATHNAME = sig + type t = string + val concat : t -> t -> t + val compare : t -> t -> int + val equal : t -> t -> bool + val exists : t -> bool + val mk : string -> t + val define_context : string -> string list -> unit + val include_dirs_of : string -> string list + val copy : t -> t -> unit + val to_string : t -> string + val print : Format.formatter -> t -> unit + val current_dir_name : t + val parent_dir_name : t + val read : t -> string + val same_contents : t -> t -> bool + val basename : t -> t + val dirname : t -> t + val is_relative : t -> bool + val readlink : t -> t + val readdir : t -> t array + val is_link : t -> bool + val is_directory : t -> bool + + val add_extension : string -> t -> t + val check_extension : t -> string -> bool + + val get_extension : t -> string + val remove_extension : t -> t + val update_extension : string -> t -> t + + val get_extensions : t -> string + val remove_extensions : t -> t + val update_extensions : string -> t -> t + + val print_path_list : Format.formatter -> t list -> unit + val pwd : t + val parent : t -> t + (** [is_prefix x y] is [x] a pathname prefix of [y] *) + val is_prefix : t -> t -> bool + val is_implicit : t -> bool + module Operators : sig + val ( / ) : t -> t -> t + val ( -.- ) : t -> string -> t + end +end + +(** Provides an abstract type for easily building complex shell commands without making + quotation mistakes. *) +module type COMMAND = sig + type tags + + (** The type [t] is basically a sequence of command specifications. This avoids having to + flatten lists of lists. *) + type t = Seq of t list | Cmd of spec | Nop + + (** The type for command specifications. *) + and spec = + | N (** No operation. *) + | S of spec list (** A sequence. This gets flattened in the last stages *) + | A of string (** An atom. *) + | P of string (** A pathname. *) + | Px of string (** A pathname, that will also be given to the call_with_target hook. *) + | Sh of string (** A bit of raw shell code, that will not be escaped. *) + | T of tags (** A set of tags, that describe properties and some semantics + information about the command, afterward these tags will be + replaced by command [spec]s (flags for instance). *) + | V of string (** A virtual command, that will be resolved at execution using [resolve_virtuals] *) + | Quote of spec (** A string that should be quoted like a filename but isn't really one. *) + + (*type v = [ `Seq of v list | `Cmd of vspec | `Nop ] + and vspec = + [ `N + | `S of vspec list + | `A of string + | `P of string (* Pathname.t *) + | `Px of string (* Pathname.t *) + | `Sh of string + | `Quote of vspec ] + + val spec_of_vspec : vspec -> spec + val vspec_of_spec : spec -> vspec + val t_of_v : v -> t + val v_of_t : t -> v*) + + (** Will convert a string list to a list of atoms by adding [A] constructors. *) + val atomize : string list -> spec + + (** Will convert a string list to a list of paths by adding [P] constructors. *) + val atomize_paths : string list -> spec + + (** Run the command. *) + val execute : ?quiet:bool -> ?pretend:bool -> t -> unit + + (** Run the commands in the given list, if possible in parallel. + See the module [Executor]. *) + val execute_many : ?quiet:bool -> ?pretend:bool -> t list -> (bool list * exn) option + + (** [setup_virtual_command_solver virtual_command solver] + the given solver can raise Not_found if it fails to find a valid + command for this virtual command. *) + val setup_virtual_command_solver : string -> (unit -> spec) -> unit + + (** Search the given command in the command path and return its absolute + pathname. *) + val search_in_path : string -> string + + (** Simplify a command by flattening the sequences and resolving the tags + into command-line options. *) + val reduce : spec -> spec + + (** Print a command. *) + val print : Format.formatter -> t -> unit + + (** Convert a command to a string. *) + val to_string : t -> string + + (** Build a string representation of a command that can be passed to the + system calls. *) + val string_of_command_spec : spec -> string +end + +(** A self-contained module implementing extended shell glob patterns who have an expressive power + equal to boolean combinations of regular expressions. *) +module type GLOB = sig + + (** A globber is a boolean combination of basic expressions indented to work on + pathnames. Known operators + are [or], [and] and [not], which may also be written [|], [&] and [~]. There are + also constants [true] and [false] (or [1] and [0]). Expression can be grouped + using parentheses. + - [true] matches anything, + - [false] matches nothing, + - {i basic} [or] {i basic} matches strings matching either one of the basic expressions, + - {i basic} [and] {i basic} matches strings matching both basic expressions, + - not {i basic} matches string that don't match the basic expression, + - {i basic} matches strings that match the basic expression. + + A basic expression can be a constant string enclosed in double quotes, in which + double quotes must be preceded by backslashes, or a glob pattern enclosed between a [<] and a [>], + - ["]{i string}["] matches the literal string {i string}, + - [<]{i glob}[>] matches the glob pattern {i glob}. + + A glob pattern is an anchored regular expression in a shell-like syntax. Most characters stand for themselves. + Character ranges are given in usual shell syntax between brackets. The star [*] stands for any sequence of + characters. The joker '?' stands for exactly one, unspecified character. Alternation is achieved using braces [{]. + - {i glob1}{i glob2} matches strings who have a prefix matching {i glob1} and the corresponding suffix + matching {i glob2}. + - [a] matches the string consisting of the single letter [a]. + - [{]{i glob1},{i glob2}[}] matches strings matching {i glob1} or {i glob2}. + - [*] matches all strings, including the empty one. + - [?] matches strings of length 1. + - [\[]{i c1}-{i c2}{i c3}-{i c4}...[\]] matches characters in the range {i c1} to {i c2} inclusive, + or in the range {i c3} to {i c4} inclusive. For instance [\[a-fA-F0-9\]] matches hexadecimal digits. + To match the dash, put it at the end. + *) + + (** The type representing globbers. Do not attempt to compare them, as they get on-the-fly optimizations. *) + type globber + + (** [parse ~dir pattern] will parse the globber pattern [pattern], optionally prefixing its patterns with [dir]. *) + val parse : ?dir:string -> string -> globber + + (** A descriptive exception raised when an invalid glob pattern description is given. *) + exception Parse_error of string + + (** [eval g u] returns [true] if and only if the string [u] matches the given glob expression. Avoid reparsing + the same pattern, since the automaton implementing the pattern is optimized on the fly. The first few evaluations + are done using a time-inefficient but memory-efficient algorithm. It then compiles the pattern into an efficient + but more memory-hungry data structure. *) + val eval : globber -> string -> bool +end + +(** Module for modulating the logging output with the logging level. *) +module type LOG = sig + (** Current logging (debugging) level. *) + val level : int ref + + (** [dprintf level fmt args...] formats the logging information [fmt] + with the arguments [args...] on the logging output if the logging + level is greater than or equal to [level]. The default level is 1. + More obscure debugging information should have a higher logging + level. Youre formats are wrapped inside these two formats + ["@\[<2>"] and ["@\]@."]. *) + val dprintf : int -> ('a, Format.formatter, unit) format -> 'a + + (** Equivalent to calling [dprintf] with a level [< 0]. *) + val eprintf : ('a, Format.formatter, unit) format -> 'a + + (** Same as dprintf but without the format wrapping. *) + val raw_dprintf : int -> ('a, Format.formatter, unit) format -> 'a +end + +module type OUTCOME = sig + type ('a,'b) t = + | Good of 'a + | Bad of 'b + + val wrap : ('a -> 'b) -> 'a -> ('b, exn) t + val ignore_good : ('a, exn) t -> unit + val good : ('a, exn) t -> 'a +end + +module type MISC = sig + val opt_print : + (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a option -> unit + val the : 'a option -> 'a + val getenv : ?default:string -> string -> string + val with_input_file : ?bin:bool -> string -> (in_channel -> 'a) -> 'a + val with_output_file : ?bin:bool -> string -> (out_channel -> 'a) -> 'a + val with_temp_file : string -> string -> (string -> 'a) -> 'a + val read_file : string -> string + val copy_chan : in_channel -> out_channel -> unit + val copy_file : string -> string -> unit + val print_string_list : Format.formatter -> string list -> unit + val ( !* ) : 'a Lazy.t -> 'a + + (** [r @:= l] is equivalent to [r := !r @ l] *) + val ( @:= ) : 'a list ref -> 'a list -> unit + + val memo : ('a -> 'b) -> ('a -> 'b) +end + +module type OPTIONS = sig + type command_spec + + val build_dir : string ref + val include_dirs : string list ref + val exclude_dirs : string list ref + val nothing_should_be_rebuilt : bool ref + val ocamlc : command_spec ref + val ocamlopt : command_spec ref + val ocamldep : command_spec ref + val ocamldoc : command_spec ref + val ocamlyacc : command_spec ref + val ocamllex : command_spec ref + val ocamlrun : command_spec ref + val ocamlmklib : command_spec ref + val hygiene : bool ref + val sterilize : bool ref + val ignore_auto : bool ref + val plugin : bool ref + val just_plugin : bool ref + val native_plugin : bool ref + val make_links : bool ref + val nostdlib : bool ref + val program_to_execute : bool ref + val must_clean : bool ref + val internal_log_file : string option ref + val use_menhir : bool ref + + val targets : string list ref + val ocaml_libs : string list ref + val ocaml_cflags : string list ref + val ocaml_lflags : string list ref + val ocaml_ppflags : string list ref + val ocaml_yaccflags : string list ref + val ocaml_lexflags : string list ref + val program_args : string list ref + val ignore_list : string list ref + val tags : string list ref + + val ext_obj : string ref + val ext_lib : string ref + val ext_dll : string ref +end + +module type ARCH = sig + type 'a arch = private + | Arch_dir of string * 'a * 'a arch list + | Arch_dir_pack of string * 'a * 'a arch list + | Arch_file of string * 'a + + val dir : string -> unit arch list -> unit arch + val dir_pack : string -> unit arch list -> unit arch + val file : string -> unit arch + + type info = private { + current_path : string; + include_dirs : string list; + for_pack : string; + } + + val annotate : 'a arch -> info arch + + val print : (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a arch -> unit + val print_include_dirs : Format.formatter -> string list -> unit + val print_info : Format.formatter -> info -> unit + + val iter_info : ('a -> unit) -> 'a arch -> unit + val fold_info : ('a -> 'b -> 'b) -> 'a arch -> 'b -> 'b + + val iter_include_dirs : info arch -> (string -> unit) -> unit + + val mk_tables : + info arch -> (string, string list) Hashtbl.t * (string, string) Hashtbl.t + val print_table : + (Format.formatter -> 'a -> unit) -> Format.formatter -> (string, 'a) Hashtbl.t -> unit +end + +(** This module contains the functions and values that can be used by plugins. *) +module type PLUGIN = sig + module Pathname : PATHNAME + module Tags : TAGS + module Command : COMMAND with type tags = Tags.t + module Outcome : OUTCOME + module String : STRING + module List : LIST + module StringSet : Set.S with type elt = String.t + module Options : OPTIONS with type command_spec = Command.spec + module Arch : ARCH + include MISC + + val ( / ) : Pathname.t -> Pathname.t -> Pathname.t + val ( -.- ) : Pathname.t -> string -> Pathname.t + + val ( ++ ) : Tags.t -> Tags.elt -> Tags.t + val ( -- ) : Tags.t -> Tags.elt -> Tags.t + val ( +++ ) : Tags.t -> Tags.elt option -> Tags.t + val ( --- ) : Tags.t -> Tags.elt option -> Tags.t + + type env = Pathname.t -> Pathname.t + type builder = Pathname.t list list -> (Pathname.t, exn) Outcome.t list + type action = env -> builder -> Command.t + + val rule : string -> + ?tags:string list -> + ?prods:string list -> + ?deps:string list -> + ?prod:string -> + ?dep:string -> + ?insert:[`top | `before of string | `after of string | `bottom] -> + action -> unit + + val file_rule : string -> + ?tags:string list -> + prod:string -> + ?deps:string list -> + ?dep:string -> + ?insert:[`top | `before of string | `after of string | `bottom] -> + cache:(env -> string) -> + (env -> out_channel -> unit) -> unit + + val custom_rule : string -> + ?tags:string list -> + ?prods:string list -> + ?prod:string -> + ?deps:string list -> + ?dep:string -> + ?insert:[`top | `before of string | `after of string | `bottom] -> + cache:(env -> string) -> + (env -> cached:bool -> unit) -> unit + + (** [copy_rule name ?insert source destination] *) + val copy_rule : string -> + ?insert:[`top | `before of string | `after of string | `bottom] -> + string -> string -> unit + + (** [dep tags deps] Will build [deps] when [tags] will be activated. *) + val dep : string list -> string list -> unit + + val flag : string list -> Command.spec -> unit + + val non_dependency : Pathname.t -> string -> unit + + val use_lib : Pathname.t -> Pathname.t -> unit + + val expand_module : + Pathname.t list -> Pathname.t -> string list -> Pathname.t list + + val string_list_of_file : Pathname.t -> string list + + val module_name_of_pathname : Pathname.t -> string + + val mv : Pathname.t -> Pathname.t -> Command.t + val cp : Pathname.t -> Pathname.t -> Command.t + val ln_f : Pathname.t -> Pathname.t -> Command.t + val ln_s : Pathname.t -> Pathname.t -> Command.t + val rm_f : Pathname.t -> Command.t + val touch : Pathname.t -> Command.t + val chmod : Command.spec -> Pathname.t -> Command.t + val cmp : Pathname.t -> Pathname.t -> Command.t + + (** [hide_package_contents pack_name] + Don't treat the given package as an open package. + So a module will not be replaced during linking by + this package even if it contains that module. *) + val hide_package_contents : string -> unit + + val tag_file : string -> string list -> unit + + val tag_any : string list -> unit + + val tags_of_pathname : Pathname.t -> Tags.t + + type hook = + | Before_hygiene + | After_hygiene + | Before_options + | After_options + | Before_rules + | After_rules + + val dispatch : (hook -> unit) -> unit +end diff --git a/ocamlbuild/slurp.ml b/ocamlbuild/slurp.ml new file mode 100644 index 0000000000..4446336e5d --- /dev/null +++ b/ocamlbuild/slurp.ml @@ -0,0 +1,186 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Berke Durak *) +(* Slurp *) +open My_std +open Outcome + +type 'a entry = + | Dir of string * string * My_unix.stats Lazy.t * 'a * 'a entry list Lazy.t + | File of string * string * My_unix.stats Lazy.t * 'a + | Error of exn + | Nothing + +let (/) = filename_concat + +let rec filter predicate = function + | Dir(path, name, st, attr, entries) -> + if predicate path name attr then + Dir(path, name, st, attr, lazy (List.map (filter predicate) !*entries)) + else + Nothing + | File(path, name, _, attr) as f -> + if predicate path name attr then + f + else + Nothing + | Nothing -> Nothing + | Error _ as e -> e + +let real_slurp path = + let cwd = Sys.getcwd () in + let abs x = if Filename.is_implicit x || Filename.is_relative x then cwd/x else x in + let visited = Hashtbl.create 1024 in + let rec scandir path names = + let (file_acc, dir_acc) = + Array.fold_left begin fun ((file_acc, dir_acc) as acc) name -> + match do_entry true path name with + | None -> acc + | Some((Dir _|Error _) as entry) -> (file_acc, entry :: dir_acc) + | Some((File _) as entry) -> (entry :: file_acc, dir_acc) + | Some Nothing -> acc + end + ([], []) + names + in + file_acc @ dir_acc + and do_entry link_mode path name = + let fn = path/name in + let absfn = abs fn in + match + try + Good(if link_mode then My_unix.lstat absfn else My_unix.stat absfn) + with + | x -> Bad x + with + | Bad x -> Some(Error x) + | Good st -> + let key = st.My_unix.stat_key in + if try Hashtbl.find visited key with Not_found -> false + then None + else + begin + Hashtbl.add visited key true; + let res = + match st.My_unix.stat_file_kind with + | My_unix.FK_link -> + let fn' = My_unix.readlink absfn in + if sys_file_exists (abs fn') then + do_entry false path name + else + Some(File(path, name, lazy st, ())) + | My_unix.FK_dir -> + (match sys_readdir absfn with + | Good names -> Some(Dir(path, name, lazy st, (), lazy (scandir fn names))) + | Bad exn -> Some(Error exn)) + | My_unix.FK_other -> None + | My_unix.FK_file -> Some(File(path, name, lazy st, ())) in + Hashtbl.replace visited key false; + res + end + in + match do_entry true "" path with + | None -> raise Not_found + | Some entry -> entry + +let split path = + let rec aux path = + if path = Filename.current_dir_name then [] + else (Filename.basename path) :: aux (Filename.dirname path) + in List.rev (aux path) + +let rec join = + function + | [] -> assert false + | [x] -> x + | x :: xs -> x/(join xs) + +let rec add root path entries = + match path, entries with + | [], _ -> entries + | xpath :: xspath, (Dir(dpath, dname, dst, dattr, dentries) as d) :: entries -> + if xpath = dname then + Dir(dpath, dname, dst, dattr, lazy (add (root/xpath) xspath !*dentries)) :: entries + else d :: add root path entries + | [xpath], [] -> + [File(root, xpath, lazy (My_unix.stat (root/xpath)), ())] + | xpath :: xspath, [] -> + [Dir(root/(join xspath), xpath, + lazy (My_unix.stat (root/(join path))), (), + lazy (add (root/xpath) xspath []))] + | _, Nothing :: entries -> add root path entries + | _, Error _ :: _ -> entries + | [xpath], (File(_, fname, _, _) as f) :: entries' -> + if xpath = fname then entries + else f :: add root path entries' + | xpath :: xspath, (File(fpath, fname, fst, fattr) as f) :: entries' -> + if xpath = fname then + Dir(fpath, fname, fst, fattr, lazy (add (root/xpath) xspath [])) :: entries' + else f :: add root path entries' + +let slurp_with_find path = + let lines = + My_unix.run_and_open (Printf.sprintf "find %s" (Filename.quote path)) begin fun ic -> + let acc = ref [] in + try while true do acc := input_line ic :: !acc done; [] + with End_of_file -> !acc + end in + let res = + List.fold_right begin fun line acc -> + add path (split line) acc + end lines [] in + match res with + | [] -> Nothing + | [entry] -> entry + | entries -> Dir(path, Filename.basename path, lazy (My_unix.stat path), (), lazy entries) + +let slurp x = if !*My_unix.is_degraded then slurp_with_find x else real_slurp x + +let rec print print_attr f entry = + match entry with + | Dir(path, name, _, attr, entries) -> + Format.fprintf f "@[<2>Dir(%S,@ %S,@ _,@ %a,@ %a)@]" + path name print_attr attr (List.print (print print_attr)) !*entries + | File(path, name, _, attr) -> + Format.fprintf f "@[<2>File(%S,@ %S,@ _,@ %a)@]" path name print_attr attr + | Nothing -> + Format.fprintf f "Nothing" + | Error(_) -> + Format.fprintf f "Error(_)" + +let rec fold f entry acc = + match entry with + | Dir(path, name, _, attr, contents) -> + f path name attr (List.fold_right (fold f) !*contents acc) + | File(path, name, _, attr) -> + f path name attr acc + | Nothing | Error _ -> acc + +let map f entry = + let rec self entry = + match entry with + | Dir(path, name, st, attr, contents) -> + Dir(path, name, st, f path name attr, lazy (List.map self !*contents)) + | File(path, name, st, attr) -> + File(path, name, st, f path name attr) + | Nothing -> Nothing + | Error e -> Error e + in self entry + +let rec force = + function + | Dir(_, _, st, _, contents) -> + let _ = !*st in List.iter force !*contents + | File(_, _, st, _) -> + ignore !*st + | Nothing | Error _ -> () diff --git a/ocamlbuild/slurp.mli b/ocamlbuild/slurp.mli new file mode 100644 index 0000000000..1b28cc1bc2 --- /dev/null +++ b/ocamlbuild/slurp.mli @@ -0,0 +1,48 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Berke Durak *) +(* Slurp *) + +(** Scans a directory lazily to build a tree that can be user-decorated. *) + +type 'a entry = + Dir of string * string * My_unix.stats Lazy.t * 'a * 'a entry list Lazy.t + (** [Dir(path, name, lst, decoration, lentries)] is a directory named [name] whose path is [path]. + Its stat is lazily stored in [lst] and its entries are lazily scanned in [lentries]. [decoration] + is of type 'a. *) + | File of string * string * My_unix.stats Lazy.t * 'a + (** [File(path, name, lst, decoration)] is a file named [name] whose path is [path]. + Its stat is lazily stored in [lst]. [decoration] is of type 'a. *) + | Error of exn + (** [Error x] means that the exception [x] was raised while scanning or statting an entry. *) + | Nothing + (** Convenient when filtering out entries. *) + +(** Recursively scan the filesystem starting at the given directory. *) +val slurp : string -> unit entry + +(** [filter f entry] only retains from [entry] the nodes for which + [f path name] returns [true]. *) +val filter : (string -> string -> 'a -> bool) -> 'a entry -> 'a entry + +(** [map f entries] changes the decoration in [entries] by applying + [f] to them. [f] is called as [f path name decoration]. *) +val map : (string -> string -> 'a -> 'b) -> 'a entry -> 'b entry + +(** [fold f entry x] iterates [f] over the entries and an accumulator + initially containing [x]; at each iteration, [f] gets the current + value of the accumulator and returns its new value. *) +val fold : (string -> string -> 'b -> 'a -> 'a) -> 'b entry -> 'a -> 'a + +(** Force the evaluation of the whole entry. *) +val force : 'a entry -> unit diff --git a/ocamlbuild/solver.ml b/ocamlbuild/solver.ml new file mode 100644 index 0000000000..541339de35 --- /dev/null +++ b/ocamlbuild/solver.ml @@ -0,0 +1,119 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +open My_std +open Log +open Format +open Outcome + +type backtrace = + | Leaf of Pathname.t + | Choice of backtrace list + | Depth of Pathname.t * backtrace + | Target of string * backtrace +exception Failed of backtrace +exception Circular of Pathname.t * Pathname.t list + +let failed target backtrace = + Resource.Cache.resource_failed target; + raise (Failed backtrace) + +let rec pp_repeat f (n, s) = + if n > 0 then (pp_print_string f s; pp_repeat f (n - 1, s)) + +let rec self depth on_the_go_orig target = + let rules = Rule.get_rules () in + let on_the_go = target :: on_the_go_orig in + + dprintf 4 "==%a> %a" pp_repeat (depth, "==") Resource.print target; + if List.mem target on_the_go_orig then raise (Circular(target, on_the_go_orig)); + match Resource.Cache.resource_state target with + | Resource.Cache.Bbuilt -> + (dprintf 5 "%a already built" Resource.print target) + | Resource.Cache.Bcannot_be_built -> + (dprintf 5 "%a already failed" Resource.print target; failed target (Leaf target)) + | Resource.Cache.Bsuspension(s) -> + (dprintf 5 "%a was suspended -> resuming" Resource.print target; + Resource.Cache.resume_suspension s) + | Resource.Cache.Bnot_built_yet -> + if Resource.is_up_to_date target then + (dprintf 5 "%a exists and up to date" Resource.print target; + Resource.Cache.resource_built target) + else if Pathname.exists_in_source_dir target then + (dprintf 5 "%a exists in source dir -> import it" Resource.print target; + Pathname.import_in_build_dir target; + Resource.Cache.resource_built target; + Resource.Cache.resource_changed target) + else + (* FIXME tags of target + let tags = Configuration.tags_of_target target in + let matching_rules = List.filter_opt (Rule.tags_matches tags) rules in *) + let matching_rules = List.filter_opt (Rule.can_produce target) (*matching_*)rules in + match matching_rules with + | [] -> failed target (Leaf target) + | _ -> + let rec until_works rs backtraces = + match rs with + | [] -> assert false + | r :: rs -> + try + List.iter (force_self (depth + 1) on_the_go) r.Rule.deps; + Rule.call (self_firsts (depth + 1) on_the_go) r + with Failed backtrace -> + if rs = [] then failed target (Depth (target, Choice (backtrace :: backtraces))) + else + let () = + match backtrace with + | Depth (top_prod, _) -> Resource.Cache.clear_resource_failed top_prod + | Target _ | Choice _ | Leaf _ -> () + in until_works rs (backtrace :: backtraces) + in until_works matching_rules [] +and self_first depth on_the_go already_failed rs = + match rs with + | [] -> Bad (Failed (Choice already_failed)) + | r :: rs -> + try self depth on_the_go r; Good r + with Failed backtrace -> self_first depth on_the_go (backtrace :: already_failed) rs +and self_firsts depth on_the_go rss = + let results = List.map (self_first depth on_the_go []) rss in + let cmds, konts = + List.fold_right begin fun res ((acc1, acc2) as acc) -> + match res with + | Bad _ -> acc + | Good res -> + match Resource.Cache.get_optional_resource_suspension res with + | None -> acc + | Some (cmd, kont) -> (cmd :: acc1, kont :: acc2) + end results ([], []) in + let count = List.length cmds in + let job_debug = if !Command.jobs = 1 then 10 else 5 in + if count > 1 then dprintf job_debug ">>> PARALLEL: %d" count; + let opt_exn = Command.execute_many cmds in + if count > 1 then dprintf job_debug "<<< PARALLEL"; + begin match opt_exn with + | Some(res, exn) -> + List.iter2 (fun res kont -> if res then kont ()) res konts; + Log.finish ~how:`Error (); + raise exn + | None -> + List.iter (fun kont -> kont ()) konts + end; + results +and force_self depth on_the_go x = self depth on_the_go x; Resource.Cache.resume_resource x + +let solve = force_self 0 [] +let solve_target name rs = + match self_first 0 [] [] rs with + | Good res -> Resource.Cache.resume_resource res; res + | Bad (Failed backtrace) -> raise (Failed (Target (name, backtrace))) + | Bad exn -> raise exn diff --git a/ocamlbuild/solver.mli b/ocamlbuild/solver.mli new file mode 100644 index 0000000000..5969c4d2ab --- /dev/null +++ b/ocamlbuild/solver.mli @@ -0,0 +1,23 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +type backtrace = private + | Leaf of Pathname.t + | Choice of backtrace list + | Depth of Pathname.t * backtrace + | Target of string * backtrace +exception Failed of backtrace +exception Circular of Pathname.t * Pathname.t list + +val solve : Pathname.t -> unit +val solve_target : string -> Pathname.t list -> Pathname.t diff --git a/ocamlbuild/start.sh b/ocamlbuild/start.sh new file mode 100755 index 0000000000..5789f81abf --- /dev/null +++ b/ocamlbuild/start.sh @@ -0,0 +1,91 @@ +#!/bin/sh +set -e +set -x +rm -rf _start +mkdir _start +cp *.ml* _start +cd _start +echo "let where = ref \"<start>\";;" > ocamlbuild_where.ml +ocamlc -c tags.mli +ocamlc -c std_signatures.mli +ocamlc -c signatures.mli +ocamlc -c ocamlbuild_where.mli +ocamlc -c my_unix.mli +ocamlc -c my_std.mli +ocamlc -c display.mli +ocamlc -c shell.mli +ocamlc -c log.mli +ocamlc -c bool.mli +ocamlc -c glob_ast.mli +ocamlc -c glob_lexer.mli +ocamlc -c glob.mli +ocamlc -c lexers.mli +ocamlc -c slurp.mli +ocamlc -c pathname.mli +ocamlc -c discard_printf.mli +ocamlc -c command.mli +ocamlc -c resource.mli +ocamlc -c rule.mli +ocamlc -c hygiene.mli +ocamlc -c options.mli +ocamlc -c tools.mli +ocamlc -c main.mli +ocamlc -c ocaml_utils.mli +ocamlc -c ocaml_tools.mli +ocamlc -c ocaml_compiler.mli +ocamlc -c ocaml_dependencies.mli +ocamlc -c hooks.mli +ocamlc -c ocamldep.mli +ocamlc -c ocaml_specific.mli +ocamlc -c configuration.mli +ocamlc -c flags.mli +ocamlc -c ocaml_arch.mli +ocamlc -c solver.mli +ocamlc -c report.mli +ocamlc -c ocamlbuild_where.ml +ocamlc -c fda.mli +ocamlc -c fda.ml +ocamlc -c tools.ml +ocamlc -c plugin.mli +ocamlc -c plugin.ml +ocamlc -c ocaml_dependencies.ml +ocamlc -c main.ml +ocamlc -c ocaml_specific.ml +ocamlc -c display.ml +ocamlc -c command.ml +ocamlc -c -rectypes discard_printf.ml +ocamlc -c my_std.ml +ocamlc -c shell.ml +ocamlc -c my_unix.ml +ocamlc -c log.ml +ocamlc -c pathname.ml +ocamlc -c options.ml +ocamlc -c slurp.ml +ocamlc -c ocaml_utils.ml +ocamlc -c ocaml_tools.ml +ocamlc -c ocaml_compiler.ml +ocamlc -c ocamldep.ml +ocamlc -c hooks.ml +ocamllex lexers.mll +ocamlc -c lexers.ml +ocamllex glob_lexer.mll +ocamlc -c glob_lexer.ml +ocamlc -c bool.ml +ocamlc -c glob_ast.ml +ocamlc -c glob.ml +ocamlc -c tags.ml +ocamlc -c configuration.ml +ocamlc -c flags.ml +ocamlc -c hygiene.ml +ocamlc -c ocaml_arch.ml +ocamlc -c resource.ml +ocamlc -c rule.ml +ocamlc -c report.ml +ocamlc -c solver.ml +ocamlc -c ocamlbuildlight.mli +ocamlc -pack discard_printf.cmo my_std.cmo bool.cmo glob_ast.cmo glob_lexer.cmo glob.cmo lexers.cmo my_unix.cmo tags.cmo display.cmo log.cmo shell.cmo slurp.cmo ocamlbuild_where.cmo command.cmo options.cmo pathname.cmo resource.cmo rule.cmo flags.cmo solver.cmo report.cmo ocaml_arch.cmo hygiene.cmo configuration.cmo tools.cmo fda.cmo plugin.cmo ocaml_utils.cmo ocamldep.cmo ocaml_dependencies.cmo ocaml_compiler.cmo ocaml_tools.cmo hooks.cmo ocaml_specific.cmo main.cmo -o ocamlbuild_pack.cmo +ocamlc -c ocamlbuildlight.ml +ocamlc ocamlbuild_pack.cmo ocamlbuildlight.cmo -o ../ocamlbuild.byte.start +cd .. +rm -rf _start +echo ocamlbuild.byte.start: Sucessfully built. diff --git a/ocamlbuild/std_signatures.mli b/ocamlbuild/std_signatures.mli new file mode 100644 index 0000000000..23df4baed3 --- /dev/null +++ b/ocamlbuild/std_signatures.mli @@ -0,0 +1,94 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +(** Some signatures from the standard library. *) + +module type LIST = sig + val length : 'a list -> int + val hd : 'a list -> 'a + val tl : 'a list -> 'a list + val nth : 'a list -> int -> 'a + val rev : 'a list -> 'a list + val append : 'a list -> 'a list -> 'a list + val rev_append : 'a list -> 'a list -> 'a list + val concat : 'a list list -> 'a list + val flatten : 'a list list -> 'a list + val iter : ('a -> unit) -> 'a list -> unit + val map : ('a -> 'b) -> 'a list -> 'b list + val rev_map : ('a -> 'b) -> 'a list -> 'b list + val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a + val fold_right : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b + val iter2 : ('a -> 'b -> unit) -> 'a list -> 'b list -> unit + val map2 : ('a -> 'b -> 'c) -> 'a list -> 'b list -> 'c list + val rev_map2 : ('a -> 'b -> 'c) -> 'a list -> 'b list -> 'c list + val fold_left2 : ('a -> 'b -> 'c -> 'a) -> 'a -> 'b list -> 'c list -> 'a + val fold_right2 : + ('a -> 'b -> 'c -> 'c) -> 'a list -> 'b list -> 'c -> 'c + val for_all : ('a -> bool) -> 'a list -> bool + val exists : ('a -> bool) -> 'a list -> bool + val for_all2 : ('a -> 'b -> bool) -> 'a list -> 'b list -> bool + val exists2 : ('a -> 'b -> bool) -> 'a list -> 'b list -> bool + val mem : 'a -> 'a list -> bool + val memq : 'a -> 'a list -> bool + val find : ('a -> bool) -> 'a list -> 'a + val filter : ('a -> bool) -> 'a list -> 'a list + val find_all : ('a -> bool) -> 'a list -> 'a list + val partition : ('a -> bool) -> 'a list -> 'a list * 'a list + val assoc : 'a -> ('a * 'b) list -> 'b + val assq : 'a -> ('a * 'b) list -> 'b + val mem_assoc : 'a -> ('a * 'b) list -> bool + val mem_assq : 'a -> ('a * 'b) list -> bool + val remove_assoc : 'a -> ('a * 'b) list -> ('a * 'b) list + val remove_assq : 'a -> ('a * 'b) list -> ('a * 'b) list + val split : ('a * 'b) list -> 'a list * 'b list + val combine : 'a list -> 'b list -> ('a * 'b) list + val sort : ('a -> 'a -> int) -> 'a list -> 'a list + val stable_sort : ('a -> 'a -> int) -> 'a list -> 'a list + val fast_sort : ('a -> 'a -> int) -> 'a list -> 'a list + val merge : ('a -> 'a -> int) -> 'a list -> 'a list -> 'a list +end + +module type STRING = sig + external length : string -> int = "%string_length" + external get : string -> int -> char = "%string_safe_get" + external set : string -> int -> char -> unit = "%string_safe_set" + external create : int -> string = "caml_create_string" + val make : int -> char -> string + val copy : string -> string + val sub : string -> int -> int -> string + val fill : string -> int -> int -> char -> unit + val blit : string -> int -> string -> int -> int -> unit + val concat : string -> string list -> string + val iter : (char -> unit) -> string -> unit + val escaped : string -> string + val index : string -> char -> int + val rindex : string -> char -> int + val index_from : string -> int -> char -> int + val rindex_from : string -> int -> char -> int + val contains : string -> char -> bool + val contains_from : string -> int -> char -> bool + val rcontains_from : string -> int -> char -> bool + val uppercase : string -> string + val lowercase : string -> string + val capitalize : string -> string + val uncapitalize : string -> string + type t = string + val compare : t -> t -> int + external unsafe_get : string -> int -> char = "%string_unsafe_get" + external unsafe_set : string -> int -> char -> unit + = "%string_unsafe_set" + external unsafe_blit : string -> int -> string -> int -> int -> unit + = "caml_blit_string" "noalloc" + external unsafe_fill : string -> int -> int -> char -> unit + = "caml_fill_string" "noalloc" +end diff --git a/ocamlbuild/tags.ml b/ocamlbuild/tags.ml new file mode 100644 index 0000000000..bd6190d456 --- /dev/null +++ b/ocamlbuild/tags.ml @@ -0,0 +1,43 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +include Set.Make(String) + +(** + does_match {foo, bar, baz} {foo} => ok + does_match {foo, bar, baz} {foo, boo} => ko + does_match {foo, bar, baz} {} => ok + does_match {foo, bar, baz} {foo, bar, baz} => ok +*) +let does_match x y = subset y x + +let of_list l = List.fold_right add l empty + +open Format + +let print f s = + let () = fprintf f "@[<0>" in + let _ = + fold begin fun elt first -> + if not first then fprintf f ",@,"; + pp_print_string f elt; + false + end s true in + fprintf f "@]" + +module Operators = struct + let ( ++ ) x y = add y x + let ( -- ) x y = remove y x + let ( +++ ) x = function Some y -> add y x | None -> x + let ( --- ) x = function Some y -> remove y x | None -> x +end diff --git a/ocamlbuild/tags.mli b/ocamlbuild/tags.mli new file mode 100644 index 0000000000..20892093b5 --- /dev/null +++ b/ocamlbuild/tags.mli @@ -0,0 +1,15 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) + +include Signatures.TAGS diff --git a/ocamlbuild/test/good-output b/ocamlbuild/test/good-output new file mode 100644 index 0000000000..5a9df660af --- /dev/null +++ b/ocamlbuild/test/good-output @@ -0,0 +1,1022 @@ + _____ _ ____ +|_ _|__ ___| |_|___ \ + | |/ _ \/ __| __| __) | + | | __/\__ \ |_ / __/ + |_|\___||___/\__|_____| + ++ CMDOPTS=-- -help ++ BUILD=../../_build/ocamlbuild.native toto.byte toto.native -no-skip -classic-display ++ BUILD1=../../_build/ocamlbuild.native toto.byte toto.native -no-skip -classic-display -- -help ++ BUILD2=../../_build/ocamlbuild.native toto.byte toto.native -no-skip -classic-display -verbose 0 -nothing-should-be-rebuilt -- -help ++ rm -rf _build ++ cp vivi1.ml vivi.ml ++ ../../_build/ocamlbuild.native toto.byte toto.native -no-skip -classic-display -- -help +ocamldep.opt -modules toto.ml > toto.ml.depends +ocamldep.opt -modules tutu.mli > tutu.mli.depends +ocamldep.opt -modules titi.ml > titi.ml.depends +ocamldep.opt -modules tata.mli > tata.mli.depends +ocamlc.opt -c -o tutu.cmi tutu.mli +ocamlc.opt -c -o titi.cmo titi.ml +ocamlc.opt -c -o tata.cmi tata.mli +ocamlc.opt -c -o toto.cmo toto.ml +ocamldep.opt -modules tutu.ml > tutu.ml.depends +ocamldep.opt -pp camlp4o -modules vivi.ml > vivi.ml.depends +ocamldep.opt -modules tyty.mli > tyty.mli.depends +ocamlc.opt -c -pp camlp4o -o vivi.cmo vivi.ml +ocamlc.opt -c -o tyty.cmi tyty.mli +ocamldep.opt -modules tata.ml > tata.ml.depends +ocamlc.opt -c -o tutu.cmo tutu.ml +ocamlc.opt -c -o tata.cmo tata.ml +ocamlc.opt tata.cmo titi.cmo vivi.cmo tutu.cmo toto.cmo -o toto.byte +ocamlopt.opt -c -pp camlp4o -o vivi.cmx vivi.ml +ocamlopt.opt -c -o tutu.cmx tutu.ml +ocamlopt.opt -c -o titi.cmx titi.ml +ocamlopt.opt -c -o tata.cmx tata.ml +ocamlopt.opt -c -o toto.cmx toto.ml +ocamlopt.opt tata.cmx titi.cmx vivi.cmx tutu.cmx toto.cmx -o toto.native +Warning: Using -- only run the last target +toto.native: _build/toto.native: Hello world!!! +Tutu.tutu => 1 +Tata.tata => "TATA2" ++ ../../_build/ocamlbuild.native toto.byte toto.native -no-skip -classic-display -verbose 0 -nothing-should-be-rebuilt -- -help +[cache hit] ocamldep.opt -modules toto.ml > toto.ml.depends +[cache hit] ocamldep.opt -modules tutu.mli > tutu.mli.depends +[cache hit] ocamlc.opt -c -o tutu.cmi tutu.mli +[cache hit] ocamldep.opt -modules titi.ml > titi.ml.depends +[cache hit] ocamlc.opt -c -o titi.cmo titi.ml +[cache hit] ocamldep.opt -modules tata.mli > tata.mli.depends +[cache hit] ocamlc.opt -c -o tata.cmi tata.mli +[cache hit] ocamlc.opt -c -o toto.cmo toto.ml +[cache hit] ocamldep.opt -modules tutu.ml > tutu.ml.depends +[cache hit] ocamldep.opt -pp camlp4o -modules vivi.ml > vivi.ml.depends +[cache hit] ocamlc.opt -c -pp camlp4o -o vivi.cmo vivi.ml +[cache hit] ocamldep.opt -modules tyty.mli > tyty.mli.depends +[cache hit] ocamlc.opt -c -o tyty.cmi tyty.mli +[cache hit] ocamlc.opt -c -o tutu.cmo tutu.ml +[cache hit] ocamldep.opt -modules tata.ml > tata.ml.depends +[cache hit] ocamlc.opt -c -o tata.cmo tata.ml +[cache hit] ocamlc.opt tata.cmo titi.cmo vivi.cmo tutu.cmo toto.cmo -o toto.byte +[cache hit] ocamlopt.opt -c -pp camlp4o -o vivi.cmx vivi.ml +[cache hit] ocamlopt.opt -c -o tutu.cmx tutu.ml +[cache hit] ocamlopt.opt -c -o titi.cmx titi.ml +[cache hit] ocamlopt.opt -c -o tata.cmx tata.ml +[cache hit] ocamlopt.opt -c -o toto.cmx toto.ml +[cache hit] ocamlopt.opt tata.cmx titi.cmx vivi.cmx tutu.cmx toto.cmx -o toto.native +Warning: Using -- only run the last target +toto.native: _build/toto.native: Hello world!!! +Tutu.tutu => 1 +Tata.tata => "TATA2" ++ cp vivi2.ml vivi.ml ++ ../../_build/ocamlbuild.native toto.byte toto.native -no-skip -classic-display -- -help +ocamldep.opt -pp camlp4o -modules vivi.ml > vivi.ml.depends +ocamlc.opt -c -pp camlp4o -o vivi.cmo vivi.ml +ocamlc.opt tata.cmo titi.cmo vivi.cmo tutu.cmo toto.cmo -o toto.byte +ocamlopt.opt -c -pp camlp4o -o vivi.cmx vivi.ml +ocamlopt.opt tata.cmx titi.cmx vivi.cmx tutu.cmx toto.cmx -o toto.native +Warning: Using -- only run the last target +toto.native: _build/toto.native: Hello world!!! +Tutu.tutu => 1 +Tata.tata => "TATA2" ++ ../../_build/ocamlbuild.native toto.byte toto.native -no-skip -classic-display -verbose 0 -nothing-should-be-rebuilt -- -help +[cache hit] ocamldep.opt -modules toto.ml > toto.ml.depends +[cache hit] ocamldep.opt -modules tutu.mli > tutu.mli.depends +[cache hit] ocamlc.opt -c -o tutu.cmi tutu.mli +[cache hit] ocamldep.opt -modules titi.ml > titi.ml.depends +[cache hit] ocamlc.opt -c -o titi.cmo titi.ml +[cache hit] ocamldep.opt -modules tata.mli > tata.mli.depends +[cache hit] ocamlc.opt -c -o tata.cmi tata.mli +[cache hit] ocamlc.opt -c -o toto.cmo toto.ml +[cache hit] ocamldep.opt -modules tutu.ml > tutu.ml.depends +[cache hit] ocamldep.opt -pp camlp4o -modules vivi.ml > vivi.ml.depends +[cache hit] ocamlc.opt -c -pp camlp4o -o vivi.cmo vivi.ml +[cache hit] ocamldep.opt -modules tyty.mli > tyty.mli.depends +[cache hit] ocamlc.opt -c -o tyty.cmi tyty.mli +[cache hit] ocamlc.opt -c -o tutu.cmo tutu.ml +[cache hit] ocamldep.opt -modules tata.ml > tata.ml.depends +[cache hit] ocamlc.opt -c -o tata.cmo tata.ml +[cache hit] ocamlc.opt tata.cmo titi.cmo vivi.cmo tutu.cmo toto.cmo -o toto.byte +[cache hit] ocamlopt.opt -c -pp camlp4o -o vivi.cmx vivi.ml +[cache hit] ocamlopt.opt -c -o tutu.cmx tutu.ml +[cache hit] ocamlopt.opt -c -o titi.cmx titi.ml +[cache hit] ocamlopt.opt -c -o tata.cmx tata.ml +[cache hit] ocamlopt.opt -c -o toto.cmx toto.ml +[cache hit] ocamlopt.opt tata.cmx titi.cmx vivi.cmx tutu.cmx toto.cmx -o toto.native +Warning: Using -- only run the last target +toto.native: _build/toto.native: Hello world!!! +Tutu.tutu => 1 +Tata.tata => "TATA2" ++ cp vivi3.ml vivi.ml ++ ../../_build/ocamlbuild.native toto.byte toto.native -no-skip -classic-display -- -help +ocamldep.opt -pp camlp4o -modules vivi.ml > vivi.ml.depends +ocamlc.opt -c -pp camlp4o -o vivi.cmo vivi.ml +ocamlc.opt -c -o tutu.cmo tutu.ml +ocamlc.opt tata.cmo titi.cmo vivi.cmo tutu.cmo toto.cmo -o toto.byte +ocamlopt.opt -c -pp camlp4o -o vivi.cmx vivi.ml +ocamlopt.opt -c -o tutu.cmx tutu.ml +ocamlopt.opt -c -o toto.cmx toto.ml +ocamlopt.opt tata.cmx titi.cmx vivi.cmx tutu.cmx toto.cmx -o toto.native +Warning: Using -- only run the last target +toto.native: _build/toto.native: Hello world!!! +Tutu.tutu => 2 +Tata.tata => "TATA2" ++ ../../_build/ocamlbuild.native toto.byte toto.native -no-skip -classic-display -verbose 0 -nothing-should-be-rebuilt -- -help +[cache hit] ocamldep.opt -modules toto.ml > toto.ml.depends +[cache hit] ocamldep.opt -modules tutu.mli > tutu.mli.depends +[cache hit] ocamlc.opt -c -o tutu.cmi tutu.mli +[cache hit] ocamldep.opt -modules titi.ml > titi.ml.depends +[cache hit] ocamlc.opt -c -o titi.cmo titi.ml +[cache hit] ocamldep.opt -modules tata.mli > tata.mli.depends +[cache hit] ocamlc.opt -c -o tata.cmi tata.mli +[cache hit] ocamlc.opt -c -o toto.cmo toto.ml +[cache hit] ocamldep.opt -modules tutu.ml > tutu.ml.depends +[cache hit] ocamldep.opt -pp camlp4o -modules vivi.ml > vivi.ml.depends +[cache hit] ocamlc.opt -c -pp camlp4o -o vivi.cmo vivi.ml +[cache hit] ocamldep.opt -modules tyty.mli > tyty.mli.depends +[cache hit] ocamlc.opt -c -o tyty.cmi tyty.mli +[cache hit] ocamlc.opt -c -o tutu.cmo tutu.ml +[cache hit] ocamldep.opt -modules tata.ml > tata.ml.depends +[cache hit] ocamlc.opt -c -o tata.cmo tata.ml +[cache hit] ocamlc.opt tata.cmo titi.cmo vivi.cmo tutu.cmo toto.cmo -o toto.byte +[cache hit] ocamlopt.opt -c -pp camlp4o -o vivi.cmx vivi.ml +[cache hit] ocamlopt.opt -c -o tutu.cmx tutu.ml +[cache hit] ocamlopt.opt -c -o titi.cmx titi.ml +[cache hit] ocamlopt.opt -c -o tata.cmx tata.ml +[cache hit] ocamlopt.opt -c -o toto.cmx toto.ml +[cache hit] ocamlopt.opt tata.cmx titi.cmx vivi.cmx tutu.cmx toto.cmx -o toto.native +Warning: Using -- only run the last target +toto.native: _build/toto.native: Hello world!!! +Tutu.tutu => 2 +Tata.tata => "TATA2" + _____ _ _____ +|_ _|__ ___| |_|___ / + | |/ _ \/ __| __| |_ \ + | | __/\__ \ |_ ___) | + |_|\___||___/\__|____/ + ++ CMDOTPS= ++ BUILD=../../_build/ocamlbuild.native a.byte a.native proj.docdir/index.html -no-skip -classic-display ++ BUILD1=../../_build/ocamlbuild.native a.byte a.native proj.docdir/index.html -no-skip -classic-display ++ BUILD2=../../_build/ocamlbuild.native a.byte a.native proj.docdir/index.html -no-skip -classic-display -verbose 0 -nothing-should-be-rebuilt ++ rm -rf _build ++ ../../_build/ocamlbuild.native a.byte a.native proj.docdir/index.html -no-skip -classic-display +ocamldep.opt -modules a.ml > a.ml.depends +ocamldep.opt -modules a.mli > a.mli.depends +ocamlc.opt -c -o a.cmi a.mli +ocamldep.opt -modules b.mli > b.mli.depends +ocamlc.opt -c -o b.cmi b.mli +ocamlc.opt -c -o a.cmo a.ml +ocamldep.opt -modules b.ml > b.ml.depends +ocamldep.opt -modules c.mli > c.mli.depends +ocamlc.opt -c -o c.cmi c.mli +ocamlc.opt -c -o b.cmo b.ml +ocamldep.opt -modules c.ml > c.ml.depends +ocamldep.opt -modules d.mli > d.mli.depends +ocamlc.opt -c -o d.cmi d.mli +ocamlc.opt -c -o c.cmo c.ml +ocamldep.opt -modules d.ml > d.ml.depends +ocamldep.opt -modules e.mli > e.mli.depends +ocamlc.opt -c -o e.cmi e.mli +ocamlc.opt -c -o d.cmo d.ml +ocamldep.opt -modules e.ml > e.ml.depends +ocamldep.opt -modules f.mli > f.mli.depends +ocamlc.opt -c -o f.cmi f.mli +ocamlc.opt -c -o e.cmo e.ml +ocamldep.opt -modules f.ml > f.ml.depends +ocamlc.opt -c -o f.cmo f.ml +ocamlc.opt unix.cma f.cmo e.cmo d.cmo c.cmo b.cmo a.cmo -o a.byte +ocamlopt.opt -c -o f.cmx f.ml +ocamlopt.opt -c -o e.cmx e.ml +ocamlopt.opt -c -o d.cmx d.ml +ocamlopt.opt -c -o c.cmx c.ml +ocamlopt.opt -c -o b.cmx b.ml +ocamlopt.opt -c -o a.cmx a.ml +ocamlopt.opt unix.cmxa f.cmx e.cmx d.cmx c.cmx b.cmx a.cmx -o a.native +ocamldoc.opt -dump a.odoc a.mli +ocamldoc.opt -dump b.odoc b.mli +ocamldoc.opt -dump c.odoc c.mli +ocamldoc.opt -dump d.odoc d.mli +ocamldoc.opt -dump e.odoc e.mli +ocamldoc.opt -dump f.odoc f.mli +rm -rf proj.docdir +mkdir -p proj.docdir +ocamldoc.opt -load a.odoc -load b.odoc -load c.odoc -load d.odoc -load e.odoc -load f.odoc -html -d proj.docdir ++ ../../_build/ocamlbuild.native a.byte a.native proj.docdir/index.html -no-skip -classic-display -verbose 0 -nothing-should-be-rebuilt +[cache hit] ocamldep.opt -modules a.ml > a.ml.depends +[cache hit] ocamldep.opt -modules a.mli > a.mli.depends +[cache hit] ocamlc.opt -c -o a.cmi a.mli +[cache hit] ocamldep.opt -modules b.mli > b.mli.depends +[cache hit] ocamlc.opt -c -o b.cmi b.mli +[cache hit] ocamlc.opt -c -o a.cmo a.ml +[cache hit] ocamldep.opt -modules b.ml > b.ml.depends +[cache hit] ocamldep.opt -modules c.mli > c.mli.depends +[cache hit] ocamlc.opt -c -o c.cmi c.mli +[cache hit] ocamlc.opt -c -o b.cmo b.ml +[cache hit] ocamldep.opt -modules c.ml > c.ml.depends +[cache hit] ocamldep.opt -modules d.mli > d.mli.depends +[cache hit] ocamlc.opt -c -o d.cmi d.mli +[cache hit] ocamlc.opt -c -o c.cmo c.ml +[cache hit] ocamldep.opt -modules d.ml > d.ml.depends +[cache hit] ocamldep.opt -modules e.mli > e.mli.depends +[cache hit] ocamlc.opt -c -o e.cmi e.mli +[cache hit] ocamlc.opt -c -o d.cmo d.ml +[cache hit] ocamldep.opt -modules e.ml > e.ml.depends +[cache hit] ocamldep.opt -modules f.mli > f.mli.depends +[cache hit] ocamlc.opt -c -o f.cmi f.mli +[cache hit] ocamlc.opt -c -o e.cmo e.ml +[cache hit] ocamldep.opt -modules f.ml > f.ml.depends +[cache hit] ocamlc.opt -c -o f.cmo f.ml +[cache hit] ocamlc.opt unix.cma f.cmo e.cmo d.cmo c.cmo b.cmo a.cmo -o a.byte +[cache hit] ocamlopt.opt -c -o f.cmx f.ml +[cache hit] ocamlopt.opt -c -o e.cmx e.ml +[cache hit] ocamlopt.opt -c -o d.cmx d.ml +[cache hit] ocamlopt.opt -c -o c.cmx c.ml +[cache hit] ocamlopt.opt -c -o b.cmx b.ml +[cache hit] ocamlopt.opt -c -o a.cmx a.ml +[cache hit] ocamlopt.opt unix.cmxa f.cmx e.cmx d.cmx c.cmx b.cmx a.cmx -o a.native +[cache hit] ocamldoc.opt -dump a.odoc a.mli +[cache hit] ocamldoc.opt -dump b.odoc b.mli +[cache hit] ocamldoc.opt -dump c.odoc c.mli +[cache hit] ocamldoc.opt -dump d.odoc d.mli +[cache hit] ocamldoc.opt -dump e.odoc e.mli +[cache hit] ocamldoc.opt -dump f.odoc f.mli +[cache hit] rm -rf proj.docdir +[cache hit] mkdir -p proj.docdir +[cache hit] ocamldoc.opt -load a.odoc -load b.odoc -load c.odoc -load d.odoc -load e.odoc -load f.odoc -html -d proj.docdir + _____ _ _ _ +|_ _|__ ___| |_| || | + | |/ _ \/ __| __| || |_ + | | __/\__ \ |_|__ _| + |_|\___||___/\__| |_| + ++ CMDOTPS= ++ BUILD=../../_build/ocamlbuild.native -I a -I b aa.byte aa.native -no-skip -classic-display ++ BUILD1=../../_build/ocamlbuild.native -I a -I b aa.byte aa.native -no-skip -classic-display ++ BUILD2=../../_build/ocamlbuild.native -I a -I b aa.byte aa.native -no-skip -classic-display -verbose 0 -nothing-should-be-rebuilt ++ rm -rf _build ++ ../../_build/ocamlbuild.native -I a -I b aa.byte aa.native -no-skip -classic-display +ocamldep.opt -modules a/aa.ml > a/aa.ml.depends +ocamldep.opt -modules a/aa.mli > a/aa.mli.depends +ocamlc.opt -c -I a -I b -o a/aa.cmi a/aa.mli +ocamldep.opt -modules b/bb.ml > b/bb.ml.depends +ocamlc.opt -c -I b -I a -o b/bb.cmo b/bb.ml +ocamlc.opt -c -I a -I b -o a/aa.cmo a/aa.ml +ocamlc.opt str.cma b/bb.cmo a/aa.cmo -o a/aa.byte +ocamlopt.opt -c -I b -I a -o b/bb.cmx b/bb.ml +ocamlopt.opt -c -I a -I b -o a/aa.cmx a/aa.ml +ocamlopt.opt str.cmxa b/bb.cmx a/aa.cmx -o a/aa.native ++ ../../_build/ocamlbuild.native -I a -I b aa.byte aa.native -no-skip -classic-display -verbose 0 -nothing-should-be-rebuilt +[cache hit] ocamldep.opt -modules a/aa.ml > a/aa.ml.depends +[cache hit] ocamldep.opt -modules a/aa.mli > a/aa.mli.depends +[cache hit] ocamlc.opt -c -I a -I b -o a/aa.cmi a/aa.mli +[cache hit] ocamldep.opt -modules b/bb.ml > b/bb.ml.depends +[cache hit] ocamlc.opt -c -I b -I a -o b/bb.cmo b/bb.ml +[cache hit] ocamlc.opt -c -I a -I b -o a/aa.cmo a/aa.ml +[cache hit] ocamlc.opt str.cma b/bb.cmo a/aa.cmo -o a/aa.byte +[cache hit] ocamlopt.opt -c -I b -I a -o b/bb.cmx b/bb.ml +[cache hit] ocamlopt.opt -c -I a -I b -o a/aa.cmx a/aa.ml +[cache hit] ocamlopt.opt str.cmxa b/bb.cmx a/aa.cmx -o a/aa.native + _____ _ ____ +|_ _|__ ___| |_| ___| + | |/ _ \/ __| __|___ \ + | | __/\__ \ |_ ___) | + |_|\___||___/\__|____/ + ++ CMDOPTS= ++ BUILD=../../_build/ocamlbuild.native d.byte d.native -no-skip -classic-display ++ BUILD1=../../_build/ocamlbuild.native d.byte d.native -no-skip -classic-display ++ BUILD2=../../_build/ocamlbuild.native d.byte d.native -no-skip -classic-display -verbose 0 -nothing-should-be-rebuilt ++ rm -rf _build ++ ../../_build/ocamlbuild.native d.byte d.native -no-skip -classic-display +ocamldep.opt -modules d.ml > d.ml.depends +ocamldep.opt -modules a.ml > a.ml.depends +ocamldep.opt -modules a.mli > a.mli.depends +ocamlc.opt -c -o a.cmi a.mli +ocamldep.opt -modules b.ml > b.ml.depends +ocamlc.opt -c -o a.cmo a.ml +ocamlc.opt -c -o b.cmo b.ml +ocamlc.opt -pack a.cmo b.cmo -o c.cmo +ocamlc.opt -c -o d.cmo d.ml +ocamlc.opt c.cmo d.cmo -o d.byte +ocamlopt.opt -c -for-pack C -o a.cmx a.ml +ocamlopt.opt -c -for-pack C -o b.cmx b.ml +mv c.cmi c.cmitmp +ocamlopt.opt -pack a.cmx b.cmx -o c.cmx +cmp c.cmitmp c.cmi +ocamlopt.opt -c -o d.cmx d.ml +ocamlopt.opt c.cmx d.cmx -o d.native ++ ../../_build/ocamlbuild.native d.byte d.native -no-skip -classic-display -verbose 0 -nothing-should-be-rebuilt +[cache hit] ocamldep.opt -modules d.ml > d.ml.depends +[cache hit] ocamldep.opt -modules a.ml > a.ml.depends +[cache hit] ocamldep.opt -modules a.mli > a.mli.depends +[cache hit] ocamlc.opt -c -o a.cmi a.mli +[cache hit] ocamlc.opt -c -o a.cmo a.ml +[cache hit] ocamldep.opt -modules b.ml > b.ml.depends +[cache hit] ocamlc.opt -c -o b.cmo b.ml +[cache hit] ocamlc.opt -pack a.cmo b.cmo -o c.cmo +[cache hit] ocamlc.opt -c -o d.cmo d.ml +[cache hit] ocamlc.opt c.cmo d.cmo -o d.byte +[cache hit] ocamlopt.opt -c -for-pack C -o a.cmx a.ml +[cache hit] ocamlopt.opt -c -for-pack C -o b.cmx b.ml +[cache hit] mv c.cmi c.cmitmp +[cache hit] ocamlopt.opt -pack a.cmx b.cmx -o c.cmx +[cache hit] cmp c.cmitmp c.cmi +[cache hit] ocamlopt.opt -c -o d.cmx d.ml +[cache hit] ocamlopt.opt c.cmx d.cmx -o d.native + _____ _ __ +|_ _|__ ___| |_ / /_ + | |/ _ \/ __| __| '_ \ + | | __/\__ \ |_| (_) | + |_|\___||___/\__|\___/ + ++ rm -rf _build ++ CMDOPTS= ++ BUILD=../../_build/ocamlbuild.native -no-skip main.byte -classic-display ++ BUILD1=../../_build/ocamlbuild.native -no-skip main.byte -classic-display ++ BUILD2=../../_build/ocamlbuild.native -no-skip main.byte -classic-display -verbose 0 -nothing-should-be-rebuilt ++ cp b.mli.v1 b.mli ++ cp d.mli.v1 d.mli ++ ../../_build/ocamlbuild.native -no-skip main.byte -classic-display +ocamldep.opt -modules main.ml > main.ml.depends +ocamldep.opt -modules main.mli > main.mli.depends +ocamlc.opt -c -o main.cmi main.mli +ocamldep.opt -modules d.mli > d.mli.depends +ocamldep.opt -modules a.mli > a.mli.depends +ocamlc.opt -c -o d.cmi d.mli +ocamlc.opt -c -o a.cmi a.mli +ocamlc.opt -c -o main.cmo main.ml +ocamldep.opt -modules d.ml > d.ml.depends +ocamldep.opt -modules a.ml > a.ml.depends +ocamldep.opt -modules b.mli > b.mli.depends +ocamlc.opt -c -o b.cmi b.mli +ocamlc.opt -c -o d.cmo d.ml +ocamlc.opt -c -o a.cmo a.ml +ocamldep.opt -modules b.ml > b.ml.depends +ocamlc.opt -c -o b.cmo b.ml +ocamlc.opt d.cmo b.cmo a.cmo main.cmo -o main.byte ++ ../../_build/ocamlbuild.native -no-skip main.byte -classic-display -verbose 0 -nothing-should-be-rebuilt +[cache hit] ocamldep.opt -modules main.ml > main.ml.depends +[cache hit] ocamldep.opt -modules main.mli > main.mli.depends +[cache hit] ocamlc.opt -c -o main.cmi main.mli +[cache hit] ocamldep.opt -modules d.mli > d.mli.depends +[cache hit] ocamlc.opt -c -o d.cmi d.mli +[cache hit] ocamldep.opt -modules a.mli > a.mli.depends +[cache hit] ocamlc.opt -c -o a.cmi a.mli +[cache hit] ocamlc.opt -c -o main.cmo main.ml +[cache hit] ocamldep.opt -modules d.ml > d.ml.depends +[cache hit] ocamlc.opt -c -o d.cmo d.ml +[cache hit] ocamldep.opt -modules a.ml > a.ml.depends +[cache hit] ocamldep.opt -modules b.mli > b.mli.depends +[cache hit] ocamlc.opt -c -o b.cmi b.mli +[cache hit] ocamlc.opt -c -o a.cmo a.ml +[cache hit] ocamldep.opt -modules b.ml > b.ml.depends +[cache hit] ocamlc.opt -c -o b.cmo b.ml +[cache hit] ocamlc.opt d.cmo b.cmo a.cmo main.cmo -o main.byte ++ cp b.mli.v2 b.mli ++ cp d.mli.v2 d.mli ++ ../../_build/ocamlbuild.native -no-skip main.byte -classic-display +ocamldep.opt -modules d.mli > d.mli.depends +ocamlc.opt -c -o d.cmi d.mli +ocamlc.opt -c -o main.cmo main.ml +ocamldep.opt -modules b.mli > b.mli.depends ++ ocamldep.opt -modules b.mli > b.mli.depends +File "b.mli", line 1, characters 0-2: +Syntax error +Command exited with code 2. ++ cp b.mli.v1 b.mli ++ ../../_build/ocamlbuild.native -no-skip main.byte -classic-display +ocamldep.opt -modules b.mli > b.mli.depends +ocamlc.opt -c -o b.cmi b.mli +ocamlc.opt -c -o d.cmo d.ml +ocamlc.opt -c -o b.cmo b.ml +ocamlc.opt d.cmo b.cmo a.cmo main.cmo -o main.byte ++ ../../_build/ocamlbuild.native -no-skip main.byte -classic-display -verbose 0 -nothing-should-be-rebuilt +[cache hit] ocamldep.opt -modules main.ml > main.ml.depends +[cache hit] ocamldep.opt -modules main.mli > main.mli.depends +[cache hit] ocamlc.opt -c -o main.cmi main.mli +[cache hit] ocamldep.opt -modules d.mli > d.mli.depends +[cache hit] ocamlc.opt -c -o d.cmi d.mli +[cache hit] ocamldep.opt -modules a.mli > a.mli.depends +[cache hit] ocamlc.opt -c -o a.cmi a.mli +[cache hit] ocamlc.opt -c -o main.cmo main.ml +[cache hit] ocamldep.opt -modules d.ml > d.ml.depends +[cache hit] ocamlc.opt -c -o d.cmo d.ml +[cache hit] ocamldep.opt -modules a.ml > a.ml.depends +[cache hit] ocamldep.opt -modules b.mli > b.mli.depends +[cache hit] ocamlc.opt -c -o b.cmi b.mli +[cache hit] ocamlc.opt -c -o a.cmo a.ml +[cache hit] ocamldep.opt -modules b.ml > b.ml.depends +[cache hit] ocamlc.opt -c -o b.cmo b.ml +[cache hit] ocamlc.opt d.cmo b.cmo a.cmo main.cmo -o main.byte ++ echo PASS +PASS + _____ _ _____ +|_ _|__ ___| ||___ | + | |/ _ \/ __| __| / / + | | __/\__ \ |_ / / + |_|\___||___/\__/_/ + ++ CMDOPTS= ++ BUILD=../../_build/ocamlbuild.native bbcc.cma main.byte bbcc.cmxa main.native -no-skip -classic-display ++ BUILD1=../../_build/ocamlbuild.native bbcc.cma main.byte bbcc.cmxa main.native -no-skip -classic-display ++ BUILD2=../../_build/ocamlbuild.native bbcc.cma main.byte bbcc.cmxa main.native -no-skip -classic-display -verbose 0 -nothing-should-be-rebuilt ++ rm -rf _build ++ svn revert bb.ml ++ ../../_build/ocamlbuild.native bbcc.cma main.byte bbcc.cmxa main.native -no-skip -classic-display +ocamlopt.opt -I /Users/ertai/local/lib/ocamlbuild unix.cmxa /Users/ertai/local/lib/ocamlbuild/ocamlbuildlib.cmxa myocamlbuild.ml /Users/ertai/local/lib/ocamlbuild/ocamlbuild.cmx -o myocamlbuild +ocamldep.opt -modules bb.ml > bb.ml.depends +ocamldep.opt -modules bb.mli > bb.mli.depends +ocamlc.opt -c -o bb.cmi bb.mli +ocamldep.opt -modules cc.ml > cc.ml.depends +ocamldep.opt -modules c2.mli > c2.mli.depends +ocamldep.opt -modules aa.ml > aa.ml.depends +ocamlc.opt -c -o c2.cmi c2.mli +ocamlc.opt -c -o aa.cmo aa.ml +ocamlc.opt -c -o bb.cmo bb.ml +ocamlc.opt -c -o cc.cmo cc.ml +ocamlc.opt -a bb.cmo cc.cmo -o bbcc.cma +ocamldep.opt -modules main.ml > main.ml.depends +ocamldep.opt -modules c3.ml > c3.ml.depends +ocamlc.opt -c -o c3.cmo c3.ml +ocamlc.opt -c -o main.cmo main.ml +ocamldep.opt -modules cool_plugin.ml > cool_plugin.ml.depends +ocamlc.opt -c -o cool_plugin.cmo cool_plugin.ml +ocamldep.opt -modules c2.ml > c2.ml.depends +ocamlc.opt -c -o c2.cmo c2.ml +ocamlc.opt aa.cmo c2.cmo bbcc.cma c3.cmo main.cmo cool_plugin.cmo -o main.byte +ocamlopt.opt -c -o bb.cmx bb.ml +ocamlopt.opt -c -o c2.cmx c2.ml +ocamlopt.opt -c -o aa.cmx aa.ml +ocamlopt.opt -c -o cc.cmx cc.ml +ocamlopt.opt -a bb.cmx cc.cmx -o bbcc.cmxa +ocamlopt.opt -c -o c3.cmx c3.ml +ocamlopt.opt -c -o main.cmx main.ml +ocamlopt.opt aa.cmx c2.cmx bbcc.cmxa c3.cmx main.cmx -o main.native ++ ../../_build/ocamlbuild.native bbcc.cma main.byte bbcc.cmxa main.native -no-skip -classic-display -verbose 0 -nothing-should-be-rebuilt +[cache hit] ocamldep.opt -modules bb.ml > bb.ml.depends +[cache hit] ocamldep.opt -modules bb.mli > bb.mli.depends +[cache hit] ocamlc.opt -c -o bb.cmi bb.mli +[cache hit] ocamlc.opt -c -o bb.cmo bb.ml +[cache hit] ocamldep.opt -modules cc.ml > cc.ml.depends +[cache hit] ocamldep.opt -modules c2.mli > c2.mli.depends +[cache hit] ocamlc.opt -c -o c2.cmi c2.mli +[cache hit] ocamldep.opt -modules aa.ml > aa.ml.depends +[cache hit] ocamlc.opt -c -o aa.cmo aa.ml +[cache hit] ocamlc.opt -c -o cc.cmo cc.ml +[cache hit] ocamlc.opt -a bb.cmo cc.cmo -o bbcc.cma +[cache hit] ocamldep.opt -modules main.ml > main.ml.depends +[cache hit] ocamldep.opt -modules c3.ml > c3.ml.depends +[cache hit] ocamlc.opt -c -o c3.cmo c3.ml +[cache hit] ocamlc.opt -c -o main.cmo main.ml +[cache hit] ocamldep.opt -modules cool_plugin.ml > cool_plugin.ml.depends +[cache hit] ocamlc.opt -c -o cool_plugin.cmo cool_plugin.ml +[cache hit] ocamldep.opt -modules c2.ml > c2.ml.depends +[cache hit] ocamlc.opt -c -o c2.cmo c2.ml +[cache hit] ocamlc.opt aa.cmo c2.cmo bbcc.cma c3.cmo main.cmo cool_plugin.cmo -o main.byte +[cache hit] ocamlopt.opt -c -o bb.cmx bb.ml +[cache hit] ocamlopt.opt -c -o c2.cmx c2.ml +[cache hit] ocamlopt.opt -c -o aa.cmx aa.ml +[cache hit] ocamlopt.opt -c -o cc.cmx cc.ml +[cache hit] ocamlopt.opt -a bb.cmx cc.cmx -o bbcc.cmxa +[cache hit] ocamlopt.opt -c -o c3.cmx c3.ml +[cache hit] ocamlopt.opt -c -o main.cmx main.ml +[cache hit] ocamlopt.opt aa.cmx c2.cmx bbcc.cmxa c3.cmx main.cmx -o main.native ++ cp bb2.ml bb.ml ++ ../../_build/ocamlbuild.native bbcc.cma main.byte bbcc.cmxa main.native -no-skip -classic-display -verbose 0 +ocamldep.opt -modules bb.ml > bb.ml.depends +[cache hit] ocamldep.opt -modules bb.mli > bb.mli.depends +[cache hit] ocamlc.opt -c -o bb.cmi bb.mli +[cache hit] ocamldep.opt -modules cc.ml > cc.ml.depends +[cache hit] ocamldep.opt -modules c2.mli > c2.mli.depends +[cache hit] ocamlc.opt -c -o c2.cmi c2.mli +[cache hit] ocamldep.opt -modules aa.ml > aa.ml.depends +[cache hit] ocamlc.opt -c -o aa.cmo aa.ml +[cache hit] ocamlc.opt -c -o cc.cmo cc.ml +ocamlc.opt -c -o bb.cmo bb.ml +ocamlc.opt -a bb.cmo cc.cmo -o bbcc.cma +[cache hit] ocamldep.opt -modules main.ml > main.ml.depends +[cache hit] ocamldep.opt -modules c3.ml > c3.ml.depends +[cache hit] ocamlc.opt -c -o c3.cmo c3.ml +[cache hit] ocamlc.opt -c -o main.cmo main.ml +[cache hit] ocamldep.opt -modules cool_plugin.ml > cool_plugin.ml.depends +[cache hit] ocamlc.opt -c -o cool_plugin.cmo cool_plugin.ml +[cache hit] ocamldep.opt -modules c2.ml > c2.ml.depends +[cache hit] ocamlc.opt -c -o c2.cmo c2.ml +ocamlc.opt aa.cmo c2.cmo bbcc.cma c3.cmo main.cmo cool_plugin.cmo -o main.byte +[cache hit] ocamlopt.opt -c -o c2.cmx c2.ml +ocamlopt.opt -c -o bb.cmx bb.ml +[cache hit] ocamlopt.opt -c -o aa.cmx aa.ml +ocamlopt.opt -c -o cc.cmx cc.ml +ocamlopt.opt -a bb.cmx cc.cmx -o bbcc.cmxa +ocamlopt.opt -c -o c3.cmx c3.ml +ocamlopt.opt -c -o main.cmx main.ml +ocamlopt.opt aa.cmx c2.cmx bbcc.cmxa c3.cmx main.cmx -o main.native ++ ../../_build/ocamlbuild.native bbcc.cma main.byte bbcc.cmxa main.native -no-skip -classic-display -verbose 0 -nothing-should-be-rebuilt +[cache hit] ocamldep.opt -modules bb.ml > bb.ml.depends +[cache hit] ocamldep.opt -modules bb.mli > bb.mli.depends +[cache hit] ocamlc.opt -c -o bb.cmi bb.mli +[cache hit] ocamlc.opt -c -o bb.cmo bb.ml +[cache hit] ocamldep.opt -modules cc.ml > cc.ml.depends +[cache hit] ocamldep.opt -modules c2.mli > c2.mli.depends +[cache hit] ocamlc.opt -c -o c2.cmi c2.mli +[cache hit] ocamldep.opt -modules aa.ml > aa.ml.depends +[cache hit] ocamlc.opt -c -o aa.cmo aa.ml +[cache hit] ocamlc.opt -c -o cc.cmo cc.ml +[cache hit] ocamlc.opt -a bb.cmo cc.cmo -o bbcc.cma +[cache hit] ocamldep.opt -modules main.ml > main.ml.depends +[cache hit] ocamldep.opt -modules c3.ml > c3.ml.depends +[cache hit] ocamlc.opt -c -o c3.cmo c3.ml +[cache hit] ocamlc.opt -c -o main.cmo main.ml +[cache hit] ocamldep.opt -modules cool_plugin.ml > cool_plugin.ml.depends +[cache hit] ocamlc.opt -c -o cool_plugin.cmo cool_plugin.ml +[cache hit] ocamldep.opt -modules c2.ml > c2.ml.depends +[cache hit] ocamlc.opt -c -o c2.cmo c2.ml +[cache hit] ocamlc.opt aa.cmo c2.cmo bbcc.cma c3.cmo main.cmo cool_plugin.cmo -o main.byte +[cache hit] ocamlopt.opt -c -o bb.cmx bb.ml +[cache hit] ocamlopt.opt -c -o c2.cmx c2.ml +[cache hit] ocamlopt.opt -c -o aa.cmx aa.ml +[cache hit] ocamlopt.opt -c -o cc.cmx cc.ml +[cache hit] ocamlopt.opt -a bb.cmx cc.cmx -o bbcc.cmxa +[cache hit] ocamlopt.opt -c -o c3.cmx c3.ml +[cache hit] ocamlopt.opt -c -o main.cmx main.ml +[cache hit] ocamlopt.opt aa.cmx c2.cmx bbcc.cmxa c3.cmx main.cmx -o main.native ++ cp bb3.ml bb.ml ++ ../../_build/ocamlbuild.native bbcc.cma main.byte bbcc.cmxa main.native -no-skip -classic-display -verbose 0 +ocamldep.opt -modules bb.ml > bb.ml.depends +[cache hit] ocamldep.opt -modules bb.mli > bb.mli.depends +[cache hit] ocamlc.opt -c -o bb.cmi bb.mli +[cache hit] ocamldep.opt -modules cc.ml > cc.ml.depends +[cache hit] ocamldep.opt -modules c2.mli > c2.mli.depends +[cache hit] ocamlc.opt -c -o c2.cmi c2.mli +[cache hit] ocamldep.opt -modules aa.ml > aa.ml.depends +[cache hit] ocamlc.opt -c -o aa.cmo aa.ml +[cache hit] ocamlc.opt -c -o cc.cmo cc.ml +ocamlc.opt -c -o bb.cmo bb.ml +ocamlc.opt -a bb.cmo cc.cmo -o bbcc.cma +[cache hit] ocamldep.opt -modules main.ml > main.ml.depends +[cache hit] ocamldep.opt -modules c3.ml > c3.ml.depends +[cache hit] ocamlc.opt -c -o c3.cmo c3.ml +[cache hit] ocamlc.opt -c -o main.cmo main.ml +[cache hit] ocamldep.opt -modules cool_plugin.ml > cool_plugin.ml.depends +[cache hit] ocamlc.opt -c -o cool_plugin.cmo cool_plugin.ml +[cache hit] ocamldep.opt -modules c2.ml > c2.ml.depends +[cache hit] ocamlc.opt -c -o c2.cmo c2.ml +ocamlc.opt aa.cmo c2.cmo bbcc.cma c3.cmo main.cmo cool_plugin.cmo -o main.byte +[cache hit] ocamlopt.opt -c -o c2.cmx c2.ml +ocamlopt.opt -c -o bb.cmx bb.ml +[cache hit] ocamlopt.opt -c -o aa.cmx aa.ml +[cache hit] ocamlopt.opt -c -o cc.cmx cc.ml +ocamlopt.opt -a bb.cmx cc.cmx -o bbcc.cmxa +[cache hit] ocamlopt.opt -c -o c3.cmx c3.ml +[cache hit] ocamlopt.opt -c -o main.cmx main.ml +ocamlopt.opt aa.cmx c2.cmx bbcc.cmxa c3.cmx main.cmx -o main.native ++ ../../_build/ocamlbuild.native bbcc.cma main.byte bbcc.cmxa main.native -no-skip -classic-display -verbose 0 -nothing-should-be-rebuilt +[cache hit] ocamldep.opt -modules bb.ml > bb.ml.depends +[cache hit] ocamldep.opt -modules bb.mli > bb.mli.depends +[cache hit] ocamlc.opt -c -o bb.cmi bb.mli +[cache hit] ocamlc.opt -c -o bb.cmo bb.ml +[cache hit] ocamldep.opt -modules cc.ml > cc.ml.depends +[cache hit] ocamldep.opt -modules c2.mli > c2.mli.depends +[cache hit] ocamlc.opt -c -o c2.cmi c2.mli +[cache hit] ocamldep.opt -modules aa.ml > aa.ml.depends +[cache hit] ocamlc.opt -c -o aa.cmo aa.ml +[cache hit] ocamlc.opt -c -o cc.cmo cc.ml +[cache hit] ocamlc.opt -a bb.cmo cc.cmo -o bbcc.cma +[cache hit] ocamldep.opt -modules main.ml > main.ml.depends +[cache hit] ocamldep.opt -modules c3.ml > c3.ml.depends +[cache hit] ocamlc.opt -c -o c3.cmo c3.ml +[cache hit] ocamlc.opt -c -o main.cmo main.ml +[cache hit] ocamldep.opt -modules cool_plugin.ml > cool_plugin.ml.depends +[cache hit] ocamlc.opt -c -o cool_plugin.cmo cool_plugin.ml +[cache hit] ocamldep.opt -modules c2.ml > c2.ml.depends +[cache hit] ocamlc.opt -c -o c2.cmo c2.ml +[cache hit] ocamlc.opt aa.cmo c2.cmo bbcc.cma c3.cmo main.cmo cool_plugin.cmo -o main.byte +[cache hit] ocamlopt.opt -c -o bb.cmx bb.ml +[cache hit] ocamlopt.opt -c -o c2.cmx c2.ml +[cache hit] ocamlopt.opt -c -o aa.cmx aa.ml +[cache hit] ocamlopt.opt -c -o cc.cmx cc.ml +[cache hit] ocamlopt.opt -a bb.cmx cc.cmx -o bbcc.cmxa +[cache hit] ocamlopt.opt -c -o c3.cmx c3.ml +[cache hit] ocamlopt.opt -c -o main.cmx main.ml +[cache hit] ocamlopt.opt aa.cmx c2.cmx bbcc.cmxa c3.cmx main.cmx -o main.native ++ svn revert bb.ml +Reverted 'bb.ml' + _____ _ ___ +|_ _|__ ___| |_( _ ) + | |/ _ \/ __| __/ _ \ + | | __/\__ \ || (_) | + |_|\___||___/\__\___/ + ++ CMDOPTS= ++ BUILD=../../_build/ocamlbuild.native a.byte a.native -no-skip -classic-display ++ BUILD1=../../_build/ocamlbuild.native a.byte a.native -no-skip -classic-display ++ BUILD2=../../_build/ocamlbuild.native a.byte a.native -no-skip -classic-display -verbose 0 -nothing-should-be-rebuilt ++ rm -rf _build ++ ../../_build/ocamlbuild.native a.byte a.native -no-skip -classic-display +ocamlopt.opt -I /Users/ertai/local/lib/ocamlbuild unix.cmxa /Users/ertai/local/lib/ocamlbuild/ocamlbuildlib.cmxa myocamlbuild.ml /Users/ertai/local/lib/ocamlbuild/ocamlbuild.cmx -o myocamlbuild +ocamldep.opt -modules a.ml > a.ml.depends +ocamldep.opt -modules myconfig.ml > myconfig.ml.depends +ocamlc.opt -c -o myconfig.cmo myconfig.ml +ocamlc.opt -c -o a.cmo a.ml +ocamlc.opt myconfig.cmo a.cmo -o a.byte +ocamlopt.opt -c -o myconfig.cmx myconfig.ml +ocamlopt.opt -c -o a.cmx a.ml +ocamlopt.opt myconfig.cmx a.cmx -o a.native ++ ../../_build/ocamlbuild.native a.byte a.native -no-skip -classic-display -verbose 0 -nothing-should-be-rebuilt +[cache hit] ocamldep.opt -modules a.ml > a.ml.depends +[cache hit] ocamldep.opt -modules myconfig.ml > myconfig.ml.depends +[cache hit] ocamlc.opt -c -o myconfig.cmo myconfig.ml +[cache hit] ocamlc.opt -c -o a.cmo a.ml +[cache hit] ocamlc.opt myconfig.cmo a.cmo -o a.byte +[cache hit] ocamlopt.opt -c -o myconfig.cmx myconfig.ml +[cache hit] ocamlopt.opt -c -o a.cmx a.ml +[cache hit] ocamlopt.opt myconfig.cmx a.cmx -o a.native + _____ _ ___ +|_ _|__ ___| |_ / _ \ + | |/ _ \/ __| __| (_) | + | | __/\__ \ |_ \__, | + |_|\___||___/\__| /_/ + +++ dirname ./test9/test.sh ++ cd ./test9/../.. ++ ./_build/ocamlbuild.native -quiet -build-dir _buildtest -no-links test/test9/testglob.native ++ ./_buildtest/test/test9/testglob.native +Globexp for "\"hello\"" OK +Globexp for "<hello>" OK +Globexp for "<hel*lo>" OK +Globexp for "<a> and <b> or <c>" OK +Globexp for "<a> titi" OK +Glob.eval "<[a]>" "a" = true OK +Glob.eval "<[a]>" "b" = false OK +Glob.eval "<[a]>" "a" = true OK +Glob.eval "<[a]>" "b" = false OK +Glob.eval "<[a]>" "a" = true OK +Glob.eval "<[a]>" "b" = false OK +Glob.eval "<[a-z]>" "a" = true OK +Glob.eval "<[a-z]>" "e" = true OK +Glob.eval "<[a-z]>" "k" = true OK +Glob.eval "<[a-z]>" "z" = true OK +Glob.eval "<[a-z]>" "0" = false OK +Glob.eval "<[a-z]>" "A" = false OK +Glob.eval "<[a-z]>" "~" = false OK +Glob.eval "<[a-z]>" "a" = true OK +Glob.eval "<[a-z]>" "e" = true OK +Glob.eval "<[a-z]>" "k" = true OK +Glob.eval "<[a-z]>" "z" = true OK +Glob.eval "<[a-z]>" "0" = false OK +Glob.eval "<[a-z]>" "A" = false OK +Glob.eval "<[a-z]>" "~" = false OK +Glob.eval "<[a-z]>" "a" = true OK +Glob.eval "<[a-z]>" "e" = true OK +Glob.eval "<[a-z]>" "k" = true OK +Glob.eval "<[a-z]>" "z" = true OK +Glob.eval "<[a-z]>" "0" = false OK +Glob.eval "<[a-z]>" "A" = false OK +Glob.eval "<[a-z]>" "~" = false OK +Glob.eval "<[a-z][0-9]>" "a0" = true OK +Glob.eval "<[a-z][0-9]>" "b9" = true OK +Glob.eval "<[a-z][0-9]>" "a00" = false OK +Glob.eval "<[a-z][0-9]>" "a0a" = false OK +Glob.eval "<[a-z][0-9]>" "b0a" = false OK +Glob.eval "<[a-z][0-9]>" "isduis" = false OK +Glob.eval "<[a-z][0-9]>" "" = false OK +Glob.eval "<[a-z][0-9]>" "a0" = true OK +Glob.eval "<[a-z][0-9]>" "b9" = true OK +Glob.eval "<[a-z][0-9]>" "a00" = false OK +Glob.eval "<[a-z][0-9]>" "a0a" = false OK +Glob.eval "<[a-z][0-9]>" "b0a" = false OK +Glob.eval "<[a-z][0-9]>" "isduis" = false OK +Glob.eval "<[a-z][0-9]>" "" = false OK +Glob.eval "<[a-z][0-9]>" "a0" = true OK +Glob.eval "<[a-z][0-9]>" "b9" = true OK +Glob.eval "<[a-z][0-9]>" "a00" = false OK +Glob.eval "<[a-z][0-9]>" "a0a" = false OK +Glob.eval "<[a-z][0-9]>" "b0a" = false OK +Glob.eval "<[a-z][0-9]>" "isduis" = false OK +Glob.eval "<[a-z][0-9]>" "" = false OK +Glob.eval "<hello>" "hello" = true OK +Glob.eval "<hello>" "helli" = false OK +Glob.eval "<hello>" "hello" = true OK +Glob.eval "<hello>" "helli" = false OK +Glob.eval "<hello>" "hello" = true OK +Glob.eval "<hello>" "helli" = false OK +Glob.eval "\"hello\"" "hello" = true OK +Glob.eval "\"hello\"" "heidi" = false OK +Glob.eval "\"hello\"" "hello" = true OK +Glob.eval "\"hello\"" "heidi" = false OK +Glob.eval "\"hello\"" "hello" = true OK +Glob.eval "\"hello\"" "heidi" = false OK +Glob.eval "<*>" "" = true OK +Glob.eval "<*>" "a" = true OK +Glob.eval "<*>" "ax" = true OK +Glob.eval "<*>" "" = true OK +Glob.eval "<*>" "a" = true OK +Glob.eval "<*>" "ax" = true OK +Glob.eval "<*>" "" = true OK +Glob.eval "<*>" "a" = true OK +Glob.eval "<*>" "ax" = true OK +Glob.eval "<a*b>" "ab" = true OK +Glob.eval "<a*b>" "acb" = true OK +Glob.eval "<a*b>" "axxxxxb" = true OK +Glob.eval "<a*b>" "ababbababb" = true OK +Glob.eval "<a*b>" "abx" = false OK +Glob.eval "<a*b>" "xxxxxab" = false OK +Glob.eval "<a*b>" "xab" = false OK +Glob.eval "<a*b>" "ab" = true OK +Glob.eval "<a*b>" "acb" = true OK +Glob.eval "<a*b>" "axxxxxb" = true OK +Glob.eval "<a*b>" "ababbababb" = true OK +Glob.eval "<a*b>" "abx" = false OK +Glob.eval "<a*b>" "xxxxxab" = false OK +Glob.eval "<a*b>" "xab" = false OK +Glob.eval "<a*b>" "ab" = true OK +Glob.eval "<a*b>" "acb" = true OK +Glob.eval "<a*b>" "axxxxxb" = true OK +Glob.eval "<a*b>" "ababbababb" = true OK +Glob.eval "<a*b>" "abx" = false OK +Glob.eval "<a*b>" "xxxxxab" = false OK +Glob.eval "<a*b>" "xab" = false OK +Glob.eval "<*.ml>" "hello.ml" = true OK +Glob.eval "<*.ml>" ".ml" = true OK +Glob.eval "<*.ml>" "ml" = false OK +Glob.eval "<*.ml>" "" = false OK +Glob.eval "<*.ml>" "toto.mli" = false OK +Glob.eval "<*.ml>" "hello.ml" = true OK +Glob.eval "<*.ml>" ".ml" = true OK +Glob.eval "<*.ml>" "ml" = false OK +Glob.eval "<*.ml>" "" = false OK +Glob.eval "<*.ml>" "toto.mli" = false OK +Glob.eval "<*.ml>" "hello.ml" = true OK +Glob.eval "<*.ml>" ".ml" = true OK +Glob.eval "<*.ml>" "ml" = false OK +Glob.eval "<*.ml>" "" = false OK +Glob.eval "<*.ml>" "toto.mli" = false OK +Glob.eval "<a>" "a" = true OK +Glob.eval "<a>" "" = false OK +Glob.eval "<a>" "aa" = false OK +Glob.eval "<a>" "ba" = false OK +Glob.eval "<a>" "ab" = false OK +Glob.eval "<a>" "abaa" = false OK +Glob.eval "<a>" "a" = true OK +Glob.eval "<a>" "" = false OK +Glob.eval "<a>" "aa" = false OK +Glob.eval "<a>" "ba" = false OK +Glob.eval "<a>" "ab" = false OK +Glob.eval "<a>" "abaa" = false OK +Glob.eval "<a>" "a" = true OK +Glob.eval "<a>" "" = false OK +Glob.eval "<a>" "aa" = false OK +Glob.eval "<a>" "ba" = false OK +Glob.eval "<a>" "ab" = false OK +Glob.eval "<a>" "abaa" = false OK +Glob.eval "<ab>" "ab" = true OK +Glob.eval "<ab>" "" = false OK +Glob.eval "<ab>" "abab" = false OK +Glob.eval "<ab>" "aba" = false OK +Glob.eval "<ab>" "abx" = false OK +Glob.eval "<ab>" "ab" = true OK +Glob.eval "<ab>" "" = false OK +Glob.eval "<ab>" "abab" = false OK +Glob.eval "<ab>" "aba" = false OK +Glob.eval "<ab>" "abx" = false OK +Glob.eval "<ab>" "ab" = true OK +Glob.eval "<ab>" "" = false OK +Glob.eval "<ab>" "abab" = false OK +Glob.eval "<ab>" "aba" = false OK +Glob.eval "<ab>" "abx" = false OK +Glob.eval "<ab?c>" "abac" = true OK +Glob.eval "<ab?c>" "abxc" = true OK +Glob.eval "<ab?c>" "abab" = false OK +Glob.eval "<ab?c>" "ababab" = false OK +Glob.eval "<ab?c>" "ababa" = false OK +Glob.eval "<ab?c>" "abac" = true OK +Glob.eval "<ab?c>" "abxc" = true OK +Glob.eval "<ab?c>" "abab" = false OK +Glob.eval "<ab?c>" "ababab" = false OK +Glob.eval "<ab?c>" "ababa" = false OK +Glob.eval "<ab?c>" "abac" = true OK +Glob.eval "<ab?c>" "abxc" = true OK +Glob.eval "<ab?c>" "abab" = false OK +Glob.eval "<ab?c>" "ababab" = false OK +Glob.eval "<ab?c>" "ababa" = false OK +Glob.eval "<*ab?cd*>" "123abecd345" = true OK +Glob.eval "<*ab?cd*>" "abccd" = true OK +Glob.eval "<*ab?cd*>" "abccd345" = true OK +Glob.eval "<*ab?cd*>" "ababcababccdab" = true OK +Glob.eval "<*ab?cd*>" "abcd" = false OK +Glob.eval "<*ab?cd*>" "aaaaabcdababcd" = false OK +Glob.eval "<*ab?cd*>" "123abecd345" = true OK +Glob.eval "<*ab?cd*>" "abccd" = true OK +Glob.eval "<*ab?cd*>" "abccd345" = true OK +Glob.eval "<*ab?cd*>" "ababcababccdab" = true OK +Glob.eval "<*ab?cd*>" "abcd" = false OK +Glob.eval "<*ab?cd*>" "aaaaabcdababcd" = false OK +Glob.eval "<*ab?cd*>" "123abecd345" = true OK +Glob.eval "<*ab?cd*>" "abccd" = true OK +Glob.eval "<*ab?cd*>" "abccd345" = true OK +Glob.eval "<*ab?cd*>" "ababcababccdab" = true OK +Glob.eval "<*ab?cd*>" "abcd" = false OK +Glob.eval "<*ab?cd*>" "aaaaabcdababcd" = false OK +Glob.eval "<*this*is*a*test*>" "this is a test" = true OK +Glob.eval "<*this*is*a*test*>" "You know this is a test really" = true OK +Glob.eval "<*this*is*a*test*>" "thisisatest" = true OK +Glob.eval "<*this*is*a*test*>" "thisatest" = false OK +Glob.eval "<*this*is*a*test*>" "this is a test" = true OK +Glob.eval "<*this*is*a*test*>" "You know this is a test really" = true OK +Glob.eval "<*this*is*a*test*>" "thisisatest" = true OK +Glob.eval "<*this*is*a*test*>" "thisatest" = false OK +Glob.eval "<*this*is*a*test*>" "this is a test" = true OK +Glob.eval "<*this*is*a*test*>" "You know this is a test really" = true OK +Glob.eval "<*this*is*a*test*>" "thisisatest" = true OK +Glob.eval "<*this*is*a*test*>" "thisatest" = false OK +Glob.eval "<b*>" "bxx" = true OK +Glob.eval "<b*>" "bx" = true OK +Glob.eval "<b*>" "aaab" = false OK +Glob.eval "<b*>" "" = false OK +Glob.eval "<b*>" "bxx" = true OK +Glob.eval "<b*>" "bx" = true OK +Glob.eval "<b*>" "aaab" = false OK +Glob.eval "<b*>" "" = false OK +Glob.eval "<b*>" "bxx" = true OK +Glob.eval "<b*>" "bx" = true OK +Glob.eval "<b*>" "aaab" = false OK +Glob.eval "<b*>" "" = false OK +Glob.eval "<*>" "" = true OK +Glob.eval "<*>" "a" = true OK +Glob.eval "<*>" "aaa" = true OK +Glob.eval "<*>" "aaaaa" = true OK +Glob.eval "<*>" "" = true OK +Glob.eval "<*>" "a" = true OK +Glob.eval "<*>" "aaa" = true OK +Glob.eval "<*>" "aaaaa" = true OK +Glob.eval "<*>" "" = true OK +Glob.eval "<*>" "a" = true OK +Glob.eval "<*>" "aaa" = true OK +Glob.eval "<*>" "aaaaa" = true OK +Glob.eval "<?>" "a" = true OK +Glob.eval "<?>" "" = false OK +Glob.eval "<?>" "aaa" = false OK +Glob.eval "<?>" "aaaaa" = false OK +Glob.eval "<?>" "a" = true OK +Glob.eval "<?>" "" = false OK +Glob.eval "<?>" "aaa" = false OK +Glob.eval "<?>" "aaaaa" = false OK +Glob.eval "<?>" "a" = true OK +Glob.eval "<?>" "" = false OK +Glob.eval "<?>" "aaa" = false OK +Glob.eval "<?>" "aaaaa" = false OK +Glob.eval "<{a,b}>" "a" = true OK +Glob.eval "<{a,b}>" "b" = true OK +Glob.eval "<{a,b}>" "" = false OK +Glob.eval "<{a,b}>" "aa" = false OK +Glob.eval "<{a,b}>" "ab" = false OK +Glob.eval "<{a,b}>" "ba" = false OK +Glob.eval "<{a,b}>" "bb" = false OK +Glob.eval "<{a,b}>" "c" = false OK +Glob.eval "<{a,b}>" "a" = true OK +Glob.eval "<{a,b}>" "b" = true OK +Glob.eval "<{a,b}>" "" = false OK +Glob.eval "<{a,b}>" "aa" = false OK +Glob.eval "<{a,b}>" "ab" = false OK +Glob.eval "<{a,b}>" "ba" = false OK +Glob.eval "<{a,b}>" "bb" = false OK +Glob.eval "<{a,b}>" "c" = false OK +Glob.eval "<{a,b}>" "a" = true OK +Glob.eval "<{a,b}>" "b" = true OK +Glob.eval "<{a,b}>" "" = false OK +Glob.eval "<{a,b}>" "aa" = false OK +Glob.eval "<{a,b}>" "ab" = false OK +Glob.eval "<{a,b}>" "ba" = false OK +Glob.eval "<{a,b}>" "bb" = false OK +Glob.eval "<{a,b}>" "c" = false OK +Glob.eval "<toto.{ml,mli}>" "toto.ml" = true OK +Glob.eval "<toto.{ml,mli}>" "toto.mli" = true OK +Glob.eval "<toto.{ml,mli}>" "toto." = false OK +Glob.eval "<toto.{ml,mli}>" "toto.mll" = false OK +Glob.eval "<toto.{ml,mli}>" "toto.ml" = true OK +Glob.eval "<toto.{ml,mli}>" "toto.mli" = true OK +Glob.eval "<toto.{ml,mli}>" "toto." = false OK +Glob.eval "<toto.{ml,mli}>" "toto.mll" = false OK +Glob.eval "<toto.{ml,mli}>" "toto.ml" = true OK +Glob.eval "<toto.{ml,mli}>" "toto.mli" = true OK +Glob.eval "<toto.{ml,mli}>" "toto." = false OK +Glob.eval "<toto.{ml,mli}>" "toto.mll" = false OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "acf" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "acg" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "adf" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "adg" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "aef" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "aeg" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "bcf" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "bcg" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "bdf" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "bdg" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "bef" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "beg" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "afg" = false OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "af" = false OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "aee" = false OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "acf" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "acg" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "adf" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "adg" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "aef" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "aeg" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "bcf" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "bcg" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "bdf" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "bdg" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "bef" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "beg" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "afg" = false OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "af" = false OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "aee" = false OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "acf" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "acg" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "adf" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "adg" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "aef" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "aeg" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "bcf" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "bcg" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "bdf" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "bdg" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "bef" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "beg" = true OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "afg" = false OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "af" = false OK +Glob.eval "<{a,b}{c,[de]}{f,g}>" "aee" = false OK +Glob.eval "(<*.ml> or <*.mli>) and not \"hello.ml\"" "a.ml" = true OK +Glob.eval "(<*.ml> or <*.mli>) and not \"hello.ml\"" "b.ml" = true OK +Glob.eval "(<*.ml> or <*.mli>) and not \"hello.ml\"" "a.mli" = true OK +Glob.eval "(<*.ml> or <*.mli>) and not \"hello.ml\"" "hello.ml" = false OK +Glob.eval "(<*.ml> or <*.mli>) and not \"hello.ml\"" "a.mli.x" = false OK +Glob.eval "(<*.ml> or <*.mli>) and not \"hello.ml\"" "a.ml" = true OK +Glob.eval "(<*.ml> or <*.mli>) and not \"hello.ml\"" "b.ml" = true OK +Glob.eval "(<*.ml> or <*.mli>) and not \"hello.ml\"" "a.mli" = true OK +Glob.eval "(<*.ml> or <*.mli>) and not \"hello.ml\"" "hello.ml" = false OK +Glob.eval "(<*.ml> or <*.mli>) and not \"hello.ml\"" "a.mli.x" = false OK +Glob.eval "(<*.ml> or <*.mli>) and not \"hello.ml\"" "a.ml" = true OK +Glob.eval "(<*.ml> or <*.mli>) and not \"hello.ml\"" "b.ml" = true OK +Glob.eval "(<*.ml> or <*.mli>) and not \"hello.ml\"" "a.mli" = true OK +Glob.eval "(<*.ml> or <*.mli>) and not \"hello.ml\"" "hello.ml" = false OK +Glob.eval "(<*.ml> or <*.mli>) and not \"hello.ml\"" "a.mli.x" = false OK +Glob.eval "<*>" "alpha" = true OK +Glob.eval "<*>" "beta" = true OK +Glob.eval "<*>" "alpha/beta" = false OK +Glob.eval "<*>" "gamma/delta" = false OK +Glob.eval "<*>" "alpha" = true OK +Glob.eval "<*>" "beta" = true OK +Glob.eval "<*>" "alpha/beta" = false OK +Glob.eval "<*>" "gamma/delta" = false OK +Glob.eval "<*>" "alpha" = true OK +Glob.eval "<*>" "beta" = true OK +Glob.eval "<*>" "alpha/beta" = false OK +Glob.eval "<*>" "gamma/delta" = false OK +Glob.eval "<alpha/**/beta>" "alpha/beta" = true OK +Glob.eval "<alpha/**/beta>" "alpha/gamma/beta" = true OK +Glob.eval "<alpha/**/beta>" "alpha/gamma/delta/beta" = true OK +Glob.eval "<alpha/**/beta>" "alpha" = false OK +Glob.eval "<alpha/**/beta>" "beta" = false OK +Glob.eval "<alpha/**/beta>" "gamma/delta" = false OK +Glob.eval "<alpha/**/beta>" "alpha/beta" = true OK +Glob.eval "<alpha/**/beta>" "alpha/gamma/beta" = true OK +Glob.eval "<alpha/**/beta>" "alpha/gamma/delta/beta" = true OK +Glob.eval "<alpha/**/beta>" "alpha" = false OK +Glob.eval "<alpha/**/beta>" "beta" = false OK +Glob.eval "<alpha/**/beta>" "gamma/delta" = false OK +Glob.eval "<alpha/**/beta>" "alpha/beta" = true OK +Glob.eval "<alpha/**/beta>" "alpha/gamma/beta" = true OK +Glob.eval "<alpha/**/beta>" "alpha/gamma/delta/beta" = true OK +Glob.eval "<alpha/**/beta>" "alpha" = false OK +Glob.eval "<alpha/**/beta>" "beta" = false OK +Glob.eval "<alpha/**/beta>" "gamma/delta" = false OK +Glob.eval "<**/*.ml>" "toto.ml" = true OK +Glob.eval "<**/*.ml>" "toto/tata.ml" = true OK +Glob.eval "<**/*.ml>" "alpha/gamma/delta/beta.ml" = true OK +Glob.eval "<**/*.ml>" "toto.mli" = false OK +Glob.eval "<**/*.ml>" "toto.ml" = true OK +Glob.eval "<**/*.ml>" "toto/tata.ml" = true OK +Glob.eval "<**/*.ml>" "alpha/gamma/delta/beta.ml" = true OK +Glob.eval "<**/*.ml>" "toto.mli" = false OK +Glob.eval "<**/*.ml>" "toto.ml" = true OK +Glob.eval "<**/*.ml>" "toto/tata.ml" = true OK +Glob.eval "<**/*.ml>" "alpha/gamma/delta/beta.ml" = true OK +Glob.eval "<**/*.ml>" "toto.mli" = false OK +Glob.eval "<toto/**>" "toto/" = true OK +Glob.eval "<toto/**>" "toto/tata" = true OK +Glob.eval "<toto/**>" "toto/alpha/gamma/delta/beta.ml" = true OK +Glob.eval "<toto/**>" "toto" = true OK +Glob.eval "<toto/**>" "toto2/tata" = false OK +Glob.eval "<toto/**>" "tata/titi" = false OK +Glob.eval "<toto/**>" "toto/" = true OK +Glob.eval "<toto/**>" "toto/tata" = true OK +Glob.eval "<toto/**>" "toto/alpha/gamma/delta/beta.ml" = true OK +Glob.eval "<toto/**>" "toto" = true OK +Glob.eval "<toto/**>" "toto2/tata" = false OK +Glob.eval "<toto/**>" "tata/titi" = false OK +Glob.eval "<toto/**>" "toto/" = true OK +Glob.eval "<toto/**>" "toto/tata" = true OK +Glob.eval "<toto/**>" "toto/alpha/gamma/delta/beta.ml" = true OK +Glob.eval "<toto/**>" "toto" = true OK +Glob.eval "<toto/**>" "toto2/tata" = false OK +Glob.eval "<toto/**>" "tata/titi" = false OK diff --git a/ocamlbuild/test/runtest.sh b/ocamlbuild/test/runtest.sh new file mode 100755 index 0000000000..7bdb76be13 --- /dev/null +++ b/ocamlbuild/test/runtest.sh @@ -0,0 +1,34 @@ +#!/bin/sh +set -e +cd `dirname $0` + +myfiglet() { + figlet $@ | sed 's/ *$//' +} + +if figlet ""; then + BANNER=myfiglet +else + echo "Install figlet to have a better output, press enter to continue with echo" + read + BANNER=echo +fi + +HERE=`pwd` + +$BANNER Test2 +./test2/test.sh $@ +$BANNER Test3 +./test3/test.sh $@ +$BANNER Test4 +./test4/test.sh $@ +$BANNER Test5 +./test5/test.sh $@ +$BANNER Test6 +./test6/test.sh $@ +$BANNER Test7 +./test7/test.sh $@ +$BANNER Test8 +./test8/test.sh $@ +$BANNER Test9 +./test9/test.sh $@ diff --git a/ocamlbuild/test/test1/foo.ml b/ocamlbuild/test/test1/foo.ml new file mode 100644 index 0000000000..43a5106519 --- /dev/null +++ b/ocamlbuild/test/test1/foo.ml @@ -0,0 +1 @@ +module MA1 = A1 diff --git a/ocamlbuild/test/test10/dbdi b/ocamlbuild/test/test10/dbdi new file mode 100644 index 0000000000..7f548108bb --- /dev/null +++ b/ocamlbuild/test/test10/dbdi @@ -0,0 +1,12 @@ +#load "discard_printf.cmo";; +#load "debug.cmo";; +#load "unix.cma";; +#load "str.cma";; +#load "my_unix.cmo";; +#load "bool.cmo";; +#load "glob_ast.cmo";; +#load "glob_lexer.cmo";; +#load "glob.cmo";; +#load "lexers.cmo";; +#load "my_std.cmo";; +#load "tags.cmo";; diff --git a/ocamlbuild/test/test10/test.sh b/ocamlbuild/test/test10/test.sh new file mode 100755 index 0000000000..1f96443c5e --- /dev/null +++ b/ocamlbuild/test/test10/test.sh @@ -0,0 +1,6 @@ +#!/bin/sh +set -e +set -x +cd `dirname $0`/../.. +./_build/ocamlbuild.native -quiet -build-dir _buildtest -no-links test/test9/testglob.native +./_buildtest/test/test9/testglob.native diff --git a/ocamlbuild/test/test2/_tags b/ocamlbuild/test/test2/_tags new file mode 100644 index 0000000000..354ad4e694 --- /dev/null +++ b/ocamlbuild/test/test2/_tags @@ -0,0 +1 @@ +"vivi.ml": camlp4o diff --git a/ocamlbuild/test/test2/tata.ml b/ocamlbuild/test/test2/tata.ml new file mode 100644 index 0000000000..361fadd354 --- /dev/null +++ b/ocamlbuild/test/test2/tata.ml @@ -0,0 +1 @@ +let tata = "TATA2" diff --git a/ocamlbuild/test/test2/tata.mli b/ocamlbuild/test/test2/tata.mli new file mode 100644 index 0000000000..7c7175c91f --- /dev/null +++ b/ocamlbuild/test/test2/tata.mli @@ -0,0 +1,2 @@ +(* a comment *) +val tata : string diff --git a/ocamlbuild/test/test2/test.sh b/ocamlbuild/test/test2/test.sh new file mode 100755 index 0000000000..8bbd7c7a7c --- /dev/null +++ b/ocamlbuild/test/test2/test.sh @@ -0,0 +1,18 @@ +#!/bin/sh +cd `dirname $0` +set -e +set -x +CMDOPTS="-- -help" +BUILD="../../_build/ocamlbuild.native toto.byte toto.native -no-skip -classic-display $@" +BUILD1="$BUILD $CMDOPTS" +BUILD2="$BUILD -verbose 0 -nothing-should-be-rebuilt $CMDOPTS" +rm -rf _build +cp vivi1.ml vivi.ml +$BUILD1 +$BUILD2 +cp vivi2.ml vivi.ml +$BUILD1 +$BUILD2 +cp vivi3.ml vivi.ml +$BUILD1 +$BUILD2 diff --git a/ocamlbuild/test/test2/titi.ml b/ocamlbuild/test/test2/titi.ml new file mode 100644 index 0000000000..3abbf1d2e2 --- /dev/null +++ b/ocamlbuild/test/test2/titi.ml @@ -0,0 +1 @@ +let titi = [] diff --git a/ocamlbuild/test/test2/toto.ml b/ocamlbuild/test/test2/toto.ml new file mode 100644 index 0000000000..dbb5a43d38 --- /dev/null +++ b/ocamlbuild/test/test2/toto.ml @@ -0,0 +1,5 @@ +let i = Tutu.tutu + 10 +let s = Tata.tata ^ ".ml" +let l = 3 :: Titi.titi +let () = Format.printf "toto.native: %s: Hello world!!!@." Sys.argv.(0) +let () = Format.printf "Tutu.tutu => %d@.Tata.tata => %S@." Tutu.tutu Tata.tata diff --git a/ocamlbuild/test/test2/tutu.ml b/ocamlbuild/test/test2/tutu.ml new file mode 100644 index 0000000000..2e8015b701 --- /dev/null +++ b/ocamlbuild/test/test2/tutu.ml @@ -0,0 +1,2 @@ +let tutu = (Array.length Vivi.vivi : Tyty.t) +let tutu' = 2.0 +. float_of_int tutu diff --git a/ocamlbuild/test/test2/tutu.mli b/ocamlbuild/test/test2/tutu.mli new file mode 100644 index 0000000000..26657f87cd --- /dev/null +++ b/ocamlbuild/test/test2/tutu.mli @@ -0,0 +1,3 @@ +(* a comment *) +val tutu : int +val tutu' : float diff --git a/ocamlbuild/test/test2/tyty.mli b/ocamlbuild/test/test2/tyty.mli new file mode 100644 index 0000000000..975adb5316 --- /dev/null +++ b/ocamlbuild/test/test2/tyty.mli @@ -0,0 +1 @@ +type t = int diff --git a/ocamlbuild/test/test2/vivi1.ml b/ocamlbuild/test/test2/vivi1.ml new file mode 100644 index 0000000000..1c0517e011 --- /dev/null +++ b/ocamlbuild/test/test2/vivi1.ml @@ -0,0 +1,2 @@ +let rec p i = [< '1; '2; p (i + 1) >] +let vivi = [|2|] diff --git a/ocamlbuild/test/test2/vivi2.ml b/ocamlbuild/test/test2/vivi2.ml new file mode 100644 index 0000000000..1fb48c1763 --- /dev/null +++ b/ocamlbuild/test/test2/vivi2.ml @@ -0,0 +1,2 @@ +let rec p i = [< '1; '2; p (i + 1) >] +let vivi = [|3|] diff --git a/ocamlbuild/test/test2/vivi3.ml b/ocamlbuild/test/test2/vivi3.ml new file mode 100644 index 0000000000..7849fad651 --- /dev/null +++ b/ocamlbuild/test/test2/vivi3.ml @@ -0,0 +1,2 @@ +let rec p i = [< '1; '2; p (i + 1) >] +let vivi = [|2.1; 1.1|] diff --git a/ocamlbuild/test/test3/_tags b/ocamlbuild/test/test3/_tags new file mode 100644 index 0000000000..4505f13fda --- /dev/null +++ b/ocamlbuild/test/test3/_tags @@ -0,0 +1 @@ +"a.byte" or "a.native": use_unix diff --git a/ocamlbuild/test/test3/a.ml b/ocamlbuild/test/test3/a.ml new file mode 100644 index 0000000000..d4586eae7b --- /dev/null +++ b/ocamlbuild/test/test3/a.ml @@ -0,0 +1 @@ +module X = B diff --git a/ocamlbuild/test/test3/a.mli b/ocamlbuild/test/test3/a.mli new file mode 100644 index 0000000000..c17617a6e8 --- /dev/null +++ b/ocamlbuild/test/test3/a.mli @@ -0,0 +1 @@ +(* Nothing *) diff --git a/ocamlbuild/test/test3/b.ml b/ocamlbuild/test/test3/b.ml new file mode 100644 index 0000000000..58c510c1e4 --- /dev/null +++ b/ocamlbuild/test/test3/b.ml @@ -0,0 +1 @@ +module X = C diff --git a/ocamlbuild/test/test3/b.mli b/ocamlbuild/test/test3/b.mli new file mode 100644 index 0000000000..5eea480960 --- /dev/null +++ b/ocamlbuild/test/test3/b.mli @@ -0,0 +1 @@ +(* nothing *) diff --git a/ocamlbuild/test/test3/c.ml b/ocamlbuild/test/test3/c.ml new file mode 100644 index 0000000000..06f0fd9185 --- /dev/null +++ b/ocamlbuild/test/test3/c.ml @@ -0,0 +1 @@ +module X = D diff --git a/ocamlbuild/test/test3/c.mli b/ocamlbuild/test/test3/c.mli new file mode 100644 index 0000000000..5eea480960 --- /dev/null +++ b/ocamlbuild/test/test3/c.mli @@ -0,0 +1 @@ +(* nothing *) diff --git a/ocamlbuild/test/test3/d.ml b/ocamlbuild/test/test3/d.ml new file mode 100644 index 0000000000..42ab724240 --- /dev/null +++ b/ocamlbuild/test/test3/d.ml @@ -0,0 +1 @@ +module X = E diff --git a/ocamlbuild/test/test3/d.mli b/ocamlbuild/test/test3/d.mli new file mode 100644 index 0000000000..5eea480960 --- /dev/null +++ b/ocamlbuild/test/test3/d.mli @@ -0,0 +1 @@ +(* nothing *) diff --git a/ocamlbuild/test/test3/e.ml b/ocamlbuild/test/test3/e.ml new file mode 100644 index 0000000000..863ea00c87 --- /dev/null +++ b/ocamlbuild/test/test3/e.ml @@ -0,0 +1 @@ +module X = F diff --git a/ocamlbuild/test/test3/e.mli b/ocamlbuild/test/test3/e.mli new file mode 100644 index 0000000000..5eea480960 --- /dev/null +++ b/ocamlbuild/test/test3/e.mli @@ -0,0 +1 @@ +(* nothing *) diff --git a/ocamlbuild/test/test3/f.ml b/ocamlbuild/test/test3/f.ml new file mode 100644 index 0000000000..00915fdc1b --- /dev/null +++ b/ocamlbuild/test/test3/f.ml @@ -0,0 +1,2 @@ +(* nothing *) +let _ = Unix.stat diff --git a/ocamlbuild/test/test3/f.mli b/ocamlbuild/test/test3/f.mli new file mode 100644 index 0000000000..5eea480960 --- /dev/null +++ b/ocamlbuild/test/test3/f.mli @@ -0,0 +1 @@ +(* nothing *) diff --git a/ocamlbuild/test/test3/proj.odocl b/ocamlbuild/test/test3/proj.odocl new file mode 100644 index 0000000000..532c720382 --- /dev/null +++ b/ocamlbuild/test/test3/proj.odocl @@ -0,0 +1 @@ +A B C D E F diff --git a/ocamlbuild/test/test3/test.sh b/ocamlbuild/test/test3/test.sh new file mode 100755 index 0000000000..396aaf2aaf --- /dev/null +++ b/ocamlbuild/test/test3/test.sh @@ -0,0 +1,11 @@ +#!/bin/sh +cd `dirname $0` +set -e +set -x +CMDOTPS="" # -- command args +BUILD="../../_build/ocamlbuild.native a.byte a.native proj.docdir/index.html -no-skip -classic-display $@" +BUILD1="$BUILD $CMDOPTS" +BUILD2="$BUILD -verbose 0 -nothing-should-be-rebuilt $CMDOPTS" +rm -rf _build +$BUILD1 +$BUILD2 diff --git a/ocamlbuild/test/test4/_tags b/ocamlbuild/test/test4/_tags new file mode 100644 index 0000000000..4b6e7986ea --- /dev/null +++ b/ocamlbuild/test/test4/_tags @@ -0,0 +1,2 @@ +# a comment +"a/aa.byte" or "a/aa.native": use_str diff --git a/ocamlbuild/test/test4/a/aa.ml b/ocamlbuild/test/test4/a/aa.ml new file mode 100644 index 0000000000..411d29bffe --- /dev/null +++ b/ocamlbuild/test/test4/a/aa.ml @@ -0,0 +1 @@ +let bar = 3 + List.length Bb.foo diff --git a/ocamlbuild/test/test4/a/aa.mli b/ocamlbuild/test/test4/a/aa.mli new file mode 100644 index 0000000000..20f3c52a88 --- /dev/null +++ b/ocamlbuild/test/test4/a/aa.mli @@ -0,0 +1 @@ +val bar : int diff --git a/ocamlbuild/test/test4/b/bb.ml b/ocamlbuild/test/test4/b/bb.ml new file mode 100644 index 0000000000..031031fba6 --- /dev/null +++ b/ocamlbuild/test/test4/b/bb.ml @@ -0,0 +1,2 @@ +let r = Str.regexp "r" +let foo = [2.2] diff --git a/ocamlbuild/test/test4/test.sh b/ocamlbuild/test/test4/test.sh new file mode 100755 index 0000000000..4b2580a8de --- /dev/null +++ b/ocamlbuild/test/test4/test.sh @@ -0,0 +1,11 @@ +#!/bin/sh +cd `dirname $0` +set -e +set -x +CMDOTPS="" # -- command args +BUILD="../../_build/ocamlbuild.native -I a -I b aa.byte aa.native -no-skip -classic-display $@" +BUILD1="$BUILD $CMDOPTS" +BUILD2="$BUILD -verbose 0 -nothing-should-be-rebuilt $CMDOPTS" +rm -rf _build +$BUILD1 +$BUILD2 diff --git a/ocamlbuild/test/test5/_tags b/ocamlbuild/test/test5/_tags new file mode 100644 index 0000000000..2f66a28c1d --- /dev/null +++ b/ocamlbuild/test/test5/_tags @@ -0,0 +1 @@ +"a.cmx" or "b.cmx": for-pack(C) diff --git a/ocamlbuild/test/test5/a.ml b/ocamlbuild/test/test5/a.ml new file mode 100644 index 0000000000..2509003015 --- /dev/null +++ b/ocamlbuild/test/test5/a.ml @@ -0,0 +1 @@ +let a = 42 diff --git a/ocamlbuild/test/test5/a.mli b/ocamlbuild/test/test5/a.mli new file mode 100644 index 0000000000..3f79c81490 --- /dev/null +++ b/ocamlbuild/test/test5/a.mli @@ -0,0 +1 @@ +val a : int diff --git a/ocamlbuild/test/test5/b.ml b/ocamlbuild/test/test5/b.ml new file mode 100644 index 0000000000..8db5ca3b86 --- /dev/null +++ b/ocamlbuild/test/test5/b.ml @@ -0,0 +1 @@ +let b = A.a + 1 diff --git a/ocamlbuild/test/test5/c.mlpack b/ocamlbuild/test/test5/c.mlpack new file mode 100644 index 0000000000..5decc2b6a1 --- /dev/null +++ b/ocamlbuild/test/test5/c.mlpack @@ -0,0 +1 @@ +A B diff --git a/ocamlbuild/test/test5/d.ml b/ocamlbuild/test/test5/d.ml new file mode 100644 index 0000000000..a5ec43298f --- /dev/null +++ b/ocamlbuild/test/test5/d.ml @@ -0,0 +1 @@ +Format.printf "C.B.b = %d@." C.B.b diff --git a/ocamlbuild/test/test5/test.sh b/ocamlbuild/test/test5/test.sh new file mode 100755 index 0000000000..9d78f1991c --- /dev/null +++ b/ocamlbuild/test/test5/test.sh @@ -0,0 +1,11 @@ +#!/bin/sh +cd `dirname $0` +set -e +set -x +CMDOPTS="" # -- command args +BUILD="../../_build/ocamlbuild.native d.byte d.native -no-skip -classic-display $@" +BUILD1="$BUILD $CMDOPTS" +BUILD2="$BUILD -verbose 0 -nothing-should-be-rebuilt $CMDOPTS" +rm -rf _build +$BUILD1 +$BUILD2 diff --git a/ocamlbuild/test/test6/a.ml b/ocamlbuild/test/test6/a.ml new file mode 100644 index 0000000000..e09e5d02a3 --- /dev/null +++ b/ocamlbuild/test/test6/a.ml @@ -0,0 +1 @@ +let a = B.b diff --git a/ocamlbuild/test/test6/a.mli b/ocamlbuild/test/test6/a.mli new file mode 100644 index 0000000000..451c586eb3 --- /dev/null +++ b/ocamlbuild/test/test6/a.mli @@ -0,0 +1 @@ +val a : 'a -> 'a diff --git a/ocamlbuild/test/test6/b.ml b/ocamlbuild/test/test6/b.ml new file mode 100644 index 0000000000..362c8fc158 --- /dev/null +++ b/ocamlbuild/test/test6/b.ml @@ -0,0 +1 @@ +let b = D.d diff --git a/ocamlbuild/test/test6/b.mli b/ocamlbuild/test/test6/b.mli new file mode 100644 index 0000000000..685b7906be --- /dev/null +++ b/ocamlbuild/test/test6/b.mli @@ -0,0 +1 @@ +val b : 'a -> 'a diff --git a/ocamlbuild/test/test6/b.mli.v1 b/ocamlbuild/test/test6/b.mli.v1 new file mode 100644 index 0000000000..685b7906be --- /dev/null +++ b/ocamlbuild/test/test6/b.mli.v1 @@ -0,0 +1 @@ +val b : 'a -> 'a diff --git a/ocamlbuild/test/test6/b.mli.v2 b/ocamlbuild/test/test6/b.mli.v2 new file mode 100644 index 0000000000..a431698375 --- /dev/null +++ b/ocamlbuild/test/test6/b.mli.v2 @@ -0,0 +1,2 @@ +.... +val b : 'a -> 'a diff --git a/ocamlbuild/test/test6/d.ml b/ocamlbuild/test/test6/d.ml new file mode 100644 index 0000000000..61c7a9c62f --- /dev/null +++ b/ocamlbuild/test/test6/d.ml @@ -0,0 +1,2 @@ +type t +let d x = x diff --git a/ocamlbuild/test/test6/d.mli b/ocamlbuild/test/test6/d.mli new file mode 100644 index 0000000000..1db89bbe67 --- /dev/null +++ b/ocamlbuild/test/test6/d.mli @@ -0,0 +1 @@ +val d : 'a -> 'a diff --git a/ocamlbuild/test/test6/d.mli.v1 b/ocamlbuild/test/test6/d.mli.v1 new file mode 100644 index 0000000000..12fea1c160 --- /dev/null +++ b/ocamlbuild/test/test6/d.mli.v1 @@ -0,0 +1,2 @@ +type t +val d : 'a -> 'a diff --git a/ocamlbuild/test/test6/d.mli.v2 b/ocamlbuild/test/test6/d.mli.v2 new file mode 100644 index 0000000000..1db89bbe67 --- /dev/null +++ b/ocamlbuild/test/test6/d.mli.v2 @@ -0,0 +1 @@ +val d : 'a -> 'a diff --git a/ocamlbuild/test/test6/main.ml b/ocamlbuild/test/test6/main.ml new file mode 100644 index 0000000000..61acf12831 --- /dev/null +++ b/ocamlbuild/test/test6/main.ml @@ -0,0 +1 @@ +A.a 2. +. D.d 1. diff --git a/ocamlbuild/test/test6/main.mli b/ocamlbuild/test/test6/main.mli new file mode 100644 index 0000000000..5eea480960 --- /dev/null +++ b/ocamlbuild/test/test6/main.mli @@ -0,0 +1 @@ +(* nothing *) diff --git a/ocamlbuild/test/test6/test.sh b/ocamlbuild/test/test6/test.sh new file mode 100755 index 0000000000..fedbc9c9b3 --- /dev/null +++ b/ocamlbuild/test/test6/test.sh @@ -0,0 +1,26 @@ +#!/bin/sh +cd `dirname $0` +set -x +rm -rf _build +CMDOPTS="" # -- command args +BUILD="../../_build/ocamlbuild.native -no-skip main.byte -classic-display $@" +BUILD1="$BUILD $CMDOPTS" +BUILD2="$BUILD -verbose 0 -nothing-should-be-rebuilt $CMDOPTS" +cp b.mli.v1 b.mli +cp d.mli.v1 d.mli +$BUILD1 +$BUILD2 +cp b.mli.v2 b.mli +cp d.mli.v2 d.mli +$BUILD1 +cp b.mli.v1 b.mli +if $BUILD1; then + if $BUILD2; then + echo PASS + else + echo "FAIL (-nothing-should-be-rebuilt)" + fi +else + echo FAIL +fi + diff --git a/ocamlbuild/test/test7/_tags b/ocamlbuild/test/test7/_tags new file mode 100644 index 0000000000..8501e32851 --- /dev/null +++ b/ocamlbuild/test/test7/_tags @@ -0,0 +1 @@ +"main.byte": my_cool_plugin diff --git a/ocamlbuild/test/test7/aa.ml b/ocamlbuild/test/test7/aa.ml new file mode 100644 index 0000000000..877d0af148 --- /dev/null +++ b/ocamlbuild/test/test7/aa.ml @@ -0,0 +1 @@ +let aa = "aa" diff --git a/ocamlbuild/test/test7/bb.ml b/ocamlbuild/test/test7/bb.ml new file mode 100644 index 0000000000..9ac2d5965e --- /dev/null +++ b/ocamlbuild/test/test7/bb.ml @@ -0,0 +1 @@ +let bb = 43 diff --git a/ocamlbuild/test/test7/bb.mli b/ocamlbuild/test/test7/bb.mli new file mode 100644 index 0000000000..9256de2b1f --- /dev/null +++ b/ocamlbuild/test/test7/bb.mli @@ -0,0 +1 @@ +val bb : int diff --git a/ocamlbuild/test/test7/bb2.ml b/ocamlbuild/test/test7/bb2.ml new file mode 100644 index 0000000000..2f91b988b8 --- /dev/null +++ b/ocamlbuild/test/test7/bb2.ml @@ -0,0 +1,3 @@ +let bb = 43 +let f x = x + 1 +let () = incr (ref 0) diff --git a/ocamlbuild/test/test7/bb3.ml b/ocamlbuild/test/test7/bb3.ml new file mode 100644 index 0000000000..b7ad4b5c23 --- /dev/null +++ b/ocamlbuild/test/test7/bb3.ml @@ -0,0 +1,3 @@ +let bb = 43 +let f x = x + 1 +let () = incr (ref 1) diff --git a/ocamlbuild/test/test7/bbcc.mllib b/ocamlbuild/test/test7/bbcc.mllib new file mode 100644 index 0000000000..a97a0e6c2a --- /dev/null +++ b/ocamlbuild/test/test7/bbcc.mllib @@ -0,0 +1 @@ +Bb Cc diff --git a/ocamlbuild/test/test7/c2.ml b/ocamlbuild/test/test7/c2.ml new file mode 100644 index 0000000000..36ff6d6fbf --- /dev/null +++ b/ocamlbuild/test/test7/c2.ml @@ -0,0 +1 @@ +let c2 = 12 diff --git a/ocamlbuild/test/test7/c2.mli b/ocamlbuild/test/test7/c2.mli new file mode 100644 index 0000000000..19fe565dfd --- /dev/null +++ b/ocamlbuild/test/test7/c2.mli @@ -0,0 +1 @@ +val c2 : int diff --git a/ocamlbuild/test/test7/c3.ml b/ocamlbuild/test/test7/c3.ml new file mode 100644 index 0000000000..277e1ee0ad --- /dev/null +++ b/ocamlbuild/test/test7/c3.ml @@ -0,0 +1 @@ +let c3 = Bb.bb + 13 diff --git a/ocamlbuild/test/test7/cc.ml b/ocamlbuild/test/test7/cc.ml new file mode 100644 index 0000000000..b39ef21df6 --- /dev/null +++ b/ocamlbuild/test/test7/cc.ml @@ -0,0 +1 @@ +let cc = (String.length Aa.aa) + Bb.bb + C2.c2 diff --git a/ocamlbuild/test/test7/cool_plugin.ml b/ocamlbuild/test/test7/cool_plugin.ml new file mode 100644 index 0000000000..3225186872 --- /dev/null +++ b/ocamlbuild/test/test7/cool_plugin.ml @@ -0,0 +1 @@ +print_endline "I am a cool plugin" diff --git a/ocamlbuild/test/test7/main.ml b/ocamlbuild/test/test7/main.ml new file mode 100644 index 0000000000..f12195969e --- /dev/null +++ b/ocamlbuild/test/test7/main.ml @@ -0,0 +1 @@ +let main = String.length Aa.aa - Bb.bb - C3.c3 - Cc.cc - 1 diff --git a/ocamlbuild/test/test7/myocamlbuild.ml b/ocamlbuild/test/test7/myocamlbuild.ml new file mode 100644 index 0000000000..12b274ff04 --- /dev/null +++ b/ocamlbuild/test/test7/myocamlbuild.ml @@ -0,0 +1,3 @@ +open Ocamlbuild_plugin;; +use_lib "main" "bbcc";; +dep ["ocaml"; "link"; "byte"; "my_cool_plugin"] ["cool_plugin.cmo"];; diff --git a/ocamlbuild/test/test7/test.sh b/ocamlbuild/test/test7/test.sh new file mode 100755 index 0000000000..0ccb69d98a --- /dev/null +++ b/ocamlbuild/test/test7/test.sh @@ -0,0 +1,19 @@ +#!/bin/sh +cd `dirname $0` +set -e +set -x +CMDOPTS="" # -- command args +BUILD="../../_build/ocamlbuild.native bbcc.cma main.byte bbcc.cmxa main.native -no-skip -classic-display $@" +BUILD1="$BUILD $CMDARGS" +BUILD2="$BUILD -verbose 0 -nothing-should-be-rebuilt $CMDARGS" +rm -rf _build +svn revert bb.ml +$BUILD1 +$BUILD2 +cp bb2.ml bb.ml +$BUILD1 -verbose 0 +$BUILD2 +cp bb3.ml bb.ml +$BUILD1 -verbose 0 +$BUILD2 +svn revert bb.ml diff --git a/ocamlbuild/test/test8/a.ml b/ocamlbuild/test/test8/a.ml new file mode 100644 index 0000000000..35ac749963 --- /dev/null +++ b/ocamlbuild/test/test8/a.ml @@ -0,0 +1 @@ +print_endline Myconfig.version;; diff --git a/ocamlbuild/test/test8/myocamlbuild.ml b/ocamlbuild/test/test8/myocamlbuild.ml new file mode 100644 index 0000000000..4a8ae9d683 --- /dev/null +++ b/ocamlbuild/test/test8/myocamlbuild.ml @@ -0,0 +1,9 @@ +open Ocamlbuild_pack;; +open Ocamlbuild_plugin;; +let version = "0.1";; +file_rule "myconfig.ml" + ~prod:"myconfig.ml" + ~cache:(fun _ -> version) + begin fun _ oc -> + Printf.fprintf oc "let version = %S;;\n%!" version + end;; diff --git a/ocamlbuild/test/test8/test.sh b/ocamlbuild/test/test8/test.sh new file mode 100755 index 0000000000..80fc7197c6 --- /dev/null +++ b/ocamlbuild/test/test8/test.sh @@ -0,0 +1,11 @@ +#!/bin/sh +cd `dirname $0` +set -e +set -x +CMDOPTS="" # -- command args +BUILD="../../_build/ocamlbuild.native a.byte a.native -no-skip -classic-display $@" +BUILD1="$BUILD $CMDOPTS" +BUILD2="$BUILD -verbose 0 -nothing-should-be-rebuilt $CMDOPTS" +rm -rf _build +$BUILD1 +$BUILD2 diff --git a/ocamlbuild/test/test9/dbgl b/ocamlbuild/test/test9/dbgl new file mode 100644 index 0000000000..ac61a3d555 --- /dev/null +++ b/ocamlbuild/test/test9/dbgl @@ -0,0 +1,10 @@ +#load "unix.cma";; +#load "str.cma";; +#load "discard_printf.cmo";; +#load "debug.cmo";; +#load "bool.cmo";; +#load "glob_ast.cmo";; +#load "glob_lexer.cmo";; +#load "my_unix.cmo";; +#use "glob.ml";; +#install_printer print_is;; diff --git a/ocamlbuild/test/test9/test.sh b/ocamlbuild/test/test9/test.sh new file mode 100755 index 0000000000..0feded2082 --- /dev/null +++ b/ocamlbuild/test/test9/test.sh @@ -0,0 +1,6 @@ +#!/bin/sh +set -e +set -x +cd `dirname $0`/../.. +./_build/ocamlbuild.native -quiet -build-dir _buildtest -no-links test/test9/testglob.native $@ +./_buildtest/test/test9/testglob.native diff --git a/ocamlbuild/test/test9/testglob.ml b/ocamlbuild/test/test9/testglob.ml new file mode 100644 index 0000000000..e307a769ca --- /dev/null +++ b/ocamlbuild/test/test9/testglob.ml @@ -0,0 +1,91 @@ +(* Testglob *) + +open Bool;; +open Glob;; + +let yep f x = + try + ignore (f x); + true + with + | _ -> false +;; + +let tests1 = [ + "\"hello\"", true; + "<hello>", true; + "<hel*lo>", true; + "<a> and <b> or <c>", true; + "<a> titi", false +];; + +let tests2 = [ + "<[a]>", ["a"], ["b"]; + "<[a-z]>", ["a";"e";"k";"z"], ["0";"A";"~"]; + "<[a-z][0-9]>", ["a0";"b9"], ["a00";"a0a";"b0a";"isduis";""]; + "<hello>", ["hello"], ["helli"]; + "\"hello\"", ["hello"], ["heidi"]; + "<*>", ["";"a";"ax"], []; + "<a*b>", ["ab";"acb";"axxxxxb";"ababbababb"], ["abx";"xxxxxab";"xab"]; + "<*.ml>", ["hello.ml";".ml"], ["ml"; ""; "toto.mli"]; + "<a>", ["a"], ["";"aa";"ba";"ab";"abaa"]; + "<ab>", ["ab"], ["";"abab";"aba";"abx"]; + "<ab?c>", ["abac";"abxc"], ["abab";"ababab";"ababa"]; + "<*ab?cd*>", ["123abecd345";"abccd";"abccd345";"ababcababccdab"], ["abcd";"aaaaabcdababcd"]; + "<*this*is*a*test*>", ["this is a test";"You know this is a test really";"thisisatest"], ["thisatest"]; + "<b*>", ["bxx";"bx"], ["aaab";""]; + "<*>", ["";"a";"aaa";"aaaaa"], []; + "<?>", ["a"],["";"aaa";"aaaaa"]; + "<{a,b}>", ["a";"b"],["";"aa";"ab";"ba";"bb";"c"]; + "<toto.{ml,mli}>", ["toto.ml";"toto.mli"],["toto.";"toto.mll"]; + "<{a,b}{c,[de]}{f,g}>", ["acf";"acg";"adf";"adg";"aef";"aeg";"bcf";"bcg";"bdf";"bdg";"bef";"beg"], + ["afg";"af";"aee"]; + "(<*.ml> or <*.mli>) and not \"hello.ml\"", + ["a.ml"; "b.ml"; "a.mli"], + ["hello.ml"; "a.mli.x"]; + "<*>", ["alpha";"beta"], ["alpha/beta";"gamma/delta"]; + "<alpha/**/beta>", ["alpha/beta";"alpha/gamma/beta";"alpha/gamma/delta/beta"], + ["alpha";"beta";"gamma/delta"]; + "<**/*.ml>", ["toto.ml";"toto/tata.ml";"alpha/gamma/delta/beta.ml"], + ["toto.mli"]; + "<toto/**>", ["toto/";"toto/tata";"toto/alpha/gamma/delta/beta.ml";"toto"], + ["toto2/tata"; "tata/titi"] +];; + + +let _ = + let times = 3 in + List.iter + begin fun (str, ast) -> + let ast' = yep Glob.parse str in + if ast <> ast' then + begin + Printf.printf "Globexp parsing failed for %S.\n%!" str; + exit 1 + end + else + Printf.printf "Globexp for %S OK\n%!" str + end + tests1; + List.iter + begin fun (gstr, yes, no) -> + let globber = Glob.parse gstr in + let check polarity = + List.iter + begin fun y -> + if Glob.eval globber y = polarity then + Printf.printf "Glob.eval %S %S = %b OK\n%!" gstr y polarity + else + begin + Printf.printf "Glob.eval %S %S = %b FAIL\n%!" gstr y (not polarity); + exit 1 + end + end + in + for k = 1 to times do + check true yes; + check false no + done + end + tests2 +;; diff --git a/ocamlbuild/tools.ml b/ocamlbuild/tools.ml new file mode 100644 index 0000000000..ed69408f3c --- /dev/null +++ b/ocamlbuild/tools.ml @@ -0,0 +1,48 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +(* Tools *) + +open My_std +open Format +open Log +open Pathname.Operators +open Tags.Operators +open Rule + +let pp_l = List.print String.print + +let default_tags = ref Tags.empty;; + +let tags_of_pathname p = + Tags.add ("file:"^p) + (Tags.union (Configuration.tags_of_filename (Pathname.to_string p)) !default_tags) +let flags_of_pathname p = Configuration.flags_of_filename (Pathname.to_string p) + +let opt_print elt ppf = + function + | Some x -> fprintf ppf "@[<2>Some@ %a@]" elt x + | None -> pp_print_string ppf "None" + +let path_and_context_of_string s = + if Pathname.is_implicit s then + let b = Pathname.basename s in + let d = Pathname.dirname s in + if d <> Pathname.current_dir_name then + let () = Pathname.define_context d [d] in + [s] + else + let include_dirs = Pathname.include_dirs_of d in + List.map (fun include_dir -> include_dir/b) include_dirs + else [s] + diff --git a/ocamlbuild/tools.mli b/ocamlbuild/tools.mli new file mode 100644 index 0000000000..de1f0d3d85 --- /dev/null +++ b/ocamlbuild/tools.mli @@ -0,0 +1,20 @@ +(***********************************************************************) +(* ocamlbuild *) +(* *) +(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) +(* *) +(* Copyright 2007 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) +(* Original author: Nicolas Pouillard *) +(* Tools *) + +val tags_of_pathname : Pathname.t -> Tags.t +val flags_of_pathname : Pathname.t -> Command.spec +val default_tags : Tags.t ref +val path_and_context_of_string : Pathname.t -> Pathname.t list +val pp_l : Format.formatter -> string list -> unit |