# --------- # ocaml env # --------- OCAML_FLAGS?=-w @a-4-6-7-9-26-27-29 OCAMLCC_FLAGS?=-ccopt -Werror -ccopt -Wall -ccopt -Wextra OCAMLCC_D_FLAGS?=-ccopt -Wno-error override OCAMLDEP_FLAGS:=-ml-synonym .cmi -ml-synonym .mll export OCAMLRUNPARAM # -------- # make env # -------- camlcase=$(shell pack=$1; printf %c $${pack} | tr '[a-z]' '[A-Z]'; printf %s $${pack\#?}) dirname=$(patsubst %/,%,$(dir $1)) filter-dir=$(patsubst %/.,%,$(wildcard $(patsubst %,%/.,$1))) filter-dir-out=$(foreach x,$1,$(if $(call filter-dir,$x),,$x)) flock=flock $1 $2 -c "$(subst ",\",$(subst \,\\,$3))" flocks=$(if $2,$(call \ flock,$1,$(firstword $2),$(call \ flocks,$1,$(wordlist 2,$(words $2),$2),$3)),$3) GNUmakefile.inc?=GNUmakefile.inc if_arg=$(if $2,$1 $2) is_parallel=$(findstring j,$(value MAKEFLAGS)) map=$(foreach x,$2,$(call $1,$x)) mul=$(foreach p,$1,$(foreach s,$2,$p$s)) rev=$(if $1,$(call \ rev,$(wordlist 2,$(words $1),$1))) $(firstword $1) uniq=$(if $1,$(call \ uniq,$(wordlist 2,$(words $1),$1),$2 $(if \ $(filter $(firstword $1),$2),,$(firstword $1)))\ ,$2) ,:=, .SECONDARY: .SUFFIXES: MAKEFLAGS += -r .SHELLFLAGS += -e # ----- # clean # ----- rmw=$(call if_arg,rm,$(wildcard $1)) clean+=$(or $(filter c,$(MAKECMDGOALS)),$(filter %/c,$(MAKECMDGOALS)),$(filter clean,$(MAKECMDGOALS)),$(filter %/clean,$(MAKECMDGOALS))) cleaner+=$(or $(filter cc,$(MAKECMDGOALS)),$(filter %/cc,$(MAKECMDGOALS)),$(filter cleaner,$(MAKECMDGOALS)),$(filter %/cleaner,$(MAKECMDGOALS))) LIBOCAML_MAKE_NO_DEPS+=$(or $(clean),$(cleaner)) %.ml/clean: %.mll $(call rmw,$(@:/clean=)) %.ml/clean: %.o/clean: $(call rmw,$(@:/clean=) $*.s $(if $(cleaner),$*.d)) %.a/clean: $(call rmw,$(@:/clean=)) %.cm/clean: %.ml/clean $(call rmw,$*.cmo $*.cmx $*.cmxs $*.o $*.s $*.startup.s $*.cmi $(if $(cleaner),$*.d)) %.ca/clean: $(call rmw,$*.cma $*.cmxa $*.a) %.cb/clean: $(call rmw,$*.byte $*.native) # --------------------- # ocaml GNUmakefile.inc # --------------------- define make_GNUmakefile.inc d := $1 dir_ptr := $$(dir_ptr).level dirstack_$$(dir_ptr) := $$d Pack := $$(if $$(Pack),$$(Pack).)$$(call camlcase,$$(notdir $1)) targets := include $1/$(GNUmakefile.inc) ifeq ($$(LIBOCAML_MAKE_NO_DEPS),) -include $$(wildcard $$(o:.o=.d) $$(cm:.cm=.d)) endif dirs := $$(call filter-dir,$$(filter-out $1,$$(call uniq,$$(basename $$(targets))))) $1/c $1/clean: clean:=clean $1/cc $1/cleaner: cleaner:=cleaner $1/b $1/byte: byte:=byte $1/n $1/native: native:=native $1/all: byte:=byte $1/all: native:=native # NOTE: no .PHONY: to trigger %/clean $1/c $1/clean: $$(addsuffix /clean,$$(targets)) $1/cc $1/cleaner: $1/clean ifeq ($$(LIBOCAML_MAKE_NO_DEPS),) targets := $$(foreach t,$$(targets), \ $$t \ $$(if $$(filter %.o,$$t)$$(filter %.cm,$$t) \ ,$$(if $$(wildcard $$(basename $$t)),,$$(basename $$t).d))) endif $1/b $1/byte: $$(patsubst %.cb,%.byte, $$(patsubst %.ca,%.cma, $$(patsubst %.cm,%.cmo,$$(targets)))) $1/n $1/native: $$(patsubst %.cb,%.native,$$(patsubst %.ca,%.cmxa,$$(patsubst %.cm,%.cmx,$$(targets)))) $1/all: $1/byte $1/native $1/xb $1/exec-byte: $$(cb:.cb=.byte.exec) $1/xn $1/exec-native: $$(cb:.cb=.native.exec) $$(foreach d,$$(dirs), \ $$(eval $1/c $1/clean: $$d/clean) \ $$(eval $1/cc $1/cleaner: $$d/cleaner) \ $$(eval $1/b $1/byte: $$d/byte) \ $$(eval $1/n $1/native: $$d/native) \ $$(eval $1/xb $1/exec-byte: $$d/exec-byte) \ $$(eval $1/xn $1/exec-native: $$d/exec-native) \ $$(call include_GNUmakefile.inc,$$d)) Pack := $$(if $$(findstring .,$$(Pack)),$$(basename $$(Pack)),) d := $$(dirstack_$$(dir_ptr)) dir_ptr := $$(basename $$(dir_ptr)) endef include_GNUmakefile.inc = $(eval $(call make_GNUmakefile.inc,$(or $1,.))) # ---------- # ocaml deps # ---------- %.d: %.ml errnos=`{ { ocamlfind ocamldep $(OCAMLDEP_FLAGS) $(OCAML_DEPS_FLAGS) $< || echo >&3 0:$$?; } | \ { sed >$@ -e 's/^[^ ]*\.cmx/&s \\\\\n&/' -e 's/ :/:/' || echo >&3 1:$$?; } \ } 3>&1` && [ -z "$$errnos" ] || { rm $@ && false; } # ------------ # ocaml object # ------------ o=$(filter %.o,$(targets)) %.o: %.c ocamlfind ocamlc -ccopt -o -ccopt $@ -c $(OCAML_FLAGS) $(OCAMLCC_FLAGS) $(if $(DEBUG),-ccopt -Wa$(,)-aln=$*.s) $< %.d: %.c ocamlfind ocamlc -ccopt -o -ccopt $@ -c -nostdlib -ccopt -MM -ccopt -MT"$@\\ $(@:d=o)" -ccopt -MF$@ $(OCAML_FLAGS) $(OCAMLCC_FLAGS) $(OCAMLCC_D_FLAGS) $< # ----------- # ocaml lexer # ----------- %.ml: %.mll ocamllex $< touch -r $< $@ # NOTE: to avoid rebuilding %.d # ------------ # ocaml module # ------------ cm=$(filter %.cm,$(targets)) lock_ro_cmi=$(if $(and $(byte),$(native),$(is_parallel)),$(call \ flocks,-s,$(patsubst %.cmx,%.cmi,$(patsubst %.cmo,%.cmi, \ $(filter %.cmi,$^) $(filter %.cmo,$^) $(filter %.cmx,$^))),$1),$1) lock_rw_cmi=$(if $(and $(byte),$(native),$(is_parallel)),$(call \ flock,-x,$*.cmi,$(call \ flocks,-s,$(patsubst %.cmx,%.cmi,$(patsubst %.cmo,%.cmi, \ $(filter %.cmi,$^) $(filter %.cmo,$^) $(filter %.cmx,$^))),$1)),$1) %.cmo %.cmi: %.ml $(call lock_rw_cmi,ocamlfind ocamlc -c $(OCAML_FLAGS) $(OCAML_DEPS_FLAGS) $(OCAMLC_FLAGS) $(filter %.ml,$^)) %.cmx %cmi %.o: %.ml $(call lock_rw_cmi,ocamlfind ocamlopt -o $*.cmx -c $(OCAML_FLAGS) $(OCAML_DEPS_FLAGS) $(OCAMLOPT_FLAGS) $(filter %.ml,$^)) %.cmxs %.cmx %.cmi %.o: %.ml $(call lock_rw_cmi,ocamlfind ocamlopt -shared -o $*.cmxs $(OCAML_FLAGS) $(OCAML_DEPS_FLAGS) $(OCAMLOPT_FLAGS) $(filter %.ml,$^)) # ---------- # ocaml pack # ---------- %.cmo %.cmi: | % $(call lock_rw_cmi,ocamlfind ocamlc -pack -o $*.cmo $(OCAML_FLAGS) $(OCAMLC_FLAGS) $(OCAMLC_PACK_FLAGS) $(filter %.cmo,$^)) %.cmx %.cmi %.o: | % $(call lock_rw_cmi,ocamlfind ocamlopt -pack -o $*.cmx $(OCAML_FLAGS) $(OCAMLOPT_FLAGS) $(OCAMLOPT_PACK_FLAGS) $(filter %.cmx,$^)) # -------------- # object archive # -------------- o_of_a=$(patsubst %,$(1:.a=)/%,$(filter %.o,$(shell ar t $1))) $(call map,o_of_a,$(patsubst %,$(1:.a=)/%,$(filter %.a,$(shell ar t $1)))) a=$(filter %.a,$(targets)) %.a: $(call flock,-x,$(dir $*),ar cSru$(AR_FLAGS) $@ $(call \ map,o_of_a,$(filter %.a,$^)) $(filter %.o,$^) && ranlib $(RANLIB_FLAGS) $@) dll%.so: CC=$(shell ocamlfind ocamlc -config | sed -ne 's/^native_c_compiler: *//p') dll%.so: lib%.a $(CC) -o $@ -shared $(filter %.o,$(shell ar t $<)) # ------------- # ocaml archive # ------------- ca=$(filter %.ca,$(targets)) %.cma: $(call lock_ro_cmi,ocamlfind ocamlc -a -o $@ $(OCAML_FLAGS) $(OCAMLC_FLAGS) $(OCAML_ARCH_FLAGS) $(OCAMLC_ARCH_FLAGS) $(filter %.cmo,$^)) %.cmxa: $(call lock_ro_cmi,ocamlfind ocamlopt -a -o $@ $(OCAML_FLAGS) $(OCAMLOPT_FLAGS) $(OCAML_ARCH_FLAGS) $(OCAMLOPT_ARCH_FLAGS) $(filter %.cmx,$^)) # ------------ # ocaml binary # ------------ cb=$(filter %.cb,$(targets)) %.byte: $(call lock_ro_cmi,ocamlfind ocamlc -o $@ -linkpkg $(OCAML_FLAGS) $(OCAML_DEPS_FLAGS) $(OCAMLC_FLAGS) $(OCAML_BIN_FLAGS) $(OCAMLC_BIN_FLAGS) $(filter %.cmo,$^)) %.native: $(call lock_ro_cmi,ocamlfind ocamlopt -o $@ -linkpkg $(OCAML_FLAGS) $(OCAML_DEPS_FLAGS) $(OCAMLOPT_FLAGS) $(OCAML_BIN_FLAGS) $(OCAMLOPT_BIN_FLAGS) $(filter %.cmx,$^)) # ----------------- # binary capability # ----------------- %.caps: setcap=$(shell PATH="$$PATH:/sbin"; which setcap) %.caps: % $(if $(caps),[ ! -x "$(setcap)" ] || $(setcap) "$(caps)" $*) # ---------------- # binary execution # ---------------- .PHONY: %.byte.exec %.native.exec %.byte.exec: %.byte PATH=".:$$PATH"; \ $< $(EXEC_FLAGS) $(EXEC_BYTE_FLAGS) %.native.exec: %.native PATH=".:$$PATH"; \ $< $(EXEC_FLAGS) $(EXEC_NATIVE_FLAGS) # --------------- # ocaml debugging # --------------- .PHONY: debug export debug debug: $(filter-out debug,$(MAKECMDGOALS)) debug: override OCAML_FLAGS+=-verbose debug: override OCAMLC_FLAGS+=-g -principal debug: override OCAMLCC_FLAGS+=-ccopt -ggdb3 debug: override OCAMLDEP_FLAGS+=-verbose debug: override OCAMLOPT_FLAGS+=-ccopt -ggdb3 debug: override OCAMLRUNPARAM+=b debug: override debug:=debug