Summary: Horrible but somewhat-documented hack to get inter-file dead code analysis for the OCaml code. Reviewed By: mbouaziz Differential Revision: D6724234 fbshipit-source-id: 3a5b9cdmaster
parent
4b1a7b1771
commit
34862f8d77
@ -0,0 +1,135 @@
|
||||
# Copyright (c) 2018 - 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.
|
||||
|
||||
# Dead code detection: type `make` in this directory.
|
||||
#
|
||||
# OCaml will not detect dead code as soon as it gets exported in a .mli file. But, it will detect
|
||||
# dead code in inlined modules, even if they have a signature. This suggests the following idea,
|
||||
# which is basically what happens here:
|
||||
#
|
||||
# 1. Put all of the infer source code into a single .ml file with an empty .mli. Each file is put
|
||||
# inside its own inline module, with its original signature included too to avoid ambiguities in
|
||||
# case of locally opened modules (which may shadow more values than intended otherwise).
|
||||
# 2. Add preprocessor instructions so that OCaml warnings are shown in the original files.
|
||||
# 3. Suppress warnings in source code copied from generated files (atdgen, lexers, and parsers).
|
||||
# 3. Run the OCaml compiler.
|
||||
# 4. Kill detected dead code; repeat until dry.
|
||||
#
|
||||
# Infer is first compiled so that atdgen, ocamllex, and menhir generate the appropriate files. The
|
||||
# ocamllex and menhir files are generated inside ../_build by jbuilder, and copied here by this
|
||||
# Makefile.
|
||||
#
|
||||
# ocamldepend is used to `cat` the source files in the right order into all_infer_in_one_file.ml.
|
||||
#
|
||||
# Beware that this is mostly a terrible hack.
|
||||
|
||||
ROOT_DIR = ../../..
|
||||
include $(ROOT_DIR)/Makefile.config
|
||||
|
||||
default: detect_dead_code
|
||||
|
||||
ml_src_files_from_mlly:=$(shell find .. -not -path "../*stubs*" -regex '\.\./[a-zA-Z].*\.ml\(l\|y\)')
|
||||
|
||||
ml_src_files:=$(shell cd .. && find . -not -path "./*stubs*" -regex '\./[a-zA-Z].*\.ml\(i\)*')
|
||||
|
||||
ml_src_files_without_mli:=$(shell cd .. && for i in */*.ml */*/*.ml; do [ -f $${i}i ] || echo $$i; done)
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
cd .. && \
|
||||
ocamldep -native \
|
||||
-I IR -I absint -I atd -I backend -I base -I bufferoverrun -I checkers \
|
||||
-I clang -I concurrency -I eradicate -I facebook -I integration -I istd \
|
||||
-I java -I labs -I python -I quandary -I unit -I unit/clang -I deadcode \
|
||||
$(ml_src_files) > deadcode/.depend
|
||||
|
||||
%.cmi:
|
||||
# deal with the .ml *and* the .mli at the same time: when all the modules are inlined in one
|
||||
# file, you need the module value to be defined before you can refer to it, even in
|
||||
# signatures. Because of this, the order given by ocamldep is not enough to avoid "Unbound
|
||||
# module MyModule" errors in the case of signatures referring to other modules.
|
||||
$(QUIET)echo "(* START OF SIGNATURE $*.mli *)" >> all_infer_in_one_file.ml
|
||||
# put too many spaces in general but you never know what the contents of the file is;
|
||||
# sometimes spaces will be needed
|
||||
$(QUIET)echo " module type " >> all_infer_in_one_file.ml
|
||||
# suppress some warnings for generated code
|
||||
$(QUIET)if [[ $@ =~ (atd|deadcode)/ ]]; then echo ' [@warning "-27-32-34-35-39"] ' >> all_infer_in_one_file.ml; fi
|
||||
# compute module name from file name: capitalize first letter
|
||||
$(QUIET)echo $(shell basename $*) | sed -e "s/\b\(.\)/ \u\1/g" >> all_infer_in_one_file.ml
|
||||
$(QUIET)echo " = sig " >> all_infer_in_one_file.ml
|
||||
# pre-processor directive to get errors in the original files and not in all_infer_in_one_file.ml
|
||||
$(QUIET)echo '# 1 "$*.mli"' >> all_infer_in_one_file.ml
|
||||
cat ../$*.mli >> all_infer_in_one_file.ml
|
||||
$(QUIET)echo " end " >> all_infer_in_one_file.ml
|
||||
$(QUIET)echo "(* END OF SIGNATURE $*.mli *)" >> all_infer_in_one_file.ml
|
||||
$(QUIET)echo >> all_infer_in_one_file.ml
|
||||
# lots of duplication from above, sorry
|
||||
$(QUIET)echo "(* START OF MODULE $*.ml *)" >> all_infer_in_one_file.ml
|
||||
$(QUIET)echo " module " >> all_infer_in_one_file.ml
|
||||
$(QUIET)if [[ $@ =~ (atd|deadcode)/ ]]; then echo ' [@warning "-27-32-34-35-39"] ' >> all_infer_in_one_file.ml; fi
|
||||
$(QUIET)echo $(shell basename $*) | sed -e "s/\b\(.\)/ \u\1/g" >> all_infer_in_one_file.ml
|
||||
$(QUIET)echo " : " >> all_infer_in_one_file.ml
|
||||
$(QUIET)echo $(shell basename $*) | sed -e "s/\b\(.\)/ \u\1/g" >> all_infer_in_one_file.ml
|
||||
$(QUIET)echo " = struct " >> all_infer_in_one_file.ml
|
||||
$(QUIET)echo '# 1 "$*.ml"' >> all_infer_in_one_file.ml
|
||||
cat ../$*.ml >> all_infer_in_one_file.ml
|
||||
$(QUIET)echo " end " >> all_infer_in_one_file.ml
|
||||
$(QUIET)echo "(* END OF MODULE $*.ml *)" >> all_infer_in_one_file.ml
|
||||
$(QUIET)echo >> all_infer_in_one_file.ml
|
||||
|
||||
$(ml_src_files_without_mli:.ml=.cmx):
|
||||
# again mostly duplicated from above
|
||||
$(QUIET)echo "(* START OF MODULE $(@) *)" >> all_infer_in_one_file.ml
|
||||
$(QUIET)echo " module " >> all_infer_in_one_file.ml
|
||||
$(QUIET)if [[ $@ =~ (atd|deadcode)/ ]]; then echo ' [@warning "-27-32-34-35-39"] ' >> all_infer_in_one_file.ml; fi
|
||||
$(QUIET)echo $(shell basename $@ .cmx) | sed -e "s/\b\(.\)/ \u\1/g" >> all_infer_in_one_file.ml
|
||||
$(QUIET)echo " = struct " >> all_infer_in_one_file.ml
|
||||
$(QUIET)echo "# 1 \"$$(echo $@ | sed -e 's/\.cmx$$/.ml/')\"" >> all_infer_in_one_file.ml
|
||||
cat ../$$(echo $@ | sed -e "s/\.cmx$$/.ml/") >> all_infer_in_one_file.ml
|
||||
$(QUIET)echo " end " >> all_infer_in_one_file.ml
|
||||
$(QUIET)echo "(* END OF MODULE $@ *)" >> all_infer_in_one_file.ml
|
||||
$(QUIET)echo >> all_infer_in_one_file.ml
|
||||
|
||||
-include .depend
|
||||
|
||||
.PHONY: all_infer_in_one_file.ml
|
||||
all_infer_in_one_file.ml: backend/infer.cmx unit/inferunit.cmx facebook/InferCreateTraceViewLinks.cmx
|
||||
$(QUIET)echo "see results in all_infer_in_one_file.ml"
|
||||
|
||||
.PHONY: detect_dead_code
|
||||
detect_dead_code:
|
||||
$(MAKE) clean
|
||||
# needed to get jbuild generated, and the generated code for the lexers and parsers in ../_build
|
||||
$(MAKE) -C .. byte
|
||||
# copy generated source files from ../_build
|
||||
for file in $(ml_src_files_from_mlly); do \
|
||||
set +e; \
|
||||
[ -f "../_build/default/$$(basename $$file .mly).ml" ] && \
|
||||
$(COPY) ../_build/default/$$(basename $$file .mly).ml .; \
|
||||
[ -f "../_build/default/$$(basename $$file .mly).mli" ] && \
|
||||
$(COPY) ../_build/default/$$(basename $$file .mly).mli .; \
|
||||
[ -f "../_build/default/$$(basename $$file .mll).ml" ] && \
|
||||
$(COPY) ../_build/default/$$(basename $$file .mll).ml .; \
|
||||
[ -f "../_build/default/$$(basename $$file .mll).mli" ] && \
|
||||
$(COPY) ../_build/default/$$(basename $$file .mll).mli .; \
|
||||
set -e; \
|
||||
done
|
||||
$(REMOVE) all_infer_in_one_file.ml
|
||||
$(MAKE) depend
|
||||
# Need to be sequential to avoid getting a garbled file. Need to re-include .depend as it may
|
||||
# have changed, so run another `make`.
|
||||
$(MAKE) -j 1 all_infer_in_one_file.ml
|
||||
# build and get dead code warnings
|
||||
jbuilder build ../_build/default/deadcode/all_infer_in_one_file.bc
|
||||
# be paranoid about cleaning because we do not want to include infer_in_one_file into infer by
|
||||
# accident and I don't know enough jbuilder to be positive that it won't happen
|
||||
$(MAKE) clean
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
$(REMOVE) .depend *.ml *.mli jbuild
|
||||
touch all_infer_in_one_file.mli all_infer_in_one_file.ml
|
@ -0,0 +1,18 @@
|
||||
(* -*- tuareg -*- *)
|
||||
(* NOTE: prepend jbuild.common to this file! *)
|
||||
|
||||
;; Format.sprintf
|
||||
{|
|
||||
(executable
|
||||
((name all_infer_in_one_file)
|
||||
(flags (%s))
|
||||
(ocamlopt_flags (%s))
|
||||
(libraries (%s))
|
||||
(modules (All_infer_in_one_file))
|
||||
(preprocess (pps (ppx_compare ppx_sexp_conv -no-check)))
|
||||
))
|
||||
|}
|
||||
(String.concat " " common_cflags)
|
||||
(String.concat " " common_optflags)
|
||||
(String.concat " " common_libraries)
|
||||
|> Jbuild_plugin.V1.send
|
@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2018 - 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.
|
||||
|
||||
|
||||
# Dead code detection for infer's source code. See comments in infer/src/deadcode/Makefile.
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
set -u
|
||||
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
make -C "$SCRIPT_DIR"/../infer/src/deadcode
|
Loading…
Reference in new issue