|
|
|
#!/usr/bin/env bash
|
|
|
|
|
|
|
|
# Convenience script to build Infer when using opam
|
|
|
|
|
|
|
|
# Copyright (c) 2015 - 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.
|
|
|
|
|
|
|
|
set -e
|
|
|
|
|
|
|
|
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
|
|
INFER_ROOT="$SCRIPT_DIR"
|
|
|
|
INFER_DEPS_DIR="$INFER_ROOT/dependencies/infer-deps"
|
|
|
|
PLATFORM="$(uname)"
|
|
|
|
NCPU="$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1)"
|
[build] switch to 4.05.0+flambda by default
Summary:
With flambda (`-O3`), compilation time is ~5x slower, but the backend is ~25% faster!
To mitigate the atrocious compilation times, introduce a new `opt` build mode in the jbuilder files.
- build in "opt" mode by default from the toplevel (so that install scripts and external users get the fastest infer by default), in "default" mode by default from infer/src (since the latter is only called directly by infer devs, for faster builds)
- `make byte` is as fast as before in any mode
- `make test` will build "opt" by default, which is very slow. Solution for testing (or building the models) locally: `make BUILD_MODE=default test`.
- You can even change the default locally with `export BUILD_MODE=default`.
The benchmarks are to be taken with a sizable pinch of salt because I ran them only once and other stuff could be running in the background. That said, the perf win is consistent across all projects, with 15-20% win in wallclock time and around 25% win in total CPU time, ~9% win in sys time, and ~25% fewer minor allocations, and ~5-10% fewer overall allocations. This is only for the backend; the capture is by and large unaffected (either the same or a tad faster within noise range).
Here are the results running on OpenSSL 1.0.2d on osx (12 cores, 32G RAM)
=== base
infer binary: 26193088 bytes
compile time: 40s
capture:
```lang=text
real 1m7.513s
user 3m11.437s
sys 0m55.236s
```
analysis:
```lang=text
real 5m41.580s
user 61m37.855s
sys 1m12.870s
```
Memory profile:
```lang=json
{
...
"minor_gb": 0.1534719169139862,
"promoted_gb": 0.0038930922746658325,
"major_gb": 0.4546157643198967,
"allocated_gb": 0.6041945889592171,
"minor_collections": 78,
"major_collections": 23,
"compactions": 7,
"top_heap_gb": 0.07388687133789062,
"stack_kb": 0.3984375,
"minor_heap_kb": 8192.0,
...
}
```
=== flambda with stock options (no `-Oclassic`, just the same flags as base)
Exactly the same as base.
=== flambda `-O3`
infer binary: 56870376 bytes (2.17x bigger)
compile time: 191s (4.78x slower)
capture is the same as base:
```lang=text
real 1m9.203s
user 3m12.242s
sys 0m58.905s
```
analysis is ~20% wallclock time faster, ~25% CPU time faster:
```lang=text
real 4m32.656s
user 46m43.987s
sys 1m2.424s
```
memory usage is a bit lower too:
```lang=json
{
...
"minor_gb": 0.11583046615123749, // 75% of previous
"promoted_gb": 0.00363825261592865, // 93% of previous
"major_gb": 0.45415670424699783, // about same
"allocated_gb": 0.5663489177823067, // 94% of previous
"minor_collections": 73,
"major_collections": 22,
"compactions": 7,
"top_heap_gb": 0.07165145874023438,
"stack_kb": 0.3359375,
"minor_heap_kb": 8192.0,
...
}
```
=== flambda `-O2`
Not nearly as exciting as `-O3`, but the compilation cost is still quite high:
infer: 37826856 bytes
compilation of infer: 100s
Capture and analysis timings are mostly the same as base.
Reviewed By: jberdine
Differential Revision: D4867979
fbshipit-source-id: 99230b7
7 years ago
|
|
|
OCAML_VERSION=${OCAML_VERSION:-"4.05.0+flambda"}
|
|
|
|
OPAM_LOCK_URL=${OPAM_LOCK_URL:-"https://github.com/rgrinberg/opam-lock"}
|
|
|
|
INFER_OPAM_SWITCH_DEFAULT=infer-"$OCAML_VERSION"
|
|
|
|
|
|
|
|
function usage() {
|
|
|
|
echo "Usage: $0 [-y] [targets]"
|
|
|
|
echo
|
|
|
|
echo " targets:"
|
|
|
|
echo " all build everything (default)"
|
|
|
|
echo " clang build C and Objective-C analyzer"
|
|
|
|
echo " java build Java analyzer"
|
|
|
|
echo
|
|
|
|
echo " options:"
|
|
|
|
echo " -h,--help show this message"
|
|
|
|
echo " --no-opam-lock do not use the opam.lock file and let opam resolve dependencies"
|
|
|
|
echo " --only-setup-opam initialize opam, install the opam dependencies of infer, and exit"
|
|
|
|
echo " --opam-switch specify the opam switch where to install infer (default: $INFER_OPAM_SWITCH_DEFAULT)"
|
|
|
|
echo " -y,--yes automatically agree to everything"
|
|
|
|
echo
|
|
|
|
echo " examples:"
|
|
|
|
echo " $0 # build Java and C/Objective-C analyzers"
|
|
|
|
echo " $0 java clang # equivalent way of doing the above"
|
|
|
|
echo " $0 java # build only the Java analyzer"
|
|
|
|
}
|
|
|
|
|
|
|
|
# arguments
|
|
|
|
BUILD_CLANG=${BUILD_CLANG:-no}
|
|
|
|
BUILD_JAVA=${BUILD_JAVA:-no}
|
|
|
|
INTERACTIVE=${INTERACTIVE:-yes}
|
|
|
|
ONLY_SETUP_OPAM=${ONLY_SETUP_OPAM:-no}
|
|
|
|
INFER_OPAM_SWITCH=${INFER_OPAM_SWITCH:-$INFER_OPAM_SWITCH_DEFAULT}
|
|
|
|
USE_OPAM_LOCK=${USE_OPAM_LOCK:-yes}
|
|
|
|
ORIG_ARGS="$*"
|
|
|
|
|
|
|
|
while [[ $# > 0 ]]; do
|
|
|
|
opt_key="$1"
|
|
|
|
case $opt_key in
|
|
|
|
all)
|
|
|
|
BUILD_CLANG=yes
|
|
|
|
BUILD_JAVA=yes
|
|
|
|
shift
|
|
|
|
continue
|
|
|
|
;;
|
|
|
|
clang)
|
|
|
|
BUILD_CLANG=yes
|
|
|
|
shift
|
|
|
|
continue
|
|
|
|
;;
|
|
|
|
java)
|
|
|
|
BUILD_JAVA=yes
|
|
|
|
shift
|
|
|
|
continue
|
|
|
|
;;
|
|
|
|
-h|--help)
|
|
|
|
usage
|
|
|
|
exit 0
|
|
|
|
;;
|
|
|
|
--no-opam-lock)
|
|
|
|
USE_OPAM_LOCK=no
|
|
|
|
shift
|
|
|
|
continue
|
|
|
|
;;
|
|
|
|
--opam-switch)
|
|
|
|
shift
|
|
|
|
[[ $# > 0 ]] || (usage; exit 1)
|
|
|
|
INFER_OPAM_SWITCH="$1"
|
|
|
|
shift
|
|
|
|
continue
|
|
|
|
;;
|
|
|
|
--only-setup-opam)
|
|
|
|
ONLY_SETUP_OPAM=yes
|
|
|
|
shift
|
|
|
|
continue
|
|
|
|
;;
|
|
|
|
-y|--yes)
|
|
|
|
INTERACTIVE=no
|
|
|
|
shift
|
|
|
|
continue
|
|
|
|
;;
|
|
|
|
*)
|
|
|
|
usage
|
|
|
|
exit 1
|
|
|
|
esac
|
|
|
|
shift
|
|
|
|
done
|
|
|
|
|
|
|
|
# if no arguments then build both clang and Java
|
|
|
|
if [ "$BUILD_CLANG" = "no" ] && [ "$BUILD_JAVA" = "no" ]; then
|
|
|
|
BUILD_CLANG=yes
|
|
|
|
BUILD_JAVA=yes
|
|
|
|
fi
|
|
|
|
|
|
|
|
# enable --yes option for some commands in non-interactive mode
|
|
|
|
YES=
|
|
|
|
if [ "$INTERACTIVE" = "no" ]; then
|
|
|
|
YES=--yes
|
|
|
|
fi
|
|
|
|
# --yes by default for opam commands
|
|
|
|
export OPAMYES=1
|
|
|
|
|
|
|
|
check_installed () {
|
|
|
|
local cmd=$1
|
|
|
|
if ! which $cmd >/dev/null 2>&1; then
|
|
|
|
echo "dependency not found: $cmd" >&2
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
opam_retry () {
|
|
|
|
"$@" || ( \
|
|
|
|
echo >&2; \
|
|
|
|
printf '*** `%s` failed\n' "$*" >&2; \
|
|
|
|
echo '*** Updating opam then retrying' >&2; \
|
|
|
|
opam update && \
|
|
|
|
"$@" || ( \
|
|
|
|
echo >&2; \
|
|
|
|
printf '*** ERROR: `%s` failed\n' "$*" >&2; \
|
|
|
|
exit 1 \
|
|
|
|
) \
|
|
|
|
) \
|
|
|
|
}
|
|
|
|
|
|
|
|
setup_opam () {
|
|
|
|
opam_retry opam init --compiler=$OCAML_VERSION -j $NCPU --no-setup
|
|
|
|
if [ "$INFER_OPAM_SWITCH" = "$INFER_OPAM_SWITCH_DEFAULT" ]; then
|
|
|
|
opam_retry opam switch set -j $NCPU "$INFER_OPAM_SWITCH" --alias-of $OCAML_VERSION
|
|
|
|
else
|
|
|
|
opam_retry opam switch set -j $NCPU "$INFER_OPAM_SWITCH"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
# Install and record the infer dependencies in opam. The main trick is to install the
|
|
|
|
# $INFER_DEPS_DIR directory instead of the much larger infer repository. That directory contains
|
|
|
|
# just enough to pretend it installs infer.
|
|
|
|
install_infer-deps () {
|
|
|
|
# remove previous infer-deps pin, which might have conflicting dependencies
|
|
|
|
opam pin remove infer-deps --no-action
|
|
|
|
INFER_TMP_DEPS_DIR=$(mktemp -d "$INFER_ROOT"/dependencies/infer-deps-XXXX)
|
|
|
|
INFER_TMP_PACKAGE_NAME="$(basename "$INFER_TMP_DEPS_DIR")"
|
|
|
|
cp -a "$INFER_DEPS_DIR"/* "$INFER_TMP_DEPS_DIR"
|
|
|
|
# give unique name to the package to force opam to recheck the dependencies are all installed
|
|
|
|
opam pin add --no-action "$INFER_TMP_PACKAGE_NAME" "$INFER_TMP_DEPS_DIR"
|
|
|
|
opam install -j $NCPU --deps-only "$INFER_TMP_PACKAGE_NAME"
|
|
|
|
opam pin remove "$INFER_TMP_PACKAGE_NAME"
|
|
|
|
rm -fr "$INFER_TMP_DEPS_DIR"
|
|
|
|
# pin infer so that opam doesn't violate its package constraints when the user does
|
|
|
|
# "opam upgrade"
|
|
|
|
opam pin add infer-deps "$INFER_DEPS_DIR"
|
|
|
|
}
|
|
|
|
|
|
|
|
# temporary: patch javalib and use local version as source of truth. can be removed once javalib
|
|
|
|
# creates a new release with the patch
|
|
|
|
# (https://gforge.inria.fr/tracker/index.php?func=detail&aid=21418&group_id=686&atid=2817)
|
|
|
|
install_patched_javalib() {
|
|
|
|
local javalib_dir="$INFER_ROOT"/dependencies/javalib
|
|
|
|
local unzipped_javalib_dir="$javalib_dir"/javalib-2.3.3
|
|
|
|
tar xvf "$javalib_dir"/javalib-2.3.3.tar.bz2 -C "$javalib_dir"
|
|
|
|
# apply the patch
|
|
|
|
patch -d "$unzipped_javalib_dir"/src < "$javalib_dir"/allow_empty_method.patch
|
|
|
|
opam pin add --no-action javalib "$unzipped_javalib_dir"
|
|
|
|
}
|
|
|
|
|
|
|
|
install_locked_deps() {
|
|
|
|
if ! opam lock 2> /dev/null; then
|
|
|
|
echo "opam-lock not found in the current switch, installing from '$OPAM_LOCK_URL'..." >&2
|
|
|
|
opam pin add -k git lock "$OPAM_LOCK_URL"
|
|
|
|
fi
|
|
|
|
opam lock --install < "$INFER_ROOT"/opam.lock
|
|
|
|
}
|
|
|
|
|
|
|
|
install_opam_deps() {
|
|
|
|
install_patched_javalib
|
|
|
|
if [ "$USE_OPAM_LOCK" = yes ]; then
|
|
|
|
install_locked_deps
|
|
|
|
else
|
|
|
|
install_infer-deps
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
echo "initializing opam... " >&2
|
|
|
|
check_installed opam
|
|
|
|
setup_opam
|
|
|
|
eval $(SHELL=bash opam config env --switch=$INFER_OPAM_SWITCH)
|
|
|
|
echo >&2
|
|
|
|
echo "installing infer dependencies; this can take up to 30 minutes... " >&2
|
|
|
|
opam_retry install_opam_deps
|
|
|
|
|
|
|
|
if [ "$ONLY_SETUP_OPAM" = "yes" ]; then
|
|
|
|
exit 0
|
|
|
|
fi
|
|
|
|
|
|
|
|
echo "preparing build... " >&2
|
|
|
|
if [ ! -f .release ]; then
|
|
|
|
if [ "$BUILD_CLANG" = "no" ]; then
|
|
|
|
SKIP_SUBMODULES=true ./autogen.sh > /dev/null
|
|
|
|
else
|
|
|
|
./autogen.sh > /dev/null
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
|
|
|
|
if [ "$BUILD_CLANG" = "no" ]; then
|
|
|
|
INFER_CONFIGURE_OPTS+=" --disable-c-analyzers"
|
|
|
|
fi
|
|
|
|
if [ "$BUILD_JAVA" = "no" ]; then
|
|
|
|
INFER_CONFIGURE_OPTS+=" --disable-java-analyzers"
|
|
|
|
fi
|
|
|
|
|
|
|
|
./configure $INFER_CONFIGURE_OPTS
|
|
|
|
|
|
|
|
if [ "$BUILD_CLANG" = "yes" ] && ! facebook-clang-plugins/clang/setup.sh --only-check-install; then
|
|
|
|
echo ""
|
|
|
|
echo " Warning: you are not using a release of Infer. The C and"
|
|
|
|
echo " Objective-C analyses require a custom clang to be compiled"
|
|
|
|
echo " now. This step takes ~30-60 minutes, possibly more."
|
|
|
|
echo ""
|
|
|
|
echo " To speed this along, you are encouraged to use a release of"
|
|
|
|
echo " Infer instead:"
|
|
|
|
echo ""
|
|
|
|
echo " http://fbinfer.com/docs/getting-started.html"
|
|
|
|
echo ""
|
|
|
|
echo " If you are only interested in analyzing Java programs, simply"
|
|
|
|
echo " run this script with only the \"java\" argument:"
|
|
|
|
echo ""
|
|
|
|
echo " $0 java"
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
confirm="n"
|
|
|
|
printf "Are you sure you want to compile clang? (y/N) "
|
|
|
|
if [ "$INTERACTIVE" = "no" ]; then
|
|
|
|
confirm="y"
|
|
|
|
echo "$confirm"
|
|
|
|
else
|
|
|
|
read confirm
|
|
|
|
fi
|
|
|
|
|
|
|
|
if [ "x$confirm" != "xy" ]; then
|
|
|
|
exit 0
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
|
|
|
|
make -j $NCPU || (
|
|
|
|
echo >&2
|
|
|
|
echo ' compilation failure; you can try running' >&2
|
|
|
|
echo >&2
|
|
|
|
echo ' make clean' >&2
|
|
|
|
echo " $0 $ORIG_ARGS" >&2
|
|
|
|
echo >&2
|
|
|
|
exit 1)
|
|
|
|
|
|
|
|
echo
|
|
|
|
echo "*** Success! Infer is now built in '$SCRIPT_PATH/infer/bin/'."
|
|
|
|
echo '*** Install infer on your system with `make install`.'
|
|
|
|
echo
|
|
|
|
echo '*** If you plan to hack on infer, check out CONTRIBUTING.md to setup your dev environment.'
|