erlc.mk 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
  2. # This file is part of erlang.mk and subject to the terms of the ISC License.
  3. .PHONY: clean-app
  4. # Configuration.
  5. ERLC_OPTS ?= -Werror +debug_info +warn_export_vars +warn_shadow_vars \
  6. +warn_obsolete_guard # +bin_opt_info +warn_export_all +warn_missing_spec
  7. COMPILE_FIRST ?=
  8. COMPILE_FIRST_PATHS = $(addprefix src/,$(addsuffix .erl,$(COMPILE_FIRST)))
  9. ERLC_EXCLUDE ?=
  10. ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
  11. ERLC_MIB_OPTS ?=
  12. COMPILE_MIB_FIRST ?=
  13. COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
  14. # Verbosity.
  15. app_verbose_0 = @echo " APP " $(PROJECT);
  16. app_verbose = $(app_verbose_$(V))
  17. appsrc_verbose_0 = @echo " APP " $(PROJECT).app.src;
  18. appsrc_verbose = $(appsrc_verbose_$(V))
  19. makedep_verbose_0 = @echo " DEPEND" $(PROJECT).d;
  20. makedep_verbose = $(makedep_verbose_$(V))
  21. erlc_verbose_0 = @echo " ERLC " $(filter-out $(patsubst %,%.erl,$(ERLC_EXCLUDE)),\
  22. $(filter %.erl %.core,$(?F)));
  23. erlc_verbose = $(erlc_verbose_$(V))
  24. xyrl_verbose_0 = @echo " XYRL " $(filter %.xrl %.yrl,$(?F));
  25. xyrl_verbose = $(xyrl_verbose_$(V))
  26. asn1_verbose_0 = @echo " ASN1 " $(filter %.asn1,$(?F));
  27. asn1_verbose = $(asn1_verbose_$(V))
  28. mib_verbose_0 = @echo " MIB " $(filter %.bin %.mib,$(?F));
  29. mib_verbose = $(mib_verbose_$(V))
  30. ifneq ($(wildcard src/),)
  31. # Targets.
  32. ifeq ($(wildcard ebin/test),)
  33. app:: $(PROJECT).d
  34. $(verbose) $(MAKE) --no-print-directory app-build
  35. else
  36. app:: clean $(PROJECT).d
  37. $(verbose) $(MAKE) --no-print-directory app-build
  38. endif
  39. ifeq ($(wildcard src/$(PROJECT)_app.erl),)
  40. define app_file
  41. {application, $(PROJECT), [
  42. {description, "$(PROJECT_DESCRIPTION)"},
  43. {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
  44. {id$(comma)$(space)"$(1)"}$(comma))
  45. {modules, [$(call comma_list,$(2))]},
  46. {registered, []},
  47. {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
  48. ]}.
  49. endef
  50. else
  51. define app_file
  52. {application, $(PROJECT), [
  53. {description, "$(PROJECT_DESCRIPTION)"},
  54. {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
  55. {id$(comma)$(space)"$(1)"}$(comma))
  56. {modules, [$(call comma_list,$(2))]},
  57. {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
  58. {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
  59. {mod, {$(PROJECT)_app, []}}
  60. ]}.
  61. endef
  62. endif
  63. app-build: ebin/$(PROJECT).app
  64. $(verbose) echo -n
  65. # Source files.
  66. ERL_FILES = $(sort $(call core_find,src/,*.erl))
  67. CORE_FILES = $(sort $(call core_find,src/,*.core))
  68. # ASN.1 files.
  69. ifneq ($(wildcard asn1/),)
  70. ASN1_FILES = $(sort $(call core_find,asn1/,*.asn1))
  71. ERL_FILES += $(addprefix src/,$(patsubst %.asn1,%.erl,$(notdir $(ASN1_FILES))))
  72. define compile_asn1
  73. $(verbose) mkdir -p include/
  74. $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
  75. $(verbose) mv asn1/*.erl src/
  76. $(verbose) mv asn1/*.hrl include/
  77. $(verbose) mv asn1/*.asn1db include/
  78. endef
  79. $(PROJECT).d:: $(ASN1_FILES)
  80. $(if $(strip $?),$(call compile_asn1,$?))
  81. endif
  82. # SNMP MIB files.
  83. ifneq ($(wildcard mibs/),)
  84. MIB_FILES = $(sort $(call core_find,mibs/,*.mib))
  85. $(PROJECT).d:: $(COMPILE_MIB_FIRST_PATHS) $(MIB_FILES)
  86. $(verbose) mkdir -p include/ priv/mibs/
  87. $(mib_verbose) erlc -v $(ERLC_MIB_OPTS) -o priv/mibs/ -I priv/mibs/ $?
  88. $(mib_verbose) erlc -o include/ -- $(addprefix priv/mibs/,$(patsubst %.mib,%.bin,$(notdir $?)))
  89. endif
  90. # Leex and Yecc files.
  91. XRL_FILES = $(sort $(call core_find,src/,*.xrl))
  92. XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
  93. ERL_FILES += $(XRL_ERL_FILES)
  94. YRL_FILES = $(sort $(call core_find,src/,*.yrl))
  95. YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
  96. ERL_FILES += $(YRL_ERL_FILES)
  97. $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
  98. $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
  99. # Erlang and Core Erlang files.
  100. define makedep.erl
  101. ErlFiles = lists:usort(string:tokens("$(ERL_FILES)", " ")),
  102. Modules = [{filename:basename(F, ".erl"), F} || F <- ErlFiles],
  103. Add = fun (Dep, Acc) ->
  104. case lists:keyfind(atom_to_list(Dep), 1, Modules) of
  105. {_, DepFile} -> [DepFile|Acc];
  106. false -> Acc
  107. end
  108. end,
  109. AddHd = fun (Dep, Acc) ->
  110. case {Dep, lists:keymember(Dep, 2, Modules)} of
  111. {"src/" ++ _, false} -> [Dep|Acc];
  112. {"include/" ++ _, false} -> [Dep|Acc];
  113. _ -> Acc
  114. end
  115. end,
  116. CompileFirst = fun (Deps) ->
  117. First0 = [case filename:extension(D) of
  118. ".erl" -> filename:basename(D, ".erl");
  119. _ -> []
  120. end || D <- Deps],
  121. case lists:usort(First0) of
  122. [] -> [];
  123. [[]] -> [];
  124. First -> ["COMPILE_FIRST +=", [[" ", F] || F <- First], "\n"]
  125. end
  126. end,
  127. Depend = [begin
  128. case epp:parse_file(F, ["include/"], []) of
  129. {ok, Forms} ->
  130. Deps = lists:usort(lists:foldl(fun
  131. ({attribute, _, behavior, Dep}, Acc) -> Add(Dep, Acc);
  132. ({attribute, _, behaviour, Dep}, Acc) -> Add(Dep, Acc);
  133. ({attribute, _, compile, {parse_transform, Dep}}, Acc) -> Add(Dep, Acc);
  134. ({attribute, _, file, {Dep, _}}, Acc) -> AddHd(Dep, Acc);
  135. (_, Acc) -> Acc
  136. end, [], Forms)),
  137. case Deps of
  138. [] -> "";
  139. _ -> [F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n", CompileFirst(Deps)]
  140. end;
  141. {error, enoent} ->
  142. []
  143. end
  144. end || F <- ErlFiles],
  145. ok = file:write_file("$(1)", Depend),
  146. halt()
  147. endef
  148. ifeq ($(if $(NO_MAKEDEP),$(wildcard $(PROJECT).d),),)
  149. $(PROJECT).d:: $(ERL_FILES) $(call core_find,include/,*.hrl)
  150. $(makedep_verbose) $(call erlang,$(call makedep.erl,$@))
  151. endif
  152. # Rebuild everything when the Makefile changes.
  153. $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(MAKEFILE_LIST)
  154. @touch $@
  155. -include $(PROJECT).d
  156. ebin/$(PROJECT).app:: ebin/
  157. ebin/:
  158. $(verbose) mkdir -p ebin/
  159. define compile_erl
  160. $(erlc_verbose) erlc -v $(if $(IS_DEP),$(filter-out -Werror,$(ERLC_OPTS)),$(ERLC_OPTS)) -o ebin/ \
  161. -pa ebin/ -I include/ $(filter-out $(ERLC_EXCLUDE_PATHS),$(COMPILE_FIRST_PATHS) $(1))
  162. endef
  163. ebin/$(PROJECT).app:: $(ERL_FILES) $(CORE_FILES) $(wildcard src/$(PROJECT).app.src)
  164. $(eval FILES_TO_COMPILE := $(filter-out src/$(PROJECT).app.src,$?))
  165. $(if $(strip $(FILES_TO_COMPILE)),$(call compile_erl,$(FILES_TO_COMPILE)))
  166. $(eval GITDESCRIBE := $(shell git describe --dirty --abbrev=7 --tags --always --first-parent 2>/dev/null || true))
  167. $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
  168. $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
  169. ifeq ($(wildcard src/$(PROJECT).app.src),)
  170. $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
  171. > ebin/$(PROJECT).app
  172. else
  173. $(verbose) if [ -z "$$(grep -E '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
  174. echo "Empty modules entry not found in $(PROJECT).app.src. Please consult the erlang.mk README for instructions." >&2; \
  175. exit 1; \
  176. fi
  177. $(appsrc_verbose) cat src/$(PROJECT).app.src \
  178. | sed "s/{[[:space:]]*modules[[:space:]]*,[[:space:]]*\[\]}/{modules, \[$(call comma_list,$(MODULES))\]}/" \
  179. | sed "s/{id,[[:space:]]*\"git\"}/{id, \"$(GITDESCRIBE)\"}/" \
  180. > ebin/$(PROJECT).app
  181. endif
  182. clean:: clean-app
  183. clean-app:
  184. $(gen_verbose) rm -rf $(PROJECT).d ebin/ priv/mibs/ $(XRL_ERL_FILES) $(YRL_ERL_FILES) \
  185. $(addprefix include/,$(patsubst %.mib,%.hrl,$(notdir $(MIB_FILES)))) \
  186. $(addprefix include/,$(patsubst %.asn1,%.hrl,$(notdir $(ASN1_FILES)))) \
  187. $(addprefix include/,$(patsubst %.asn1,%.asn1db,$(notdir $(ASN1_FILES)))) \
  188. $(addprefix src/,$(patsubst %.asn1,%.erl,$(notdir $(ASN1_FILES))))
  189. endif