# --------- # ocaml env # --------- OCAML_FLAGS?=-w @a-4-6-7-9-23-26-27-29-41 OCAMLCC_FLAGS?=-ccopt -Werror -ccopt -Wall -ccopt -Wextra OCAMLCC_D_FLAGS?=-ccopt -Wno-error 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\#?}) ocamlc_version:=$(shell ocamlfind ocamlc -version) ocamlopt_version:=$(shell ocamlfind ocamlopt -version) filter-flag=$(if $2 \ ,$(if $(patsubst $1,,$(firstword $2)), \ $(call filter-flag,$1,$(wordlist 2,$(words $2),$2)), \ $(wordlist 2,2,$2) $(call filter-flag,$1,$(wordlist 3,$(words $2),$2))) \ ,) dllpath-pkg-recursive=$(call filter-flag,-dllpath,$(shell \ OCAMLPATH='$(OCAMLPATH)' \ OCAMLFIND_COMMANDS='ocamlc=echo' \ ocamlfind ocamlc -dllpath-all $1)) 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)) cleaner+=$(or $(filter cc,$(MAKECMDGOALS)),$(filter %/cc,$(MAKECMDGOALS)),$(filter cleaner,$(MAKECMDGOALS)),$(filter %/cleaner,$(MAKECMDGOALS))) clean+=$(or $(filter c,$(MAKECMDGOALS)),$(filter %/c,$(MAKECMDGOALS)),$(filter clean,$(MAKECMDGOALS)),$(filter %/clean,$(MAKECMDGOALS)))$(cleaner) LIBOCAML_MAKE_NO_DEPS+=$(or $(clean),$(cleaner)) %.ml/clean: %.mll $(call rmw,$(@:/clean=)) %.ml/clean: %.mli/clean: $(call rmw,$(if $(cleaner),$*.d)) %.o/clean: $(call rmw,$(@:/clean=) $*.s $(if $(cleaner),$*.d)) %.a/clean: %.a.clean lib%.a.clean: dll%.so.clean $(call rmw,$(@:.clean=)) %.so/clean: %.so.clean dll%.so.clean: lib%.so.clean $(call rmw,$(@:.clean=)) lib%.so.clean: $(call rmw,$(@:.clean=)) %.cm/clean %.cd/clean: %.ml/clean $(call rmw,$*.cmo $*.cmx $*.cmxs $*.o $*.s $*.startup.s $*.cmi $(if $(cleaner),$*.d)) %.cmi/clean: %.mli/clean $(call rmw,$*.cmi $(if $(cleaner),$*.d)) %.ca/clean: $(call rmw,$*.cma $*.cmxa $*.a) %.cb/clean: $(call rmw,$*.byte $*.native) %/configure/clean: $(call rmw,$(@:/clean=.*)) # --------------------- # 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 := ifneq ($$(wildcard $1/configure),) ifeq ($$(clean),) include $1/configure.make endif endif include $1/$(GNUmakefile.inc) ifeq ($$(LIBOCAML_MAKE_NO_DEPS),) -include $$(wildcard $$(o:.o=.d) $$(cm:.cm=.d) $$(cd:.cd=.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 ifneq ($(ocamlc_version),) $1/all: byte:=byte endif ifneq ($(ocamlopt_version),) $1/all: native:=native endif # NOTE: no .PHONY: to trigger %/clean $1/clean: $$(addsuffix /clean,$$(targets)) $1/clean: $1/configure/clean $1/cleaner: $1/clean $1/c: $1/clean $1/cc: $1/cleaner ifeq ($$(LIBOCAML_MAKE_NO_DEPS),) targets := $$(foreach t,$$(targets), \ $$t \ $$(if $$(filter %.o,$$t)$$(filter %.cm,$$t)$$(filter %.cd,$$t) \ ,$$(if $$(wildcard $$(basename $$t)),,$$(basename $$t).d))) endif $1/byte: $$(patsubst %.cb,%.byte, $$(patsubst %.ca,%.cma, $$(patsubst %.cm,%.cmo,$$(patsubst %.cd,%.cmo, $$(targets))))) $1/native: $$(patsubst %.cb,%.native,$$(patsubst %.ca,%.cmxa,$$(patsubst %.cm,%.cmx,$$(patsubst %.cd,%.cmxs,$$(targets))))) $1/all: $(if $(ocamlc_version),$1/byte) $(if $(ocamlopt_version),$1/native) $1/exec-byte: $$(cb:.cb=.byte.exec) $1/exec-native: $$(cb:.cb=.native.exec) $1/b: $1/byte $1/n: $1/native $1/xb: $1/exec-byte $1/xn: $1/exec-native $$(foreach d,$$(dirs), \ $$(eval $1/clean: $$d/clean) \ $$(eval $1/cleaner: $$d/cleaner) \ $$(eval $1/byte: $$d/byte) \ $$(eval $1/native: $$d/native) \ $$(eval $1/exec-byte: $$d/exec-byte) \ $$(eval $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 configure # --------------- %/configure.c %/configure.log %/configure.make %/configure.ml %/configure.mlp): %/configure ocaml $< $(CONFIGURE_FLAGS) # ---------- # ocaml deps # ---------- %.d: %.mli %.ml ocamlfind ocamldep $(OCAML_DEPS_FLAGS) $(OCAMLDEP_FLAGS) $*.mli >$@ || { rm $@ && false; } errnos=`{ { ocamlfind ocamldep $(OCAML_DEPS_FLAGS) $(OCAMLDEP_FLAGS) $*.ml || echo >&3 0:$$?; } | \ { sed >>$@ -e 's/^[^ ]*\.cmx/&s \\\\\n&/' -e 's/ :/:/' || echo >&3 1:$$?; } \ } 3>&1` && [ -z "$$errnos" ] || { rm $@ && false; } %.d: %.mli ocamlfind ocamldep $(OCAML_DEPS_FLAGS) $(OCAMLDEP_FLAGS) $< >$@ || { rm $@ && false; } %.d: %.ml errnos=`{ { ocamlfind ocamldep $(OCAML_DEPS_FLAGS) $(OCAMLDEP_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) $(OCAML_DEPS_FLAGS) $(OCAMLCC_FLAGS) $(foreach x,$(CFLAGS),-ccopt $x) $(foreach x,$(LDFLAGS),-cclib $x) $(if $(DEBUG),-ccopt -Wa$(,)-aln=$*.s) $< %.d: %.c ocamlfind ocamlc -ccopt -o -ccopt $@ -c $(OCAML_FLAGS) $(OCAML_DEPS_FLAGS) $(OCAMLCC_FLAGS) $(OCAMLCC_D_FLAGS) -nostdlib -ccopt -MM -ccopt -MT"$@\\ $(@:d=o)" -ccopt -MF$@ $< # ----------- # ocaml lexer # ----------- %.ml: %.mll ocamllex $< touch -r $< $@ # NOTE: to avoid rebuilding %.d # --------------- # ocaml interface # --------------- cmi=$(filter %.cmi,$(targets)) %.cmi: %.mli $(call lock_rw_cmi,ocamlfind ocamlc -c $(OCAML_FLAGS) $(OCAML_DEPS_FLAGS) $(OCAML_INTF_FLAGS) $(filter %.mli,$^)) %.cmi: %.ml # ------------ # ocaml module # ------------ cm=$(filter %.cm,$(targets)) cd=$(filter %.cd,$(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: %.ml %.cmi $(call lock_rw_cmi,ocamlfind ocamlc -c $(OCAML_FLAGS) $(OCAML_DEPS_FLAGS) $(OCAMLC_FLAGS) $(filter %.ml,$^)) %.cmx %.o: %.ml %.cmi $(call lock_rw_cmi,ocamlfind ocamlopt -c $(OCAML_FLAGS) $(OCAML_DEPS_FLAGS) $(OCAMLOPT_FLAGS) $(filter %.ml,$^)) %.cmxs %.cmx %.o: %.ml %.cmi $(call lock_rw_cmi,ocamlfind ocamlopt -shared -o $*.cmxs $(OCAML_FLAGS) $(OCAML_DEPS_FLAGS) $(OCAMLOPT_FLAGS) $(filter %.ml,$^)) # NOTE : remember to install the .cmx files as they enable cross-module optimizations # ---------- # ocaml pack # ---------- %.cmo: | % $(call lock_rw_cmi,ocamlfind ocamlc -pack -o $*.cmo $(OCAML_FLAGS) $(OCAMLC_FLAGS) $(OCAMLC_PACK_FLAGS) $(filter %.cmo,$^)) %.cmx %.o: | % $(call lock_rw_cmi,ocamlfind ocamlopt -pack -o $*.cmx $(OCAML_FLAGS) $(OCAMLOPT_FLAGS) $(OCAMLOPT_PACK_FLAGS) $(filter %.cmx,$^)) # -------------- # object archive # -------------- a=$(filter %.a,$(targets)) lock_rw_a=$(if $(is_parallel),$(call flock,-x,$(@D),$1),$1) lib%.a dll%.so: $(call lock_rw_a,ocamlfind ocamlmklib -failsafe $(OCAML_DEPS_FLAGS) $(OCAMLMKLIB_FLAGS) -oc $* $(OCAMLMKLIB_OBJECT_FLAGS) $(foreach x,$(LDFLAGS),-ldopt $x) $(filter %.o,$^) \ $(if $(STRIP), && strip --strip-unneeded $(STRIP_FLAGS) $(STRIP_LIB_FLAGS) $(@D)/lib$(*F).a \ && if [ -e '$(@D)/dll$(*F).so' ]; then strip $(STRIP_FLAGS) $(STRIP_DLL_FLAGS) $(@D)/dll$(*F).so; fi)) # ------------- # ocaml archive # ------------- ca=$(filter %.ca,$(targets)) %.cma: $(if $(filter %.a,$^), \ $(call lock_ro_cmi,ocamlfind ocamlmklib -o $* \ -oc $(patsubst lib%.a,%,$(notdir $(firstword $(filter %.a,$^)))) \ $(OCAML_DEPS_FLAGS) $(OCAMLMKLIB_FLAGS) $(OCAMLMKLIB_OCAML_FLAGS) $(OCAMLMKLIB_BYTE_FLAGS) \ $(filter %.cmo,$^)), \ $(call lock_ro_cmi,ocamlfind ocamlc -a -o $@ $(OCAMLC_ARCHIVE_FLAGS) $(filter %.cmo,$^))) %.cmxa: $(if $(filter %.a,$^), \ $(call lock_ro_cmi,ocamlfind ocamlmklib -o $* \ -oc $(patsubst lib%.a,%,$(notdir $(firstword $(filter %.a,$^)))) \ $(OCAML_DEPS_FLAGS) $(OCAMLMKLIB_FLAGS) $(OCAMLMKLIB_OCAML_FLAGS) $(OCAMLMKLIB_NATIVE_FLAGS) \ $(filter %.cmx,$^)), \ $(call lock_ro_cmi,ocamlfind ocamlopt -a -o $@ $(OCAMLOPT_ARCHIVE_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,$^) \ $(if $(STRIP), && strip $(STRIP_FLAGS) $(STRIP_NATIVE_FLAGS) $@)) # ------------- # ocaml linking # ------------- ocaml_stublibs/=$(abspath $d/stublibs) %/dynamic: export LINKING_MODE=dynamic %/dynamic: ,ocaml-predicates= \ $(patsubst -thread,$(,)mt$(,)mt_posix threads.posix,$(filter -thread,$(OCAML_DEPS_FLAGS))) %/dynamic: ocaml-packages= \ $(shell OCAMLPATH='$(OCAMLPATH)' ocamlfind query -recursive -format %p \ -predicates byte$(,ocaml-predicates) \ $(call filter-flag,-package,$(OCAML_DEPS_FLAGS))) %/dynamic: ocaml-dll=$(shell ocamlobjinfo \ $$(OCAMLPATH='$(OCAMLPATH)' ocamlfind query -recursive -format %d/%a \ -predicates byte$(,ocaml-predicates) $(ocaml-packages)) | sed -n \ -e '/^Extra dynamically-loaded libraries:/{s/^[^:]*://;p}') %/dynamic: \ override OCAMLOPT_BIN_FLAGS+= \ -noautolink \ -cclib -L$(shell ocamlfind printconf stdlib)/stublibs \ $(shell ocamlobjinfo \ $$(OCAMLPATH='$(OCAMLPATH)' ocamlfind query -recursive -format %d/%a \ -predicates native$(,ocaml-predicates) $(ocaml-packages) | \ { \ set --; \ while IFS= read -r line; \ do set -- "$$line" "$$@"; \ done; \ printf '%s\n' "$$@"; \ }) | sed -n \ -e '/^Extra C options:/{s/^[^:]*://;s/[^ ][^ ]*/-ccopt \0/g;p}' \ -e '/^Extra C object files:/{s/^[^:]*://; \ $(patsubst -l%,s/ -l\(%\)\( \|$$\)/ -l:dll\1.so /g;,$(ocaml-dll))s/[^ ][^ ]*/-cclib \0/g;p}') # NOTE: the above rule enables the dynamic linking # against dll*.so in native code, by rewriting # the -l* flags for ocaml stubs (listed in .cma archives) # embedded in *.cmxa archives, to -l:dll*.so flags. # Beware that the paths where dll*.so are found at link-time # will be used at runtime to load them. # Note also that the gain in size is likely to be small # and not worth the complexity of dynamic linking. %/dynamic: % %/static: export LINKING_MODE=static %/static: override OCAMLMKLIB_FLAGS+=-custom %/static: override OCAMLC_FLAGS+=-custom -ccopt -static -cclib -Wl,--start-group -cclib -ltermcap # NOTE : with static linking -lcamlrun needs -ltermcap # this unclosed (sic) --start-group is needed # because ocamlc puts -lcamlrun after all the -cclib . %/static: override OCAMLOPT_FLAGS+=-nodynlink -ccopt -static $(if $(filter x86_64,$(shell uname -m)),-fno-PIC) %/static: % %/strip: export STRIP=strip %/strip: % # ----------------- # 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 # --------------- export DEBUG %/debug: override OCAML_FLAGS+=-verbose %/debug: override OCAMLC_FLAGS+=-g -principal %/debug: override OCAMLCC_FLAGS+=-ccopt -ggdb3 -ccopt -rdynamic -ccopt -DDEBUG %/debug: override OCAMLMKLIB_FLAGS+=-verbose %/debug: override OCAMLOPT_FLAGS+=-principal -ccopt -ggdb3 -ccopt -rdynamic %/debug: override OCAMLRUNPARAM+=b %/debug: override DEBUG:=debug %/debug: % $(call include_GNUmakefile.inc,$(SRCDIR))