diff --git a/.gitignore b/.gitignore index a01aee8c0e0..be132252341 100644 --- a/.gitignore +++ b/.gitignore @@ -199,6 +199,7 @@ rebar3.crashdump /infer/src/java/dune /infer/src/nullsafe/unit/dune /infer/src/opensource/dune +/infer/src/python/unit/dune /infer/src/unit/dune .merlin diff --git a/Makefile.autoconf.in b/Makefile.autoconf.in index d53ec9a8135..65e46f0c06f 100644 --- a/Makefile.autoconf.in +++ b/Makefile.autoconf.in @@ -82,6 +82,7 @@ PATCHELF := @PATCHELF@ PATH := @PATH@ PLATFORM_ENV := @PLATFORM_ENV@ prefix := @prefix@ +PYTHON := @PYTHON@ REALPATH := @REALPATH@ REBAR3 := @REBAR3@ SDKROOT := @SDKROOT@ diff --git a/build-infer.sh b/build-infer.sh index a8a09fbf4ac..de6462dee6a 100755 --- a/build-infer.sh +++ b/build-infer.sh @@ -34,6 +34,7 @@ function usage() { echo " erlang build Erlang analyzer" echo " hack build Hack analyzer" echo " java build Java analyzer" + echo " python build Python analyzer" echo echo " options:" echo " -h,--help show this message" @@ -56,6 +57,7 @@ BUILD_CLANG=${BUILD_CLANG:-no} BUILD_ERLANG=${BUILD_ERLANG:-no} BUILD_HACK=${BUILD_HACK:-no} BUILD_JAVA=${BUILD_JAVA:-no} +BUILD_PYTHON=${BUILD_PYTHON:-no} INTERACTIVE=${INTERACTIVE:-yes} JOBS=${JOBS:-$NCPU} ONLY_SETUP_OPAM=${ONLY_SETUP_OPAM:-no} @@ -69,6 +71,7 @@ function build_all() { BUILD_ERLANG=yes BUILD_HACK=yes BUILD_JAVA=yes + BUILD_PYTHON=yes } while [[ $# -gt 0 ]]; do @@ -99,6 +102,11 @@ while [[ $# -gt 0 ]]; do shift continue ;; + python) + BUILD_PYTHON=yes + shift + continue + ;; -h|--help) usage exit 0 @@ -137,7 +145,8 @@ while [[ $# -gt 0 ]]; do done if [ "$BUILD_CLANG" == "no" ] && [ "$BUILD_ERLANG" == "no" ] && \ - [ "$BUILD_HACK" == "no" ] && [ "$BUILD_JAVA" == "no" ]; then + [ "$BUILD_HACK" == "no" ] && [ "$BUILD_JAVA" == "no" ] && \ + [ "$BUILD_PYTHON" == "no" ]; then build_all fi @@ -198,6 +207,9 @@ fi if [ "$BUILD_JAVA" == "no" ]; then CONFIGURE_PREPEND_OPTS+=" --disable-java-analyzers" fi +if [ "$BUILD_PYTHON" == "no" ]; then + CONFIGURE_PREPEND_OPTS+=" --disable-python-analyzers" +fi set -x # empty arrays trigger unbound variable errors, use horrible syntax to work around it diff --git a/configure.ac b/configure.ac index 7c85e6fe027..c5b932171c8 100644 --- a/configure.ac +++ b/configure.ac @@ -130,6 +130,14 @@ AC_ARG_ENABLE(java-analyzers, BUILD_JAVA_ANALYZERS=$enable_java_analyzers AC_SUBST([BUILD_JAVA_ANALYZERS]) +AC_ARG_ENABLE(python-analyzers, + AS_HELP_STRING([--disable-python-analyzers], + [do not build the Python analyzers (default is to build them)]), + , + enable_python_analyzers=yes) +BUILD_python_ANALYZERS=$enable_python_analyzers +AC_SUBST([BUILD_python_ANALYZERS]) + AC_ARG_WITH(fcp-clang, AS_HELP_STRING([--with-fcp-clang], [use $CLANG_PREFIX/bin/clang to override the default compiler (default is not to override)]), @@ -167,6 +175,17 @@ AS_IF([test "x$enable_c_analyzers" = "xyes"], [ ]) # end if($enable_c_analyzers) +AS_IF([test "x$enable_python_analyzers" = "xyes"], [ + AC_CHECK_TOOL([PYTHON], [python3], [no]) + AC_ASSERT_PROG([python3], [$PYTHON]) + AC_MSG_CHECKING([python version]) + python_version=$("$PYTHON" --version) + case $python_version in + Python\ 3.8.*) AC_MSG_RESULT([$python_version]); break;; + *) AC_MSG_ERROR([python version $python_version is not supported, please install python version 3.8 instead or disable python support with --disable-python-analyzers]); break;; + esac +]) # end if($enable_python_analyzers) + AC_CHECK_TOOL([XCODE_SELECT], [xcode-select], [no]) AS_IF([test "x$XCODE_SELECT" != "xno"], [XCODE_SELECT_OUT=`xcode-select -p`]) @@ -281,7 +300,7 @@ AC_ARG_VAR([OPAMSWITCH], [Opam switch used for building infer.]) AC_CHECK_TOOL([OPAM], [opam], [no]) AS_IF([test "$OPAM" != "no"], [ AC_MSG_CHECKING([opam version]) - opam_version=$(opam --version) + opam_version=$("$OPAM" --version) case $opam_version in 2.*) AC_MSG_RESULT([$opam_version]); break;; *) AC_MSG_ERROR([opam version $opam_version is not supported, please install opam version 2 instead]); break;; diff --git a/infer/src/Makefile b/infer/src/Makefile index 8ad48495f6c..b17cce55ee3 100644 --- a/infer/src/Makefile +++ b/infer/src/Makefile @@ -56,7 +56,8 @@ GENERATED_FROM_AUTOCONF = dune.common ../dune-workspace base/Version.ml .PHONY: dune-workspace dune-workspace: ../dune-workspace -GENERATED_DUNES += dune clang/dune integration/dune java/dune opensource/dune unit/dune +GENERATED_DUNES += \ + dune clang/dune integration/dune java/dune opensource/dune python/unit/dune unit/dune SRC_BUILD_COMMON = $(GENERATED_FROM_AUTOCONF) $(GENERATED_DUNES) $(OCAML_SOURCES) ifeq ($(BUILD_C_ANALYZERS),yes) @@ -233,6 +234,7 @@ $(GENERATED_FROM_AUTOCONF): $(MAKEFILE_LIST) -e "s|@OPAMSWITCH[@]|$(OPAMSWITCH)|g" \ -e "s|@XCODE_SELECT[@]|$(XCODE_SELECT)|g" \ -e "s|@INFER_MAN_LAST_MODIFIED[@]|$(INFER_MAN_LAST_MODIFIED)|g" \ + -e "s|@PYTHON[@]|$(PYTHON)|g" \ $@.in > "$$TMPFILE"; \ cat "$$TMPFILE" > $@; \ echo -e "\n;;" >> $@; \ @@ -249,6 +251,7 @@ clang/dune: clang/dune.in integration/dune: integration/dune.in java/dune: java/dune.in opensource/dune: opensource/dune.in +python/unit/dune: python/unit/dune.in unit/dune: unit/dune.in .PHONY: clean diff --git a/infer/src/base/Version.ml.in b/infer/src/base/Version.ml.in index 97a82592749..2c41d52609c 100644 --- a/infer/src/base/Version.ml.in +++ b/infer/src/base/Version.ml.in @@ -56,3 +56,5 @@ let java_version = int_of_string_opt "@JAVA_MAJOR_VERSION@" let xcode_enabled = is_not_no "@XCODE_SELECT@" let man_pages_last_modify_date = "@INFER_MAN_LAST_MODIFIED@" + +let python_exe = "@PYTHON@" diff --git a/infer/src/base/Version.mli b/infer/src/base/Version.mli index 2d5cd11b906..cbda0300c97 100644 --- a/infer/src/base/Version.mli +++ b/infer/src/base/Version.mli @@ -36,3 +36,5 @@ val java_version : int option val xcode_enabled : bool val man_pages_last_modify_date : string + +val python_exe : string [@@warning "-unused-value-declaration"] (* used in unit tests *) diff --git a/infer/src/datalog/scripts/test/Makefile b/infer/src/datalog/scripts/test/Makefile index 886a01652f4..0eca2e58148 100644 --- a/infer/src/datalog/scripts/test/Makefile +++ b/infer/src/datalog/scripts/test/Makefile @@ -17,7 +17,7 @@ build-targets: make -C java_targets test-classdump: build-targets - python3 $(SCRIPTS_DIR)/classdump.py --output $(SCRIPTS_DIR)/test/classdump_out $(JAR_FILES) + "$PYTHON" $(SCRIPTS_DIR)/classdump.py --output $(SCRIPTS_DIR)/test/classdump_out $(JAR_FILES) @$(foreach jarfile,$(notdir $(JAR_FILES)),$(call check_no_diff,$(SCRIPTS_DIR)/test/classdump_out/$(jarfile).classes,$(SCRIPTS_DIR)/test/classdump_exp/$(jarfile).classes.exp);) clean: diff --git a/infer/src/dune.common.in b/infer/src/dune.common.in index 717bd1991bc..31e13e4bec1 100644 --- a/infer/src/dune.common.in +++ b/infer/src/dune.common.in @@ -19,6 +19,8 @@ let hack = is_yes "@BUILD_HACK_ANALYZERS@" let java = is_yes "@BUILD_JAVA_ANALYZERS@" +let python = is_yes "@BUILD_PYTHON_ANALYZERS@" + let facebook = is_yes "@IS_FACEBOOK_TREE@" let extra_cflags = if is_empty "@EXTRA_CFLAGS@" then [] else ["@EXTRA_CFLAGS@"] diff --git a/infer/src/python/FFI.ml b/infer/src/python/FFI.ml index 00fa19cb0db..4a42120baa9 100644 --- a/infer/src/python/FFI.ml +++ b/infer/src/python/FFI.ml @@ -345,7 +345,7 @@ let from_bytecode filename = let from_file ~is_binary filename = - Py.initialize ~interpreter:"/usr/bin/python3.8" () ; + Py.initialize ~interpreter:Version.python_exe () ; let code = if is_binary then from_bytecode filename else from_source filename in Py.finalize () ; code diff --git a/infer/src/python/unit/FFITest.ml b/infer/src/python/unit/FFITest.ml index ad926be91bd..389a2890365 100644 --- a/infer/src/python/unit/FFITest.ml +++ b/infer/src/python/unit/FFITest.ml @@ -13,7 +13,7 @@ let%test_module "load_code" = let source = "x = 42" let%expect_test _ = - Py.initialize ~version:3 ~minor:8 () ; + Py.initialize ~interpreter:Version.python_exe () ; let res = FFI.from_string ~source ~filename:"dummy" in Py.finalize () ; F.printf "%s" (FFI.Code.show res) ; diff --git a/infer/src/python/unit/PyTransTest.ml b/infer/src/python/unit/PyTransTest.ml index 87185f80e10..ef4a4160a7e 100644 --- a/infer/src/python/unit/PyTransTest.ml +++ b/infer/src/python/unit/PyTransTest.ml @@ -14,7 +14,7 @@ let%test_module "to_proc_desc" = let%expect_test _ = let source = "x = 42" in - Py.initialize ~version:3 ~minor:8 () ; + Py.initialize ~interpreter:Version.python_exe () ; let code = FFI.from_string ~source ~filename:"dummy" in Py.finalize () ; let res = PyTrans.to_module ~sourcefile "$toplevel::main" code in @@ -44,7 +44,7 @@ let%test_module "to_proc_desc" = x = 42 print(x) |} in - Py.initialize ~version:3 ~minor:8 () ; + Py.initialize ~interpreter:Version.python_exe () ; let code = FFI.from_string ~source ~filename:"dummy" in Py.finalize () ; let res = PyTrans.to_module ~sourcefile "$toplevel::main" code in @@ -79,7 +79,7 @@ x = 42 y = 10 print(x + y) |} in - Py.initialize ~version:3 ~minor:8 () ; + Py.initialize ~interpreter:Version.python_exe () ; let code = FFI.from_string ~source ~filename:"dummy" in Py.finalize () ; let res = PyTrans.to_module ~sourcefile "$toplevel::main" code in @@ -132,7 +132,7 @@ z = my_fun(42, a) print(z) |} in - Py.initialize ~version:3 ~minor:8 () ; + Py.initialize ~interpreter:Version.python_exe () ; let code = FFI.from_string ~source ~filename:"dummy" in Py.finalize () ; let res = PyTrans.to_module ~sourcefile "$toplevel::main" code in @@ -197,7 +197,7 @@ update_global() print(z) |} in - Py.initialize ~version:3 ~minor:8 () ; + Py.initialize ~interpreter:Version.python_exe () ; let code = FFI.from_string ~source ~filename:"dummy" in Py.finalize () ; let res = PyTrans.to_module ~sourcefile "$toplevel::main" code in diff --git a/infer/src/python/unit/dune b/infer/src/python/unit/dune deleted file mode 100644 index 5ec227efa45..00000000000 --- a/infer/src/python/unit/dune +++ /dev/null @@ -1,23 +0,0 @@ -; Copyright (c) Facebook, Inc. and its affiliates. -; -; This source code is licensed under the MIT license found in the -; LICENSE file in the root directory of this source tree. - -(library - (name PythonFrontendTest) - (flags - (:standard - -open - IStdlib - -open - IStd - -open - IBase - -open - PythonFrontend - -open - Textuallib)) - (libraries IStdlib IBase PythonFrontend Textuallib) - (preprocess - (pps ppx_deriving.std ppx_expect ppx_inline_test)) - (inline_tests)) diff --git a/infer/src/python/unit/dune.in b/infer/src/python/unit/dune.in new file mode 100644 index 00000000000..a4bd296d13d --- /dev/null +++ b/infer/src/python/unit/dune.in @@ -0,0 +1,35 @@ +(* -*- tuareg -*- *) +(* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) +(* NOTE: prepend dune.common to this file! *) + +let library = + Printf.sprintf + {| +(library + (name PythonFrontendTest) + (flags + (:standard + -open + IStdlib + -open + IStd + -open + IBase + -open + PythonFrontend + -open + Textuallib)) + (libraries IStdlib IBase PythonFrontend Textuallib) + (preprocess + (pps ppx_deriving.std ppx_expect ppx_inline_test)) + %s +) +|} (if python then "(inline_tests)" else "") +;; + +Jbuild_plugin.V1.send library