# This is a generically useful Makefile for F* that is self-contained # # We expect: # 1. `fstar.exe` to be in PATH (alternatively, you can also set # $FSTAR_HOME to be set to your F* repo/install directory) # # 2. `cargo`, `rustup`, `hax` and `jq` to be installed and in PATH. # # 3. the extracted Cargo crate to have "hax-lib" as a dependency: # `hax-lib = { version = "0.1.0-pre.1", git = "https://github.com/hacspec/hax"}` # # Optionally, you can set `HACL_HOME`. # # ROOTS contains all the top-level F* files you wish to verify # The default target `verify` verified ROOTS and its dependencies # To lax-check instead, set `OTHERFLAGS="--lax"` on the command-line # # To make F* emacs mode use the settings in this file, you need to # add the following lines to your .emacs # # (setq-default fstar-executable "/bin/fstar.exe") # (setq-default fstar-smt-executable "/bin/z3") # # (defun my-fstar-compute-prover-args-using-make () # "Construct arguments to pass to F* by calling make." # (with-demoted-errors "Error when constructing arg string: %S" # (let* ((fname (file-name-nondirectory buffer-file-name)) # (target (concat fname "-in")) # (argstr (car (process-lines "make" "--quiet" target)))) # (split-string argstr)))) # (setq fstar-subp-prover-args #'my-fstar-compute-prover-args-using-make) # PATH_TO_CHILD_MAKEFILE := "$(abspath $(firstword $(MAKEFILE_LIST)))" PATH_TO_TEMPLATE_MAKEFILE := "$(abspath $(lastword $(MAKEFILE_LIST)))" HACL_HOME ?= $(HOME)/.hax/hacl_home # Expand variable FSTAR_BIN_DETECT now, so that we don't run this over and over FSTAR_BIN_DETECT := $(if $(shell command -v fstar.exe), fstar.exe, $(FSTAR_HOME)/bin/fstar.exe) FSTAR_BIN ?= $(FSTAR_BIN_DETECT) GIT_ROOT_DIR := $(shell git rev-parse --show-toplevel)/ CACHE_DIR ?= ${GIT_ROOT_DIR}.fstar-cache/checked HINT_DIR ?= ${GIT_ROOT_DIR}.fstar-cache/hints # Makes command quiet by default Q ?= @ # Verify the required executable are in PATH EXECUTABLES = cargo cargo-hax jq K := $(foreach exec,$(EXECUTABLES),\ $(if $(shell which $(exec)),some string,$(error "No $(exec) in PATH"))) export ANSI_COLOR_BLUE=\033[34m export ANSI_COLOR_RED=\033[31m export ANSI_COLOR_BBLUE=\033[1;34m export ANSI_COLOR_GRAY=\033[90m export ANSI_COLOR_TONE=\033[35m export ANSI_COLOR_RESET=\033[0m ifdef NO_COLOR export ANSI_COLOR_BLUE= export ANSI_COLOR_RED= export ANSI_COLOR_BBLUE= export ANSI_COLOR_GRAY= export ANSI_COLOR_TONE= export ANSI_COLOR_RESET= endif # The following is a bash script that discovers F* libraries. # Due to incompatibilities with make 4.3, I had to make a "oneliner" bash script... define FINDLIBS : "Prints a path if and only if it exists. Takes one argument: the path."; \ function print_if_exists() { \ if [ -d "$$1" ]; then \ echo "$$1"; \ fi; \ } ; \ : "Asks Cargo all the dependencies for the current crate or workspace,"; \ : "and extract all "root" directories for each. Takes zero argument."; \ function dependencies() { \ cargo metadata --format-version 1 | \ jq -r ".packages | .[] | .manifest_path | split(\"/\") | .[:-1] | join(\"/\")"; \ } ; \ : "Find hax libraries *around* a given path. Takes one argument: the"; \ : "path."; \ function find_hax_libraries_at_path() { \ path="$$1" ; \ : "if there is a [proofs/fstar/extraction] subfolder, then that s a F* library" ; \ print_if_exists "$$path/proofs/fstar/extraction" ; \ : "Maybe the [proof-libs] folder of hax is around?" ; \ MAYBE_PROOF_LIBS=$$(realpath -q "$$path/../proof-libs/fstar") ; \ if [ $$? -eq 0 ]; then \ print_if_exists "$$MAYBE_PROOF_LIBS/core" ; \ print_if_exists "$$MAYBE_PROOF_LIBS/rust_primitives" ; \ fi ; \ } ; \ { while IFS= read path; do \ find_hax_libraries_at_path "$$path"; \ done < <(dependencies) ; } | sort -u endef export FINDLIBS FSTAR_INCLUDE_DIRS_EXTRA ?= FINDLIBS_OUTPUT := $(shell bash -c '${FINDLIBS}') FSTAR_INCLUDE_DIRS = $(HACL_HOME)/lib $(FSTAR_INCLUDE_DIRS_EXTRA) $(FINDLIBS_OUTPUT) ../models # Make sure FSTAR_INCLUDE_DIRS has the `proof-libs`, print hints and # an error message otherwise ifneq (,$(findstring proof-libs/fstar,$(FSTAR_INCLUDE_DIRS))) else K += $(info ) ERROR := $(shell printf '${ANSI_COLOR_RED}Error: could not detect `proof-libs`!${ANSI_COLOR_RESET}') K += $(info ${ERROR}) ERROR := $(shell printf ' > Do you have `${ANSI_COLOR_BLUE}hax-lib${ANSI_COLOR_RESET}` in your `${ANSI_COLOR_BLUE}Cargo.toml${ANSI_COLOR_RESET}` as a ${ANSI_COLOR_BLUE}git${ANSI_COLOR_RESET} or ${ANSI_COLOR_BLUE}path${ANSI_COLOR_RESET} dependency?') K += $(info ${ERROR}) ERROR := $(shell printf ' ${ANSI_COLOR_BLUE}> Tip: you may want to run `cargo add --git https://github.com/hacspec/hax hax-lib`${ANSI_COLOR_RESET}') K += $(info ${ERROR}) K += $(info ) K += $(error Fatal error: `proof-libs` is required.) endif .PHONY: all verify clean all: $(Q)rm -f .depend $(Q)$(MAKE) -f $(PATH_TO_CHILD_MAKEFILE) .depend hax.fst.config.json verify all-keep-going: $(Q)rm -f .depend $(Q)$(MAKE) -f $(PATH_TO_CHILD_MAKEFILE) --keep-going .depend hax.fst.config.json verify # If $HACL_HOME doesn't exist, clone it ${HACL_HOME}: $(Q)mkdir -p "${HACL_HOME}" $(info Cloning Hacl* in ${HACL_HOME}...) git clone --depth 1 https://github.com/hacl-star/hacl-star.git "${HACL_HOME}" $(info Cloning Hacl* in ${HACL_HOME}... done!) # If no any F* file is detected, we run hax ifeq "$(wildcard *.fst *fsti)" "" $(shell cargo hax into fstar) endif # By default, we process all the files in the current directory ROOTS ?= $(wildcard *.fst *fsti) ADMIT_MODULES ?= ADMIT_MODULE_FLAGS ?= --admit_smt_queries true # Can be useful for debugging purposes FINDLIBS.sh: $(Q)echo '${FINDLIBS}' > FINDLIBS.sh include-dirs: $(Q)bash -c '${FINDLIBS}' FSTAR_FLAGS = \ --warn_error -321-331-241-274-239-271 \ --ext context_pruning --z3version 4.13.3 --query_stats \ --cache_checked_modules --cache_dir $(CACHE_DIR) \ --already_cached "+Prims+FStar+LowStar+C+Spec.Loops+TestLib" \ $(addprefix --include ,$(FSTAR_INCLUDE_DIRS)) FSTAR := $(FSTAR_BIN) $(FSTAR_FLAGS) .depend: $(HINT_DIR) $(CACHE_DIR) $(ROOTS) $(HACL_HOME) @$(FSTAR) --dep full $(ROOTS) --extract '* -Prims -LowStar -FStar' > $@ include .depend $(HINT_DIR) $(CACHE_DIR): $(Q)mkdir -p $@ define HELPMESSAGE echo "hax' default Makefile for F*" echo "" echo "The available targets are:" echo "" function target() { printf ' ${ANSI_COLOR_BLUE}%-20b${ANSI_COLOR_RESET} %s\n' "$$1" "$$2" } target "all" "Verify every F* files (stops whenever an F* fails first)" target "all-keep-going" "Verify every F* files (tries as many F* module as possible)" target "" "" target "run/${ANSI_COLOR_TONE} " 'Runs F* on `MyModule.fst` only' target "" "" target "vscode" 'Generates a `hax.fst.config.json` file' target "${ANSI_COLOR_TONE}${ANSI_COLOR_BLUE}-in " 'Useful for Emacs, outputs the F* prefix command to be used' target "" "" target "clean" 'Cleanup the target' target "include-dirs" 'List the F* include directories' target "" "" target "describe" 'List the F* root modules, and describe the environment.' echo "" echo "Variables:" target "NO_COLOR" "Set to anything to disable colors" target "ADMIT_MODULES" "List of modules where F* will assume every SMT query" target "FSTAR_INCLUDE_DIRS_EXTRA" "List of extra include F* dirs" endef export HELPMESSAGE describe: @printf '${ANSI_COLOR_BBLUE}F* roots:${ANSI_COLOR_RESET}\n' @for root in ${ROOTS}; do \ filename=$$(basename -- "$$root") ;\ ext="$${filename##*.}" ;\ noext="$${filename%.*}" ;\ printf "${ANSI_COLOR_GRAY}$$(dirname -- "$$root")/${ANSI_COLOR_RESET}%s${ANSI_COLOR_GRAY}.${ANSI_COLOR_TONE}%s${ANSI_COLOR_RESET}%b\n" "$$noext" "$$ext" $$([[ "${ADMIT_MODULES}" =~ (^| )$$root($$| ) ]] && echo '${ANSI_COLOR_RED}\t[ADMITTED]${ANSI_COLOR_RESET}'); \ done @printf '\n${ANSI_COLOR_BBLUE}Environment:${ANSI_COLOR_RESET}\n' @printf ' - ${ANSI_COLOR_BLUE}HACL_HOME${ANSI_COLOR_RESET} = %s\n' '${HACL_HOME}' @printf ' - ${ANSI_COLOR_BLUE}FSTAR_BIN${ANSI_COLOR_RESET} = %s\n' '${FSTAR_BIN}' @printf ' - ${ANSI_COLOR_BLUE}GIT_ROOT_DIR${ANSI_COLOR_RESET} = %s\n' '${GIT_ROOT_DIR}' @printf ' - ${ANSI_COLOR_BLUE}CACHE_DIR${ANSI_COLOR_RESET} = %s\n' '${CACHE_DIR}' @printf ' - ${ANSI_COLOR_BLUE}HINT_DIR${ANSI_COLOR_RESET} = %s\n' '${HINT_DIR}' @printf ' - ${ANSI_COLOR_BLUE}ADMIT_MODULE_FLAGS${ANSI_COLOR_RESET} = %s\n' '${ADMIT_MODULE_FLAGS}' @printf ' - ${ANSI_COLOR_BLUE}FSTAR_INCLUDE_DIRS_EXTRA${ANSI_COLOR_RESET} = %s\n' '${FSTAR_INCLUDE_DIRS_EXTRA}' help: ;@bash -c "$$HELPMESSAGE" h: ;@bash -c "$$HELPMESSAGE" HEADER = $(Q)printf '${ANSI_COLOR_BBLUE}[CHECK] %s ${ANSI_COLOR_RESET}\n' "$(basename $(notdir $@))" run/%: | .depend $(HINT_DIR) $(CACHE_DIR) $(HACL_HOME) ${HEADER} $(Q)$(FSTAR) $(OTHERFLAGS) $(@:run/%=%) VERIFIED_CHECKED = $(addsuffix .checked, $(addprefix $(CACHE_DIR)/,$(ROOTS))) ADMIT_CHECKED = $(addsuffix .checked, $(addprefix $(CACHE_DIR)/,$(ADMIT_MODULES))) $(ADMIT_CHECKED): $(Q)printf '${ANSI_COLOR_BBLUE}[${ANSI_COLOR_TONE}ADMIT${ANSI_COLOR_BBLUE}] %s ${ANSI_COLOR_RESET}\n' "$(basename $(notdir $@))" $(Q)$(FSTAR) $(OTHERFLAGS) $(ADMIT_MODULE_FLAGS) $< $(ENABLE_HINTS) --hint_file $(HINT_DIR)/$(notdir $*).hints || { \ echo "" ; \ exit 1 ; \ } $(Q)printf "\n\n" $(CACHE_DIR)/%.checked: | .depend $(HINT_DIR) $(CACHE_DIR) $(HACL_HOME) ${HEADER} $(Q)$(FSTAR) $(OTHERFLAGS) $< $(ENABLE_HINTS) --hint_file $(HINT_DIR)/$(notdir $*).hints || { \ echo "" ; \ exit 1 ; \ } touch $@ $(Q)printf "\n\n" verify: $(VERIFIED_CHECKED) $(ADMIT_CHECKED) # Targets for Emacs %.fst-in: $(info $(FSTAR_FLAGS) \ $(ENABLE_HINTS) --hint_file $(HINT_DIR)/$(basename $@).fst.hints) %.fsti-in: $(info $(FSTAR_FLAGS) \ $(ENABLE_HINTS) --hint_file $(HINT_DIR)/$(basename $@).fsti.hints) # Targets for VSCode hax.fst.config.json: .depend $(Q)echo "$(FSTAR_INCLUDE_DIRS)" | jq --arg fstar "$(FSTAR_BIN)" -R 'split(" ") | {fstar_exe: $$fstar | gsub("^\\s+|\\s+$$";""), include_dirs: .}' > $@ vscode: $(Q)rm -f .depend $(Q)$(MAKE) -f $(PATH_TO_CHILD_MAKEFILE) hax.fst.config.json SHELL=bash # Clean target clean: rm -rf $(CACHE_DIR)/* rm *.fst