123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
- # This file is part of erlang.mk and subject to the terms of the ISC License.
- .PHONY: clean-app
- # Configuration.
- ERLC_OPTS ?= -Werror +debug_info +warn_export_vars +warn_shadow_vars \
- +warn_obsolete_guard # +bin_opt_info +warn_export_all +warn_missing_spec
- COMPILE_FIRST ?=
- COMPILE_FIRST_PATHS = $(addprefix src/,$(addsuffix .erl,$(COMPILE_FIRST)))
- ERLC_EXCLUDE ?=
- ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
- ERLC_ASN1_OPTS ?=
- ERLC_MIB_OPTS ?=
- COMPILE_MIB_FIRST ?=
- COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
- # Verbosity.
- app_verbose_0 = @echo " APP " $(PROJECT);
- app_verbose_2 = set -x;
- app_verbose = $(app_verbose_$(V))
- appsrc_verbose_0 = @echo " APP " $(PROJECT).app.src;
- appsrc_verbose_2 = set -x;
- appsrc_verbose = $(appsrc_verbose_$(V))
- makedep_verbose_0 = @echo " DEPEND" $(PROJECT).d;
- makedep_verbose_2 = set -x;
- makedep_verbose = $(makedep_verbose_$(V))
- erlc_verbose_0 = @echo " ERLC " $(filter-out $(patsubst %,%.erl,$(ERLC_EXCLUDE)),\
- $(filter %.erl %.core,$(?F)));
- erlc_verbose_2 = set -x;
- erlc_verbose = $(erlc_verbose_$(V))
- xyrl_verbose_0 = @echo " XYRL " $(filter %.xrl %.yrl,$(?F));
- xyrl_verbose_2 = set -x;
- xyrl_verbose = $(xyrl_verbose_$(V))
- asn1_verbose_0 = @echo " ASN1 " $(filter %.asn1,$(?F));
- asn1_verbose_2 = set -x;
- asn1_verbose = $(asn1_verbose_$(V))
- mib_verbose_0 = @echo " MIB " $(filter %.bin %.mib,$(?F));
- mib_verbose_2 = set -x;
- mib_verbose = $(mib_verbose_$(V))
- ifneq ($(wildcard src/),)
- # Targets.
- app:: $(if $(wildcard ebin/test),clean) deps
- $(verbose) $(MAKE) --no-print-directory $(PROJECT).d
- $(verbose) $(MAKE) --no-print-directory app-build
- ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
- define app_file
- {application, '$(PROJECT)', [
- {description, "$(PROJECT_DESCRIPTION)"},
- {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
- {id$(comma)$(space)"$(1)"}$(comma))
- {modules, [$(call comma_list,$(2))]},
- {registered, []},
- {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(foreach dep,$(DEPS),$(call dep_name,$(dep))))]},
- {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
- ]}.
- endef
- else
- define app_file
- {application, '$(PROJECT)', [
- {description, "$(PROJECT_DESCRIPTION)"},
- {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
- {id$(comma)$(space)"$(1)"}$(comma))
- {modules, [$(call comma_list,$(2))]},
- {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
- {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(foreach dep,$(DEPS),$(call dep_name,$(dep))))]},
- {mod, {$(PROJECT_MOD), []}},
- {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
- ]}.
- endef
- endif
- app-build: ebin/$(PROJECT).app
- $(verbose) :
- # Source files.
- ALL_SRC_FILES := $(sort $(call core_find,src/,*))
- ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
- CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
- # ASN.1 files.
- ifneq ($(wildcard asn1/),)
- ASN1_FILES = $(sort $(call core_find,asn1/,*.asn1))
- ERL_FILES += $(addprefix src/,$(patsubst %.asn1,%.erl,$(notdir $(ASN1_FILES))))
- define compile_asn1
- $(verbose) mkdir -p include/
- $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
- $(verbose) mv asn1/*.erl src/
- -$(verbose) mv asn1/*.hrl include/
- $(verbose) mv asn1/*.asn1db include/
- endef
- $(PROJECT).d:: $(ASN1_FILES)
- $(if $(strip $?),$(call compile_asn1,$?))
- endif
- # SNMP MIB files.
- ifneq ($(wildcard mibs/),)
- MIB_FILES = $(sort $(call core_find,mibs/,*.mib))
- $(PROJECT).d:: $(COMPILE_MIB_FIRST_PATHS) $(MIB_FILES)
- $(verbose) mkdir -p include/ priv/mibs/
- $(mib_verbose) erlc -v $(ERLC_MIB_OPTS) -o priv/mibs/ -I priv/mibs/ $?
- $(mib_verbose) erlc -o include/ -- $(addprefix priv/mibs/,$(patsubst %.mib,%.bin,$(notdir $?)))
- endif
- # Leex and Yecc files.
- XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
- XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
- ERL_FILES += $(XRL_ERL_FILES)
- YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
- YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
- ERL_FILES += $(YRL_ERL_FILES)
- $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
- $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
- # Erlang and Core Erlang files.
- define makedep.erl
- E = ets:new(makedep, [bag]),
- G = digraph:new([acyclic]),
- ErlFiles = lists:usort(string:tokens("$(ERL_FILES)", " ")),
- DepsDir = "$(call core_native_path,$(DEPS_DIR))",
- AppsDir = "$(call core_native_path,$(APPS_DIR))",
- DepsDirsSrc = "$(if $(wildcard $(DEPS_DIR)/*/src), $(call core_native_path,$(wildcard $(DEPS_DIR)/*/src)))",
- DepsDirsInc = "$(if $(wildcard $(DEPS_DIR)/*/include), $(call core_native_path,$(wildcard $(DEPS_DIR)/*/include)))",
- AppsDirsSrc = "$(if $(wildcard $(APPS_DIR)/*/src), $(call core_native_path,$(wildcard $(APPS_DIR)/*/src)))",
- AppsDirsInc = "$(if $(wildcard $(APPS_DIR)/*/include), $(call core_native_path,$(wildcard $(APPS_DIR)/*/include)))",
- DepsDirs = lists:usort(string:tokens(DepsDirsSrc++DepsDirsInc, " ")),
- AppsDirs = lists:usort(string:tokens(AppsDirsSrc++AppsDirsInc, " ")),
- Modules = [{list_to_atom(filename:basename(F, ".erl")), F} || F <- ErlFiles],
- Add = fun (Mod, Dep) ->
- case lists:keyfind(Dep, 1, Modules) of
- false -> ok;
- {_, DepFile} ->
- {_, ModFile} = lists:keyfind(Mod, 1, Modules),
- ets:insert(E, {ModFile, DepFile}),
- digraph:add_vertex(G, Mod),
- digraph:add_vertex(G, Dep),
- digraph:add_edge(G, Mod, Dep)
- end
- end,
- AddHd = fun (F, Mod, DepFile) ->
- case file:open(DepFile, [read]) of
- {error, enoent} ->
- ok;
- {ok, Fd} ->
- {_, ModFile} = lists:keyfind(Mod, 1, Modules),
- case ets:match(E, {ModFile, DepFile}) of
- [] ->
- ets:insert(E, {ModFile, DepFile}),
- F(F, Fd, Mod,0);
- _ -> ok
- end
- end
- end,
- SearchHrl = fun
- F(_Hrl, []) -> {error,enoent};
- F(Hrl, [Dir|Dirs]) ->
- HrlF = filename:join([Dir,Hrl]),
- case filelib:is_file(HrlF) of
- true ->
- {ok, HrlF};
- false -> F(Hrl,Dirs)
- end
- end,
- Attr = fun
- (_F, Mod, behavior, Dep) ->
- Add(Mod, Dep);
- (_F, Mod, behaviour, Dep) ->
- Add(Mod, Dep);
- (_F, Mod, compile, {parse_transform, Dep}) ->
- Add(Mod, Dep);
- (_F, Mod, compile, Opts) when is_list(Opts) ->
- case proplists:get_value(parse_transform, Opts) of
- undefined -> ok;
- Dep -> Add(Mod, Dep)
- end;
- (F, Mod, include, Hrl) ->
- case SearchHrl(Hrl, ["src", "include",AppsDir,DepsDir]++AppsDirs++DepsDirs) of
- {ok, FoundHrl} -> AddHd(F, Mod, FoundHrl);
- {error, _} -> false
- end;
- (F, Mod, include_lib, Hrl) ->
- case SearchHrl(Hrl, ["src", "include",AppsDir,DepsDir]++AppsDirs++DepsDirs) of
- {ok, FoundHrl} -> AddHd(F, Mod, FoundHrl);
- {error, _} -> false
- end;
- (F, Mod, import, {Imp, _}) ->
- IsFile =
- case lists:keyfind(Imp, 1, Modules) of
- false -> false;
- {_, FilePath} -> filelib:is_file(FilePath)
- end,
- case IsFile of
- false -> ok;
- true -> Add(Mod, Imp)
- end;
- (_, _, _, _) -> ok
- end,
- MakeDepend = fun
- (F, Fd, Mod, StartLocation) ->
- {ok, Filename} = file:pid2name(Fd),
- case io:parse_erl_form(Fd, undefined, StartLocation) of
- {ok, AbsData, EndLocation} ->
- case AbsData of
- {attribute, _, Key, Value} ->
- Attr(F, Mod, Key, Value),
- F(F, Fd, Mod, EndLocation);
- _ -> F(F, Fd, Mod, EndLocation)
- end;
- {eof, _ } -> file:close(Fd);
- {error, ErrorDescription } ->
- file:close(Fd);
- {error, ErrorInfo, ErrorLocation} ->
- F(F, Fd, Mod, ErrorLocation)
- end,
- ok
- end,
- [begin
- Mod = list_to_atom(filename:basename(F, ".erl")),
- case file:open(F, [read]) of
- {ok, Fd} -> MakeDepend(MakeDepend, Fd, Mod,0);
- {error, enoent} -> ok
- end
- end || F <- ErlFiles],
- Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
- CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
- TargetPath = fun(Target) ->
- case lists:keyfind(Target, 1, Modules) of
- false -> "";
- {_, DepFile} ->
- DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
- string:join(DirSubname ++ [atom_to_list(Target)], "/")
- end
- end,
- Output0 = [
- "# Generated by Erlang.mk. Edit at your own risk!\n\n",
- [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
- "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
- ],
- Output = case "é" of
- [233] -> unicode:characters_to_binary(Output0);
- _ -> Output0
- end,
- ok = file:write_file("$(1)", Output),
- halt()
- endef
- ifeq ($(if $(NO_MAKEDEP),$(wildcard $(PROJECT).d),),)
- $(PROJECT).d:: $(ERL_FILES) $(call core_find,include/,*.hrl) $(MAKEFILE_LIST)
- $(makedep_verbose) $(call erlang,$(call makedep.erl,$@))
- endif
- ifeq ($(IS_APP)$(IS_DEP),)
- ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
- # Rebuild everything when the Makefile changes.
- $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST) | $(ERLANG_MK_TMP)
- $(verbose) if test -f $@; then \
- touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
- touch -c $(PROJECT).d; \
- fi
- $(verbose) touch $@
- $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
- ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
- endif
- endif
- $(PROJECT).d::
- $(verbose) :
- include $(wildcard $(PROJECT).d)
- ebin/$(PROJECT).app:: ebin/
- ebin/:
- $(verbose) mkdir -p ebin/
- define compile_erl
- $(erlc_verbose) erlc -v $(if $(IS_DEP),$(filter-out -Werror,$(ERLC_OPTS)),$(ERLC_OPTS)) -o ebin/ \
- -pa ebin/ -I include/ $(filter-out $(ERLC_EXCLUDE_PATHS),$(COMPILE_FIRST_PATHS) $(1))
- endef
- define validate_app_file
- case file:consult("ebin/$(PROJECT).app") of
- {ok, _} -> halt();
- _ -> halt(1)
- end
- endef
- ebin/$(PROJECT).app:: $(ERL_FILES) $(CORE_FILES) $(wildcard src/$(PROJECT).app.src)
- $(eval FILES_TO_COMPILE := $(filter-out src/$(PROJECT).app.src,$?))
- $(if $(strip $(FILES_TO_COMPILE)),$(call compile_erl,$(FILES_TO_COMPILE)))
- # Older git versions do not have the --first-parent flag. Do without in that case.
- $(eval GITDESCRIBE := $(shell git describe --dirty --abbrev=7 --tags --always --first-parent 2>/dev/null \
- || git describe --dirty --abbrev=7 --tags --always 2>/dev/null || true))
- $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
- $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
- ifeq ($(wildcard src/$(PROJECT).app.src),)
- $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
- > ebin/$(PROJECT).app
- $(verbose) if ! $(call erlang,$(call validate_app_file)); then \
- echo "The .app file produced is invalid. Please verify the value of PROJECT_ENV." >&2; \
- exit 1; \
- fi
- else
- $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
- echo "Empty modules entry not found in $(PROJECT).app.src. Please consult the erlang.mk documentation for instructions." >&2; \
- exit 1; \
- fi
- $(appsrc_verbose) cat src/$(PROJECT).app.src \
- | sed "s/{[[:space:]]*modules[[:space:]]*,[[:space:]]*\[\]}/{modules, \[$(call comma_list,$(MODULES))\]}/" \
- | sed "s/{id,[[:space:]]*\"git\"}/{id, \"$(subst /,\/,$(GITDESCRIBE))\"}/" \
- > ebin/$(PROJECT).app
- endif
- ifneq ($(wildcard src/$(PROJECT).appup),)
- $(verbose) cp src/$(PROJECT).appup ebin/
- endif
- clean:: clean-app
- clean-app:
- $(gen_verbose) rm -rf $(PROJECT).d ebin/ priv/mibs/ $(XRL_ERL_FILES) $(YRL_ERL_FILES) \
- $(addprefix include/,$(patsubst %.mib,%.hrl,$(notdir $(MIB_FILES)))) \
- $(addprefix include/,$(patsubst %.asn1,%.hrl,$(notdir $(ASN1_FILES)))) \
- $(addprefix include/,$(patsubst %.asn1,%.asn1db,$(notdir $(ASN1_FILES)))) \
- $(addprefix src/,$(patsubst %.asn1,%.erl,$(notdir $(ASN1_FILES))))
- endif
|