Skip to content
Snippets Groups Projects
Makefile 89.75 KiB
##########################################################################
#                                                                        #
#  This file is part of Frama-C.                                         #
#                                                                        #
#  Copyright (C) 2007-2021                                               #
#    CEA (Commissariat à l'énergie atomique et aux énergies              #
#         alternatives)                                                  #
#                                                                        #
#  you can redistribute it and/or modify it under the terms of the GNU   #
#  Lesser General Public License as published by the Free Software       #
#  Foundation, version 2.1.                                              #
#                                                                        #
#  It is distributed in the hope that it will be useful,                 #
#  but WITHOUT ANY WARRANTY; without even the implied warranty of        #
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         #
#  GNU Lesser General Public License for more details.                   #
#                                                                        #
#  See the GNU Lesser General Public License version 2.1                 #
#  for more details (enclosed in the file licenses/LGPLv2.1).            #
#                                                                        #
##########################################################################

# This file is the main makefile of Frama-C.

FRAMAC_SRC=.
MAKECONFIG_DIR=share
PLUGIN_TESTS_LIST:=
include share/Makefile.common
include share/Makefile.dynamic_config.internal

#Check share/Makefile.config available
ifndef FRAMAC_ROOT_SRCDIR
$(error \
  "You should run ./configure first (or autoconf if there is no configure)")
endif

###################
# Frama-C Version #
###################

VERSION:=$(shell $(CAT) VERSION)
VERSION_SAFE=$(subst ~,-,$(VERSION))
VERSION_CODENAME:=$(shell $(CAT) VERSION_CODENAME)

###########################
# Global plugin variables #
###########################

# the directory where compiled plugin non-GUI files are stored
PLUGIN_TOP_LIB_DIR= $(PLUGIN_LIB_DIR)/top

# the directory where compiled plugin GUI files are stored
PLUGIN_GUI_LIB_DIR= $(PLUGIN_LIB_DIR)/gui

# the directory where the other Makefiles are
FRAMAC_SHARE	= share

# Shared lists between Makefile.plugin and Makefile :
# initialized them as "simply extended variables" (with :=)
# for a correct behavior of += (see section 6.6 of GNU Make manual)
PLUGIN_LIST	:=
PLUGIN_GENERATED_LIST:=
PLUGIN_CMO_LIST	:=
PLUGIN_CMX_LIST	:=
PLUGIN_META_LIST :=
PLUGIN_DYN_CMI_LIST :=
PLUGIN_DYN_CMO_LIST :=
PLUGIN_DYN_CMX_LIST :=
PLUGIN_INTERNAL_CMO_LIST:=
PLUGIN_INTERNAL_CMX_LIST:=
PLUGIN_GUI_CMO_LIST:=
PLUGIN_GUI_CMX_LIST:=
PLUGIN_DYN_DEP_GUI_CMO_LIST:=
PLUGIN_DYN_DEP_GUI_CMX_LIST:=
PLUGIN_DYN_GUI_CMO_LIST :=
PLUGIN_DYN_GUI_CMX_LIST :=
PLUGIN_TYPES_CMO_LIST :=
PLUGIN_TYPES_CMX_LIST :=
PLUGIN_DEP_LIST:=
PLUGIN_DOC_LIST :=
PLUGIN_DOC_DIRS :=
PLUGIN_DISTRIBUTED_LIST:=
PLUGIN_DIST_TARGET_LIST:=
PLUGIN_DIST_DOC_LIST:=
PLUGIN_BIN_DOC_LIST:=
PLUGIN_DIST_EXTERNAL_LIST:=
PLUGIN_DIST_TESTS_LIST:=
PLUGIN_DISTRIBUTED_NAME_LIST:=
MERLIN_PACKAGES:=

PLUGIN_HEADER_SPEC_LIST :=
PLUGIN_HEADER_DIRS_LIST :=
PLUGIN_HEADER_EXCEPTIONS_LIST :=
PLUGIN_CEA_PROPRIETARY_HEADERS_LIST :=
PLUGIN_CEA_PROPRIETARY_FILES_LIST :=

# default value used for HEADER_SPEC and PLUGIN_HEADER_SPEC
DEFAULT_HEADER_SPEC := headers/header_spec.txt
# default value used for HEADER_DIRS and PLUGIN_HEADER_DIRS
DEFAULT_HEADER_DIRS := headers
# default value used for HEADER_EXCEPTIONS and PLUGIN_HEADER_EXCEPTIONS
DEFAULT_HEADER_EXCEPTIONS := configure
# default value used for CEA_PROPRIETARY_FILES and PLUGIN_CEA_PROPRIETARY_FILES
DEFAULT_CEA_PROPRIETARY_FILES := tests/non-free/%
# default value used for CEA_PROPRIETARY_HEADERS
# and PLUGIN_CEA_PROPRIETARY_HEADERS
DEFAULT_CEA_PROPRIETARY_HEADERS := CEA_PROPRIETARY

MERLIN_PACKAGES:=

###############################
# Additional global variables #
###############################

# Directories containing some source code
SRC_DIRS= ptests $(PLUGIN_LIB_DIR) $(FRAMAC_SRC_DIRS)

# Directory containing source code documentation
DOC_DIR	= doc/code

# Source files to document
MODULES_TODOC=

# Directories to include when compiling
INCLUDES=$(addprefix -I ,$(FRAMAC_SRC_DIRS)) -I $(PLUGIN_LIB_DIR)
ifneq ($(ENABLE_GUI),no)
GUI_INCLUDES = $(addprefix -package ,$(LIBRARY_NAMES_GUI))
else
GUI_INCLUDES =
endif

# Files for which dependencies must be computed.
# Other files are added later in this Makefile.
FILES_FOR_OCAMLDEP+=$(PLUGIN_LIB_DIR)/*.mli

BFLAGS	= $(PACKAGES) $(FLAGS) $(DEBUG) $(INCLUDES) \
		$(FRAMAC_USER_BFLAGS)
OFLAGS	= $(PACKAGES) $(FLAGS) $(DEBUG) $(INCLUDES) -compact \
		$(FRAMAC_USER_OFLAGS)
BLINKFLAGS += -linkpkg $(BFLAGS) -linkall -custom
OLINKFLAGS += -linkpkg $(OFLAGS) -linkall

DOC_FLAGS= -charset utf8 -colorize-code -stars -m A $(PACKAGES) $(INCLUDES) $(GUI_INCLUDES)

ifneq ($(VERBOSEMAKE),yes)
DOC_FLAGS+= -hide-warnings
endif

# Files that depend on external libraries -namely Zarith- whose interface
# has not been explicitely declared -opaque, hence would trigger warning 58.
# This can't be solved by Frama-C itself, we can only wait for an update of
# said library.

NON_OPAQUE_DEPS:=

# Libraries generated by Frama-C
GEN_BYTE_LIBS=
GEN_OPT_LIBS=

# Libraries used in Frama-C
EXTRA_OPT_LIBS:=
PACKAGES = $(addprefix -package ,$(LIBRARY_NAMES))
BYTE_LIBS = $(GEN_BYTE_LIBS)
OPT_LIBS = $(EXTRA_OPT_LIBS)

OPT_LIBS+= $(GEN_OPT_LIBS)

ICONS:= $(addprefix share/,\
		frama-c.ico frama-c.png unmark.png \
		switch-on.png switch-off.png)

THEME_ICON_NAMES:= \
		never_tried.png \
		unknown.png \
		surely_valid.png \
		surely_invalid.png \
		considered_valid.png \
		valid_under_hyp.png \
		invalid_under_hyp.png \
		invalid_but_dead.png \
		unknown_but_dead.png \
		valid_but_dead.png \
		inconsistent.png \
		fold.png unfold.png

THEME_ICONS_DEFAULT:= \
  $(addprefix share/theme/default/,$(THEME_ICON_NAMES))
THEME_ICONS_COLORBLIND:= \
  $(addprefix share/theme/colorblind/,$(THEME_ICON_NAMES))
THEME_ICONS_FLAT:= \
  $(addprefix share/theme/flat/,$(THEME_ICON_NAMES))

ROOT_LIBC_DIR:= share/libc
LIBC_SUBDIRS:= sys netinet net arpa
LIBC_DIR:= $(ROOT_LIBC_DIR) $(addprefix $(ROOT_LIBC_DIR)/,$(LIBC_SUBDIRS))
LIBC_FILES:= \
	$(wildcard share/*.h share/*.c) \
	$(wildcard $(addsuffix /*.h,$(LIBC_DIR))) \
	$(wildcard $(addsuffix /*.c,$(LIBC_DIR)))

# Checks that all .h can be included multiple times.
ALL_LIBC_HEADERS:=$(wildcard share/*.h $(addsuffix /*.h,$(LIBC_DIR)))

check-libc: bin/toplevel.$(OCAMLBEST)$(EXE)
	@echo "checking libc..."; \
	 EXIT_VALUE=0; \
	 for file in $(filter-out share/builtin.h,$(ALL_LIBC_HEADERS)); do \
	   echo "#include \"$$file\"" > check-libc.c; \
	   echo "#include \"$$file\"" >> check-libc.c; \
	   FRAMAC_SHARE=share bin/toplevel.$(OCAMLBEST)$(EXE) \
	   -cpp-extra-args="-Ishare/libc -nostdinc" check-libc.c \
	       > $$(basename $$file .h).log 2>&1; \
	   if test $$? -ne 0; then \
	     if grep -q -e '#error "Frama-C:' $$file; then : ; \
	     else \
	       echo "$$file cannot be included twice. \
Output is in $$(basename $$file .h).log"; \
	       EXIT_VALUE=1; \
	     fi; \
	   else \
	     rm $$(basename $$file .h).log; \
	   fi; \
	 done; \
	 rm check-libc.c; \
	 exit $$EXIT_VALUE

clean-check-libc:
	$(RM) *.log

# Kernel files to be included in the distribution.
# Plug-ins should use PLUGIN_DISTRIB_EXTERNAL if they export something else
# than *.ml* files in their directory.
# NB: configure for the distribution is generated in the distrib directory
# itself, rather than copied: otherwise, it could include references to
# non-distributed plug-ins.
DISTRIB_FILES:=\
      $(wildcard bin/migration_scripts/*2*.sh) bin/local_export.sh      \
      bin/frama-c bin/frama-c.byte bin/frama-c-gui bin/frama-c-gui.byte \
      bin/frama-c-config bin/frama-c-script                             \
      share/frama-c.WIN32.rc share/frama-c.Unix.rc                      \
      $(ICONS) $(THEME_ICONS_DEFAULT) $(THEME_ICONS_COLORBLIND)         \
      $(THEME_ICONS_FLAT)                                               \
      man/frama-c.1 doc/README						\
      doc/code/docgen.ml                                                \
      $(wildcard doc/code/*.css) doc/code/intro_plugin.txt              \
      doc/code/intro_plugin_D_and_S.txt                                 \
      doc/code/intro_plugin_default.txt                                 \
      doc/code/intro_kernel_plugin.txt					\
      doc/code/toc_head.htm                                             \
      doc/code/toc_tail.htm                                             \
      doc/Makefile                                                      \
      $(filter-out ptests/ptests_config.ml,$(wildcard ptests/*.ml*))   \
      configure.in Makefile Makefile.generating				\
      Changelog config.h.in						\
      VERSION VERSION_CODENAME $(wildcard licenses/*)                   \
      $(LIBC_FILES)							\
      share/analysis-scripts/analysis.mk                                \
      share/analysis-scripts/benchmark_database.py                      \
      share/analysis-scripts/build_callgraph.py                         \
      share/analysis-scripts/cmd-dep.sh                                 \
      share/analysis-scripts/concat-csv.sh                              \
      share/analysis-scripts/clone.sh                                   \
      share/analysis-scripts/creduce.sh                                 \
      share/analysis-scripts/detect_recursion.py                        \
      share/analysis-scripts/epilogue.mk                                \
      share/analysis-scripts/estimate_difficulty.py                     \
      share/analysis-scripts/fc_stubs.c                                 \
      share/analysis-scripts/find_fun.py                                \
      share/analysis-scripts/flamegraph.pl                              \
      share/analysis-scripts/frama_c_results.py                         \
      share/analysis-scripts/function_finder.py                         \
      share/analysis-scripts/git_utils.py                               \
      share/analysis-scripts/heuristic_list_functions.py                \
      share/analysis-scripts/list_files.py                              \
      share/analysis-scripts/list_functions.ml                          \
      share/analysis-scripts/make_template.py                           \
      share/analysis-scripts/make_wrapper.py                            \
      share/analysis-scripts/normalize_jcdb.py                          \
      share/analysis-scripts/parse-coverage.sh                          \
      share/analysis-scripts/print_callgraph.py                         \
      share/analysis-scripts/prologue.mk                                \
      share/analysis-scripts/README.md                                  \
      share/analysis-scripts/results_display.py                         \
      share/analysis-scripts/script_for_creduce_fatal.sh                \
      share/analysis-scripts/script_for_creduce_non_fatal.sh            \
      share/analysis-scripts/summary.py                                 \
      share/analysis-scripts/template.mk                                \
      $(wildcard share/emacs/*.el) share/autocomplete_frama-c           \
      share/_frama-c                                                    \
      share/compliance/c11_functions.json                               \
      share/compliance/c11_headers.json                                 \
      share/compliance/glibc_functions.json                             \
      share/compliance/nonstandard_identifiers.json                     \
      share/compliance/posix_identifiers.json                           \
      share/configure.ac                                                \
      share/Makefile.config.in share/Makefile.common                    \
      share/Makefile.generic						\
      share/Makefile.plugin.template share/Makefile.dynamic		\
      share/Makefile.dynamic_config.external				\
      share/Makefile.dynamic_config.internal				\
      share/META.frama-c                                                \
      $(filter-out src/kernel_internals/runtime/fc_config.ml,              \
	  $(wildcard src/kernel_internals/runtime/*.ml*))               \
      $(wildcard src/kernel_services/abstract_interp/*.ml*)             \
      $(wildcard src/plugins/gui/*.ml*)                                 \
      $(wildcard src/libraries/stdlib/*.ml*)                            \
      $(wildcard src/libraries/utils/*.ml*)                             \
      $(wildcard src/libraries/utils/*.c)                               \
      $(wildcard src/libraries/project/*.ml*)                           \
      $(filter-out src/kernel_internals/parsing/check_logic_parser.ml,  \
          $(wildcard src/kernel_internals/parsing/*.ml*))               \
      $(wildcard src/kernel_internals/typing/*.ml*)                     \
      $(wildcard src/kernel_services/ast_data/*.ml*)                    \
      $(wildcard src/kernel_services/ast_queries/*.ml*)                 \
			$(wildcard src/kernel_services/ast_building/*.ml*)                \
      $(wildcard src/kernel_services/ast_printing/*.ml*)                \
      $(wildcard src/kernel_services/cmdline_parameters/*.ml*)          \
      $(wildcard src/kernel_services/analysis/*.ml*)                    \
      $(wildcard src/kernel_services/ast_transformations/*.ml*)         \
      $(wildcard src/kernel_services/plugin_entry_points/*.ml*)         \
      $(wildcard src/kernel_services/visitors/*.ml*)                    \
      $(wildcard src/kernel_services/parsetree/*.ml*)                   \
      $(wildcard src/libraries/datatype/*.ml*)                          \
      bin/sed_get_make_major bin/sed_get_make_minor                     \
      INSTALL.md README.md .make-clean	                        \
      .make-clean-stamp .force-reconfigure 	\
      opam/opam \

# Test files to be included in the distribution (without header checking).
# Plug-ins should use PLUGIN_DISTRIB_TESTS to export their test files.
DISTRIB_TESTS=$(shell find \
                  tests \
                  src/plugins/aorai/tests \
                  src/plugins/report/tests \
                  src/plugins/wp/tests \
                  -type d -name result -prune -false \
                  -o -type d -name 'result_*' -prune -false \
                  -o -path 'tests/crowbar/*' -type d \! -name input -prune -false \
                  -o -type f \! -name "*\.log" \! -name "*\.o" \
                    \! -name '*~' \! -name "*\.cm*"  \! -name "*.sav" \
                    -perm /u+w | sed -e 's/ /@/g')

# files that are needed to compile API documentation of external plugins
DOC_GEN_FILES:=$(addprefix doc/code/,\
	*.css intro_plugin.txt intro_kernel_plugin.txt \
	intro_plugin_default.txt intro_plugin_D_and_S \
	kernel-doc.ocamldoc \
	docgen.ml docgen.cm* *.htm)
################
# Main targets #
################

# additional compilation targets for 'make all'.
# cannot be delayed after 'make all'
EXTRAS	= ptests

ifneq ($(ENABLE_GUI),no)
ifeq ($(HAS_LABLGTK),yes)
EXTRAS	+= gui
endif
endif

all:: byte $(OCAMLBEST) $(EXTRAS) plugins_ptests_config

.PHONY: top opt byte dist bdist rebuild rebuild-branch

dist: clean
	$(QUIET_MAKE) all

clean-rebuild: clean
	$(QUIET_MAKE) all

rebuild: config.status
	$(MAKE) smartclean
	$(MAKE) depend $(FRAMAC_PARALLEL)
	$(MAKE) all $(FRAMAC_PARALLEL) || \
		(touch .force-reconfigure; \
		 $(MAKE) config.status && \
		 $(MAKE) depend $(FRAMAC_PARALLEL) && \
		 $(MAKE) all $(FRAMAC_PARALLEL))

sinclude .Makefile.user
# Should define FRAMAC_PARALLEL, FRAMAC_USER_FLAGS, FRAMAC_USER_MERLIN_FLAGS

#Create link in share for local execution if
.PHONY:create_share_link
create_share_link: share/.gitignore

# note: when using opam pin path in a cloned Frama-C git, the symbolic links
# become directories, so a different command is necessary for each situation

share/.gitignore: share/Makefile.config
	if test -f $@; then \
	  for link in $$(cat $@); do \
	    if test -L share$$link; then \
	      rm -f share$$link \
	    else \
	      rm -rf share$$link; \
	    fi; \
	  done; \
	fi
	$(RM) $@.tmp
	touch $@.tmp
	$(foreach dir,$(EXTERNAL_PLUGINS),\
		if test -d $(dir)/share; then \
			echo "Sharing $(dir)/link"; \
			ln -s $(realpath $(dir)/share) share/$(notdir $(dir)); \
			echo /$(notdir $(dir)) >> $@.tmp; \
		fi; )
	mv $@.tmp $@

ifeq ("$(DEVELOPMENT)","yes")
all:: share/.gitignore
endif

clean_share_link:
	if test -f share/.gitignore; then \
	  for link in $$(cat share/.gitignore); do \
	    if test -L share$$link; then \
	      rm -f share$$link \
	    else \
	      rm -rf share$$link; \
	    fi; \
	  done; \
	  rm share/.gitignore; \
	fi

clean:: clean_share_link

##################
# Frama-C Kernel #
##################

# Libraries which could be compiled fully independently
#######################################################

VERY_FIRST_CMO = src/kernel_internals/runtime/frama_c_init.cmo
CMO	+= $(VERY_FIRST_CMO)

LIB_CMO =\
        src/libraries/stdlib/transitioning \
	src/libraries/stdlib/FCHashtbl \
	src/libraries/stdlib/extlib \
	src/libraries/datatype/unmarshal \
	src/libraries/datatype/unmarshal_z \
	src/libraries/datatype/structural_descr \
	src/libraries/datatype/type \
	src/libraries/datatype/descr \
	src/libraries/utils/filepath \
	src/libraries/utils/sanitizer \
	src/libraries/utils/pretty_utils \
	src/libraries/utils/hook \
	src/libraries/utils/bag \
	src/libraries/utils/wto \
	src/libraries/utils/vector \
	src/libraries/utils/indexer \
	src/libraries/utils/rgmap \
	src/libraries/utils/bitvector \
	src/libraries/utils/qstack \
	src/libraries/stdlib/integer \
	src/libraries/utils/json \
	src/libraries/utils/markdown \
	src/libraries/utils/rich_text \
	src/libraries/utils/dotgraph

NON_OPAQUE_DEPS+=\
  src/libraries/datatype/unmarshal_z \
  src/libraries/stdlib/integer

LIB_CMO:= $(addsuffix .cmo,$(LIB_CMO))
CMO	+= $(LIB_CMO)

# Very first files to be linked (most modules use them)
###############################

FIRST_CMO= src/kernel_internals/runtime/fc_config \
	src/kernel_internals/runtime/gui_init \
	src/kernel_services/plugin_entry_points/log \
	src/kernel_services/cmdline_parameters/cmdline \
	src/libraries/project/project_skeleton \
	src/libraries/datatype/datatype \
	src/kernel_services/plugin_entry_points/journal

# project_skeleton requires log
# datatype requires project_skeleton
# rangemap requires datatype

FIRST_CMO:= $(addsuffix .cmo,$(FIRST_CMO))
CMO	+= $(FIRST_CMO)

#Project (Project_skeleton must be linked before Journal)
PROJECT_CMO= \
	state \
	state_dependency_graph \
	state_topological \
	state_selection \
	project \
	state_builder
PROJECT_CMO:= $(patsubst %,src/libraries/project/%.cmo,$(PROJECT_CMO))
CMO	+= $(PROJECT_CMO)

# kernel
########

KERNEL_CMO=\
	src/libraries/utils/utf8_logic.cmo                              \
	src/libraries/utils/binary_cache.cmo                            \
	src/libraries/utils/hptmap.cmo                                  \
	src/libraries/utils/hptset.cmo                                  \
	src/libraries/utils/escape.cmo                                  \
	src/kernel_services/ast_queries/cil_datatype.cmo             \
	src/kernel_services/cmdline_parameters/typed_parameter.cmo      \
	src/kernel_services/plugin_entry_points/dynamic.cmo             \
	src/kernel_services/cmdline_parameters/parameter_category.cmo   \
	src/kernel_services/cmdline_parameters/parameter_customize.cmo  \
	src/kernel_services/cmdline_parameters/parameter_state.cmo      \
	src/kernel_services/cmdline_parameters/parameter_builder.cmo    \
	src/kernel_services/plugin_entry_points/plugin.cmo              \
	src/kernel_services/plugin_entry_points/kernel.cmo              \
	src/libraries/utils/unicode.cmo                                 \
	src/kernel_services/plugin_entry_points/emitter.cmo             \
	src/libraries/utils/floating_point.cmo                          \
	src/libraries/utils/rangemap.cmo                                \
	src/kernel_services/ast_printing/cil_types_debug.cmo            \
	src/kernel_services/ast_printing/printer_builder.cmo            \
	src/libraries/utils/cilconfig.cmo                               \
	src/kernel_internals/typing/alpha.cmo                         \
	src/kernel_services/ast_queries/cil_state_builder.cmo        \
	src/kernel_internals/runtime/machdeps.cmo                       \
	src/kernel_services/ast_queries/cil_const.cmo                \
	src/kernel_services/ast_queries/logic_env.cmo                \
	src/kernel_services/ast_queries/logic_const.cmo              \
	src/kernel_services/visitors/visitor_behavior.cmo		\
	src/kernel_services/ast_queries/cil.cmo                      \
	src/kernel_services/ast_queries/cil_builtins.cmo             \
	src/kernel_internals/parsing/errorloc.cmo                      \
	src/kernel_services/ast_printing/cil_printer.cmo                \
	src/kernel_services/ast_printing/cil_descriptive_printer.cmo    \
	src/kernel_services/parsetree/cabshelper.cmo                         \
	src/kernel_services/ast_queries/logic_utils.cmo              \
	src/kernel_services/ast_printing/logic_print.cmo                \
	src/kernel_internals/parsing/logic_parser.cmo                  \
	src/kernel_internals/parsing/logic_lexer.cmo                   \
	src/kernel_services/ast_queries/logic_typing.cmo             \
	src/kernel_services/ast_queries/acsl_extension.cmo				\
	src/kernel_services/ast_queries/ast_info.cmo                 \
	src/kernel_services/ast_data/ast.cmo                            \
	src/kernel_services/ast_printing/cprint.cmo                     \
	src/kernel_services/visitors/cabsvisit.cmo                      \
	src/kernel_internals/typing/cabs2cil.cmo                      \
	src/kernel_services/ast_data/globals.cmo                        \
	src/kernel_internals/typing/cfg.cmo                           \
	src/kernel_services/ast_data/kernel_function.cmo                \
	src/kernel_services/ast_data/property.cmo                       \
	src/kernel_services/ast_data/property_status.cmo                \
	src/kernel_services/ast_data/annotations.cmo                    \
	src/kernel_services/ast_printing/printer.cmo                    \
	src/kernel_internals/typing/logic_builtin.cmo                 \
	src/kernel_services/ast_printing/cabs_debug.cmo                 \
	src/kernel_internals/parsing/lexerhack.cmo                     \
	src/kernel_internals/parsing/clexer.cmo                        \
	src/kernel_internals/parsing/cparser.cmo                       \
	src/kernel_internals/parsing/logic_preprocess.cmo              \
	src/kernel_internals/typing/mergecil.cmo                      \
	src/kernel_internals/typing/rmtmps.cmo                        \
	src/kernel_internals/typing/oneret.cmo                        \
	src/kernel_internals/typing/frontc.cmo                        \
	src/kernel_internals/typing/substitute_const_globals.cmo        \
	src/kernel_services/analysis/ordered_stmt.cmo                   \
	src/kernel_services/analysis/wto_statement.cmo                  \
	src/kernel_services/analysis/dataflows.cmo                      \
	src/kernel_services/analysis/dataflow2.cmo                      \
	src/kernel_services/analysis/stmts_graph.cmo                    \
	src/kernel_services/analysis/dominators.cmo                     \
	src/kernel_services/analysis/service_graph.cmo                  \
	src/kernel_services/analysis/undefined_sequence.cmo             \
	src/kernel_services/analysis/interpreted_automata.cmo           \
	src/kernel_services/ast_data/alarms.cmo                         \
	src/kernel_services/ast_printing/description.cmo                \
	src/kernel_services/abstract_interp/lattice_messages.cmo        \
	src/kernel_services/abstract_interp/abstract_interp.cmo         \
	src/kernel_services/abstract_interp/bottom.cmo                  \
	src/kernel_services/abstract_interp/int_Base.cmo                \
	src/kernel_services/analysis/bit_utils.cmo                      \
	src/kernel_services/abstract_interp/fc_float.cmo                \
	src/kernel_services/abstract_interp/float_interval.cmo          \
	src/kernel_services/abstract_interp/fval.cmo                    \
	src/kernel_services/abstract_interp/int_interval.cmo            \
	src/kernel_services/abstract_interp/int_set.cmo                 \
	src/kernel_services/abstract_interp/int_val.cmo                 \
	src/kernel_services/abstract_interp/ival.cmo                    \
	src/kernel_services/abstract_interp/base.cmo                    \
	src/kernel_services/abstract_interp/origin.cmo                  \
	src/kernel_services/abstract_interp/map_lattice.cmo             \
	src/kernel_services/abstract_interp/tr_offset.cmo               \
	src/kernel_services/abstract_interp/offsetmap.cmo               \
	src/kernel_services/abstract_interp/int_Intervals.cmo           \
	src/kernel_services/abstract_interp/locations.cmo               \
	src/kernel_services/abstract_interp/lmap.cmo                    \
	src/kernel_services/abstract_interp/lmap_bitwise.cmo            \
	src/kernel_services/abstract_interp/multidim.cmo                \
	src/kernel_services/abstract_interp/abstract_offset.cmo         \
	src/kernel_services/abstract_interp/abstract_memory.cmo         \
	src/kernel_services/visitors/visitor.cmo                        \
	src/kernel_services/ast_data/statuses_by_call.cmo               \
	src/kernel_services/ast_printing/printer_tag.cmo                \
	$(PLUGIN_TYPES_CMO_LIST)                                        \
	src/kernel_services/plugin_entry_points/db.cmo                  \
	src/libraries/utils/command.cmo                                 \
	src/libraries/utils/task.cmo                                    \
	src/kernel_services/ast_queries/filecheck.cmo                \
	src/kernel_services/ast_queries/json_compilation_database.cmo   \
	src/kernel_services/ast_queries/file.cmo                     \
	src/kernel_internals/typing/translate_lightweight.cmo         \
	src/kernel_internals/typing/ghost_cfg.cmo			\
	src/kernel_internals/typing/ghost_accesses.cmo  		\
	src/kernel_internals/typing/allocates.cmo                     \
	src/kernel_internals/typing/unroll_loops.cmo                  \
	src/kernel_internals/typing/asm_contracts.cmo                 \
	src/kernel_services/analysis/loop.cmo                           \
	src/kernel_services/analysis/exn_flow.cmo                       \
        src/kernel_services/analysis/destructors.cmo                    \
	src/kernel_services/analysis/logic_interp.cmo                   \
	src/kernel_internals/typing/infer_annotations.cmo             \
	src/kernel_services/ast_transformations/clone.cmo                           \
	src/kernel_services/ast_transformations/filter.cmo                          \
	src/kernel_services/ast_transformations/inline.cmo              \
	src/kernel_internals/runtime/dump_config.cmo                    \
	src/kernel_services/ast_transformations/contract_special_float.cmo   \
	src/kernel_internals/runtime/special_hooks.cmo                  \
	src/kernel_internals/runtime/messages.cmo                       \
	src/kernel_services/ast_building/cil_builder.cmo                       \

CMO	+= $(KERNEL_CMO)

MLI_ONLY+=\
	src/libraries/utils/hptmap_sig.mli                                   \
	src/kernel_services/cmdline_parameters/parameter_sig.mli             \
	src/kernel_services/ast_data/cil_types.mli                           \
	src/kernel_services/parsetree/cabs.mli                               \
	src/kernel_services/parsetree/logic_ptree.mli                        \
	src/kernel_services/ast_printing/printer_api.mli                     \
	src/kernel_services/abstract_interp/float_sig.mli                    \
	src/kernel_services/abstract_interp/float_interval_sig.mli           \
	src/kernel_services/abstract_interp/lattice_type.mli                 \
	src/kernel_services/abstract_interp/eva_lattice_type.mli             \
	src/kernel_services/abstract_interp/int_Intervals_sig.mli            \
	src/kernel_services/abstract_interp/offsetmap_lattice_with_isotropy.mli \
	src/kernel_services/abstract_interp/offsetmap_sig.mli                \
	src/kernel_services/abstract_interp/lmap_sig.mli                     \
	src/kernel_services/abstract_interp/offsetmap_bitwise_sig.mli

MODULES_NODOC+= src/kernel_internals/runtime/machdep_ppc_32.ml \
	src/kernel_internals/runtime/machdep_x86_16.ml         \
	src/kernel_internals/runtime/machdep_x86_32.ml         \
	src/kernel_internals/runtime/machdep_x86_64.ml         \
	external/unmarshal_z.mli


GENERATED += $(addprefix src/kernel_internals/parsing/,\
		clexer.ml cparser.ml cparser.mli \
		logic_lexer.ml logic_parser.ml \
		logic_parser.mli logic_preprocess.ml)


.PHONY: check-logic-parser-wildcard
check-logic-parser-wildcard:
	cd src/kernel_internals/parsing && ocaml check_logic_parser.ml

NON_OPAQUE_DEPS+= src/kernel_services/plugin_entry_points/dynamic

# abstract_memory.cmi must _not_ inherit the '-rectypes' flag, so we
# eagerly assign it _before_ adding -rectypes to the .cmo/.cmx files
src/kernel_services/abstract_interp/abstract_memory.cmi: BFLAGS := $(BFLAGS)
src/kernel_services/abstract_interp/abstract_memory.cmo: BFLAGS += -rectypes
src/kernel_services/abstract_interp/abstract_memory.cmx: OFLAGS += -rectypes

# C Bindings
############

GEN_C_BINDINGS=src/libraries/utils/c_bindings.o
GEN_C_BINDINGS_FLAGS= -fPIC
GEN_BYTE_LIBS+= $(GEN_C_BINDINGS)
GEN_OPT_LIBS+= $(GEN_C_BINDINGS)

src/libraries/utils/c_bindings.o: src/libraries/utils/c_bindings.c
	$(PRINT_CC) $@
	$(CC) $(GEN_C_BINDINGS_FLAGS) -c -I$(call winpath,$(OCAMLLIB)) -O3 -Wall -o $@ $<

# Common startup module
# All link command should add it as last linked module and depend on it.
########################################################################

STARTUP_CMO=src/kernel_internals/runtime/boot.cmo
STARTUP_CMX=$(STARTUP_CMO:.cmo=.cmx)

# GUI modules
# See below for GUI compilation
##############################################################################

WTOOLKIT= \
	wutil widget wbox wfile wpane wpalette wtext wtable

ifeq ("$(LABLGTK_VERSION)","3")

src/plugins/gui/GSourceView.ml: src/plugins/gui/GSourceView3.ml.in
	$(CP) $< $@
	$(CHMOD_RO) $@

src/plugins/gui/GSourceView.mli: src/plugins/gui/GSourceView3.mli.in
	$(CP) $< $@
	$(CHMOD_RO) $@

else
src/plugins/gui/GSourceView.ml: src/plugins/gui/GSourceView2.ml.in
	$(CP) $< $@
	$(CHMOD_RO) $@

src/plugins/gui/GSourceView.mli: src/plugins/gui/GSourceView2.mli.in
	$(CP) $< $@
	$(CHMOD_RO) $@

endif

SOURCEVIEWCOMPAT:=GSourceView
GENERATED+=src/plugins/gui/GSourceView.ml src/plugins/gui/GSourceView.mli \
           src/plugins/gui/dgraph_helper.ml src/plugins/gui/gtk_compat.ml

ifeq ($(LABLGTK),lablgtk3)
src/plugins/gui/gtk_compat.ml: src/plugins/gui/gtk_compat.3.ml
	$(CP) $< $@
	$(CHMOD_RO) $@
else
src/plugins/gui/gtk_compat.ml: src/plugins/gui/gtk_compat.2.ml
	$(CP) $< $@
	$(CHMOD_RO) $@
endif
GENERATED+=src/plugins/gui/gtk_compat.ml

SINGLE_GUI_CMO:= \
	wutil_once \
	gtk_compat \
	$(WTOOLKIT) \
	$(SOURCEVIEWCOMPAT) \
	gui_parameters \
	gtk_helper \
        dgraph_helper \
        gtk_form \
	source_viewer pretty_source source_manager book_manager \
	warning_manager \
	filetree \
	launcher \
	menu_manager \
	history \
	gui_printers \
	design \
	analyses_manager file_manager project_manager \
	help_manager \
        $(DGRAPHFILES) \
        property_navigator \

SINGLE_GUI_CMO:= $(patsubst %,src/plugins/gui/%.cmo,$(SINGLE_GUI_CMO))

###############################################################################
#                                                                             #
####################                                                          #
# Plug-in sections #                                                          #
####################                                                          #
#                                                                             #
# For 'internal' developers:                                                  #
# you can add your own plug-in here,                                          #
# but it is better to have your own separated Makefile                        #
###############################################################################

###########
# Metrics #
###########

PLUGIN_ENABLE:=$(ENABLE_METRICS)
PLUGIN_NAME:=Metrics
PLUGIN_DISTRIBUTED:=yes
PLUGIN_DIR:=src/plugins/metrics
PLUGIN_CMO:= metrics_parameters css_html metrics_base metrics_acsl \
	     metrics_cabs metrics_cilast metrics_coverage \
	     register
PLUGIN_GUI_CMO:= metrics_gui register_gui
PLUGIN_DEPENDENCIES:=Eva
PLUGIN_INTERNAL_TEST:=yes
$(eval $(call include_generic_plugin_Makefile,$(PLUGIN_NAME)))

#############
# Callgraph #
#############

PLUGIN_ENABLE:=$(ENABLE_CALLGRAPH)
PLUGIN_NAME:=Callgraph
PLUGIN_DISTRIBUTED:=yes
PLUGIN_DIR:=src/plugins/callgraph
PLUGIN_CMO:= options journalize subgraph cg services uses register
ifeq ($(HAS_DGRAPH),yes)
PLUGIN_GUI_CMO:=cg_viewer
PLUGIN_GENERATED:=$(PLUGIN_DIR)/cg_viewer.ml
else
PLUGIN_GUI_CMO:=
endif
PLUGIN_DISTRIB_EXTERNAL:=cg_viewer.yes.ml
PLUGIN_CMI:= callgraph_api
PLUGIN_INTERNAL_TEST:=yes
PLUGIN_TESTS_DIRS:=callgraph
PLUGIN_TESTS_LIB:=tests/callgraph/function_pointer.ml
$(eval $(call include_generic_plugin_Makefile,$(PLUGIN_NAME)))


##################
# Evolved Value Analysis #
##################

PLUGIN_ENABLE:=$(ENABLE_EVA)
PLUGIN_NAME:=Eva
PLUGIN_DIR:=src/plugins/value
PLUGIN_EXTRA_DIRS:=engine values domains api domains/cvalue domains/apron \
	domains/gauges domains/equality legacy partitioning utils gui_files \
	api values/numerors domains/numerors
PLUGIN_TESTS_DIRS+=value/traces

# Files for the binding to Apron domains. Only available if Apron is available.
ifeq ($(HAS_APRON),yes)
PLUGIN_REQUIRES+= apron.octMPQ apron.boxMPQ apron.polkaMPQ apron.apron gmp
APRON_CMO:= domains/apron/apron_domain
else
APRON_CMO:=
PLUGIN_DISTRIB_EXTERNAL+= \
	domains/apron/apron_domain.ml domains/apron/apron_domain.mli
endif

# Files for the numerors domain. Only available is MPFR is available.
NUMERORS_FILES:= \
	values/numerors/numerors_utils values/numerors/numerors_float \
	values/numerors/numerors_interval values/numerors/numerors_arithmetics \
	values/numerors/numerors_value domains/numerors/numerors_domain

ifeq ($(HAS_MPFR),yes)
PLUGIN_REQUIRES+= gmp
PLUGIN_TESTS_DIRS+=value/numerors
NUMERORS_CMO:= $(NUMERORS_FILES)
else
# Do not compile numerors files, but include them in the distributed files.
NUMERORS_CMO:=
PLUGIN_DISTRIB_EXTERNAL+= $(addsuffix .ml,$(NUMERORS_FILES))
PLUGIN_DISTRIB_EXTERNAL+= $(addsuffix .mli,$(NUMERORS_FILES))
endif

# General rules for ordering files within PLUGIN_CMO:
# - try to keep the legacy Value before Eva
PLUGIN_CMO:= partitioning/split_strategy domains/domain_mode value_parameters \
	utils/eva_audit utils/value_perf utils/eva_annotations \
	utils/eva_dynamic utils/value_util utils/red_statuses \
	utils/mark_noresults \
	utils/widen_hints_ext utils/widen \
	partitioning/split_return \
	partitioning/per_stmt_slevel \
	utils/library_functions \
	utils/eval_typ utils/backward_formals \
	alarmset eval utils/structure utils/abstract \
	values/value_product values/location_lift \
	values/cvalue_forward values/cvalue_backward \
	values/main_values values/main_locations \
	values/offsm_value values/sign_value \
	legacy/eval_op legacy/function_args \
	domains/domain_store domains/domain_builder \
	domains/domain_product domains/domain_lift domains/unit_domain \
	domains/printer_domain \
	domains/traces_domain \
	domains/simple_memory \
	domains/octagons \
	domains/gauges/gauges_domain \
	domains/hcexprs \
	domains/equality/equality domains/equality/equality_domain \
	domains/offsm_domain \
	domains/multidim_domain \
	domains/symbolic_locs \
	domains/sign_domain \
	domains/cvalue/warn domains/cvalue/locals_scoping \
	domains/cvalue/cvalue_offsetmap \
	utils/value_results \
	utils/summary \
	domains/cvalue/builtins domains/cvalue/builtins_malloc \
	domains/cvalue/builtins_string domains/cvalue/builtins_misc \
	domains/cvalue/builtins_memory domains/cvalue/builtins_print_c \
	domains/cvalue/builtins_watchpoint \
	domains/cvalue/builtins_float domains/cvalue/builtins_split \
	domains/inout_domain \
	legacy/eval_terms legacy/eval_annots \
	domains/powerset engine/transfer_logic \
	domains/cvalue/cvalue_transfer domains/cvalue/cvalue_init \
	domains/cvalue/cvalue_specification \
	domains/cvalue/cvalue_domain \
	partitioning/auto_loop_unroll \
	partitioning/partition partitioning/partitioning_parameters \
	partitioning/partitioning_index partitioning/trace_partitioning \
	engine/subdivided_evaluation engine/evaluation engine/abstractions \
	engine/recursion engine/transfer_stmt engine/transfer_specification \
	engine/mem_exec engine/iterator engine/initialization \
	engine/compute_functions engine/analysis register \
	domains/taint_domain \
	$(APRON_CMO) $(NUMERORS_CMO) \
	api/general_requests api/values_request \
	utils/unit_tests
PLUGIN_CMI:= values/abstract_value values/abstract_location \
	domains/abstract_domain domains/simpler_domains
PLUGIN_DEPENDENCIES:=Server

# These files are used by the GUI, but do not depend on Lablgtk
VALUE_GUI_AUX:=gui_files/gui_types gui_files/gui_eval \
		gui_files/gui_callstacks_filters
PLUGIN_GUI_CMO:=$(VALUE_GUI_AUX) gui_files/gui_callstacks_manager \
		gui_files/gui_red gui_files/register_gui

PLUGIN_INTERNAL_TEST:= yes
PLUGIN_TESTS_LIB=tests/float/fval_test.ml
PLUGIN_DISTRIBUTED:=yes
VALUE_TYPES:=$(addprefix src/plugins/value_types/,\
		cilE cvalue precise_locs value_types widen_type)
PLUGIN_TYPES_CMO:=$(VALUE_TYPES)
PLUGIN_TYPES_TODOC:=$(addsuffix .mli,$(VALUE_TYPES))

$(eval $(call include_generic_plugin_Makefile,$(PLUGIN_NAME)))

#########
# Reduc #
#########
PLUGIN_ENABLE:=$(ENABLE_REDUC)
PLUGIN_DYNAMIC:=$(DYNAMIC_REDUC)
PLUGIN_NAME:=Reduc
PLUGIN_DISTRIBUTED:=yes
PLUGIN_DIR:=src/plugins/reduc
PLUGIN_CMO:=reduc_options misc value2acsl collect hyp register
PLUGIN_DEPENDENCIES:=Eva

$(eval $(call include_generic_plugin_Makefile,$(PLUGIN_NAME)))

##################
# Occurrence     #
##################

PLUGIN_ENABLE:=$(ENABLE_OCCURRENCE)
PLUGIN_NAME:=Occurrence
PLUGIN_DISTRIBUTED:=yes
PLUGIN_DIR:=src/plugins/occurrence
PLUGIN_CMO:= options register
PLUGIN_GUI_CMO:=register_gui
PLUGIN_INTRO:=doc/code/intro_occurrence.txt
PLUGIN_INTERNAL_TEST:=yes
PLUGIN_DEPENDENCIES:=Eva
$(eval $(call include_generic_plugin_Makefile,$(PLUGIN_NAME)))

################################################
# Runtime Error Annotation Generation analysis #
################################################

PLUGIN_ENABLE:=$(ENABLE_RTEGEN)
PLUGIN_NAME:=RteGen
PLUGIN_DIR:=src/plugins/rte
PLUGIN_CMO:= options generator rte flags visit register
PLUGIN_DISTRIBUTED:=yes
PLUGIN_INTERNAL_TEST:=yes
PLUGIN_TESTS_DIRS:=rte rte_manual
PLUGIN_TESTS_LIB:=\
  tests/rte/my_annotation.ml \
  tests/rte/rte_get_annot.ml \
  tests/rte/compute_annot.ml \
  tests/rte/my_annot_proxy.ml
$(eval $(call include_generic_plugin_Makefile,$(PLUGIN_NAME)))

#################
# From analysis #
#################
PLUGIN_ENABLE:=$(ENABLE_FROM_ANALYSIS)
PLUGIN_NAME:=From
PLUGIN_DIR:=src/plugins/from
PLUGIN_CMO:= from_parameters from_compute \
	functionwise callwise from_register
PLUGIN_GUI_CMO:=from_register_gui
PLUGIN_TESTS_DIRS:=idct test float
PLUGIN_DISTRIBUTED:=yes
PLUGIN_INTERNAL_TEST:=yes
FROM_TYPES:=src/plugins/value_types/function_Froms
PLUGIN_TYPES_CMO:=$(FROM_TYPES)
PLUGIN_TYPES_TODOC:=$(addsuffix .mli,$(FROM_TYPES))
PLUGIN_DEPENDENCIES:=Callgraph Eva Postdominators

$(eval $(call include_generic_plugin_Makefile,$(PLUGIN_NAME)))

##################
# Users analysis #
##################

PLUGIN_ENABLE:=$(ENABLE_USERS)
PLUGIN_NAME:=Users
PLUGIN_DIR:=src/plugins/users
PLUGIN_CMO:= users_register
PLUGIN_NO_TEST:=yes
PLUGIN_DISTRIBUTED:=yes
PLUGIN_INTERNAL_TEST:=yes
PLUGIN_DEPENDENCIES:=Callgraph Eva

$(eval $(call include_generic_plugin_Makefile,$(PLUGIN_NAME)))

########################
# Constant propagation #
########################

PLUGIN_ENABLE:=$(ENABLE_CONSTANT_PROPAGATION)
PLUGIN_NAME:=Constant_Propagation
PLUGIN_DIR:=src/plugins/constant_propagation
PLUGIN_TESTS_LIB:=tests/constant_propagation/introduction_of_non_explicit_cast.ml
PLUGIN_CMO:= propagationParameters \
	api
PLUGIN_DISTRIBUTED:=yes
PLUGIN_INTERNAL_TEST:=yes
PLUGIN_DEPENDENCIES:=Eva

$(eval $(call include_generic_plugin_Makefile,$(PLUGIN_NAME)))

###################
# Post-dominators #
###################

PLUGIN_ENABLE:=$(ENABLE_POSTDOMINATORS)
PLUGIN_NAME:=Postdominators
PLUGIN_DIR:=src/plugins/postdominators
PLUGIN_CMO:= postdominators_parameters print compute
PLUGIN_NO_TEST:=yes
PLUGIN_DISTRIBUTED:=yes
PLUGIN_INTERNAL_TEST:=yes
$(eval $(call include_generic_plugin_Makefile,$(PLUGIN_NAME)))

#########
# inout #
#########

PLUGIN_ENABLE:=$(ENABLE_INOUT)
PLUGIN_NAME:=Inout
PLUGIN_DIR:=src/plugins/inout
PLUGIN_CMO:= inout_parameters cumulative_analysis \
	     operational_inputs outputs inputs derefs register
PLUGIN_NO_TEST:=yes
PLUGIN_DISTRIBUTED:=yes
PLUGIN_INTERNAL_TEST:=yes
INOUT_TYPES:=src/plugins/value_types/inout_type
PLUGIN_TYPES_CMO:=$(INOUT_TYPES)
PLUGIN_TYPES_TODOC:=$(addsuffix .mli,$(INOUT_TYPES))
PLUGIN_DEPENDENCIES:=Callgraph Eva From

$(eval $(call include_generic_plugin_Makefile,$(PLUGIN_NAME)))

###################
# Impact analysis #
###################

PLUGIN_ENABLE:=$(ENABLE_IMPACT)
PLUGIN_NAME:=Impact
PLUGIN_DIR:=src/plugins/impact
PLUGIN_CMO:= options pdg_aux reason_graph compute_impact register
PLUGIN_GUI_CMO:= register_gui
PLUGIN_DISTRIBUTED:=yes
PLUGIN_INTERNAL_TEST:=yes
PLUGIN_DEPENDENCIES:=Inout Eva Pdg Slicing

$(eval $(call include_generic_plugin_Makefile,$(PLUGIN_NAME)))

##################################
# PDG : program dependence graph #
##################################

PLUGIN_ENABLE:=$(ENABLE_PDG)
PLUGIN_NAME:=Pdg
PLUGIN_DIR:=src/plugins/pdg
PLUGIN_TESTS_LIB:=tests/pdg/dyn_dpds.ml \
                  tests/pdg/sets.ml
PLUGIN_TESTS_DIRS:=pdg
PLUGIN_CMO:= pdg_parameters \
	    ctrlDpds \
	    pdg_state \
	    build \
	    sets \
	    annot \
	    marks \
	    register

PDG_TYPES:=pdgIndex pdgTypes pdgMarks
PDG_TYPES:=$(addprefix src/plugins/pdg_types/,$(PDG_TYPES))
PLUGIN_TYPES_CMO:=$(PDG_TYPES)

PLUGIN_INTRO:=doc/code/intro_pdg.txt
PLUGIN_TYPES_TODOC:=$(addsuffix .mli,$(PDG_TYPES))
PLUGIN_DEPENDENCIES:=Callgraph Eva From
PLUGIN_DISTRIBUTED:=yes
PLUGIN_INTERNAL_TEST:=yes

$(eval $(call include_generic_plugin_Makefile,$(PLUGIN_NAME)))

################################################
# Scope : show different kinds of dependencies #
################################################

PLUGIN_ENABLE:=$(ENABLE_SCOPE)
PLUGIN_NAME:=Scope
PLUGIN_DIR:=src/plugins/scope
PLUGIN_TESTS_LIB:=tests/scope/bts971.ml \
                  tests/scope/zones.ml
PLUGIN_CMO:= datascope zones defs
PLUGIN_GUI_CMO:=dpds_gui
PLUGIN_DEPENDENCIES:=Eva Inout
PLUGIN_INTRO:=doc/code/intro_scope.txt
PLUGIN_DISTRIBUTED:=yes
PLUGIN_INTERNAL_TEST:=yes
$(eval $(call include_generic_plugin_Makefile,$(PLUGIN_NAME)))

#####################################
# Sparecode : unused code detection #
#####################################

PLUGIN_ENABLE:=$(ENABLE_SPARECODE)
PLUGIN_NAME:=Sparecode
PLUGIN_DIR:=src/plugins/sparecode
PLUGIN_CMO:= sparecode_params globs spare_marks transform register
PLUGIN_INTRO:=doc/code/intro_sparecode.txt
PLUGIN_DISTRIBUTED:=yes
PLUGIN_INTERNAL_TEST:=yes
PLUGIN_DEPENDENCIES:=Pdg Eva Users

$(eval $(call include_generic_plugin_Makefile,$(PLUGIN_NAME)))

###########
# Slicing #
###########

PLUGIN_ENABLE:=$(ENABLE_SLICING)
PLUGIN_NAME:=Slicing
PLUGIN_DIR:=src/plugins/slicing
PLUGIN_CMO:= slicingInternals \
	    slicingTypes \
	    slicingParameters \
	    slicingState \
	    slicingMacros \
	    slicingMarks \
	    slicingActions \
	    fct_slice \
	    printSlice \
	    slicingProject \
	    slicingTransform \
	    slicingSelect \
	    slicingCmds \
	    api \
	    register

PLUGIN_GUI_CMO:=register_gui

PLUGIN_INTRO:=doc/code/intro_slicing.txt
PLUGIN_UNDOC:=register.ml # slicing_gui.ml

PLUGIN_TESTS_DIRS:= slicing
PLUGIN_TESTS_LIB:= tests/slicing/libSelect.ml tests/slicing/libAnim.ml \
	tests/slicing/simple_intra_slice.ml tests/slicing/combine.ml \
	tests/slicing/ex_spec_interproc.ml tests/slicing/horwitz.ml \
	tests/slicing/mark_all_slices.ml tests/slicing/merge.ml \
	tests/slicing/min_call.ml tests/slicing/select_by_annot.ml \
	tests/slicing/select_simple.ml tests/slicing/simple_intra_slice.ml \
	tests/slicing/slice_no_body.ml tests/slicing/switch.ml \
	tests/slicing/adpcm.ml
PLUGIN_DISTRIBUTED:=yes
PLUGIN_INTERNAL_TEST:=yes
PLUGIN_DEPENDENCIES:=Pdg Callgraph Eva Sparecode

$(eval $(call include_generic_plugin_Makefile,$(PLUGIN_NAME)))

#####################
# External plug-ins #
#####################

define INCLUDE_PLUGIN
FRAMAC_SHARE:=$(FRAMAC_ROOT_SRCDIR)/share
FRAMAC_PLUGIN:=$(FRAMAC_ROOT_SRCDIR)/lib/plugins
FRAMAC_PLUGIN_GUI:=$(FRAMAC_ROOT_SRCDIR)/lib/plugins/gui
PLUGIN_DIR:=$(1)
include $(1)/Makefile
endef

$(foreach p,$(EXTERNAL_PLUGINS),$(eval $(call INCLUDE_PLUGIN,$p)))

###############################################################################
#                                                                             #
###########################                                                   #
# End of plug-in sections #                                                   #
###########################                                                   #
#                                                                             #
###############################################################################

#####################
# Generic variables #
#####################

CMX	= $(CMO:.cmo=.cmx)
CMI	= $(CMO:.cmo=.cmi)

ALL_CMO	= $(CMO) $(PLUGIN_CMO_LIST) $(STARTUP_CMO)
ALL_CMX	= $(CMX) $(PLUGIN_CMX_LIST) $(STARTUP_CMX)

FILES_FOR_OCAMLDEP+= $(addsuffix /*.mli,$(FRAMAC_SRC_DIRS)) \
	$(addsuffix /*.ml,$(FRAMAC_SRC_DIRS))

MODULES_TODOC+=$(filter-out $(MODULES_NODOC),\
	$(MLI_ONLY) \
	$(filter-out $(PLUGIN_TYPES_CMO_LIST:.cmo=.mli),$(CMO:.cmo=.mli)))

################################
# toplevel.{byte,opt} binaries #
################################

ALL_BATCH_CMO= $(filter-out src/kernel_internals/runtime/gui_init.cmo,\
	$(ALL_CMO))
ALL_BATCH_CMX= $(filter-out src/kernel_internals/runtime/gui_init.cmx,\
	$(ALL_CMX))

bin/toplevel.byte$(EXE): $(ALL_BATCH_CMO) $(GEN_BYTE_LIBS) \
			$(PLUGIN_DYN_CMO_LIST)
	$(PRINT_LINKING) $@
	$(OCAMLC) $(BLINKFLAGS) -o $@ $(BYTE_LIBS) $(ALL_BATCH_CMO)

#Profiling version of toplevel.byte using ocamlprof
bin/toplevel.prof$(EXE): $(ALL_BATCH_CMO) $(GEN_BYTE_LIBS) \
			$(PLUGIN_DYN_CMO_LIST)
	$(PRINT_OCAMLCP) $@
	$(OCAMLCP) $(BLINKFLAGS) -o $@ $(BYTE_LIBS) $(ALL_BATCH_CMO)

bin/toplevel.opt$(EXE): $(ALL_BATCH_CMX) $(GEN_OPT_LIBS) \
			$(PLUGIN_DYN_CMX_LIST)
	$(PRINT_LINKING) $@
	$(OCAMLOPT) $(OLINKFLAGS) -o $@ $(OPT_LIBS) $(ALL_BATCH_CMX)

LIB_KERNEL_CMO= $(filter-out src/kernel_internals/runtime/gui_init.cmo, $(CMO))
LIB_KERNEL_CMX= $(filter-out src/kernel_internals/runtime/gui_init.cmx, $(CMX))

lib/fc/frama-c.cma: $(LIB_KERNEL_CMO) $(GEN_BYTE_LIBS) lib/fc/META.frama-c
	$(PRINT_LINKING) $@
	$(MKDIR) $(FRAMAC_LIB)
	$(OCAMLMKLIB) -o lib/fc/frama-c $(BYTE_LIBS) $(LIB_KERNEL_CMO)

lib/fc/frama-c.cmxa: lib/fc/frama-c.cma $(GEN_OPT_LIBS) $(LIB_KERNEL_CMX)
	$(MKDIR) $(FRAMAC_LIB)
	$(PRINT_LINKING) $@
	$(OCAMLMKLIB) -o lib/fc/frama-c $(OPT_LIBS) $(LIB_KERNEL_CMX)

####################
# (Ocaml) Toplevel #
####################

bin/toplevel.top$(EXE): $(filter-out src/kernel_internals/runtime/boot.ml,$(ALL_BATCH_CMO)) \
			src/kernel_internals/runtime/toplevel_config.cmo \
			$(GEN_BYTE_LIBS) $(PLUGIN_DYN_CMO_LIST)
	$(PRINT_OCAMLMKTOP) $@
	$(OCAMLMKTOP) $(BFLAGS) -warn-error -31 -custom -o $@ \
	  -linkpkg $(BYTE_LIBS) $(ALL_BATCH_CMO) \
	   src/kernel_internals/runtime/toplevel_config.cmo

#######
# GUI #
#######

ifneq ($(ENABLE_GUI),no)

SINGLE_GUI_CMI = $(SINGLE_GUI_CMO:.cmo=.cmi)
SINGLE_GUI_CMX = $(SINGLE_GUI_CMO:.cmo=.cmx)

GUICMO += $(SINGLE_GUI_CMO) $(PLUGIN_GUI_CMO_LIST)

MODULES_TODOC+= $(filter-out src/plugins/gui/book_manager.mli,\
	$(SINGLE_GUI_CMO:.cmo=.mli))

GUICMI = $(GUICMO:.cmo=.cmi)
GUICMX = $(SINGLE_GUI_CMX) $(PLUGIN_GUI_CMX_LIST)

$(GUICMI) $(GUICMO) bin/viewer.byte$(EXE): BFLAGS+= $(GUI_INCLUDES) $(THREAD)
$(GUICMX) bin/viewer.opt$(EXE): OFLAGS+= $(GUI_INCLUDES) $(THREAD)

$(PLUGIN_DYN_DEP_GUI_CMO_LIST): BFLAGS+= $(GUI_INCLUDES)
$(PLUGIN_DYN_DEP_GUI_CMX_LIST): OFLAGS+= $(GUI_INCLUDES)

.PHONY:gui

gui-byte:: bin/viewer.byte$(EXE) share/Makefile.dynamic_config \
           $(PLUGIN_META_LIST)

gui-opt:: gui-byte bin/viewer.opt$(EXE)

gui: gui-$(OCAMLBEST)

ALL_GUI_CMO= $(ALL_CMO) $(GRAPH_GUICMO) $(GUICMO)
ALL_GUI_CMX= $(patsubst %.cma,%.cmxa,$(ALL_GUI_CMO:.cmo=.cmx))

bin/viewer.byte$(EXE): BYTE_LIBS+= $(GRAPH_GUICMO)
bin/viewer.byte$(EXE): $(filter-out $(GRAPH_GUICMO),$(ALL_GUI_CMO)) \
			$(GEN_BYTE_LIBS) \
			$(PLUGIN_DYN_CMO_LIST) $(PLUGIN_DYN_GUI_CMO_LIST)
	$(PRINT_LINKING) $@
	$(OCAMLC) $(BLINKFLAGS) $(THREAD) -o $@ $(BYTE_LIBS) \
	  $(CMO) \
	  $(filter-out \
	    $(patsubst $(PLUGIN_GUI_LIB_DIR)/%,$(PLUGIN_LIB_DIR)/%,\
		$(PLUGIN_GUI_CMO_LIST)),\
	    $(PLUGIN_CMO_LIST)) \
	  $(GUICMO) $(STARTUP_CMO)

bin/viewer.opt$(EXE): OPT_LIBS+= $(GRAPH_GUICMX)
bin/viewer.opt$(EXE): $(filter-out $(GRAPH_GUICMX),$(ALL_GUI_CMX)) \
			$(GEN_OPT_LIBS) \
			$(PLUGIN_DYN_CMX_LIST) $(PLUGIN_DYN_GUI_CMX_LIST) \
			$(PLUGIN_CMX_LIST) $(PLUGIN_GUI_CMX_LIST)
	$(PRINT_LINKING) $@
	$(OCAMLOPT) $(OLINKFLAGS) $(THREAD) -o $@ $(OPT_LIBS) \
	  $(CMX) \
	  $(filter-out \
	    $(patsubst $(PLUGIN_GUI_LIB_DIR)/%,$(PLUGIN_LIB_DIR)/%,\
	      $(PLUGIN_GUI_CMX_LIST)),\
	    $(PLUGIN_CMX_LIST)) \
	  $(GUICMX) $(STARTUP_CMX)
endif

#####################
# Config Ocaml File #
#####################

CONFIG_DIR=src/kernel_internals/runtime
CONFIG_FILE=$(CONFIG_DIR)/fc_config.ml
CONFIG_CMO=$(CONFIG_DIR)/fc_config.cmo
GENERATED +=$(CONFIG_FILE)
#Generated in Makefile.generating

empty:=
space:=$(empty) $(empty)

ifeq ($(ENABLE_GUI),no)
CONFIG_CMO=$(ALL_CMO)
CONFIG_PLUGIN_CMO=$(PLUGIN_CMO_LIST)
else
CONFIG_CMO=$(ALL_GUI_CMO)
CONFIG_PLUGIN_CMO=$(PLUGIN_GUI_CMO_LIST)
endif

ifeq ($(HAS_DOT),yes)
OPTDOT=Some \"$(DOT)\"
else
OPTDOT=None
endif

COMPILATION_UNITS=\
  $(foreach p,$(CONFIG_CMO),\"$(notdir $(patsubst %.cmo,%,$p))\"; )

###################
# Generating part #
###################
# It is in another file in order to have a dependency only on Makefile.generating.
# It must be before `.depend` definition because it modifies $GENERATED.

include Makefile.generating

#########
# Tests #
#########

ifeq ($(OCAMLBEST),opt)
PTESTS_FILES=ptests_config.cmi ptests_config.cmx ptests_config.o
else
PTESTS_FILES=ptests_config.cmi ptests_config.cmo
endif

.PHONY: tests oracles btests tests_dist libc_tests plugins_ptests_config external_tests \
	update_external_tests

tests:: byte opt ptests
	$(PRINT_EXEC) ptests
	time -p $(PTESTS) $(PTESTS_OPTS) $(FRAMAC_PARALLEL) \
		-make "$(MAKE)" $(PLUGIN_TESTS_LIST)

external_tests: byte opt ptests
tests:: external_tests

update_external_tests: PTESTS_OPTS="-update"
update_external_tests: external_tests

oracles: byte opt ptests
	$(PRINT_MAKING) oracles
	./bin/ptests.$(OCAMLBEST)$(EXE) -make "$(MAKE)" $(PLUGIN_TESTS_LIST) \
		> /dev/null 2>&1
	./bin/ptests.$(OCAMLBEST)$(EXE) -make "$(MAKE)" -update \
		$(PLUGIN_TESTS_LIST)

btests: byte ./bin/ptests.byte$(EXE)
	$(PRINT_EXEC) ptests -byte
	time -p ./bin/ptests.byte$(EXE) -make "$(MAKE)" -byte \
		$(PLUGIN_TESTS_LIST)

tests_dist: dist ptests
	$(PRINT_EXEC) ptests
	time -p ./bin/ptests.$(OCAMLBEST)$(EXE) -make "$(MAKE)" \
		$(PLUGIN_TESTS_LIST)

# test only one test suite : make suite_tests
%_tests: opt ptests
	$(PRINT_EXEC) ptests
	./bin/ptests.$(OCAMLBEST)$(EXE) -make "$(MAKE)" $($*_TESTS_OPTS) $*

# full test suite
wp_TESTS_OPTS=-j 1
fulltests: tests wp_tests

acsl_tests: byte
	$(PRINT_EXEC) acsl_tests
	find doc/speclang -name \*.c -exec ./bin/toplevel.byte$(EXE) {} \; > /dev/null

LONELY_TESTS_DIR:=$(wildcard $(TEST_DIRS_AS_PLUGIN:%=tests/%))
ifeq ($(strip $(LONELY_TESTS_DIR)),)
  LONELY_TESTS_ML_FILES:=
else
  LONELY_TESTS_ML_FILES:=\
    $(sort $(shell find $(TEST_DIRS_AS_PLUGIN:%=tests/%) -not -path '*/\.*' -name '*.ml'))
endif
$(foreach file,$(LONELY_TESTS_ML_FILES),\
  $(eval $(file:%.ml=%.cmo): BFLAGS+=-w -70 -I $(dir $(file))))
$(foreach file,$(LONELY_TESTS_ML_FILES),\
  $(eval $(file:%.ml=%.cmx): OFLAGS+=-I $(dir $(file))))
$(foreach file,$(LONELY_TESTS_ML_FILES),\
  $(eval $(file:%.ml=%.cmxs): OFLAGS+=-I $(dir $(file))))
.PRECIOUS: $(LONELY_TESTS_ML_FILES:%.ml=%.cmx) \
           $(LONELY_TESTS_ML_FILES:%.ml=%.cmxs) \
           $(LONELY_TESTS_ML_FILES:%.ml=%.cmo) \
           $(LONELY_TESTS_ML_FILES:%.ml=%.cmi)

bin/ocamldep_transitive_closure: devel_tools/ocamldep_transitive_closure.ml
	$(OCAMLOPT) -package ocamlgraph -package str -linkpkg -o $@ $<

tests/crowbar/.%.depend: tests/crowbar/%.ml
	$(OCAMLDEP) $(INCLUDES) $< > $@

tests/crowbar/%: tests/crowbar/%.ml tests/crowbar/.%.depend .depend \
                 bin/ocamldep_transitive_closure bin/toplevel.opt
	$(OCAMLOPT) $(OLINKFLAGS) -w -42 -package crowbar -o $@ \
        $(GEN_C_BINDINGS) \
        $$(bin/ocamldep_transitive_closure -root tests/crowbar/$*.cmx \
             -deps tests/crowbar/.$*.depend -deps .depend) \
        $<

tests/crowbar/full-link-%: tests/crowbar/%.ml lib/fc/frama-c.cmxa
	$(OCAMLOPT) $(OLINKFLAGS) -ccopt "-Llib/fc" -w -42 -package crowbar -o $@ \
	lib/fc/frama-c.cmxa $<

crowbar-%: tests/crowbar/%
	$<

crowbar-afl-%: tests/crowbar/%
	$(MKDIR) tests/crowbar/output-$*
	afl-fuzz $(AFL_OPTS) -i tests/crowbar/input -o tests/crowbar/output-$* $< @@

crowbar-afl-check-%: tests/crowbar/%
	$(foreach file,$(wildcard tests/crowbar/output-$*/crashes/id*), \
          $< $(file) >/dev/null 2>&1 || \
          echo "$(file) leads to a real test failure";)

##############
# Emacs tags #
##############

.PHONY: tags
# otags gives a better tagging of ocaml files than etags
ifdef OTAGS
tags:
	$(OTAGS) -r src lib
vtags:
	$(OTAGS) -vi -r src lib
else
tags:
	find . -name "*.ml[ily]" -o -name "*.ml" | sort -r | xargs \
	etags "--regex=/[ \t]*let[ \t]+\([^ \t]+\)/\1/" \
	      "--regex=/[ \t]*let[ \t]+rec[ \t]+\([^ \t]+\)/\1/" \
	      "--regex=/[ \t]*and[ \t]+\([^ \t]+\)/\1/" \
	      "--regex=/[ \t]*type[ \t]+\([^ \t]+\)/\1/" \
	      "--regex=/[ \t]*exception[ \t]+\([^ \t]+\)/\1/" \
	      "--regex=/[ \t]*val[ \t]+\([^ \t]+\)/\1/" \
	      "--regex=/[ \t]*module[ \t]+\([^ \t]+\)/\1/"
endif

#################
# Documentation #
#################

.PHONY: doc doc-distrib

# private targets, useful for recompiling the doc without dependencies
# (too long!)
.PHONY: doc-kernel doc-index plugins-doc doc-update doc-tgz

DOC_DEPEND=$(MODULES_TODOC) byte $(DOC_PLUGIN)
ifneq ($(ENABLE_GUI),no)
DOC_DEPEND+=bin/viewer.byte$(EXE)
endif

$(DOC_DIR)/docgen.cmo: $(DOC_DIR)/docgen.ml
	$(PRINT_OCAMLC) $@
	$(OCAMLC) -c -I +ocamldoc -I +compiler-libs -I $(CONFIG_DIR) $(DOC_DIR)/docgen.ml

$(DOC_DIR)/docgen.cmxs: $(DOC_DIR)/docgen.ml
	$(PRINT_PACKING) $@
	$(OCAMLOPT) -o $@ -shared -I +ocamldoc -I +compiler-libs -I $(CONFIG_DIR) \
	  $(DOC_DIR)/docgen.ml

clean-doc::
	$(PRINT_RM) "documentation generator"
	$(RM) $(DOC_DIR)/docgen.cm*

DOC_NOT_FOR_DISTRIB=yes
plugins-doc:
	$(QUIET_MAKE) \
	 $(if $(DOC_NOT_FOR_DISTRIB),$(PLUGIN_DOC_LIST),\
	   $(filter \
	     $(addsuffix _DOC,$(PLUGIN_DISTRIBUTED_NAME_LIST)),\
	     $(PLUGIN_DOC_LIST)))

.PHONY: server-doc-md server-doc-html server-doc

server-doc-md: byte
	$(PRINT) 'Generating Markdown server documentation'
	@rm -fr doc/server
	@mkdir -p doc/server
	./bin/frama-c.byte -server-doc doc/server

server-doc-html: server-doc-md
	$(PRINT) 'Generating HTML server documentation'
	@find doc/server -name "*.md" -print -exec pandoc \
			--standalone --toc --toc-depth=2 --to html \
			--template doc/pandoc/template.html \
			--metadata-file {}.json \
			--lua-filter doc/pandoc/href.lua \
			{} -o {}.html \;
	@cp -f doc/pandoc/style.css doc/server/
	$(PRINT) 'HTML server documentation ready:'
	$(PRINT) '  open doc/server/readme.md.html'

server-doc: server-doc-html

# to make the documentation for one plugin only,
# the name of the plugin should begin with a capital letter :
# Example for the pdg doc : make Pdg_DOC
# While working on the documentation of a plugin, it can also be useful
# to use : make -o doc/code/kernel-doc.ocamldoc Plugin_DOC
# to avoid redoing the global documentation each time.

STDLIB_FILES:=\
	array \
	buffer \
        bytes \
	char \
	format \
	hashtbl \
	int64 \
	list \
	map \
	marshal \
	obj \
        parsing \
	printf \
	queue \
	scanf \
	set \
	stack \
	string \
	sys \
	weak \
	ephemeron

STDLIB_FILES:=$(patsubst %,$(OCAMLLIB)/%.mli,$(STDLIB_FILES))

.PHONY: doc-kernel
doc-kernel: $(DOC_DIR)/kernel-doc.ocamldoc

$(DOC_DIR)/kernel-doc.ocamldoc: $(DOC_DEPEND)
	$(PRINT_DOC) Kernel Documentation
	$(MKDIR) $(DOC_DIR)/html
	$(RM) $(DOC_DIR)/html/*.html
	$(OCAMLDOC) $(DOC_FLAGS) \
	  $(addprefix -passopt -stdlib ,$(STDLIB_FILES)) \
	  -t "Frama-C Kernel" \
	  -sort -css-style ../style.css \
	  -g $(DOC_PLUGIN) \
	  -d $(DOC_DIR)/html -dump $@ \
	  $(MODULES_TODOC); \
	  RES=$$?; \
	  if test $$RES -ne 0; then \
	    $(RM) $@; \
	    exit $$RES; \
	  fi

DYN_MLI_DIR := src/plugins/print_api

.PHONY: doc-dynamic
doc-dynamic: doc-kernel
	$(RM) $(DYN_MLI_DIR)/dynamic_plugins.mli
	./bin/frama-c.byte \
		-print_api $(call winpath,$(FRAMAC_ROOT_SRCDIR)/$(DYN_MLI_DIR))
	$(PRINT_DOC) Dynamically registered plugins Documentation
	$(MKDIR) $(DOC_DIR)/dynamic_plugins
	$(RM) $(DOC_DIR)/dynamic_plugins/*.html
	$(OCAMLDOC) $(DOC_FLAGS) -I $(FRAMAC_LIB) -I $(OCAMLLIB) \
	  -passopt -docpath $(DOC_DIR)/html \
	  -sort -css-style ../style.css \
	  -load $(DOC_DIR)/kernel-doc.ocamldoc \
	  -t " Dynamically registered plugins" \
	  -g $(DOC_PLUGIN) \
	  -d $(DOC_DIR)/dynamic_plugins \
	  $(DYN_MLI_DIR)/dynamic_plugins.mli
	$(ECHO) '<li><a href="dynamic_plugins/Dynamic_plugins.html">Dynamically registered plugins</a </li>' > $(DOC_DIR)/dynamic_plugins.toc

doc-index: doc-kernel doc-dynamic plugins-doc
	$(PRINT_MAKING) doc/code/index.html
	$(CAT)  $(DOC_DIR)/toc_head.htm $(DOC_DIR)/*.toc \
		$(DOC_DIR)/toc_tail.htm > $(DOC_DIR)/index.html

doc-update: doc-kernel doc-dynamic plugins-doc doc-index

doc:: doc-kernel doc-dynamic plugins-doc doc-index

doc-kernel doc-dynamic plugins-doc doc-index: $(DOC_DEPEND)

doc-tgz:
	$(PRINT_MAKING) frama-c-api.tar.gz
	cd $(DOC_DIR); \
	  $(TAR) zcf tmp.tgz index.html *.txt \
	   $(notdir $(wildcard $(DOC_DIR)/*.css $(DOC_DIR)/*.png \
			       $(DOC_DIR)/dynamic_plugins*)) \
	   html \
	   $(foreach p,$(PLUGIN_DISTRIBUTED_NAME_LIST),\
	     $(notdir $($(p)_DOC_DIR)))
	$(MKDIR) frama-c-api
	$(RM) -r frama-c-api/*
	cd frama-c-api; $(TAR) zxf ../$(DOC_DIR)/tmp.tgz
	$(TAR) zcf frama-c-api.tar.gz frama-c-api
	$(RM) -r frama-c-api $(DOC_DIR)/tmp.tgz

doc-distrib:
	$(QUIET_MAKE) clean-doc
	$(QUIET_MAKE) doc DOC_NOT_FOR_DISTRIB=
	$(QUIET_MAKE) doc-tgz

#find src -name "*.ml[i]" -o -name "*.ml" -maxdepth 3 | sort -r | xargs
dots: $(ALL_CMO)
	$(PRINT_DOC) callgraph
	$(OCAMLDOC) $(DOC_FLAGS) $(INCLUDES) -o doc/call_graph.dot \
	  -dot -dot-include-all -dot-reduce $(MODULES_TODOC)
	$(QUIET_MAKE) doc/call_graph.svg
	$(QUIET_MAKE) doc/call_graph.ps

# pandoc is required to regenerate the manpage
man/frama-c.1: man/frama-c.1.md Makefile
	$(PRINT) 'generating $@'
	$(RM) $@
	pandoc -s -t man $< > $@
	$(CHMOD_RO) $@

# Checking consistency with the current implementation
######################################################

DOC_DEV_DIR = doc/developer
CHECK_API_DIR=$(DOC_DEV_DIR)/check_api

$(CHECK_API_DIR)/check_code.cmo: $(CHECK_API_DIR)/check_code.ml
	$(PRINT_OCAMLC) $@
	$(OCAMLC) -c -I +ocamldoc str.cma $(CHECK_API_DIR)/check_code.ml

$(CHECK_API_DIR)/check_code.cmxs: $(CHECK_API_DIR)/check_code.ml
	$(PRINT_PACKING) $@
	$(OCAMLOPT) -o $@ -shared -I +ocamldoc \
		str.cmxa $(CHECK_API_DIR)/check_code.ml

CHECK_CODE=$(CHECK_API_DIR)/check_code.cmxs

.PHONY: check-devguide
check-devguide: $(CHECK_CODE) $(DOC_DEPEND) $(DOC_DIR)/kernel-doc.ocamldoc
	$(PRINT) 'Checking     developer guide consistency'
	$(MKDIR) $(CHECK_API_DIR)/html
	$(OCAMLDOC) $(DOC_FLAGS) -I $(OCAMLLIB) \
	  -g $(CHECK_CODE) \
	  -passopt -docdevpath -passopt "`pwd`/$(CHECK_API_DIR)" \
	  -load $(DOC_DIR)/kernel-doc.ocamldoc \
	  -d $(CHECK_API_DIR)/html
	$(RM) -r  $(CHECK_API_DIR)/html
	$(MAKE) --silent -C $(CHECK_API_DIR) main.idx
	$(MAKE) --silent -C $(CHECK_API_DIR) >$(CHECK_API_DIR)/summary.txt
	$(ECHO) see all the information displayed here \
		in $(CHECK_API_DIR)/summary.txt
	$(RM) code_file
################################
# Code prettyfication and lint #
################################

# We're interested by any .ml[i]? file in src, except for scripts in test
# directories, and generated files (in particular lexers and parsers)
# Note: the find command below is *very* ugly, but it should be POSIX-compliant.

ALL_ML_FILES:=$(shell find src -name '*.ml' -print -o -name '*.mli' -print -o -path '*/tests' -prune '!' -name '*')
ALL_ML_FILES+=ptests/ptests.ml

ifeq ($(origin MANUAL_ML_FILES),undefined)
MANUAL_ML_FILES:=$(ALL_ML_FILES)
endif

MANUAL_ML_FILES:=\
  $(filter-out $(GENERATED) $(PLUGIN_GENERATED_LIST), $(MANUAL_ML_FILES))

# Allow control of files to be linted/fixed by external sources
# (e.g. pre-commit hook that will concentrate on files which have changed)

sinclude .Makefile.lint

HAS_GIT_FILE:=$(wildcard .git/HEAD)

ifeq ("$(HAS_GIT_FILE)","")
LINT_OTHER_SOURCES:=
else
LINT_OTHER_SOURCES:=\
  $(filter-out \
    $(shell git ls-tree --name-only HEAD src/plugins/*), \
    $(wildcard src/plugins/*))
endif

$(foreach dir,$(LINT_OTHER_SOURCES),$(eval sinclude $(dir)/.Makefile.lint))

ML_LINT_MISSING:=$(filter-out $(MANUAL_ML_FILES), $(ML_LINT_KO))

# By default, also checks files with unknown status:
# this requires new files to pass lint checker from the beginning

ML_LINT_CHECK?=$(filter-out $(ML_LINT_KO), $(MANUAL_ML_FILES))

# this NEWLINE variable containing a literal newline character is used to avoid
# the error "argument list too long", in some instances of "foreach".
# For details, see https://stackoverflow.com/questions/7039811
define NEWLINE


endef

# pre-requisite intentionally left blank: this target should only be used
# if the file is not present to generate it once and forall,
# and be edited manually afterwards
# double colon here tells make not to attempt updating the .Makefile.lint
# if it does not exist, but just to ignore it.
.Makefile.lint::
	echo "ML_LINT_KO:=" >> $@
	$(foreach file,$(sort $(MANUAL_ML_FILES)), \
            if ! $(MAKE) ML_LINT_CHECK=$(file) lint; \
            then echo "ML_LINT_KO+=$(file)" >> $@; fi;$(NEWLINE) )

$(foreach dir,$(LINT_OTHER_SOURCES),\
  $(eval $(dir)/.Makefile.lint:: ; \
     $(foreach file, $(sort $(filter $(dir)/%, $(MANUAL_ML_FILES))), \
            if ! $$(MAKE) ML_LINT_CHECK=$(file) lint; \
            then echo "ML_LINT_KO+=$(file)" >> $$@; fi; )))

.PHONY: stats-lint
stats-lint:
	echo \
         "scale = 2; bad = $(words $(ML_LINT_MISSING)); \
          all = $(words $(sort $(MANUAL_ML_FILES))); \
	  fail = $(words $(ML_LINT_KO)); \
          \"lint coverage: \"; \
          ((all - fail) * 100) / all; " | bc
	echo "number of files supposed to pass lint: $(words $(ML_LINT_CHECK))"
	echo "number of files supposed to fail lint: $(words $(ML_LINT_KO))"
ifneq ($(strip $(ML_LINT_MISSING)),)
	echo "number of files missing from src/ : $(words $(ML_LINT_MISSING))"
	$(foreach file, $(ML_LINT_MISSING), echo $(file);)
	exit 1
endif

INDENT_TARGET= $(patsubst %,%.indent,$(ML_LINT_CHECK))

LINT_TARGET= $(patsubst %,%.lint,$(ML_LINT_CHECK))

FIX_SYNTAX_TARGET=$(patsubst %,%.fix-syntax,$(ML_LINT_CHECK))

.PHONY: $(INDENT_TARGET) $(LINT_TARGET) $(FIX_SYNTAX_TARGET) \
        indent lint fix-syntax

indent: $(INDENT_TARGET)

lint:: $(LINT_TARGET)

check-ocp-indent-version:
	if command -v ocp-indent >/dev/null; then \
		if [ -z "$(shell ocp-indent --version)" ]; then echo "warning: ocp-indent returned an empty string, assuming it is the correct version"; \
		else \
			$(eval ocp_version_major := $(shell ocp-indent --version | $(SED) -E "s/^([0-9]+)\.[0-9]+\..*/\1/")) \
			$(eval ocp_version_minor := $(shell ocp-indent --version | $(SED) -E "s/^[0-9]+\.([0-9]+)\..*/\1/")) \
			if [ "$(ocp_version_major)" -lt 1 -o "$(ocp_version_minor)" -lt 7 ]; then \
				echo "error: ocp-indent 1.7.0 required for linting (got $(ocp_version_major).$(ocp_version_minor))"; \
			exit 1; \
			fi; \
		fi; \
	else \
		exit 1; \
	fi;

fix-syntax: $(FIX_SYNTAX_TARGET)

$(INDENT_TARGET): %.indent: % check-ocp-indent-version
	ocp-indent -i $<
$(LINT_TARGET): %.lint: % check-ocp-indent-version
	# See SO 1825552 on mixing grep and \t (and cry)
	# For OK_NL, we have three cases:
	# - for empty files, the computation boils down to 0 - 0 == 0
	# - for non-empty files with a proper \n at the end, to 1 - 1 == 0
	# - for empty files without \n, to 1 - 0 == 1 that will be catched
	OK_TAB=$$(grep -c -e "$$(printf '^ *\t')" $<) ; \
        OK_SPACE=$$(grep -c -e '[[:blank:]]$$' $<) ; \
        OK_NL=$$(($$(tail -c -1 $< | wc -c) - $$(tail -n -1 $< | wc -l))) ; \
        OK_EMPTY=$$(tail -n -1 $< | grep -c -e '^[[:blank:]]*$$') ; \
        ERROR=$$(($$OK_TAB + $$OK_SPACE + $$OK_NL + $$OK_EMPTY)) ; \
        if test $$ERROR -gt 0; then \
          echo "File $< does not pass syntactic checks:"; \
          echo "$$OK_TAB lines indented with tabulation instead of spaces"; \
          echo "$$OK_SPACE lines with spaces at end of line"; \
          test $$OK_NL -eq 0 || echo "No newline at end of file"; \
          test $$OK_EMPTY -eq 0 || echo "Empty line(s) at end of file"; \
          echo "Please run make ML_LINT_CHECK=$< fix-syntax"; \
          exit 1 ; \
        fi
	ocp-indent $< > $<.tmp;
	if cmp -s $< $<.tmp; \
        then rm -f $<.tmp; \
        else \
          echo "File $< is not indented correctly."; \
          echo "Please run make ML_LINT_CHECK=$< indent";\
          rm $<.tmp; \
          exit 1; \
        fi

$(FIX_SYNTAX_TARGET): %.fix-syntax: %
	$(ISED) -e 's/^ *\t\+/ /' $<
	$(ISED) -e 's/\(.*[^[:blank:]]\)[[:blank:]]\+$$/\1/' $<
	$(ISED) -e 's/^[ \t]\+$$//' $<
	if test \( $$(tail -n -1 $< | wc -l) -eq 0 \) -a \( $$(wc -c $< | cut -d " " -f 1) -gt 0 \) ; then \
          echo "" >> $<; \
        else \
          while tail -n -1 $< | grep -l -e '^[ \t]*$$'; do \
            head -n -1 $< > $<.tmp; \
            mv $<.tmp $<; \
          done; \
        fi

# Avoid a UTF-8 locale at all cost: in such setting, sed does not work
# reliably if you happen to have latin-1 encoding somewhere,
# which is still unfortunately the case in some dark corners of the platform
%.fix-syntax: LC_ALL = C

################
# Installation #
################

#       line below does not work if INCLUDES contains twice the same directory
#       Do not attempt to copy gui interfaces if gui is disabled
#Byte
ALL_BATCH_CMO_FIXED=$(filter-out src/kernel_internals/runtime/gui_init.cmo,$(CMO) $(STARTUP_CMO))
LIB_BYTE_TO_INSTALL=\
	$(MLI_ONLY:.mli=.cmi) \
	$(ALL_BATCH_CMO_FIXED:.cmo=.cmi) \
	$(ALL_BATCH_CMO_FIXED) \
	$(filter-out %.o,$(GEN_BYTE_LIBS:.cmo=.cmi)) \
	$(GEN_BYTE_LIBS)

#Byte GUI
ifneq ("$(ENABLE_GUI)","no")
LIB_BYTE_TO_INSTALL+=$(SINGLE_GUI_CMI) $(SINGLE_GUI_CMO)
endif

#Opt
ifeq ("$(OCAMLBEST)","opt")
ALL_BATCH_CMX_FIXED= $(filter-out src/kernel_internals/runtime/gui_init.cmx,\
	$(CMX) $(STARTUP_CMX))
LIB_OPT_TO_INSTALL +=\
	$(ALL_BATCH_CMX) \
	$(filter %.a,$(ALL_BATCH_CMX_FIXED:.cmxa=.a)) \
	$(filter %.o,$(ALL_BATCH_CMX_FIXED:.cmx=.o)) \
	$(filter-out %.o,$(GEN_OPT_LIBS)) \
	$(filter-out $(GEN_BYTE_LIBS),$(filter %.o,$(GEN_OPT_LIBS:.cmx=.o)))

#Opt GUI
ifneq ("$(ENABLE_GUI)","no")
LIB_OPT_TO_INSTALL += $(SINGLE_GUI_CMX) $(SINGLE_GUI_CMX:.cmx=.o)
endif

endif

clean-install:
	$(PRINT_RM) "Installation directory"
	$(RM) -r $(FRAMAC_LIBDIR)

install-lib-byte: clean-install
	$(PRINT_INSTALL) kernel API
	$(MKDIR) $(FRAMAC_LIBDIR)
	$(CP) $(LIB_BYTE_TO_INSTALL) $(FRAMAC_LIBDIR)
	$(CP) $(addprefix lib/fc/,dllframa-c$(DLLEXT) libframa-c.a frama-c.cma META.frama-c) $(FRAMAC_LIBDIR)

install-lib-opt: install-lib-byte
	$(CP) $(LIB_OPT_TO_INSTALL) $(FRAMAC_LIBDIR)
	$(CP) $(addprefix lib/fc/,frama-c.a frama-c.cmxa) $(FRAMAC_LIBDIR)

install-doc-code:
	$(PRINT_INSTALL) API documentation
	$(MKDIR) $(FRAMAC_DATADIR)/doc/code
	(cd doc ; tar cf - --exclude='.svn' --exclude='*.toc' \
			--exclude='*.htm' --exclude='*.txt' \
			--exclude='*.ml' \
			code \
		| (cd $(FRAMAC_DATADIR)/doc ; tar xf -))

.PHONY: install
install:: install-lib-$(OCAMLBEST)
	$(PRINT_MAKING) destination directories
	$(MKDIR) $(BINDIR)
	$(MKDIR) $(MANDIR)/man1
	$(MKDIR) $(FRAMAC_PLUGINDIR)/top
	$(MKDIR) $(FRAMAC_PLUGINDIR)/gui
	$(MKDIR) $(FRAMAC_DATADIR)/theme/default
	$(MKDIR) $(FRAMAC_DATADIR)/theme/colorblind
	$(MKDIR) $(FRAMAC_DATADIR)/theme/flat
	$(MKDIR) $(FRAMAC_DATADIR)/libc/sys
	$(MKDIR) $(FRAMAC_DATADIR)/libc/netinet
	$(MKDIR) $(FRAMAC_DATADIR)/libc/net
	$(MKDIR) $(FRAMAC_DATADIR)/libc/arpa
	$(PRINT_INSTALL) shared files
	$(CP) \
	  $(wildcard share/*.c share/*.h) \
	  share/Makefile.dynamic share/Makefile.plugin.template \
	  share/Makefile.config share/Makefile.common share/Makefile.generic \
	  share/configure.ac share/autocomplete_frama-c share/_frama-c \
	  $(FRAMAC_DATADIR)
	$(MKDIR) $(FRAMAC_DATADIR)/analysis-scripts
	$(CP) \
	  share/analysis-scripts/analysis.mk \
	  share/analysis-scripts/benchmark_database.py \
	  share/analysis-scripts/cmd-dep.sh \
	  share/analysis-scripts/concat-csv.sh \
	  share/analysis-scripts/clone.sh \
	  share/analysis-scripts/creduce.sh \
	  share/analysis-scripts/epilogue.mk \
	  share/analysis-scripts/fc_stubs.c \
	  share/analysis-scripts/find_fun.py \
	  share/analysis-scripts/flamegraph.pl \
	  share/analysis-scripts/frama_c_results.py \
	  share/analysis-scripts/function_finder.py \
	  share/analysis-scripts/git_utils.py \
	  share/analysis-scripts/list_files.py \
	  share/analysis-scripts/list_functions.ml \
	  share/analysis-scripts/make_template.py \
	  share/analysis-scripts/make_wrapper.py \
	  share/analysis-scripts/normalize_jcdb.py \
	  share/analysis-scripts/parse-coverage.sh \
	  share/analysis-scripts/prologue.mk \
	  share/analysis-scripts/README.md \
	  share/analysis-scripts/results_display.py \
	  share/analysis-scripts/script_for_creduce_fatal.sh \
	  share/analysis-scripts/script_for_creduce_non_fatal.sh \
	  share/analysis-scripts/summary.py \
	  share/analysis-scripts/template.mk \
	  $(FRAMAC_DATADIR)/analysis-scripts
	$(MKDIR) $(FRAMAC_DATADIR)/compliance
	$(CP) share/compliance/c11_functions.json \
	  share/compliance/c11_headers.json \
	  share/compliance/glibc_functions.json \
	  share/compliance/nonstandard_identifiers.json \
	  share/compliance/posix_identifiers.json \
	  $(FRAMAC_DATADIR)/compliance
	$(MKDIR) $(FRAMAC_DATADIR)/emacs
	$(CP) $(wildcard share/emacs/*.el) $(FRAMAC_DATADIR)/emacs
	$(CP) share/frama-c.rc $(ICONS) $(FRAMAC_DATADIR)
	$(CP) $(THEME_ICONS_DEFAULT) $(FRAMAC_DATADIR)/theme/default
	$(CP) $(THEME_ICONS_COLORBLIND) $(FRAMAC_DATADIR)/theme/colorblind
	$(CP) $(THEME_ICONS_FLAT) $(FRAMAC_DATADIR)/theme/flat
	if [ -d $(EMACS_DATADIR) ]; then \
	  $(CP) $(wildcard share/emacs/*.el) $(EMACS_DATADIR); \
	fi
	$(CP) share/Makefile.dynamic_config.external \
	      $(FRAMAC_DATADIR)/Makefile.dynamic_config
	$(PRINT_INSTALL) C standard library
	$(CP) $(wildcard share/libc/*.c share/libc/*.i share/libc/*.h) \
	      $(FRAMAC_DATADIR)/libc
	$(CP) share/libc/sys/*.[ch] $(FRAMAC_DATADIR)/libc/sys
	$(CP) share/libc/arpa/*.[ch] $(FRAMAC_DATADIR)/libc/arpa
	$(CP) share/libc/net/*.[ch] $(FRAMAC_DATADIR)/libc/net
	$(CP) share/libc/netinet/*.[ch] $(FRAMAC_DATADIR)/libc/netinet
	$(PRINT_INSTALL) binaries
	$(CP) bin/toplevel.$(OCAMLBEST) $(BINDIR)/frama-c$(EXE)
	$(CP) bin/toplevel.byte$(EXE) $(BINDIR)/frama-c.byte$(EXE)
	if [ -x bin/toplevel.top ] ; then \
	  $(CP) bin/toplevel.top $(BINDIR)/frama-c.toplevel$(EXE); \
	fi
	if [ -x bin/viewer.$(OCAMLBEST) ] ; then \
	  $(CP) bin/viewer.$(OCAMLBEST) $(BINDIR)/frama-c-gui$(EXE);\
	fi
	if [ -x bin/viewer.byte$(EXE) ] ; then \
	  $(CP) bin/viewer.byte$(EXE) $(BINDIR)/frama-c-gui.byte$(EXE); \
	fi
	$(CP) bin/ptests.$(OCAMLBEST)$(EXE) \
	      $(BINDIR)/ptests.$(OCAMLBEST)$(EXE)
	$(CP) bin/frama-c-config $(BINDIR)/frama-c-config; \
	if [ -x bin/frama-c-script ] ; then \
		$(CP) bin/frama-c-script $(BINDIR)/frama-c-script; \
	fi
	$(PRINT_INSTALL) config files
	$(CP) $(addprefix ptests/,$(PTESTS_FILES)) $(FRAMAC_LIBDIR)
	$(PRINT_INSTALL) API documentation
	$(MKDIR) $(FRAMAC_DATADIR)/doc/code
	$(CP) $(wildcard $(DOC_GEN_FILES)) $(FRAMAC_DATADIR)/doc/code
	$(PRINT_INSTALL) plug-ins
	if [ -d "$(FRAMAC_PLUGIN)" ]; then \
	  $(CP)  $(PLUGIN_DYN_CMI_LIST) $(PLUGIN_META_LIST) \
		 $(FRAMAC_PLUGINDIR); \
	  $(CP)  $(PLUGIN_DYN_CMO_LIST) $(FRAMAC_PLUGINDIR)/top; \
	  if [ "$(OCAMLBEST)" = "opt" ]; then \
	    $(CP) $(PLUGIN_DYN_CMX_LIST) $(FRAMAC_PLUGINDIR)/top; \
	  fi; \
	fi
	$(PRINT_INSTALL) gui plug-ins
	if [ -d "$(FRAMAC_PLUGIN_GUI)" -a "$(PLUGIN_DYN_GUI_EXISTS)" = "yes" ]; \
	then \
	  $(CP) $(patsubst %.cma,%.cmi,$(PLUGIN_DYN_GUI_CMO_LIST:.cmo=.cmi)) \
		$(PLUGIN_DYN_GUI_CMO_LIST) $(FRAMAC_PLUGINDIR)/gui; \
	  if [ "$(OCAMLBEST)" = "opt" ]; then \
	    $(CP) $(PLUGIN_DYN_GUI_CMX_LIST) $(FRAMAC_PLUGINDIR)/gui; \
	  fi; \
	fi
	$(PRINT_INSTALL) man pages
	$(CP) man/frama-c.1 $(MANDIR)/man1/frama-c.1
	$(CP) man/frama-c.1 $(MANDIR)/man1/frama-c-gui.1

.PHONY: uninstall
uninstall::
	$(PRINT_RM) installed binaries
	$(RM) $(BINDIR)/frama-c* $(BINDIR)/ptests.$(OCAMLBEST)$(EXE)
	$(PRINT_RM) installed shared files
	$(RM) -R $(FRAMAC_DATADIR)
	$(PRINT_RM) installed libraries
	$(RM) -R $(FRAMAC_LIBDIR) $(FRAMAC_PLUGINDIR)
	$(PRINT_RM) installed man files
	$(RM) $(MANDIR)/man1/frama-c.1 $(MANDIR)/man1/frama-c-gui.1

################################
# File headers: license policy #
################################


# Generating headers
####################

# Default header specification files
HEADER_SPEC := $(DEFAULT_HEADER_SPEC)
# The list can be extended by external plugins using PLUGIN_HEADER_SPEC variable
HEADER_SPEC += $(PLUGIN_HEADER_SPEC_LIST)
HEADER_SPEC += ivette/./headers/header_spec.txt
# Default list of header specification files can be overloaded.
HEADER_SPEC_FILE?=$(HEADER_SPEC)

# Default directory (containing subdirectories open-source and close-source)
HEADER_DIRS := $(DEFAULT_HEADER_DIRS)
# The list can be extended by external plugins using PLUGIN_HEADER_DIRS variable
HEADER_DIRS += $(PLUGIN_HEADER_DIRS_LIST)
# Takes into account the kind of distribution (open-souce/close-source)
DISTRIB_HEADER_DIRS?=$(addsuffix /$(DISTRIB_HEADERS),$(HEADER_DIRS))

# List of distributed files allowed to have no entry into the HEADER_SPEC_FILE
HEADER_EXCEPTIONS := $(DEFAULT_HEADER_EXCEPTIONS)
HEADER_EXCEPTIONS += opam/files $(wildcard $(PLUGIN_HEADER_EXCEPTIONS_LIST))

# List of headers that cannot be part of an open-source distribution
CEA_PROPRIETARY_HEADERS := $(DEFAULT_CEA_PROPRIETARY_HEADERS)
CEA_PROPRIETARY_HEADERS += $(PLUGIN_CEA_PROPRIETARY_HEADERS_LIST)

# List of files that cannot be part of an open-source distribution
CEA_PROPRIETARY_FILES := $(DEFAULT_CEA_PROPRIETARY_FILES)
CEA_PROPRIETARY_FILES += $(PLUGIN_CEA_PROPRIETARY_FILES_LIST)

HDRCK=./headers/hdrck$(EXE)

HDRCK_EXTRA?=$(STRICT_HEADERS)
# Can be set to "-exit-on-warning"
ifeq ($(HDRCK_EXTRA),no)
HDRCK_EXTRA:=""
else
ifeq ($(HDRCK_EXTRA),yes)
HDRCK_EXTRA:="-exit-on-warning"
endif
endif

.PHONY: headers

# OPEN_SOURCE: set it to 'no' if you want to apply close source headers.
# STRICT_HEADERS: set it to 'yes' if you want to consider warnings as errors
headers:: $(HDRCK)
	$(PRINT) "|$(OPEN_SOURCE)|$(SPECIFIED_OPEN_SOURCE)|"
	$(PRINT) "Applying $(HDRCK_DISTRIB_HEADERS) headers (OPEN_SOURCE=$(HDRCK_OPEN_SOURCE))..."
	$(PRINT) "- HEADER_SPEC_FILE=$(HEADER_SPEC_FILE)"
	$(PRINT) "- DISTRIB_HEADER_DIRS=$(HDRCK_DISTRIB_HEADER_DIRS)"
	$(HDRCK) \
		$(HDRCK_EXTRA) \
		-update -C . \
		$(addprefix -header-dirs ,$(HDRCK_DISTRIB_HEADER_DIRS)) \
		-headache-config-file ./headers/headache_config.txt \
		$(HEADER_SPEC_FILE)

hdrck: $(HDRCK)

$(HDRCK): headers/hdrck.ml
	$(PRINT_MAKING)	$@
ifeq ($(OCAMLBEST),opt)
	$(OCAMLOPT) str.cmxa unix.cmxa $< -o $@
else
	$(OCAMLC) str.cma unix.cma $< -o $@
endif

hdrck-clean:
	$(RM) $(HDRCK) headers/hdrck.o
	$(RM) headers/hdrck.cmx headers/hdrck.cmi headers/hdrck.cmp

clean:: hdrck-clean

CURRENT_HEADERS?=open-source
CURRENT_HEADER_DIRS?=$(addsuffix /$(CURRENT_HEADERS),$(HEADER_DIRS))

CHECK_NEWLINES:=./bin/check_newlines$(EXE)

$(CHECK_NEWLINES): bin/check_newlines.ml
	$(PRINT_MAKING)	$@
ifeq ($(OCAMLBEST),opt)
	$(OCAMLOPT) unix.cmxa $< -o $@
else
	$(OCAMLC) unix.cma $< -o $@
endif

check-newlines-clean:
	$(RM) $(CHECK_NEWLINES) bin/check_newlines.cm* bin/check_newlines.o

clean:: check-newlines-clean

ISUTF8:=./bin/isutf8$(EXE)

$(ISUTF8): bin/isutf8.ml
	$(PRINT_MAKING)	$@
ifeq ($(OCAMLBEST),opt)
	$(OCAMLOPT) $< -o $@
else
	$(OCAMLC) $< -o $@
endif

isutf8-clean:
	$(RM) $(ISUTF8) bin/isutf8.cm* bin/isutf8.o
clean:: isutf8-clean

BINARY_DISTRIB_FILES := \
  $(sort $(wildcard ivette/src/dome/doc/template/static/fonts/*)) \
  $(sort $(wildcard share/*.ico share/*.png share/theme/*/*.png)) \

FILES_WITHOUT_NEWLINE := \
  $(BINARY_DISTRIB_FILES) \
  VERSION \
  VERSION_CODENAME \

BINARY_DISTRIB_TESTS := \
  tests/misc/oracle/interpreted_automata_dataflow_backward.dot \
  tests/misc/oracle/interpreted_automata_dataflow_forward.dot \

TESTS_WITHOUT_NEWLINE := \
  $(BINARY_DISTRIB_TESTS) \
  tests/spec/unfinished-oneline-acsl-comment.i \

# OPEN_SOURCE: set it to 'yes' if you want to check open source headers
# STRICT_HEADERS: set it to 'yes' if you want to consider warnings as errors
# The target check-headers does the following checks:
# 1. Checks entries of HEADER_SPEC_FILE
# 2. Checks that every DISTRIB_FILES (except HEADER_EXCEPTIONS) have an entry
#    inside HEADER_SPEC_FILE
# 3. Checks that all these files are not under DISTRIB_PROPRIETARY_HEADERS
#    licences
# Also check that distributed files are not encoded in ISO-8859. Do this first,
# because identical headers but with different encodings are not exactly
# easy to distinguish
.PHONY: check-headers
check-headers: $(HDRCK) $(CHECK_NEWLINES) $(ISUTF8)
	$(PRINT) "Checking $(DISTRIB_HEADERS) headers (OPEN_SOURCE=$(OPEN_SOURCE), CURRENT_HEADERS=$(CURRENT_HEADERS))..."
	$(PRINT) "- HEADER_SPEC_FILE=$(HEADER_SPEC_FILE)"
	$(PRINT) "- CURRENT_HEADER_DIRS=$(CURRENT_HEADER_DIRS)"
	$(PRINT) "- FORBIDDEN_HEADERS=$(DISTRIB_PROPRIETARY_HEADERS)"
	# Workaround to avoid "argument list too long" in Cygwin
	$(file >distrib_files.tmp) $(foreach O,$(DISTRIB_FILES),$(file >>distrib_files.tmp,$O))
	$(file >distrib_tests.tmp) $(foreach O,$(DISTRIB_TESTS),$(file >>distrib_tests.tmp,$(subst @, ,$(O))))
	$(file >header_exceptions.tmp) $(foreach O,$(HEADER_EXCEPTIONS),$(file >>header_exceptions.tmp,$O))
	echo "Checking that distributed files terminate with a newline..."
	$(CHECK_NEWLINES) distrib_files.tmp $(FILES_WITHOUT_NEWLINE)
	$(CHECK_NEWLINES) distrib_tests.tmp $(TESTS_WITHOUT_NEWLINE)
	echo "Checking that distributed files do not use iso-8859..."
	$(ISUTF8) distrib_files.tmp $(BINARY_DISTRIB_FILES)
	$(ISUTF8) distrib_tests.tmp $(BINARY_DISTRIB_TESTS)
	echo "Checking headers..."
	$(HDRCK) \
		$(HDRCK_EXTRA) \
		$(addprefix -header-dirs ,$(CURRENT_HEADER_DIRS)) \
		$(addprefix -forbidden-headers ,$(DISTRIB_PROPRIETARY_HEADERS)) \
		-headache-config-file ./headers/headache_config.txt \
		-distrib-file distrib_files.tmp \
		-header-except-file header_exceptions.tmp \
		$(HEADER_SPEC_FILE)
	$(RM) distrib_files.tmp distrib_tests.tmp header_exceptions.tmp

########################################################################
# Makefile is rebuilt whenever Makefile.in or configure.in is modified #
########################################################################

share/Makefile.config: share/Makefile.config.in config.status
	$(PRINT_MAKING) $@
	./config.status --file $@

share/Makefile.dynamic_config: share/Makefile.dynamic_config.internal
	$(PRINT_MAKING) $@
	$(RM) $@
	$(CP) $< $@
	$(CHMOD_RO) $@

config.status: configure
	$(PRINT_MAKING) $@
	./config.status --recheck

configure: configure.in .force-reconfigure
	$(PRINT_MAKING) $@
	autoconf -f

# If 'make clean' has to be performed after 'svn update':
# change '.make-clean-stamp' before 'svn commit'
.make-clean: .make-clean-stamp
	$(TOUCH) $@
	$(QUIET_MAKE) clean

include .make-clean

# force "make clean" to be executed for all users of SVN
force-clean:
	expr `$(CAT) .make-clean-stamp` + 1 > .make-clean-stamp

# force a reconfiguration for all svn users
force-reconfigure:
	expr `$(CAT) .force-reconfigure` + 1 > .force-reconfigure

.PHONY: force-clean force-reconfigure

############
# cleaning #
############

clean-journal:
	$(PRINT_RM) journal
	$(RM) frama_c_journal*

clean-tests:
	$(PRINT_RM) tests
	$(RM) tests/*/*.byte$(EXE) tests/*/*.opt$(EXE) tests/*/*.cm* \
		tests/dynamic/.cm* tests/*/*~ tests/*/#*
	$(RM) tests/*/result/*.*

clean-doc:: $(PLUGIN_LIST:=_CLEAN_DOC)
	$(PRINT_RM) documentation
	$(RM) -r $(DOC_DIR)/html
	$(RM) $(DOC_DIR)/docgen.cm* $(DOC_DIR)/*~
	$(RM) doc/db/*~ doc/db/ocamldoc.sty doc/db/db.tex
	$(RM) doc/training/*/*.cm*
	if [ -f doc/developer/Makefile ]; then \
	  $(MAKE) --silent -C doc/developer clean; \
	fi

clean-gui::
	$(PRINT_RM) gui
	$(RM) src/*/*/*_gui.cm* src/*/*/*_gui.o \
	      src/plugins/gui/*.cm* src/plugins/gui/*.o

clean:: $(PLUGIN_LIST:=_CLEAN) \
		clean-tests clean-journal clean-check-libc
	$(PRINT_RM) lib/plugins
	$(RM) $(addprefix $(PLUGIN_LIB_DIR)/,*.mli *.cm* *.o META.*)
	$(RM) $(addprefix $(PLUGIN_TOP_LIB_DIR)/,*.mli *.cm* *.o *.a)
	$(RM) $(addprefix $(PLUGIN_GUI_LIB_DIR)/,*.mli *.cm* *.o *.a)
	$(PRINT_RM) local installation
	$(RM) lib/*.cm* lib/*.o lib/fc/*.cm* lib/fc/*.o lib/gui/*.cm* lib/*.cm*
	$(PRINT_RM) other sources
	for d in . $(SRC_DIRS) src/plugins/gui share; do \
	  $(RM) $$d/*.cm* $$d/*.o $$d/*.a $$d/*.annot $$d/*~ $$d/*.output \
		$$d/*.annot $$d/\#*; \
	done
	$(PRINT_RM) generated files
	$(RM) $(GENERATED)
	$(PRINT_RM) binaries
	$(RM) bin/toplevel.byte$(EXE) bin/viewer.byte$(EXE) \
		bin/ptests.byte$(EXE) bin/*.opt$(EXE) bin/toplevel.top$(EXE)

smartclean:
	$(MAKE) -f share/Makefile.clean smartclean

# Do NOT use :: for this rule: it is mandatory to remove share/Makefile.config
# as the very last step performed by make (who'll otherwise try to regenerate
# it in the middle of cleaning)
dist-clean distclean: clean clean-doc \
	              $(PLUGIN_LIST:=_DIST_CLEAN)
	$(PRINT_RM) config
	$(RM) share/Makefile.config
	$(RM) config.cache config.log config.h
	$(RM) -r autom4te.cache
	$(PRINT_RM) documentation
	$(RM) $(DOC_DIR)/kernel-doc.ocamldoc
	$(PRINT_RM) dummy plug-ins
	$(RM) src/dummy/*/*.cm* src/dummy/*/*.o src/dummy/*/*.a \
		src/dummy/*/*.annot src/dummy/*/*~ src/dummy/*/*.output \
		src/dummy/*/*.annot src/dummy/*/\#*
	$(RM) $(CHECK_NEWLINES) $(ISUTF8)

ifeq ($(OCAMLWIN32),yes)
# Use Win32 typical resources
share/frama-c.rc: share/frama-c.WIN32.rc
	$(PRINT_MAKING) $@
	$(CP) $^ $@
else
# Use Unix typical resources
share/frama-c.rc: share/frama-c.Unix.rc
	$(PRINT_MAKING) $@
	$(CP) $^ $@
endif

GENERATED+=share/frama-c.rc

##########
# Depend #
##########

PLUGIN_DEP_LIST:=$(PLUGIN_LIST)

.PHONY: depend

# tell make not to remove generated files even if they are only a byproduct
# of making .depend.
.PRECIOUS: $(GENERATED) share/Makefile.dynamic_config

# in case .depend is absent, we will make it. Otherwise, it will be left
# untouched. Only make depend will force a recomputation of dependencies
.depend: $(GENERATED) share/Makefile.dynamic_config
	$(MAKE) depend

depend:: $(GENERATED) share/Makefile.dynamic_config
	$(PRINT_MAKING) .depend
	$(RM) .depend
	$(OCAMLDEP) $(INCLUDES) $(FILES_FOR_OCAMLDEP) > .depend
	$(OCAMLDEP) $(INCLUDES) $(TEST_DIRS_AS_PLUGIN:%=-I tests/%) \
          $(LONELY_TESTS_ML_FILES) >> .depend
	$(CHMOD_RO) .depend

#Used by internal plugins to wait until the *.mli of all the plugins are in
# $(PLUGIN_LIB_DIR) before computing their .depend. Otherwise ocamldep doesn't
# mark inter-plugin dependencies
$(PLUGIN_LIB_DIR)/.placeholders_ready:
	touch $@
ifneq ($(MAKECMDGOALS),clean)
ifneq ($(MAKECMDGOALS),distclean)
ifneq ($(MAKECMDGOALS),smartclean)
ifneq ($(MAKECMDGOALS),depend)
sinclude .depend
endif
endif
endif
endif

#####################
# ptest development #
#####################

.PHONY: ptests

PTESTS_SRC=ptests/ptests_config.ml ptests/ptests.ml

# Do not generate tests/ptests_config if we are compiling a distribution
# that does not contain a 'tests' dir
PTESTS_CONFIG:= $(shell if test -d tests; then echo tests/ptests_config; fi)

ifneq ("$(PTESTS_CONFIG)","")
GENERATED_TESTS:=tests/spec/preprocess_dos.c
else
GENERATED_TESTS:=
endif

tests/spec/preprocess_dos.c: tests/spec/preprocess_dos.c.in \
                             Makefile share/Makefile.config
	$(RM) $@
	$(SED) -e "s|@UNIX2DOS@|$(PP_DOS_UNIX2DOS)|g" \
               -e "s|@DONTRUN@|$(PP_DOS_DONTRUN)|g" \
               $< > $@
	$(CHMOD_RO) $@

ifneq ("$(HAS_UNIX2DOS)","no")
tests/spec/preprocess_dos.c: PP_DOS_UNIX2DOS=$(UNIX2DOS)
tests/spec/preprocess_dos.c: PP_DOS_DONTRUN=
else
tests/spec/preprocess_dos.c: PP_DOS_UNIX2DOS=unix2dos
tests/spec/preprocess_dos.c: PP_DOS_DONTRUN=DONTRUN: no unix2dos found
endif

ptests: bin/ptests.$(OCAMLBEST)$(EXE) $(PTESTS_CONFIG) $(GENERATED_TESTS)

bin/ptests.byte$(EXE): $(PTESTS_SRC)
	$(PRINT_LINKING) $@
	$(OCAMLC) -I ptests -dtypes -thread -g -o $@ \
	    unix.cma threads.cma str.cma dynlink.cma $^

bin/ptests.opt$(EXE): $(PTESTS_SRC)
	$(PRINT_LINKING) $@
	$(OCAMLOPT) -I ptests -dtypes -thread -o $@ \
	    unix.cmxa threads.cmxa str.cmxa dynlink.cmxa $^

GENERATED+=ptests/ptests_config.ml tests/ptests_config $(GENERATED_TESTS)

#######################
# Source distribution #
#######################

.PHONY: src-distrib

STANDALONE_PLUGINS_FILES = \
	$(addprefix src/dummy/hello_world/,hello_world.ml Makefile) \
	$(addprefix src/dummy/untyped_metrics/,count_for.ml Makefile)

DISTRIB_FILES += $(wildcard $(PLUGIN_DISTRIBUTED_LIST)                   \
                    $(PLUGIN_DIST_EXTERNAL_LIST)                         \
                    $(PLUGIN_DIST_DOC_LIST) $(STANDALONE_PLUGINS_FILES))
DISTRIB_FILES:=$(filter-out $(GENERATED) $(PLUGIN_GENERATED_LIST),\
                  $(DISTRIB_FILES))

sinclude ivette/Makefile.distrib

DISTRIB_TESTS += $(wildcard $(PLUGIN_DIST_TESTS_LIST))


SPECIFIED_OPEN_SOURCE:=$(OPEN_SOURCE)
OPEN_SOURCE  ?= no

ifneq ($(OPEN_SOURCE),yes)
# close source version
DISTRIB_HEADERS:=close-source
DISTRIB_PROPRIETARY_HEADERS:=
else
# open source version
DISTRIB_HEADERS:=open-source
# for checking that distributed files aren't under proprietary licence.
DISTRIB_PROPRIETARY_HEADERS:=$(CEA_PROPRIETARY_HEADERS)
# DISTRIB_TESTS contains files that can be distributed without header checking
DISTRIB_TESTS:=$(filter-out $(CEA_PROPRIETARY_FILES) ,\
                  $(DISTRIB_TESTS))
endif

# Set some variables for `headers`target.
ifeq ($(OPEN_SOURCE),$(SPECIFIED_OPEN_SOURCE))
# The OPEN_SOURCE variable is specified. So, use it for `make headers`
HDRCK_OPEN_SOURCE=$(SPECIFIED_OPEN_SOURCE)
HDRCK_DISTRIB_HEADERS=$(DISTRIB_HEADERS)
HDRCK_DISTRIB_HEADER_DIRS=$(DISTRIB_HEADER_DIRS)
else
# The OPEN_SOURCE variable is unspecified. So, use open-source default for `make headers`
HDRCK_OPEN_SOURCE=unspecified
HDRCK_DISTRIB_HEADERS=open-source
HDRCK_DISTRIB_HEADER_DIRS?=$(addsuffix /$(HDRCK_DISTRIB_HEADERS),$(HEADER_DIRS))
endif

# Variables governing the name of the generated .tar.gz.
# Optionally define them as empty to silence warnings about undefined variables
CLIENT ?=

DISTRIB_DIR=tmp
ifeq ("$(CLIENT)","")
VERSION_NAME:=$(VERSION_SAFE)
else
VERSION_NAME:=$(VERSION_SAFE)-$(CLIENT)
endif

DISTRIB?=frama-c-$(VERSION_NAME)-$(VERSION_CODENAME)
CLIENT_DIR=$(DISTRIB_DIR)/$(DISTRIB)


# useful parameters:
# CLIENT: name of the client (in the version number, the archive name, etc)
# DISTRIB: name of the generated tarball and of the root tarball directory
# OPEN_SOURCE: set it to 'yes' if you want to exclude close source files
# note: make headers has to be applied...
src-distrib: $(HDRCK) check-headers
ifeq ("$(CLIENT)","")
	$(PRINT_BUILD) "$(DISTRIB_HEADERS) tarball $(DISTRIB) (OPEN_SOURCE=$(OPEN_SOURCE))"
else
	$(PRINT_BUILD) "$(DISTRIB_HEADERS) tarball $(DISTRIB) for $(CLIENT) (OPEN_SOURCE=$(OPEN_SOURCE))"
endif
	$(RM) -r $(CLIENT_DIR)
	$(MKDIR) -p $(CLIENT_DIR)
	#Workaround to avoid "argument list too long" in Cygwin
	$(file >file_list_to_archive.tmp)
	$(foreach f,$(DISTRIB_FILES) $(DISTRIB_TESTS), \
             $(file >>file_list_to_archive.tmp,$(subst @, ,$(f))))
	$(TAR) -cf - --files-from file_list_to_archive.tmp | $(TAR) -C $(CLIENT_DIR) -xf -
	$(RM) file_list_to_archive.tmp
	$(PRINT_MAKING) files
	(cd $(CLIENT_DIR) ; \
	   echo "$(VERSION_NAME)" > VERSION && \
	   DISTRIB_CONF=yes autoconf > ../../.log.autoconf 2>&1)
	$(MKDIR) $(CLIENT_DIR)/bin
	$(MKDIR) $(CLIENT_DIR)/lib/plugins
	$(MKDIR) $(CLIENT_DIR)/lib/gui
	$(RM) ../$(DISTRIB).tar.gz
	$(PRINT) "Updating files to archive with $(DISTRIB_HEADERS) headers"
	$(HDRCK) \
		$(HDRCK_EXTRA) \
		-update -C $(CLIENT_DIR) \
		$(addprefix -header-dirs ,$(DISTRIB_HEADER_DIRS)) \
		-headache-config-file ./headers/headache_config.txt \
		$(HEADER_SPEC_FILE)
	$(PRINT_TAR) $(DISTRIB).tar.gz
	(cd $(DISTRIB_DIR); $(TAR) cf - \
			--numeric-owner --owner=0 --group=0 --sort=name \
			--mtime="$$(date +"%F") Z" --mode='a+rw' \
			--exclude "*autom4te.cache*" \
			$(DISTRIB) | gzip -9 -n > ../$(DISTRIB).tar.gz \
	)
	$(PRINT_RM) $(DISTRIB_DIR)
	$(RM) -r $(DISTRIB_DIR)

doc-companions:
	$(MAKE) -C doc/developer archives VERSION=$(VERSION_SAFE)-$(VERSION_CODENAME)
	$(MV) doc/developer/hello-$(VERSION_SAFE)-$(VERSION_CODENAME).tar.gz hello-$(VERSION_SAFE)-$(VERSION_CODENAME).tar.gz
	$(ECHO) "The documentation companion hello-$(VERSION_SAFE)-$(VERSION_CODENAME).tar.gz has been generated."

clean-distrib: dist-clean
	$(PRINT_RM) distrib
	$(RM) -r $(DISTRIB_DIR) $(DISTRIB).tar.gz

create_lib_to_install_list = $(addprefix $(FRAMAC_LIB)/,$(call map,notdir,$(1)))

byte:: bin/toplevel.byte$(EXE) lib/fc/frama-c.cma share/Makefile.dynamic_config \
	$(call create_lib_to_install_list,$(LIB_BYTE_TO_INSTALL)) \
      $(PLUGIN_META_LIST) lib/fc/META.frama-c

opt:: bin/toplevel.opt$(EXE) lib/fc/frama-c.cmxa share/Makefile.dynamic_config \
	$(call create_lib_to_install_list,$(LIB_OPT_TO_INSTALL)) \
	$(filter %.o %.cmi,\
	   $(call create_lib_to_install_list,$(LIB_BYTE_TO_INSTALL))) \
      $(PLUGIN_META_LIST) lib/fc/META.frama-c

top: bin/toplevel.top$(EXE) \
	$(call create_lib_to_install_list,$(LIB_BYTE_TO_INSTALL)) \
      $(PLUGIN_META_LIST)

##################
# Copy in lib/fc #
##################

define copy_in_lib
$(FRAMAC_LIB)/$(notdir $(1)): $(1)
	$(MKDIR) $(FRAMAC_LIB)
	$(CP) $$< $$@

endef
$(eval $(foreach file,$(LIB_BYTE_TO_INSTALL),$(call copy_in_lib,$(file))))
$(eval $(foreach file,$(LIB_OPT_TO_INSTALL),$(call copy_in_lib,$(file))))

################
# Generic part #
################
$(NON_OPAQUE_DEPS:%=%.cmx): OFLAGS := $(OFLAGS) -w -58

$(CROWBAR_AFL_TARGET:%=%.cmx): OFLAGS:=$(OFLAGS) -afl-instrument

include share/Makefile.generic

###############################################################################
# Local Variables:
# compile-command: "make"
# End: