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