From 8de2b887830c58cd6515097a68f226b819a2e1a5 Mon Sep 17 00:00:00 2001 From: Jules Villard Date: Thu, 10 Aug 2017 06:00:01 -0700 Subject: [PATCH] [make] s/ocamlbuild/jbuilder/g MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Use jbuilder to build infer instead of ocamlbuild. This is mainly to get faster builds: ``` times in 10ms, ±differences measured in speedups, 4 cores | | ocb total | jb | ±total | ocb user | jb | ±user | ocb cpu | jb | ±cpu | ocb sys | jb | ±sys | |-----------------------------------+-----------+------+--------+----------+------+-------+---------+-----+------+---------+------+------| | byte from scratch | 6428 | 2456 | 2.62 | 7743 | 6662 | 1.16 | 138 | 331 | 2.40 | 1184 | 1477 | 0.80 | | native from scratch | 9841 | 4289 | 2.29 | 9530 | 8834 | 1.08 | 110 | 245 | 2.23 | 1373 | 1712 | 0.80 | | byte after native | 29578 | 1602 | 18.46 | 4514 | 4640 | 0.97 | 170 | 325 | 1.91 | 543 | 576 | 0.94 | | change infer.ml byte | 344 | 282 | 1.22 | 292 | 215 | 1.36 | 96 | 99 | 1.03 | 040 | 066 | 0.61 | | change infer.ml native | 837 | 223 | 3.75 | 789 | 174 | 4.53 | 98 | 99 | 1.01 | 036 | 47 | 0.77 | | change Config.ml byte | 451 | 339 | 1.33 | 382 | 336 | 1.14 | 97 | 122 | 1.26 | 056 | 80 | 0.70 | | change Config.ml native | 4024 | 1760 | 2.29 | 4585 | 4225 | 1.09 | 127 | 276 | 2.17 | 559 | 644 | 0.87 | | change cFrontend_config.ml byte | 348 | 643 | 0.54 | 297 | 330 | 0.90 | 96 | 67 | 0.70 | 038 | 102 | 0.37 | | change cFrontend_config.ml native | 1480 | 584 | 2.53 | 1435 | 906 | 1.58 | 106 | 185 | 1.75 | 136 | 178 | 0.76 | #+TBLFM: $4=$2/$3;f2::$7=$5/$6;f2::$10=$9/$8;f2::$13=$11/$12;f2 50 cores | | ocb total | jb | ±total | ocb user | jb | ±user | ocb cpu | jb | ±cpu | ocb sys | jb | ±sys | |---------------------+-----------+------+--------+----------+------+-------+---------+----+------+---------+------+------| | byte from scratch | 9114 | 2061 | 4.42 | 9334 | 5133 | 1.82 | | | 0/0 | 2566 | 1726 | 1.49 | | native from scratch | 13481 | 3967 | 3.40 | 12291 | 7608 | 1.62 | | | 0/0 | 3003 | 2100 | 1.43 | | byte after native | 3467 | 1476 | 2.35 | 5067 | 3912 | 1.30 | | | 0/0 | 971 | 801 | 1.21 | #+TBLFM: $4=$2/$3;f2::$7=$5/$6;f2::$10=$9/$8;f2::$13=$11/$12;f2 ``` Menu: 1. Write a jbuild file, autogenerated from jbuild.in because we need to fill in some information at build-time (really, at configure time, but TODO), such as whether or not clang is enabled. 2. Nuke lots of stuff from infer/src/Makefile that is now in the jbuild file 3. The jbuild file lives in infer/src/ so it can see all the sources. If we put it somewhere else, eg, infer/, then `jbuilder` scans too many files (all irrelevant) and takes 2.5s to start instead of .8s. Adding irrelevant directories to jbuild-ignore does not help. 4. jbuilder does not support subdirectories, so resort to listing all the source files in the generated jbuild (only source directories need to be manually listed in jbuild.in though). Still, the generated .merlin is wrong and makes merlin find source files in _build, so manually tune it to get good merlin support. We also lose some of merlin for unit tests as it cannot see their build artefacts anymore. 5. checkCopyright gets its own jbuild because it's standalone. Also, remove some deprecation warnings in checkCopyright due to the new version of Core from a while ago. 6. Drop less-used Makefile features (they had regressed anyway) such as building individual modules. Also, building mod_dep.pdf now takes all the source files available so they better build (before, it would only take the source files from the config, eg with or without clang) (that's pretty minor). 7. The toplevel is now built as a custom toplevel because that was easier. It should soon be even easier: https://github.com/janestreet/jbuilder/issues/210 8. Move BUILTINS.mli to BUILTINS.ml because jbuilder is not happy about interface files without implementations. In particular, I did not try to migrate too much of the Makefile logic to jbuilder, more can be done in the future. Reviewed By: jberdine Differential Revision: D5573661 fbshipit-source-id: 4ca6d8f --- .gitignore | 11 +- Makefile | 44 ++-- Makefile.autoconf.in | 1 + Makefile.config | 2 +- configure.ac | 1 + infer/.merlin | 20 -- infer/src/.merlin | 38 +++ infer/src/IR/{BUILTINS.mli => BUILTINS.ml} | 0 infer/src/Makefile | 239 +++++------------- infer/src/README.md | 8 + infer/src/eradicate/modelTables.ml | 2 +- infer/src/infertop.ml | 9 + infer/src/jbuild-workspace.in | 9 + infer/src/jbuild.in | 209 +++++++++++++++ infer/src/scripts/checkCopyright.ml | 4 +- infer/src/scripts/jbuild | 7 + .../buck_flavors_deterministic/Makefile | 3 + infer/tests/build_systems/infertop/Makefile | 31 +++ .../tests/build_systems/infertop/toplevel.exp | 11 + .../codetoanalyze/java/eradicate/issues.exp | 16 +- opam | 1 + scripts/infer_repl | 23 +- scripts/toplevel_init | 7 +- 23 files changed, 455 insertions(+), 241 deletions(-) delete mode 100644 infer/.merlin create mode 100644 infer/src/.merlin rename infer/src/IR/{BUILTINS.mli => BUILTINS.ml} (100%) create mode 100644 infer/src/README.md create mode 100644 infer/src/infertop.ml create mode 100644 infer/src/jbuild-workspace.in create mode 100644 infer/src/jbuild.in create mode 100644 infer/src/scripts/jbuild create mode 100644 infer/tests/build_systems/infertop/Makefile create mode 100644 infer/tests/build_systems/infertop/toplevel.exp diff --git a/.gitignore b/.gitignore index 584e2abf771..4b9b46b5872 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,6 @@ # generated by build and tests /_build /_build_logs -/infer/_build /infer/tests/codetoanalyze/java/*/codetoanalyze /dependencies/infer-deps-* _build_infer @@ -93,8 +92,8 @@ buck-out/ #other /infer/bin/infer -/infer/bin/infer.byte -/infer/bin/infer.native +/infer/bin/infer.bc +/infer/bin/infer.exe /infer/bin/infer-analyze /infer/bin/infer-capture /infer/bin/infer-compile @@ -151,3 +150,9 @@ infer/src/.project # generated by Maven /infer/annotations/target + +# jbuilder +/infer/src/_build +/infer/src/jbuild +/infer/src/jbuild-workspace +/infer/src/scripts/.merlin diff --git a/Makefile b/Makefile index 5cd1f38b36c..c1d55b2a556 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,11 @@ default: infer ROOT_DIR = . include $(ROOT_DIR)/Makefile.config +ifneq ($(UTOP),no) +BUILD_SYSTEMS_TESTS += infertop +build_infertop_print build_infertop_replace build_infertop_test: toplevel_test +endif + ifeq ($(BUILD_C_ANALYZERS),yes) BUILD_SYSTEMS_TESTS += \ assembly \ @@ -81,8 +86,8 @@ endif ifneq ($(BUCK),no) BUILD_SYSTEMS_TESTS += buck genrule # do not run these two tests in parallel otherwise Buck has a bad time -build_genrule_test: build_buck_test -build_genrule_print: build_buck_print +build_genrule_test: | build_buck_test +build_genrule_print: | build_buck_print endif ifneq ($(MVN),no) BUILD_SYSTEMS_TESTS += mvn @@ -149,14 +154,9 @@ byte: src_build_common test_build: src_build_common $(QUIET)$(call silent_on_success,Testing Infer builds without warnings,\ $(MAKE) -C $(SRC_DIR) TEST=1 byte_no_install) -# byte_no_install builds most of what toplevel needs, so it's more efficient to run the -# toplevel build straight after it rather than in parallel. Note that both targets build files -# that the other doesn't. - $(QUIET)$(call silent_on_success,Testing Infer toplevel builds,\ - $(MAKE) -C $(SRC_DIR) TEST=1 toplevel) ifeq ($(IS_FACEBOOK_TREE),yes) -byte src_build test_build: fb-setup +byte src_build_common src_build test_build: fb-setup endif ifeq ($(BUILD_C_ANALYZERS),yes) @@ -219,7 +219,7 @@ endif .PHONY: ocaml_unit_test ocaml_unit_test: test_build $(QUIET)$(call silent_on_success,Running OCaml unit tests,\ - $(BUILD_DIR)/test/infer/unit/inferunit.byte) + $(BUILD_DIR)/test/inferunit.bc) define silence_make ($(1) 2> >(grep -v "warning: \(ignoring old\|overriding\) \(commands\|recipe\) for target") \ @@ -296,15 +296,17 @@ check_missing_mli: $(QUIET)for x in $$(find $(INFER_DIR)/src -name "*.ml"); do \ test -f "$$x"i || echo Missing "$$x"i; done -.PHONY: toplevel -toplevel: clang_plugin src_build_common - $(QUIET)$(MAKE) -C $(SRC_DIR) toplevel +# depend on test_build because jbuilder doesn't like running concurrently with itself +.PHONY: toplevel_test +toplevel_test: src_build_common | test_build +# build with TEST=1 as the infer_repl scripts expects to find the toplevel in the test build + $(QUIET)$(call silent_on_success,Building infer toplevel (test mode),\ + $(MAKE) -C $(SRC_DIR) TEST=1 toplevel) -.PHONY: inferScriptMode_test -inferScriptMode_test: test_build - $(QUIET)$(call silent_on_success,Testing infer OCaml REPL,\ - INFER_REPL_BINARY=ocaml TOPLEVEL_DIR=$(BUILD_DIR)/test/infer $(SCRIPT_DIR)/infer_repl \ - $(INFER_DIR)/tests/repl/infer_batch_script.mltop) +.PHONY: toplevel +toplevel: src_build_common + $(QUIET)$(call silent_on_success,Building infer toplevel,\ + $(MAKE) -C $(SRC_DIR) toplevel) .PHONY: checkCopyright checkCopyright: @@ -342,8 +344,7 @@ else endif .PHONY: config_tests -config_tests: test_build ocaml_unit_test endtoend_test inferScriptMode_test \ - checkCopyright validate-skel +config_tests: test_build ocaml_unit_test endtoend_test checkCopyright validate-skel $(QUIET)$(call silent_on_success,Building Infer source dependency graph,\ $(MAKE) -C $(SRC_DIR) mod_dep.dot) @@ -550,7 +551,10 @@ opam.lock: opam $(QUIET)$(call silent_on_success,generating opam.lock,\ $(OPAM) lock --pkg $(INFER_PKG_OPAMLOCK) > opam.lock) -OPAM_DEV_DEPS = ocp-indent merlin tuareg +# This is a magical version number that doesn't reinstall the world when added on top of what we +# have in opam.lock. To upgrade this version number, manually try to install several utop versions +# until you find one that doesn't recompile the world. TODO(t20828442): get rid of magic +OPAM_DEV_DEPS = ocp-indent merlin tuareg utop.2.0.0 .PHONY: devsetup devsetup: Makefile.autoconf diff --git a/Makefile.autoconf.in b/Makefile.autoconf.in index c733943cf5c..da2aa8544ae 100644 --- a/Makefile.autoconf.in +++ b/Makefile.autoconf.in @@ -64,6 +64,7 @@ prefix = @prefix@ PYTHON_lxml = @PYTHON_lxml@ SHASUM = @SHASUM@ USER_JAVA_HOME = @USER_JAVA_HOME@ +UTOP = @UTOP@ XCODE_SELECT = @XCODE_SELECT@ XCPRETTY = @XCPRETTY@ diff --git a/Makefile.config b/Makefile.config index 7a36026d4ca..069dc07890c 100644 --- a/Makefile.config +++ b/Makefile.config @@ -45,7 +45,7 @@ JAVA_BUILTINS_DIR = $(MODELS_DIR)/java/builtins JAVA_MODELS_DIR = $(MODELS_DIR)/java/src SRC_DIR = $(INFER_DIR)/src -BUILD_DIR = $(INFER_DIR)/_build +BUILD_DIR = $(SRC_DIR)/_build JAVA_LIB_DIR = $(LIB_DIR)/java SPECS_LIB_DIR = $(LIB_DIR)/specs diff --git a/configure.ac b/configure.ac index 3a226476acf..79758dfc4c6 100644 --- a/configure.ac +++ b/configure.ac @@ -187,6 +187,7 @@ AC_ASSERT_OCAML_PKG([biniou]) AC_ASSERT_OCAML_PKG([camlzip], [zip]) AC_ASSERT_OCAML_PKG([easy-format]) AC_ASSERT_OCAML_PKG([oUnit], [], [2.0.0]) +AC_CHECK_TOOL([UTOP], [utop], [no]) AC_ASSERT_OCAML_PKG([yojson]) AC_ARG_VAR([CAML_LD_LIBRARY_PATH], diff --git a/infer/.merlin b/infer/.merlin deleted file mode 100644 index 7f6e6831442..00000000000 --- a/infer/.merlin +++ /dev/null @@ -1,20 +0,0 @@ -S src/** -B _build/infer/** -B _build/test/infer/unit/** -PKG ANSITerminal -PKG atdgen -PKG cmdliner -PKG core -PKG javalib -PKG oUnit -PKG parmap -PKG ppx_compare -PKG ptrees -PKG sawja -PKG str -PKG unix -PKG xmlm -PKG yojson -PKG zip -FLG -principal -safe-string -short-paths -strict-formats -strict-sequence -FLG -w +a-4-9-40-41-42-45-48 diff --git a/infer/src/.merlin b/infer/src/.merlin new file mode 100644 index 00000000000..6822d2620cd --- /dev/null +++ b/infer/src/.merlin @@ -0,0 +1,38 @@ +B _build/default/** +PKG ANSITerminal +PKG atdgen +PKG cmdliner +PKG core +PKG javalib +PKG oUnit +PKG parmap +PKG ppx_compare +PKG ptrees +PKG sawja +PKG str +PKG unix +PKG xmlm +PKG yojson +PKG zip +FLG -principal -safe-string -short-paths -strict-formats -strict-sequence +FLG -w +a-4-9-40-41-42-45-48 +FLG -open InferBaseStdlib -open InferGenerated -open InferModules +S backend +S base +S bufferoverrun +S checkers +S clang +S clang_plugin +S clang_stubs +S eradicate +S facebook +S harness +S integration +S IR +S java +S java_stubs +S labs +S opensource +S quandary +S scripts +S unit diff --git a/infer/src/IR/BUILTINS.mli b/infer/src/IR/BUILTINS.ml similarity index 100% rename from infer/src/IR/BUILTINS.mli rename to infer/src/IR/BUILTINS.ml diff --git a/infer/src/Makefile b/infer/src/Makefile index f5277d7b78a..3224a0a94a0 100644 --- a/infer/src/Makefile +++ b/infer/src/Makefile @@ -11,61 +11,20 @@ include $(ROOT_DIR)/Makefile.config #### Global declarations #### ETC_DIR = $(INFER_DIR)/etc +# paths to BUILD_DIR are relative because that's how jbuilder likes it +JBUILDER_BUILD_DEFAULT = _build/default +JBUILDER_BUILD_TEST = _build/test ifeq ($(TEST),1) -BASE_BUILD_DIR = $(BUILD_DIR)/test +INFER_BUILD_DIR = $(JBUILDER_BUILD_TEST) else -BASE_BUILD_DIR = $(BUILD_DIR) +INFER_BUILD_DIR = $(JBUILDER_BUILD_DEFAULT) endif -INFER_BUILD_DIR = $(BASE_BUILD_DIR)/infer ATDGEN_SUFFIXES = _t.ml _t.mli _j.ml _j.mli -#### ocamlbuild options #### - -GENERATED_OCAML_SOURCES_GLOB = <*{clang_plugin/*,backend/jsonbug_*,checkers/stacktree_*}> - -OCAML_FATAL_WARNINGS = +3+5+6+8+10+11+12+18+19+20+21+23+26+29+27+32+33+34+35+37+38+39+50+52+57 - -# options for ocamlbuild -# Notice that use-ocamlfind is used here to avoid a linker bug in ocamlbuild when using -tag thread -OCAMLBUILD_OPTIONS = \ - -r \ - -use-menhir -menhir '$(MENHIR) --explain --strict'\ - -use-ocamlfind \ - -cflags -g -lflags -g \ - -cflags -short-paths \ - -cflags -safe-string \ - -cflags -principal \ - -cflags -strict-formats \ - -cflags -strict-sequence \ - -cflags -w,$(OCAML_FATAL_WARNINGS)-4-9-40-41-42-45-48 \ - -tag-line "$(GENERATED_OCAML_SOURCES_GLOB): warn(-27-32-34-35-39)" \ - -tag-line "not $(GENERATED_OCAML_SOURCES_GLOB) and <*/{,*/}*.{ml,re}{,i}>: package(ppx_compare)" \ - -tag-line "not $(GENERATED_OCAML_SOURCES_GLOB) and not <*{base/IList,base/IStd}.*>: open(IStd)" \ - -tag thread \ - -pkgs ANSITerminal,atdgen,cmdliner,core,extlib,oUnit,parmap,str,unix,xmlm,yojson,zip - -ifeq ($(ENABLE_OCAML_BINANNOT),yes) -OCAMLBUILD_OPTIONS += -cflags -bin-annot -endif - -ifeq ($(ENABLE_OCAMLOPT_CUSTOM_CC),yes) -OCAMLBUILD_OPTIONS += -lflags -cc,$(CC) -cflags -cc,$(CC) -endif - -ifneq (,$(findstring s,$(MAKEFLAGS))) -OCAMLBUILD_OPTIONS += -quiet -endif - -ifeq ($(TEST),1) -OCAMLBUILD_OPTIONS += -cflags -warn-error,$(OCAML_FATAL_WARNINGS) -endif - -export OCAMLFIND_IGNORE_DUPS_IN=$(shell $(OCAMLC) -where)/compiler-libs - #### Backend declarations #### -INFER_MAIN = backend/infer +INFER_MAIN = infer #### Checkers declarations #### @@ -83,18 +42,16 @@ INFERPRINT_ATDGEN_STUBS = $(addprefix $(INFERPRINT_ATDGEN_STUB_BASE), $(ATDGEN_S FACEBOOK_DIR = facebook INFER_CREATE_TRACEVIEW_LINKS_MODULE = InferCreateTraceViewLinks -INFER_CREATE_TRACEVIEW_LINKS_MAIN = $(FACEBOOK_DIR)/$(INFER_CREATE_TRACEVIEW_LINKS_MODULE) +INFER_CREATE_TRACEVIEW_LINKS_MAIN = $(INFER_CREATE_TRACEVIEW_LINKS_MODULE) ### InferUnit declarations ### UNIT_SOURCES = unit -INFERUNIT_MAIN = $(UNIT_SOURCES)/inferunit +INFERUNIT_MAIN = inferunit #### Java declarations #### -JAVA_OCAMLBUILD_OPTIONS = -pkgs javalib,ptrees,sawja - JAVA_SOURCES = java #### Clang declarations #### @@ -127,65 +84,44 @@ CLANG_BINIOU_DICT = $(ETC_DIR)/clang_ast.dict SCRIPT_SOURCES = scripts CHECKCOPYRIGHT_BIN = $(SCRIPT_DIR)/checkCopyright -CHECKCOPYRIGHT_MAIN = $(SCRIPT_SOURCES)/checkCopyright +CHECKCOPYRIGHT_MAIN = checkCopyright #### End of declarations #### -ifeq ($(IS_FACEBOOK_TREE),yes) -EXTRA_DEPS = facebook -else -EXTRA_DEPS = opensource +ifeq ($(ENABLE_OCAMLOPT_CUSTOM_CC),yes) +EXTRA_CFLAGS += -cc,$(CC) endif -DEPENDENCIES = \ - absint backend base bufferoverrun checkers eradicate harness integration IR labs quandary unit \ - $(EXTRA_DEPS) - -# ocamlbuild command with options common to all build targets -OCAMLBUILD_BASE = \ - $(OCAMLBUILD) $(OCAMLBUILD_OPTIONS) -j $(NCPU) $(addprefix -I , $(DEPENDENCIES)) \ - -# ocamlbuild with options necessary to build all targets at once, regardless of configure flags -OCAMLBUILD_ALL = $(OCAMLBUILD_BASE) $(JAVA_OCAMLBUILD_OPTIONS) - -# configure-aware ocamlbuild commands and targets -OCAMLBUILD_CONFIG = $(OCAMLBUILD_BASE) # list of ocamlbuild targets common to all build targets -- native version -INFER_CONFIG_TARGETS = $(INFER_MAIN).native +INFER_CONFIG_TARGETS = $(INFER_BUILD_DIR)/$(INFER_MAIN).exe ifeq ($(IS_FACEBOOK_TREE),yes) -INFER_CONFIG_TARGETS += $(INFER_CREATE_TRACEVIEW_LINKS_MAIN).native +INFER_CONFIG_TARGETS += $(INFER_BUILD_DIR)/$(INFER_CREATE_TRACEVIEW_LINKS_MAIN).exe endif -ifeq ($(BUILD_JAVA_ANALYZERS),yes) -OCAMLBUILD_CONFIG += $(JAVA_OCAMLBUILD_OPTIONS) -DEPENDENCIES += java -else -DEPENDENCIES += java_stubs -endif -ifeq ($(BUILD_C_ANALYZERS),yes) -DEPENDENCIES += clang clang_plugin unit/clang -else -DEPENDENCIES += clang_stubs unit/clang_stubs -endif ifeq ($(TEST),1) -INFER_CONFIG_TARGETS += $(INFERUNIT_MAIN).native +INFER_CONFIG_TARGETS += $(INFER_BUILD_DIR)/$(INFERUNIT_MAIN).exe endif -OCAML_BASE_SOURCES = \ - $(wildcard $(DEPENDENCIES:=/[a-zA-Z]*.ml) $(DEPENDENCIES:=/[a-zA-Z]*.mli) \ - $(DEPENDENCIES:=/[a-zA-Z]*.mll) $(DEPENDENCIES:=/[a-zA-Z]*.mly) \ - $(DEPENDENCIES:=/[a-zA-Z]*.re) $(DEPENDENCIES:=/[a-zA-Z]*.rei)) \ - $(STACKTREE_ATDGEN_STUBS) $(INFERPRINT_ATDGEN_STUBS) -OCAML_CONFIG_SOURCES = $(OCAML_BASE_SOURCES) +OCAML_SOURCES = \ + $(wildcard */[a-zA-Z]*.ml */[a-zA-Z]*.ml[ily]) \ + base/Version.ml $(STACKTREE_ATDGEN_STUBS) $(INFERPRINT_ATDGEN_STUBS) + ifeq ($(BUILD_C_ANALYZERS),yes) -OCAML_CONFIG_SOURCES += $(CLANG_ATDGEN_STUBS) $(CLANG_PLUGIN_MIRRORED_FILES) +OCAML_SOURCES += $(CLANG_ATDGEN_STUBS) $(CLANG_PLUGIN_MIRRORED_FILES) endif -OCAML_ALL_SOURCES = $(OCAML_BASE_SOURCES) $(CLANG_ATDGEN_STUBS) $(CLANG_PLUGIN_MIRRORED_FILES) .PHONY: all all: infer -SRC_BUILD_COMMON = base/Version.ml $(OCAML_CONFIG_SOURCES) +$(INFER_BUILD_DIR)/.ppx/ppx_compare/ppx.exe: jbuild jbuild-workspace $(OCAML_SOURCES) +# some voodoo to make jbuilder tolerate being run in parallel: force jbuilder to build its +# jbuild files and some files that have been seen to race otherwise + jbuilder build \ + $(JBUILDER_BUILD_DEFAULT)/.ppx/ppx_compare/ppx.exe \ + $(JBUILDER_BUILD_TEST)/.ppx/ppx_compare/ppx.exe + touch $@ + +SRC_BUILD_COMMON = $(INFER_BUILD_DIR)/.ppx/ppx_compare/ppx.exe $(OCAML_SOURCES) ifeq ($(BUILD_C_ANALYZERS),yes) SRC_BUILD_COMMON += $(CLANG_BINIOU_DICT) endif @@ -193,12 +129,10 @@ endif .PHONY: src_build_common src_build_common: $(SRC_BUILD_COMMON) -# single out infer.native as the source of truth for make, knowing that in fact several targets are +# single out infer.exe as the source of truth for make, knowing that in fact several targets are # produced by the build -$(INFER_BUILD_DIR)/$(INFER_MAIN).native: $(SRC_BUILD_COMMON) $(MAKEFILE_LIST) - $(MKDIR_P) $(BASE_BUILD_DIR) - $(OCAMLBUILD_CONFIG) -build-dir $(INFER_BUILD_DIR) \ - $(INFER_CONFIG_TARGETS) +$(INFER_BUILD_DIR)/$(INFER_MAIN).exe: $(SRC_BUILD_COMMON) $(MAKEFILE_LIST) + jbuilder build $(INFER_CONFIG_TARGETS) # let make know that the target is up-to-date even if ocamlbuild cached it $(QUIET)touch $@ @@ -210,59 +144,38 @@ $(INFER_BIN_ALIASES): Makefile $(QUIET)cd $(@D) && $(LN_S) -f infer $(@F) $(QUIET)touch $@ -$(INFER_BIN).native: $(INFER_BUILD_DIR)/$(INFER_MAIN).native $(INFER_BIN_ALIASES) - $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_MAIN).native $(INFER_BIN) - $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_MAIN).native $(INFER_BIN).native +$(INFER_BIN).exe: $(INFER_BUILD_DIR)/$(INFER_MAIN).exe $(INFER_BIN_ALIASES) + $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_MAIN).exe $(INFER_BIN) + $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_MAIN).exe $(INFER_BIN).exe ifeq ($(IS_FACEBOOK_TREE),yes) - $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_CREATE_TRACEVIEW_LINKS_MAIN).native \ + $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_CREATE_TRACEVIEW_LINKS_MAIN).exe \ $(INFER_CREATE_TRACEVIEW_LINKS_BIN) endif .PHONY: infer -infer: $(INFER_BIN).native +infer: $(INFER_BIN).exe -$(INFER_BUILD_DIR)/$(INFER_MAIN).byte: $(SRC_BUILD_COMMON) $(MAKEFILE_LIST) - $(MKDIR_P) $(BASE_BUILD_DIR) - $(OCAMLBUILD_CONFIG) -build-dir $(INFER_BUILD_DIR) \ - $(INFER_CONFIG_TARGETS:.native=.byte) +$(INFER_BUILD_DIR)/$(INFER_MAIN).bc: $(SRC_BUILD_COMMON) $(MAKEFILE_LIST) + jbuilder build $(INFER_CONFIG_TARGETS:.exe=.bc) $(QUIET)touch $@ -$(INFER_BIN).byte: $(INFER_BUILD_DIR)/$(INFER_MAIN).byte $(INFER_BIN_ALIASES) - $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_MAIN).byte $(INFER_BIN) - $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_MAIN).byte $(INFER_BIN).byte +$(INFER_BIN).bc: $(INFER_BUILD_DIR)/$(INFER_MAIN).bc $(INFER_BIN_ALIASES) + $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_MAIN).bc $(INFER_BIN) + $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_MAIN).bc $(INFER_BIN).bc ifeq ($(TEST),1) - $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFERUNIT_MAIN).byte $(INFERUNIT_BIN) + $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFERUNIT_MAIN).bc $(INFERUNIT_BIN) endif ifeq ($(IS_FACEBOOK_TREE),yes) - $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_CREATE_TRACEVIEW_LINKS_MAIN).byte \ + $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_CREATE_TRACEVIEW_LINKS_MAIN).bc \ $(INFER_CREATE_TRACEVIEW_LINKS_BIN) endif .PHONY: byte -byte: $(INFER_BIN).byte +byte: $(INFER_BIN).bc .PHONY: byte_no_install -byte_no_install: $(INFER_BUILD_DIR)/$(INFER_MAIN).byte - -# to build only the single module (and its dependencies) with extra flags execute: -# make MFLAGS= M=.cm{o,x} module -# for example, to build the assembly for the Ident module, execute -# make MFLAGS="-ocamlopt 'ocamlopt -S'" M=Ident.cmx module -M= -MFLAGS= - -.PHONY: module -module: $(SRC_BUILD_COMMON) $(OCAML_ALL_SOURCES) - $(MKDIR_P) $(BASE_BUILD_DIR) - $(OCAMLBUILD_ALL) -build-dir $(INFER_BUILD_DIR) \ - $(MFLAGS) \ - $(M) - -# to generate interface file.mli from implementation file.ml execute: -# make M=file mli -mli: - $(OCAMLFIND) ocamlc -package atdgen,oUnit,str,unix,yojson,zip $(addprefix -I $(INFER_BUILD_DIR),$(DEPENDENCIES)) -i $(M).ml > $(M).mli +byte_no_install: $(INFER_BUILD_DIR)/$(INFER_MAIN).bc roots:=Infer ifeq ($(IS_FACEBOOK_TREE),yes) @@ -270,8 +183,8 @@ roots += $(INFER_CREATE_TRACEVIEW_LINKS_MODULE) endif clusters:=base clang java IR -ml_src_files:=$(shell find $(DEPENDENCIES) -regex '.*\.ml\(i\)*') -inc_flags:=$(foreach dir,$(DEPENDENCIES),-I $(dir)) +ml_src_files:=$(shell find . -regex '.*\.ml\(i\)*') +inc_flags:=$(foreach dir,$(shell find . -type d),-I $(dir)) root_flags:=$(foreach root,$(roots),-r $(root)) cluster_flags:=$(foreach cluster,$(clusters),-c $(cluster)) @@ -279,7 +192,7 @@ mod_dep.dot: $(ml_src_files) $(MAKE) -C $(DEPENDENCIES_DIR)/ocamldot ocamldep.opt $(inc_flags) $(ml_src_files) \ | $(DEPENDENCIES_DIR)/ocamldot/ocamldot $(cluster_flags) $(root_flags) \ - | grep -v -e "\"IList\"\|\"Utils\"" \ + | grep -v -e "\"IList\"\|\"Utils\"\|\"IStd\"\|\"Infertop\"" \ > mod_dep.dot mod_dep.pdf: mod_dep.dot @@ -289,47 +202,19 @@ mod_dep.pdf: mod_dep.dot dsort: $(QUIET)ocamldep.opt -sort $(inc_flags) $(ml_src_files) -define to_ocaml_module -$(shell \ - echo $(basename $(1)) \ - | awk 'BEGIN { FS = "/"; OFS = "/" } ; {$$NF=toupper(substr($$NF,1,1))substr($$NF,2); print $$0}') -endef - -toplevel.mlpack: $(SRC_BUILD_COMMON) $(MAKEFILE_LIST) -# We need to pack all the infer modules into another module to avoid name clashes with some -# of them coming from ocaml libraries (Ident for example). To do that, we generate a .mlpack -# file containing namespaced modules. -# 1. we filter out roots because they execute code upon loading -# 2. each source file is converted to a module by capitalizing its first letter -# 3. we `echo` each module individually to avoid too long a command line -# 4. prevent race conditions by using a temp file - $(eval $@_tmp := $(shell mktemp $@.tmp.XXXX)) - $(foreach module,\ - $(filter-out $(foreach root,$(roots),%/$(root)),\ - $(foreach source,\ - $(filter-out unit/%,$(OCAML_CONFIG_SOURCES)),\ - $(call to_ocaml_module,$(source)))),\ - $(shell echo $(module) >> $($@_tmp))) - mv $($@_tmp) $@ - -$(INFER_BUILD_DIR)/toplevel.cmo: toplevel.mlpack - $(MKDIR_P) $(BASE_BUILD_DIR) - $(OCAMLBUILD_CONFIG) -build-dir $(INFER_BUILD_DIR) toplevel.cmo +$(INFER_BUILD_DIR)/infertop.bc: $(SRC_DIR)/infertop.ml $(SRC_BUILD_COMMON) $(MAKEFILE_LIST) + jbuilder build $@ $(QUIET)touch $@ .PHONY: toplevel -toplevel: $(INFER_BUILD_DIR)/toplevel.cmo +toplevel: $(INFER_BUILD_DIR)/infertop.bc .PHONY: checkCopyright checkCopyright: $(CHECKCOPYRIGHT_BIN) -$(CHECKCOPYRIGHT_BIN): $(CHECKCOPYRIGHT_MAIN).ml $(MAKEFILE_LIST) - $(MKDIR_P) $(BASE_BUILD_DIR) - $(OCAMLBUILD) -quiet -r -j $(NCPU) -build-dir $(BASE_BUILD_DIR)/checkCopyright \ - -cflags -g,-safe-string -lflags -g \ - -pkgs core,str -tag thread -use-ocamlfind $(CHECKCOPYRIGHT_MAIN).native - $(INSTALL_PROGRAM) \ - $(BASE_BUILD_DIR)/checkCopyright/$(CHECKCOPYRIGHT_MAIN).native $(CHECKCOPYRIGHT_BIN) +$(CHECKCOPYRIGHT_BIN): scripts/$(CHECKCOPYRIGHT_MAIN).ml $(MAKEFILE_LIST) + jbuilder build $(INFER_BUILD_DIR)/scripts/$(CHECKCOPYRIGHT_MAIN).exe + $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/scripts/$(CHECKCOPYRIGHT_MAIN).exe $(CHECKCOPYRIGHT_BIN) define gen_atdgen_rules # generate files using atdgen @@ -376,24 +261,30 @@ $(CLANG_BINIOU_DICT): $(CLANG_ATDGEN_STUB_ATD) | sort | uniq \ > $@ -base/Version.ml: base/Version.ml.in $(MAKEFILE_LIST) +jbuild jbuild-workspace base/Version.ml: $(MAKEFILE_LIST) TMPFILE=$$(mktemp $@.tmp.XXXX); \ INFER_GIT_COMMIT=$$(git --work-tree=$(ROOT_DIR) --git-dir=$(ROOT_DIR)/.git rev-parse --short HEAD || printf "unknown"); \ INFER_GIT_BRANCH=$$(git --work-tree=$(ROOT_DIR) --git-dir=$(ROOT_DIR)/.git rev-parse --abbrev-ref HEAD || printf "unknown"); \ sed \ + -e 's|@EXTRA_CFLAGS[@]|$(EXTRA_CFLAGS)|g' \ -e 's|@INFER_MAJOR[@]|$(INFER_MAJOR)|g' \ -e 's|@INFER_MINOR[@]|$(INFER_MINOR)|g' \ -e 's|@INFER_PATCH[@]|$(INFER_PATCH)|g' \ + -e 's|@IS_FACEBOOK_TREE[@]|$(IS_FACEBOOK_TREE)|g' \ -e 's|@IS_RELEASE_TREE[@]|$(IS_RELEASE_TREE)|g' \ -e "s|@INFER_GIT_COMMIT[@]|$$INFER_GIT_COMMIT|g" \ -e "s|@INFER_GIT_BRANCH[@]|$$INFER_GIT_BRANCH|g" \ -e "s|@BUILD_C_ANALYZERS[@]|$(BUILD_C_ANALYZERS)|g" \ -e "s|@BUILD_JAVA_ANALYZERS[@]|$(BUILD_JAVA_ANALYZERS)|g" \ + -e "s|@OPAMSWITCH[@]|$(OPAMSWITCH)|g" \ -e "s|@XCODE_SELECT[@]|$(XCODE_SELECT)|g" \ -e "s|@INFER_MAN_LAST_MODIFIED[@]|$(INFER_MAN_LAST_MODIFIED)|g" \ - $< > "$$TMPFILE"; \ + $@.in > "$$TMPFILE"; \ cat "$$TMPFILE" > $@; \ $(REMOVE) "$$TMPFILE" +jbuild: jbuild.in +jbuild-workspace: jbuild-workspace.in +base/Version.ml: base/Version.ml.in .PHONY: clean clean: @@ -401,13 +292,13 @@ clean: $(REMOVE) toplevel.mlpack $(REMOVE_DIR) $(BUILD_DIR) $(REMOVE) $(ETC_DIR)/clang_ast.dict - $(REMOVE) base/Version.ml - $(REMOVE) base/Version.ml.tmp.* + $(REMOVE) base/Version.ml jbuild jbuild-workspace + $(REMOVE) base/Version.ml.tmp.* jbuild.tmp.* jbuild-workspace.tmp.* $(REMOVE) backend/jsonbug_{j,t}.ml{,i} $(REMOVE) checkers/stacktree_{j,t}.ml{,i} # be a bit more aggressive than needed with what we remove here so that stale binaries that # only existed in previous versions get removed as well - $(REMOVE) $(BIN_DIR)/Infer* $(BIN_DIR)/infer-* $(INFER_BIN){,.byte,.native} $(INFER_BIN_ALIASES) \ + $(REMOVE) $(BIN_DIR)/Infer* $(BIN_DIR)/infer-* $(INFER_BIN){,.bc,.exe} $(INFER_BIN_ALIASES) \ $(INFERUNIT_BIN) $(CHECKCOPYRIGHT_BIN) $(REMOVE) $(INFER_CREATE_TRACEVIEW_LINKS_BIN) $(REMOVE) $(CLANG_PLUGIN_MIRROR)/* diff --git a/infer/src/README.md b/infer/src/README.md new file mode 100644 index 00000000000..4c15e27fff4 --- /dev/null +++ b/infer/src/README.md @@ -0,0 +1,8 @@ +The OCaml source files for infer live here. The Makefile is +responsible for building them, together with jbuild.in. + +If you make changes to the build process, also update .merlin +accordingly. For instance, if you want to add a new source directory +you will need to: +- add it to jbuild.in +- add it to .merlin diff --git a/infer/src/eradicate/modelTables.ml b/infer/src/eradicate/modelTables.ml index dc473519282..35d21e0df80 100644 --- a/infer/src/eradicate/modelTables.ml +++ b/infer/src/eradicate/modelTables.ml @@ -418,7 +418,7 @@ let mk_table list = List.iter ~f:(function v, pn_id -> Hashtbl.replace map pn_id v) list ; map -let this_file = __FILE__ +let this_file = Filename.basename __FILE__ let annotated_table_nullable = mk_table annotated_list_nullable diff --git a/infer/src/infertop.ml b/infer/src/infertop.ml new file mode 100644 index 00000000000..99491e89f56 --- /dev/null +++ b/infer/src/infertop.ml @@ -0,0 +1,9 @@ +(* + * Copyright (c) 2017 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + *) +;; UTop_main.main () diff --git a/infer/src/jbuild-workspace.in b/infer/src/jbuild-workspace.in new file mode 100644 index 00000000000..bb5c3ebfa71 --- /dev/null +++ b/infer/src/jbuild-workspace.in @@ -0,0 +1,9 @@ +;; Do not let jbuilder generate .merlin files because it gets it +;; wrong. This is mainly due to the fact that source files live in +;; sub-directories and are copied into _build/ by our jbuild. jbuilder +;; wants to write "S ." in .merlin but that makes merlin use the +;; copied source files in _build/ instead of only using the original +;; files. + +(context ((switch @OPAMSWITCH@) (name default))) +(context ((switch @OPAMSWITCH@) (name test))) diff --git a/infer/src/jbuild.in b/infer/src/jbuild.in new file mode 100644 index 00000000000..9495e18f22d --- /dev/null +++ b/infer/src/jbuild.in @@ -0,0 +1,209 @@ +(* -*- tuareg -*- *) +(* use strings so that it looks like OCaml even before substituting, e.g. to use ocamlformat *) + +let is_yes = String.equal "yes" + +let clang = is_yes "@BUILD_C_ANALYZERS@" + +let java = is_yes "@BUILD_JAVA_ANALYZERS@" + +let facebook = is_yes "@IS_FACEBOOK_TREE@" + +let extra_cflags = if "@EXTRA_CFLAGS" = "" then [] else ["@EXTRA_CFLAGS@"] + +let ( ^/ ) = Filename.concat + +(* Compute the list of all the source files. + + This is needed until jbuilder supports finding sources in subdirectories somehow + (https://github.com/janestreet/jbuilder/issues/34). *) + +let sources = + let src_dirs = + let src_dir = "." in + (* if you update the list of source directories, do not forget to also update .merlin *) + src_dir + :: ( if facebook then + (* do not use the symlinks in src/facebook/: jbuilder will not detect that the files have changed if they are hidden behind a symlink *) + "../../facebook/skel/infer/src/facebook" + else src_dir ^/ "opensource" ) + :: List.map (Filename.concat src_dir) + ( ( if clang then ["clang"; "clang_plugin"; ("unit" ^/ "clang")] + else ["clang_stubs"; ("unit" ^/ "clang_stubs")] ) + @ [ (if java then "java" else "java_stubs") + ; "absint" + ; "backend" + ; "base" + ; "bufferoverrun" + ; "checkers" + ; "eradicate" + ; "harness" + ; "integration" + ; "IR" + ; "labs" + ; "quandary" + ; "unit" ] ) + in + let files = ref [] in + let ml_suffixes = [".ml"; ".mli"; ".mll"; ".mly"] in + let add_file dir file = + if List.exists (Filename.check_suffix file) ml_suffixes then files := (dir ^/ file) :: !files + in + let one_dir dir = + (* absolute path so that running jbuilder from any subdirectory (in particular from src/Makefile) + points at the right original files in ocamlc's error messages *) + let abs_dir = Sys.getcwd () ^/ dir in + Array.iter (add_file abs_dir) (Sys.readdir dir) + in + List.iter one_dir src_dirs ; !files + +let common_cflags = + let fatal_warnings = "+3+5+6+8+10+11+12+18+19+20+21+23+26+29+27+33+34+35+37+38+39+50+52+57" in + let warnings = fatal_warnings ^ "-4-9-32-40-41-42-45-48" in + let common_flags = + [ "-g" + ; "-short-paths" + ; "-safe-string" + ; "-principal" + ; "-strict-formats" + ; "-strict-sequence" + ; "-bin-annot" + ; "-w" + ; warnings ] + in + match Jbuild_plugin.V1.context with + | "test" + -> "-warn-error" :: fatal_warnings :: common_flags + | "default" + -> common_flags + | ctx + -> invalid_arg ("unknown context: " ^ ctx) + +(** Make a library that includes just IStd and IList. These files have different compilation flags + than infer (they don't include IStd!). *) +let inferBaseStdlib = "InferBaseStdlib" + +let iStd = "IStd" + +let inferBaseStdlib_modules = ["IList"; iStd] + +let inferBaseStdlib_cflags = common_cflags + +(** Generated code (eg, from atdgen) is also compiled using different flags, so pack it in a + separated library. *) +let inferGenerated = "InferGenerated" + +let inferGenerated_modules = + ( if clang then + [ "Clang_ast_b" + ; "Clang_ast_j" + ; "Clang_ast_t" + ; "Clang_ast_v" + ; "Clang_ast_proj" + ; "Clang_ast_types" + ; "Clang_ast_visit" ] + else [] ) + @ ["Jsonbug_j"; "Jsonbug_t"; "Stacktree_j"; "Stacktree_t"] + +let inferGenerated_cflags = common_cflags @ ["-w"; "-27-32-34-35-39"] + +let infer_binaries = + ["infer"; "inferunit"] @ if facebook then ["InferCreateTraceViewLinks"] else [] + +let infer_cflags = + common_cflags @ ["-open"; inferBaseStdlib; "-open"; iStd; "-open"; inferGenerated] + +let common_libraries = + (if java then ["javalib"; "ptrees"; "sawja"] else []) + @ [ "ANSITerminal" + ; "atdgen" + ; "cmdliner" + ; "core" + ; "extlib" + ; "oUnit" + ; "parmap" + ; "str" + ; "unix" + ; "xmlm" + ; "yojson" + ; "zip" ] + +(** Return the best copy action for a given [source] file. That is, when possibly, use "copy#" so + that jump-to-defition and compiler errors go to the right original source file, otherwise do a + normal copy. *) +let copy_action_of_source source = + if Filename.check_suffix source ".mly" then + (* menhir doesn't support '# 1 ""' directives at the start of the file inserted by + copy# actions *) + "copy" + else "copy#" + +(** The build stanzas to be passed to jbuilder *) +let stanzas = + ( if clang then + ["(ocamllex (types_lexer ctl_lexer))"; "(menhir ((modules (types_parser ctl_parser))))"] + else [] ) + @ [ Format.sprintf + {| +(library + ((name %s) + (flags (%s)) + (modules (%s)) + (libraries (atdgen)) + )) +|} + inferGenerated (String.concat " " inferGenerated_cflags) + (String.concat " " inferGenerated_modules) + ; Format.sprintf + {| +(library + ((name %s) + (flags (%s)) + (modules (%s)) + (libraries (%s)) + )) +|} + inferBaseStdlib (String.concat " " inferBaseStdlib_cflags) + (String.concat " " inferBaseStdlib_modules) (String.concat " " common_libraries) + ; Format.sprintf + {| +(library + ((name InferModules) + (flags (%s)) + (libraries (%s %s %s)) + (modules (:standard \ %s infertop %s %s)) + (preprocess (pps (ppx_compare))) + )) +|} + (String.concat " " infer_cflags) (String.concat " " common_libraries) inferBaseStdlib + inferGenerated (String.concat " " infer_binaries) + (String.concat " " inferBaseStdlib_modules) (String.concat " " inferGenerated_modules) + ; Format.sprintf + {| +(executables + ((names (%s)) + (flags (%s -open InferModules)) + (libraries (InferModules)) + (modules (%s)) + (preprocess (pps (ppx_compare))) + )) +|} + (String.concat " " infer_binaries) (String.concat " " infer_cflags) + (String.concat " " infer_binaries) + ; Format.sprintf + {| +(executable + ((name infertop) + (flags (%s)) + (libraries (utop InferModules)) + (modules (:standard \ %s)) + (link_flags (-linkall -warn-error -31)))) +|} + (String.concat " " infer_cflags) (String.concat " " infer_binaries) ] + @ List.map + (fun source -> + Printf.sprintf "(rule (%s %s %s))" (copy_action_of_source source) source + (Filename.basename source)) + sources + +;; String.concat "\n" stanzas |> Jbuild_plugin.V1.send diff --git a/infer/src/scripts/checkCopyright.ml b/infer/src/scripts/checkCopyright.ml index 76687c0cd78..62cba5dce33 100644 --- a/infer/src/scripts/checkCopyright.ml +++ b/infer/src/scripts/checkCopyright.ml @@ -7,7 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. *) -open! Core.Std +open! Core module F = Format let copyright_modified_exit_code = 1 @@ -193,7 +193,7 @@ let copyright_has_changed mono fb_year com_style prefix cstart cend lines_arr = let update_file fname mono fb_year com_style prefix cstart cend lines_arr = try - let cout = open_out fname in + let cout = Out_channel.create fname in let fmt = F.formatter_of_out_channel cout in for i = 0 to cstart - 1 do F.fprintf fmt "%s@." lines_arr.(i) done ; pp_copyright mono fb_year com_style fmt prefix ; diff --git a/infer/src/scripts/jbuild b/infer/src/scripts/jbuild new file mode 100644 index 00000000000..b6de5ab89ee --- /dev/null +++ b/infer/src/scripts/jbuild @@ -0,0 +1,7 @@ +(jbuild_version 1) + +(executable + ((name checkCopyright) + (flags (:standard -g -short-paths -safe-string -principal -strict-formats -strict-sequence -bin-annot -w +3+5+6+8+10+11+12+18+19+20+21+23+26+29+27+33+34+35+37+38+39+50+52+57-4-9-32-40-41-42-45-48)) + (libraries (core str)) + )) diff --git a/infer/tests/build_systems/buck_flavors_deterministic/Makefile b/infer/tests/build_systems/buck_flavors_deterministic/Makefile index 93dfdb302d4..c6b291e42e6 100644 --- a/infer/tests/build_systems/buck_flavors_deterministic/Makefile +++ b/infer/tests/build_systems/buck_flavors_deterministic/Makefile @@ -45,6 +45,9 @@ differences.exp.test: $(CLANG_DEPS) $(SOURCES) $(MAKEFILE_LIST) ) $(QUIET)$(REMOVE) capture_hash-1.sha capture_hash-2.sha +.PHONY: print +print: differences.exp.test + .PHONY: test test: differences.exp.test $(QUIET)diff -u differences.exp $< diff --git a/infer/tests/build_systems/infertop/Makefile b/infer/tests/build_systems/infertop/Makefile new file mode 100644 index 00000000000..f52aaa8f85c --- /dev/null +++ b/infer/tests/build_systems/infertop/Makefile @@ -0,0 +1,31 @@ +# Copyright (c) 2017 - present Facebook, Inc. +# All rights reserved. +# +# This source code is licensed under the BSD style license found in the +# LICENSE file in the root directory of this source tree. An additional grant +# of patent rights can be found in the PATENTS file in the same directory. + +TESTS_DIR = ../.. +ROOT_DIR = $(TESTS_DIR)/../.. + +include $(TESTS_DIR)/base.make + +toplevel.exp.test: $(BUILD_DIR)/test/infertop.bc + $(QUIET)$(call silent_on_success,Testing infer OCaml REPL,\ + TOPLEVEL_DIR=$(BUILD_DIR)/test $(SCRIPT_DIR)/infer_repl \ + $(INFER_DIR)/tests/repl/infer_batch_script.mltop > $@) + +.PHONY: print +print: toplevel.exp.test + +.PHONY: test +test: toplevel.exp.test + $(QUIET)diff -u toplevel.exp $< + +.PHONY: replace +replace: toplevel.exp.test + $(QUIET)$(COPY) $< toplevel.exp + +.PHONY: clean +clean: + $(QUIET)$(REMOVE) toplevel.exp.test diff --git a/infer/tests/build_systems/infertop/toplevel.exp b/infer/tests/build_systems/infertop/toplevel.exp new file mode 100644 index 00000000000..5272fa1cc09 --- /dev/null +++ b/infer/tests/build_systems/infertop/toplevel.exp @@ -0,0 +1,11 @@ +Findlib has been successfully loaded. Additional directives: + #require "package";; to load a package + #list;; to list the available packages + #camlp4o;; to load camlp4 (standard syntax) + #camlp4r;; to load camlp4 (revised syntax) + #predicates "p,q,...";; to set these predicates + Topfind.reset();; to force that packages will be reloaded + #thread;; to enable threads + +n$2 +false diff --git a/infer/tests/codetoanalyze/java/eradicate/issues.exp b/infer/tests/codetoanalyze/java/eradicate/issues.exp index c5b294efec0..811cd53a5b9 100644 --- a/infer/tests/codetoanalyze/java/eradicate/issues.exp +++ b/infer/tests/codetoanalyze/java/eradicate/issues.exp @@ -27,11 +27,11 @@ codetoanalyze/java/eradicate/InconsistentSubclassAnnotation.java, String Inconsi codetoanalyze/java/eradicate/InconsistentSubclassAnnotation.java, SubclassExample$T SubclassExample$B.foo(), 0, ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION, [Method `SubclassExample$B.foo()` is annotated with `@Nullable` but overrides unannotated method `SubclassExample$A.foo()`.] codetoanalyze/java/eradicate/InconsistentSubclassAnnotation.java, SubclassExample$T SubclassExample$C.baz(), 0, ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION, [Method `SubclassExample$C.baz()` is annotated with `@Nullable` but overrides unannotated method `SubclassExample$I.baz()`.] codetoanalyze/java/eradicate/InconsistentSubclassAnnotation.java, void SubclassExample$D.deref(SubclassExample$T), 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, [First parameter `t` of method `SubclassExample$D.deref(...)` is not `@Nullable` but is declared `@Nullable`in the parent class method `SubclassExample$A.deref(...)`.] -codetoanalyze/java/eradicate/LibraryCalls.java, String LibraryCalls.badAtomicReferenceDereference(AtomicReference), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `ref.get()` in the call to `toString()` could be null. (Origin: call to get() modelled in eradicate/modelTables.ml at line 37)] -codetoanalyze/java/eradicate/LibraryCalls.java, String LibraryCalls.badPhantomReferenceDereference(PhantomReference), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `ref.get()` in the call to `toString()` could be null. (Origin: call to get() modelled in eradicate/modelTables.ml at line 29)] -codetoanalyze/java/eradicate/LibraryCalls.java, String LibraryCalls.badReferenceDereference(Reference), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `ref.get()` in the call to `toString()` could be null. (Origin: call to get() modelled in eradicate/modelTables.ml at line 21)] -codetoanalyze/java/eradicate/LibraryCalls.java, String LibraryCalls.badSoftReferenceDereference(SoftReference), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `ref.get()` in the call to `toString()` could be null. (Origin: call to get() modelled in eradicate/modelTables.ml at line 33)] -codetoanalyze/java/eradicate/LibraryCalls.java, String LibraryCalls.badWeakReferenceDereference(WeakReference), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `ref.get()` in the call to `toString()` could be null. (Origin: call to get() modelled in eradicate/modelTables.ml at line 25)] +codetoanalyze/java/eradicate/LibraryCalls.java, String LibraryCalls.badAtomicReferenceDereference(AtomicReference), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `ref.get()` in the call to `toString()` could be null. (Origin: call to get() modelled in modelTables.ml at line 37)] +codetoanalyze/java/eradicate/LibraryCalls.java, String LibraryCalls.badPhantomReferenceDereference(PhantomReference), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `ref.get()` in the call to `toString()` could be null. (Origin: call to get() modelled in modelTables.ml at line 29)] +codetoanalyze/java/eradicate/LibraryCalls.java, String LibraryCalls.badReferenceDereference(Reference), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `ref.get()` in the call to `toString()` could be null. (Origin: call to get() modelled in modelTables.ml at line 21)] +codetoanalyze/java/eradicate/LibraryCalls.java, String LibraryCalls.badSoftReferenceDereference(SoftReference), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `ref.get()` in the call to `toString()` could be null. (Origin: call to get() modelled in modelTables.ml at line 33)] +codetoanalyze/java/eradicate/LibraryCalls.java, String LibraryCalls.badWeakReferenceDereference(WeakReference), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `ref.get()` in the call to `toString()` could be null. (Origin: call to get() modelled in modelTables.ml at line 25)] codetoanalyze/java/eradicate/NullFieldAccess.java, Object NullFieldAccess.arrayAccess(), 1, ERADICATE_NULL_FIELD_ACCESS, [origin,Object `NullFieldAccess.objects` could be null when accessing element at index `0`. (Origin: field NullFieldAccess.objects at line 63)] codetoanalyze/java/eradicate/NullFieldAccess.java, int NullFieldAccess.arrayLength(), 1, ERADICATE_NULL_FIELD_ACCESS, [origin,Object `NullFieldAccess.objects` could be null when accessing field `length`. (Origin: field NullFieldAccess.objects at line 59)] codetoanalyze/java/eradicate/NullFieldAccess.java, int NullFieldAccess.useInterface(NullFieldAccess$I), 2, ERADICATE_NULL_FIELD_ACCESS, [origin,Object `c` could be null when accessing field `NullFieldAccess$C.n`. (Origin: field NullFieldAccess$I.c at line 52)] @@ -39,11 +39,11 @@ codetoanalyze/java/eradicate/NullFieldAccess.java, int NullFieldAccess.useX(), 2 codetoanalyze/java/eradicate/NullFieldAccess.java, int NullFieldAccess.useZ(), 2, ERADICATE_NULL_FIELD_ACCESS, [origin,Object `c` could be null when accessing field `NullFieldAccess$C.n`. (Origin: field NullFieldAccess.z at line 47)] codetoanalyze/java/eradicate/NullMethodCall.java, int NullMethodCall$Inner.outerField(), 2, ERADICATE_NULL_METHOD_CALL, [origin,The value of `s` in the call to `length()` could be null. (Origin: field NullMethodCall.fld at line 71)] codetoanalyze/java/eradicate/NullMethodCall.java, int NullMethodCall$Inner.outerPrivateField(), 2, ERADICATE_NULL_METHOD_CALL, [origin,The value of `s` in the call to `length()` could be null. (Origin: field NullMethodCall.pfld at line 82)] -codetoanalyze/java/eradicate/NullMethodCall.java, int NullMethodCall.testSystemGetenvBad(), 2, ERADICATE_NULL_METHOD_CALL, [origin,The value of `envValue` in the call to `length()` could be null. (Origin: call to getenv(...) modelled in eradicate/modelTables.ml at line 243)] +codetoanalyze/java/eradicate/NullMethodCall.java, int NullMethodCall.testSystemGetenvBad(), 2, ERADICATE_NULL_METHOD_CALL, [origin,The value of `envValue` in the call to `length()` could be null. (Origin: call to getenv(...) modelled in modelTables.ml at line 243)] codetoanalyze/java/eradicate/NullMethodCall.java, void NullMethodCall.callOnNull(), 2, ERADICATE_NULL_METHOD_CALL, [origin,The value of `s` in the call to `length()` could be null. (Origin: null constant at line 22)] codetoanalyze/java/eradicate/NullMethodCall.java, void NullMethodCall.testExceptionPerInstruction(int), 6, ERADICATE_NULL_METHOD_CALL, [origin,The value of `s` in the call to `length()` could be null. (Origin: null constant at line 183)] codetoanalyze/java/eradicate/NullMethodCall.java, void NullMethodCall.testFieldAssignmentIfThenElse(String), 2, ERADICATE_NULL_METHOD_CALL, [origin,The value of `s` in the call to `length()` could be null. (Origin: null constant at line 174)] -codetoanalyze/java/eradicate/NullMethodCall.java, void NullMethodCall.testSystemGetPropertyReturn(), 2, ERADICATE_NULL_METHOD_CALL, [origin,The value of `s` in the call to `length()` could be null. (Origin: call to getProperty(...) modelled in eradicate/modelTables.ml at line 238)] +codetoanalyze/java/eradicate/NullMethodCall.java, void NullMethodCall.testSystemGetPropertyReturn(), 2, ERADICATE_NULL_METHOD_CALL, [origin,The value of `s` in the call to `length()` could be null. (Origin: call to getProperty(...) modelled in modelTables.ml at line 238)] codetoanalyze/java/eradicate/ParameterNotNullable.java, ParameterNotNullable$ConstructorCall.(ParameterNotNullable,int), 1, ERADICATE_PARAMETER_NOT_NULLABLE, [origin,`ParameterNotNullable$ConstructorCall(...)` needs a non-null value in parameter 2 but argument `null` can be null. (Origin: null constant at line 102)] codetoanalyze/java/eradicate/ParameterNotNullable.java, String ParameterNotNullable.testSystemGetPropertyArgument(), 1, ERADICATE_PARAMETER_NOT_NULLABLE, [origin,`getProperty(...)` needs a non-null value in parameter 1 but argument `null` can be null. (Origin: null constant at line 71)] codetoanalyze/java/eradicate/ParameterNotNullable.java, String ParameterNotNullable.testSystemGetenvBad(), 1, ERADICATE_PARAMETER_NOT_NULLABLE, [origin,`getenv(...)` needs a non-null value in parameter 1 but argument `null` can be null. (Origin: null constant at line 76)] @@ -67,4 +67,4 @@ codetoanalyze/java/eradicate/ReturnNotNullable.java, String ReturnNotNullable.re codetoanalyze/java/eradicate/ReturnNotNullable.java, String ReturnNotNullable.returnNullable(String), 0, ERADICATE_RETURN_NOT_NULLABLE, [Method `returnNullable(...)` may return null but it is not annotated with `@Nullable`. (Origin: method parameter s)] codetoanalyze/java/eradicate/ReturnNotNullable.java, String ReturnNotNullable.return_null_in_catch(), 0, ERADICATE_RETURN_NOT_NULLABLE, [origin,Method `return_null_in_catch()` may return null but it is not annotated with `@Nullable`. (Origin: null constant at line 111)] codetoanalyze/java/eradicate/ReturnNotNullable.java, String ReturnNotNullable.return_null_in_catch_after_throw(), 0, ERADICATE_RETURN_NOT_NULLABLE, [origin,Method `return_null_in_catch_after_throw()` may return null but it is not annotated with `@Nullable`. (Origin: null constant at line 123)] -codetoanalyze/java/eradicate/ReturnNotNullable.java, URL ReturnNotNullable.getResourceNullable(Class,String), 0, ERADICATE_RETURN_NOT_NULLABLE, [origin,Method `getResourceNullable(...)` may return null but it is not annotated with `@Nullable`. (Origin: call to getResource(...) modelled in eradicate/modelTables.ml at line 128)] +codetoanalyze/java/eradicate/ReturnNotNullable.java, URL ReturnNotNullable.getResourceNullable(Class,String), 0, ERADICATE_RETURN_NOT_NULLABLE, [origin,Method `getResourceNullable(...)` may return null but it is not annotated with `@Nullable`. (Origin: call to getResource(...) modelled in modelTables.ml at line 128)] diff --git a/opam b/opam index 9a042c39787..fe3513645fe 100644 --- a/opam +++ b/opam @@ -34,6 +34,7 @@ depends: [ "ctypes" {>="0.9.2"} "extlib-compat" "javalib" {>="2.3.3"} + "jbuilder" {build & >="1.0+beta11"} "ocamlfind" {build} "ounit" {="2.0.0"} "parmap" {>="1.0-rc8"} diff --git a/scripts/infer_repl b/scripts/infer_repl index 87cc921e2a9..654adc512d0 100755 --- a/scripts/infer_repl +++ b/scripts/infer_repl @@ -1,20 +1,25 @@ #!/bin/bash -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +## Wrapper around infertop. To build infertop, run `make toplevel`. -# where to find toplevel.cmo, can be overridden -TOPLEVEL_DIR=${TOPLEVEL_DIR:-"$SCRIPT_DIR"/../infer/_build/infer} +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -# how to load the toplevel; can be overridden (with "ocaml" for example) -INFER_REPL_BINARY=${INFER_REPL_BINARY:-"utop"} +# where to find toplevel.bc, can be overridden +TOPLEVEL_DIR=${TOPLEVEL_DIR:-"$SCRIPT_DIR"/../infer/src/_build/default} -# to build new toplevel, run `make toplevel` -# -init option is used only in interactive mode -# in batch mode, scripts need to import toplevel_init themselves +# The -init option is effective only in interactive mode. +# In batch mode, scripts need to import toplevel_init themselves. # It can be done by adding #use "toplevel_init";; to the beginning # of a script. # NOTE: $SCRIPT_DIR is added search path for batch scripts # so they can be located anywhere and still find toplevel_init # file. In interactive mode $SCRIPT_DIR isn't needed -"$INFER_REPL_BINARY" -init "$SCRIPT_DIR"/toplevel_init -I "$TOPLEVEL_DIR" -I "$SCRIPT_DIR" $@ +set -x +# infertop expects to be run from where jbuild is located +cd "$SCRIPT_DIR"/../infer +"$TOPLEVEL_DIR"/infertop.bc \ + -init "$SCRIPT_DIR"/toplevel_init \ + -I "$TOPLEVEL_DIR" \ + -I "$SCRIPT_DIR" \ + "$@" diff --git a/scripts/toplevel_init b/scripts/toplevel_init index ff5a83ec36a..ddaad8650c7 100644 --- a/scripts/toplevel_init +++ b/scripts/toplevel_init @@ -1,3 +1,5 @@ +(* to be used with infertop, infer's custom toplevel *) + (* load dependencies *) #use "topfind";; #thread;; @@ -10,6 +12,5 @@ #require "xmlm";; #require "ANSITerminal";; -(* load infer code *) -#load_rec "toplevel.cmo";; -open Toplevel;; +open InferModules;; +open InferBaseStdlib;;