diff options
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/asciidoc.mk | 4 | ||||
-rw-r--r-- | plugins/bootstrap.mk | 4 | ||||
-rw-r--r-- | plugins/c_src.mk | 32 | ||||
-rw-r--r-- | plugins/ci.mk | 24 | ||||
-rw-r--r-- | plugins/cover.mk | 24 | ||||
-rw-r--r-- | plugins/ct.mk | 6 | ||||
-rw-r--r-- | plugins/dialyzer.mk | 4 | ||||
-rw-r--r-- | plugins/edoc.mk | 6 | ||||
-rw-r--r-- | plugins/escript.mk | 12 | ||||
-rw-r--r-- | plugins/eunit.mk | 3 | ||||
-rw-r--r-- | plugins/proper.mk | 4 | ||||
-rw-r--r-- | plugins/protobuffs.mk | 5 | ||||
-rw-r--r-- | plugins/relx.mk | 104 | ||||
-rw-r--r-- | plugins/triq.mk | 4 | ||||
-rw-r--r-- | plugins/xref.mk | 223 |
15 files changed, 366 insertions, 93 deletions
diff --git a/plugins/asciidoc.mk b/plugins/asciidoc.mk index f4be7ff..778308b 100644 --- a/plugins/asciidoc.mk +++ b/plugins/asciidoc.mk @@ -57,8 +57,8 @@ try }) end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]], halt(0) -catch C:E -> - io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]), +catch C:E$(if $V,:S) -> + io:format("Exception: ~p:~p~n$(if $V,Stacktrace: ~p~n)", [C, E$(if $V,$(comma) S)]), halt(1) end. endef diff --git a/plugins/bootstrap.mk b/plugins/bootstrap.mk index 23dd238..2e0ad8f 100644 --- a/plugins/bootstrap.mk +++ b/plugins/bootstrap.mk @@ -93,6 +93,8 @@ endef define bs_relx_config {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}. +{dev_mode, false}. +{include_erts, true}. {extended_start_script, true}. {sys_config, "config/sys.config"}. {vm_args, "config/vm.args"}. @@ -449,6 +451,8 @@ endif $(verbose) mkdir config/ $(verbose) $(call core_render,bs_sys_config,config/sys.config) $(verbose) $(call core_render,bs_vm_args,config/vm.args) + $(verbose) awk '/^include erlang.mk/ && !ins {print "BUILD_DEPS += relx";ins=1};{print}' Makefile > Makefile.bak + $(verbose) mv Makefile.bak Makefile new-app: ifndef in diff --git a/plugins/c_src.mk b/plugins/c_src.mk index d6d5d15..fd9c2d7 100644 --- a/plugins/c_src.mk +++ b/plugins/c_src.mk @@ -15,17 +15,24 @@ C_SRC_TYPE ?= shared ifeq ($(PLATFORM),msys2) C_SRC_OUTPUT_EXECUTABLE_EXTENSION ?= .exe C_SRC_OUTPUT_SHARED_EXTENSION ?= .dll + C_SRC_OUTPUT_STATIC_EXTENSION ?= .lib else C_SRC_OUTPUT_EXECUTABLE_EXTENSION ?= C_SRC_OUTPUT_SHARED_EXTENSION ?= .so + C_SRC_OUTPUT_STATIC_EXTENSION ?= .a endif ifeq ($(C_SRC_TYPE),shared) C_SRC_OUTPUT_FILE = $(C_SRC_OUTPUT)$(C_SRC_OUTPUT_SHARED_EXTENSION) +else ifeq ($(C_SRC_TYPE),static) + C_SRC_OUTPUT_FILE = $(C_SRC_OUTPUT)$(C_SRC_OUTPUT_STATIC_EXTENSION) else C_SRC_OUTPUT_FILE = $(C_SRC_OUTPUT)$(C_SRC_OUTPUT_EXECUTABLE_EXTENSION) endif +RANLIB ?= ranlib +ARFLAGS ?= cr + ifeq ($(PLATFORM),msys2) # We hardcode the compiler used on MSYS2. The default CC=cc does # not produce working code. The "gcc" MSYS2 package also doesn't. @@ -35,9 +42,9 @@ ifeq ($(PLATFORM),msys2) CXXFLAGS ?= -O3 -finline-functions -Wall else ifeq ($(PLATFORM),darwin) CC ?= cc - CFLAGS ?= -O3 -std=c99 -arch x86_64 -Wall -Wmissing-prototypes - CXXFLAGS ?= -O3 -arch x86_64 -Wall - LDFLAGS ?= -arch x86_64 -flat_namespace -undefined suppress + CFLAGS ?= -O3 -std=c99 -Wall -Wmissing-prototypes + CXXFLAGS ?= -O3 -Wall + LDFLAGS ?= -flat_namespace -undefined suppress else ifeq ($(PLATFORM),freebsd) CC ?= cc CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes @@ -53,6 +60,11 @@ ifneq ($(PLATFORM),msys2) CXXFLAGS += -fPIC endif +ifeq ($(C_SRC_TYPE),static) + CFLAGS += -DSTATIC_ERLANG_NIF=1 + CXXFLAGS += -DSTATIC_ERLANG_NIF=1 +endif + CFLAGS += -I"$(ERTS_INCLUDE_DIR)" -I"$(ERL_INTERFACE_INCLUDE_DIR)" CXXFLAGS += -I"$(ERTS_INCLUDE_DIR)" -I"$(ERL_INTERFACE_INCLUDE_DIR)" @@ -69,6 +81,12 @@ cpp_verbose = $(cpp_verbose_$(V)) link_verbose_0 = @echo " LD " $(@F); link_verbose = $(link_verbose_$(V)) +ar_verbose_0 = @echo " AR " $(@F); +ar_verbose = $(ar_verbose_$(V)) + +ranlib_verbose_0 = @echo " RANLIB" $(@F); +ranlib_verbose = $(ranlib_verbose_$(V)) + # Targets. ifeq ($(wildcard $(C_SRC_DIR)),) @@ -97,11 +115,19 @@ app:: $(C_SRC_ENV) $(C_SRC_OUTPUT_FILE) test-build:: $(C_SRC_ENV) $(C_SRC_OUTPUT_FILE) +ifneq ($(C_SRC_TYPE),static) $(C_SRC_OUTPUT_FILE): $(OBJECTS) $(verbose) mkdir -p $(dir $@) $(link_verbose) $(CC) $(OBJECTS) \ $(LDFLAGS) $(if $(filter $(C_SRC_TYPE),shared),-shared) $(LDLIBS) \ -o $(C_SRC_OUTPUT_FILE) +else +$(C_SRC_OUTPUT_FILE): $(OBJECTS) + $(verbose) mkdir -p $(dir $@) + $(ar_verbose) $(AR) $(ARFLAGS) $(C_SRC_OUTPUT_FILE) $(OBJECTS) + $(ranlib_verbose) $(RANLIB) $(C_SRC_OUTPUT_FILE) +endif + $(OBJECTS): $(MAKEFILE_LIST) $(C_SRC_ENV) diff --git a/plugins/ci.mk b/plugins/ci.mk index 4b902be..b97c087 100644 --- a/plugins/ci.mk +++ b/plugins/ci.mk @@ -4,24 +4,14 @@ .PHONY: ci ci-prepare ci-setup CI_OTP ?= -CI_HIPE ?= -CI_ERLLVM ?= - -ifeq ($(CI_VM),native) -ERLC_OPTS += +native -TEST_ERLC_OPTS += +native -else ifeq ($(CI_VM),erllvm) -ERLC_OPTS += +native +'{hipe, [to_llvm]}' -TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}' -endif -ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),) +ifeq ($(strip $(CI_OTP)),) ci:: else -ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM))) +ci:: $(addprefix ci-,$(CI_OTP)) -ci-prepare: $(addprefix $(KERL_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE))) +ci-prepare: $(addprefix ci-prepare-,$(CI_OTP)) ci-setup:: $(verbose) : @@ -33,7 +23,10 @@ ci_verbose_0 = @echo " CI " $(1); ci_verbose = $(ci_verbose_$(V)) define ci_target -ci-$1: $(KERL_INSTALL_DIR)/$2 +ci-prepare-$1: $(KERL_INSTALL_DIR)/$2 + $(verbose) : + +ci-$1: ci-prepare-$1 $(verbose) $(MAKE) --no-print-directory clean $(ci_verbose) \ PATH="$(KERL_INSTALL_DIR)/$2/bin:$(PATH)" \ @@ -45,11 +38,8 @@ ci-$1: $(KERL_INSTALL_DIR)/$2 endef $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp))) -$(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native))) -$(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm))) $(foreach otp,$(filter-out $(ERLANG_OTP),$(CI_OTP)),$(eval $(call kerl_otp_target,$(otp)))) -$(foreach otp,$(filter-out $(ERLANG_HIPE),$(sort $(CI_HIPE) $(CI_ERLLLVM))),$(eval $(call kerl_hipe_target,$(otp)))) help:: $(verbose) printf "%s\n" "" \ diff --git a/plugins/cover.mk b/plugins/cover.mk index 68f5b98..395e8b8 100644 --- a/plugins/cover.mk +++ b/plugins/cover.mk @@ -8,6 +8,7 @@ COVER_DATA_DIR ?= $(COVER_REPORT_DIR) ifdef COVER COVER_APPS ?= $(notdir $(ALL_APPS_DIRS)) COVER_DEPS ?= +COVER_EXCLUDE_MODS ?= endif # Code coverage for Common Test. @@ -23,7 +24,8 @@ $(TEST_DIR)/ct.cover.spec: cover-data-dir "{incl_dirs, '$(PROJECT)', [\"$(call core_native_path,$(CURDIR)/ebin)\" \ $(foreach a,$(COVER_APPS),$(comma) \"$(call core_native_path,$(APPS_DIR)/$a/ebin)\") \ $(foreach d,$(COVER_DEPS),$(comma) \"$(call core_native_path,$(DEPS_DIR)/$d/ebin)\")]}." \ - '{export,"$(call core_native_path,$(abspath $(COVER_DATA_DIR))/ct.coverdata)"}.' > $@ + '{export,"$(call core_native_path,$(abspath $(COVER_DATA_DIR))/ct.coverdata)"}.' \ + "{excl_mods, '$(PROJECT)', [$(call comma_list,$(COVER_EXCLUDE_MODS))]}." > $@ CT_RUN += -cover $(TEST_DIR)/ct.cover.spec endif @@ -38,14 +40,18 @@ define cover.erl Dirs = ["$(call core_native_path,$(CURDIR)/ebin)" $(foreach a,$(COVER_APPS),$(comma) "$(call core_native_path,$(APPS_DIR)/$a/ebin)") $(foreach d,$(COVER_DEPS),$(comma) "$(call core_native_path,$(DEPS_DIR)/$d/ebin)")], - [begin - case filelib:is_dir(Dir) of - false -> false; - true -> - case cover:compile_beam_directory(Dir) of - {error, _} -> halt(1); - _ -> true - end + Excludes = [$(call comma_list,$(foreach e,$(COVER_EXCLUDE_MODS),"$e"))], + [case file:list_dir(Dir) of + {error, enotdir} -> false; + {error, _} -> halt(2); + {ok, Files} -> + BeamFiles = [filename:join(Dir, File) || + File <- Files, + not lists:member(filename:basename(File, ".beam"), Excludes), + filename:extension(File) =:= ".beam"], + case cover:compile_beam(BeamFiles) of + {error, _} -> halt(1); + _ -> true end end || Dir <- Dirs] end, diff --git a/plugins/ct.mk b/plugins/ct.mk index ba65b2c..820af16 100644 --- a/plugins/ct.mk +++ b/plugins/ct.mk @@ -78,9 +78,9 @@ endif endif define ct_suite_target -ct-$(1): test-build - $(verbose) mkdir -p $(CT_LOGS_DIR) - $(gen_verbose_esc) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS) +ct-$1: test-build + $$(verbose) mkdir -p $$(CT_LOGS_DIR) + $$(gen_verbose_esc) $$(CT_RUN) -sname ct_$$(PROJECT) -suite $$(addsuffix _SUITE,$1) $$(CT_EXTRA) $$(CT_OPTS) endef $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test)))) diff --git a/plugins/dialyzer.mk b/plugins/dialyzer.mk index 0ac2ef0..bb4723a 100644 --- a/plugins/dialyzer.mk +++ b/plugins/dialyzer.mk @@ -10,7 +10,7 @@ export DIALYZER_PLT PLT_APPS ?= DIALYZER_DIRS ?= --src -r $(wildcard src) $(ALL_APPS_DIRS) -DIALYZER_OPTS ?= -Werror_handling -Wrace_conditions -Wunmatched_returns # -Wunderspecs +DIALYZER_OPTS ?= -Werror_handling -Wunmatched_returns # -Wunderspecs DIALYZER_PLT_OPTS ?= # Core targets. @@ -69,6 +69,6 @@ dialyze: $(if $(filter --src,$(DIALYZER_DIRS)),,deps app) else dialyze: $(DIALYZER_PLT) endif - $(verbose) dialyzer --no_native `$(ERL) \ + $(verbose) dialyzer `$(ERL) \ -eval "$(subst $(newline),,$(call escape_dquotes,$(call filter_opts.erl)))" \ -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS) $(if $(wildcard ebin/),-pa ebin/) diff --git a/plugins/edoc.mk b/plugins/edoc.mk index 1d6c0f6..0a0e8eb 100644 --- a/plugins/edoc.mk +++ b/plugins/edoc.mk @@ -11,7 +11,11 @@ EDOC_OUTPUT ?= doc define edoc.erl SrcPaths = lists:foldl(fun(P, Acc) -> - filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc + filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") + ++ lists:filter(fun(D) -> + filelib:is_dir(D) + end, filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}/**")) + ++ Acc end, [], [$(call comma_list,$(patsubst %,'%',$(call core_native_path,$(EDOC_SRC_DIRS))))]), DefaultOpts = [{dir, "$(EDOC_OUTPUT)"}, {source_path, SrcPaths}, {subpackages, false}], edoc:application($(1), ".", [$(2)] ++ DefaultOpts), diff --git a/plugins/escript.mk b/plugins/escript.mk index 3719684..1790dcb 100644 --- a/plugins/escript.mk +++ b/plugins/escript.mk @@ -29,11 +29,11 @@ help:: escript-zip:: FULL=1 escript-zip:: deps app - $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP)) - $(verbose) rm -f $(ESCRIPT_ZIP_FILE) - $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/* + $(verbose) mkdir -p $(dir $(abspath $(ESCRIPT_ZIP_FILE))) + $(verbose) rm -f $(abspath $(ESCRIPT_ZIP_FILE)) + $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(abspath $(ESCRIPT_ZIP_FILE)) $(PROJECT)/ebin/* ifneq ($(DEPS),) - $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \ + $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(abspath $(ESCRIPT_ZIP_FILE)) \ $(subst $(DEPS_DIR)/,,$(addsuffix /*,$(wildcard \ $(addsuffix /ebin,$(shell cat $(ERLANG_MK_TMP)/deps.log))))) endif @@ -43,8 +43,8 @@ escript:: escript-zip "#!$(ESCRIPT_SHEBANG)" \ "%% $(ESCRIPT_COMMENT)" \ "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE) - $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE) + $(verbose) cat $(abspath $(ESCRIPT_ZIP_FILE)) >> $(ESCRIPT_FILE) $(verbose) chmod +x $(ESCRIPT_FILE) distclean-escript: - $(gen_verbose) rm -f $(ESCRIPT_FILE) + $(gen_verbose) rm -f $(ESCRIPT_FILE) $(abspath $(ESCRIPT_ZIP_FILE)) diff --git a/plugins/eunit.mk b/plugins/eunit.mk index aa4cc9a..211a744 100644 --- a/plugins/eunit.mk +++ b/plugins/eunit.mk @@ -8,6 +8,7 @@ EUNIT_OPTS ?= EUNIT_ERL_OPTS ?= +EUNIT_TEST_SPEC ?= $1 # Core targets. @@ -23,7 +24,7 @@ help:: define eunit.erl $(call cover.erl) CoverSetup(), - case eunit:test($1, [$(EUNIT_OPTS)]) of + case eunit:test($(call EUNIT_TEST_SPEC,$1), [$(EUNIT_OPTS)]) of ok -> ok; error -> halt(2) end, diff --git a/plugins/proper.mk b/plugins/proper.mk index 6262199..55a2788 100644 --- a/plugins/proper.mk +++ b/plugins/proper.mk @@ -37,8 +37,8 @@ define proper_check.erl end of true -> halt(0); _ -> halt(1) - catch error:undef -> - io:format("Undefined property or module?~n~p~n", [erlang:get_stacktrace()]), + catch error:undef$(if $V,:Stacktrace) -> + io:format("Undefined property or module?~n$(if $V,~p~n)", [$(if $V,Stacktrace)]), halt(0) end. endef diff --git a/plugins/protobuffs.mk b/plugins/protobuffs.mk index 809df42..0ebe159 100644 --- a/plugins/protobuffs.mk +++ b/plugins/protobuffs.mk @@ -41,10 +41,13 @@ else define compile_proto.erl [begin gpb_compile:file(F, [ + $(foreach i,$(sort $(dir $(PROTO_FILES))),{i$(comma) "$i"}$(comma)) {include_as_lib, true}, {module_name_suffix, "_pb"}, {o_hrl, "./include"}, - {o_erl, "./src"}]) + {o_erl, "./src"}, + {use_packages, true} + ]) end || F <- string:tokens("$1", " ")], halt(). endef diff --git a/plugins/relx.mk b/plugins/relx.mk index 4e29031..cb8f06d 100644 --- a/plugins/relx.mk +++ b/plugins/relx.mk @@ -1,15 +1,14 @@ # Copyright (c) 2013-2016, Loïc Hoguin <[email protected]> # This file is part of erlang.mk and subject to the terms of the ISC License. +ifeq ($(filter relx,$(BUILD_DEPS) $(DEPS) $(REL_DEPS)),relx) .PHONY: relx-rel relx-relup distclean-relx-rel run # Configuration. -RELX ?= $(ERLANG_MK_TMP)/relx RELX_CONFIG ?= $(CURDIR)/relx.config +RELX_CONFIG_SCRIPT ?= $(CURDIR)/relx.config.script -RELX_URL ?= https://erlang.mk/res/relx-v3.27.0 -RELX_OPTS ?= RELX_OUTPUT_DIR ?= _rel RELX_REL_EXT ?= RELX_TAR ?= 1 @@ -18,16 +17,10 @@ ifdef SFX RELX_TAR = 1 endif -ifeq ($(firstword $(RELX_OPTS)),-o) - RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS)) -else - RELX_OPTS += -o $(RELX_OUTPUT_DIR) -endif - # Core targets. ifeq ($(IS_DEP),) -ifneq ($(wildcard $(RELX_CONFIG)),) +ifneq ($(wildcard $(RELX_CONFIG))$(wildcard $(RELX_CONFIG_SCRIPT)),) rel:: relx-rel relup:: relx-relup @@ -38,21 +31,85 @@ distclean:: distclean-relx-rel # Plugin-specific targets. -$(RELX): | $(ERLANG_MK_TMP) - $(gen_verbose) $(call core_http_get,$(RELX),$(RELX_URL)) - $(verbose) chmod +x $(RELX) +define relx_get_config.erl + (fun() -> + Config0 = + case file:consult("$(call core_native_path,$(RELX_CONFIG))") of + {ok, Terms} -> + Terms; + {error, _} -> + [] + end, + case filelib:is_file("$(call core_native_path,$(RELX_CONFIG_SCRIPT))") of + true -> + Bindings = erl_eval:add_binding('CONFIG', Config0, erl_eval:new_bindings()), + {ok, Config1} = file:script("$(call core_native_path,$(RELX_CONFIG_SCRIPT))", Bindings), + Config1; + false -> + Config0 + end + end)() +endef -relx-rel: $(RELX) rel-deps app - $(verbose) $(RELX) $(if $(filter 1,$V),-V 3) -c $(RELX_CONFIG) $(RELX_OPTS) release +define relx_release.erl + Config = $(call relx_get_config.erl), + {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config), + Vsn = case Vsn0 of + {cmd, Cmd} -> os:cmd(Cmd); + semver -> ""; + {semver, _} -> ""; + {git, short} -> string:trim(os:cmd("git rev-parse --short HEAD"), both, "\n"); + {git, long} -> string:trim(os:cmd("git rev-parse HEAD"), both, "\n"); + VsnStr -> Vsn0 + end, + {ok, _} = relx:build_release(#{name => Name, vsn => Vsn}, Config ++ [{output_dir, "$(RELX_OUTPUT_DIR)"}]), + halt(0). +endef + +define relx_tar.erl + Config = $(call relx_get_config.erl), + {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config), + Vsn = case Vsn0 of + {cmd, Cmd} -> os:cmd(Cmd); + semver -> ""; + {semver, _} -> ""; + {git, short} -> string:trim(os:cmd("git rev-parse --short HEAD"), both, "\n"); + {git, long} -> string:trim(os:cmd("git rev-parse HEAD"), both, "\n"); + VsnStr -> Vsn0 + end, + {ok, _} = relx:build_tar(#{name => Name, vsn => Vsn}, Config ++ [{output_dir, "$(RELX_OUTPUT_DIR)"}]), + halt(0). +endef + +define relx_relup.erl + Config = $(call relx_get_config.erl), + {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config), + Vsn = case Vsn0 of + {cmd, Cmd} -> os:cmd(Cmd); + semver -> ""; + {semver, _} -> ""; + {git, short} -> string:trim(os:cmd("git rev-parse --short HEAD"), both, "\n"); + {git, long} -> string:trim(os:cmd("git rev-parse HEAD"), both, "\n"); + VsnStr -> Vsn0 + end, + {ok, _} = relx:build_relup(Name, Vsn, undefined, Config ++ [{output_dir, "$(RELX_OUTPUT_DIR)"}]), + halt(0). +endef + +relx-rel: rel-deps app + $(call erlang,$(call relx_release.erl),-pa ebin/) $(verbose) $(MAKE) relx-post-rel ifeq ($(RELX_TAR),1) - $(verbose) $(RELX) $(if $(filter 1,$V),-V 3) -c $(RELX_CONFIG) $(RELX_OPTS) tar + $(call erlang,$(call relx_tar.erl),-pa ebin/) endif -relx-relup: $(RELX) rel-deps app - $(verbose) $(RELX) $(if $(filter 1,$V),-V 3) -c $(RELX_CONFIG) $(RELX_OPTS) release +relx-relup: rel-deps app + $(call erlang,$(call relx_release.erl),-pa ebin/) $(MAKE) relx-post-rel - $(verbose) $(RELX) $(if $(filter 1,$V),-V 3) -c $(RELX_CONFIG) $(RELX_OPTS) relup $(if $(filter 1,$(RELX_TAR)),tar) + $(call erlang,$(call relx_relup.erl),-pa ebin/) +ifeq ($(RELX_TAR),1) + $(call erlang,$(call relx_tar.erl),-pa ebin/) +endif distclean-relx-rel: $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR) @@ -63,17 +120,19 @@ relx-post-rel:: # Run target. -ifeq ($(wildcard $(RELX_CONFIG)),) +ifeq ($(wildcard $(RELX_CONFIG))$(wildcard $(RELX_CONFIG_SCRIPT)),) run:: else define get_relx_release.erl - {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"), + Config = $(call relx_get_config.erl), {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config), Vsn = case Vsn0 of {cmd, Cmd} -> os:cmd(Cmd); semver -> ""; {semver, _} -> ""; + {git, short} -> string:trim(os:cmd("git rev-parse --short HEAD"), both, "\n"); + {git, long} -> string:trim(os:cmd("git rev-parse HEAD"), both, "\n"); VsnStr -> Vsn0 end, Extended = case lists:keyfind(extended_start_script, 1, Config) of @@ -100,7 +159,7 @@ ifdef RELOAD rel:: $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) ping $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) \ - eval "io:format(\"~p~n\", [c:lm()])" + eval "io:format(\"~p~n\", [c:lm()])." endif help:: @@ -109,3 +168,4 @@ help:: " run Compile the project, build the release and run it" endif +endif diff --git a/plugins/triq.mk b/plugins/triq.mk index 8791e2c..6d41376 100644 --- a/plugins/triq.mk +++ b/plugins/triq.mk @@ -26,8 +26,8 @@ define triq_check.erl end of true -> halt(0); _ -> halt(1) - catch error:undef -> - io:format("Undefined property or module?~n~p~n", [erlang:get_stacktrace()]), + catch error:undef$(if $V,:Stacktrace) -> + io:format("Undefined property or module?~n$(if $V,~p~n)", [$(if $V,Stacktrace)]), halt(0) end. endef diff --git a/plugins/xref.mk b/plugins/xref.mk index 7da0f37..0ecace2 100644 --- a/plugins/xref.mk +++ b/plugins/xref.mk @@ -1,39 +1,218 @@ -# Copyright (c) 2016, Loïc Hoguin <[email protected]> -# Copyright (c) 2015, Erlang Solutions Ltd. +# Copyright (c) 2022, Loïc Hoguin <[email protected]> # This file is part of erlang.mk and subject to the terms of the ISC License. -.PHONY: xref distclean-xref +.PHONY: xref # Configuration. -ifeq ($(XREF_CONFIG),) - XREFR_ARGS := -else - XREFR_ARGS := -c $(XREF_CONFIG) -endif +# We do not use locals_not_used or deprecated_function_calls +# because the compiler will error out by default in those +# cases with Erlang.mk. Deprecated functions may make sense +# in some cases but few libraries define them. We do not +# use exports_not_used by default because it hinders more +# than it helps library projects such as Cowboy. Finally, +# undefined_functions provides little that undefined_function_calls +# doesn't already provide, so it's not enabled by default. +XREF_CHECKS ?= [undefined_function_calls] + +# Instead of predefined checks a query can be evaluated +# using the Xref DSL. The $q variable is used in that case. + +# The scope is a list of keywords that correspond to +# application directories, being essentially an easy way +# to configure which applications to analyze. With: +# +# - app: . +# - apps: $(ALL_APPS_DIRS) +# - deps: $(ALL_DEPS_DIRS) +# - otp: Built-in Erlang/OTP applications. +# +# The default is conservative (app) and will not be +# appropriate for all types of queries (for example +# application_call requires adding all applications +# that might be called or they will not be found). +XREF_SCOPE ?= app # apps deps otp + +# If the above is not enough, additional application +# directories can be configured. +XREF_EXTRA_APP_DIRS ?= -XREFR ?= $(CURDIR)/xrefr -export XREFR +# As well as additional non-application directories. +XREF_EXTRA_DIRS ?= -XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr +# Erlang.mk supports -ignore_xref([...]) with forms +# {M, F, A} | {F, A} | M, the latter ignoring whole +# modules. Ignores can also be provided project-wide. +XREF_IGNORE ?= [] + +# All callbacks may be ignored. Erlang.mk will ignore +# them automatically for exports_not_used (unless it +# is explicitly disabled by the user). +XREF_IGNORE_CALLBACKS ?= # Core targets. help:: $(verbose) printf '%s\n' '' \ 'Xref targets:' \ - ' xref Run Xrefr using $$XREF_CONFIG as config file if defined' - -distclean:: distclean-xref + ' xref Analyze the project using Xref' \ + ' xref q=QUERY Evaluate an Xref query' # Plugin-specific targets. -$(XREFR): - $(gen_verbose) $(call core_http_get,$(XREFR),$(XREFR_URL)) - $(verbose) chmod +x $(XREFR) - -xref: deps app $(XREFR) - $(gen_verbose) $(XREFR) $(XREFR_ARGS) +define xref.erl + {ok, Xref} = xref:start([]), + Scope = [$(call comma_list,$(XREF_SCOPE))], + AppDirs0 = [$(call comma_list,$(foreach d,$(XREF_EXTRA_APP_DIRS),"$d"))], + AppDirs1 = case lists:member(otp, Scope) of + false -> AppDirs0; + true -> + RootDir = code:root_dir(), + AppDirs0 ++ [filename:dirname(P) || P <- code:get_path(), lists:prefix(RootDir, P)] + end, + AppDirs2 = case lists:member(deps, Scope) of + false -> AppDirs1; + true -> [$(call comma_list,$(foreach d,$(ALL_DEPS_DIRS),"$d"))] ++ AppDirs1 + end, + AppDirs3 = case lists:member(apps, Scope) of + false -> AppDirs2; + true -> [$(call comma_list,$(foreach d,$(ALL_APPS_DIRS),"$d"))] ++ AppDirs2 + end, + AppDirs = case lists:member(app, Scope) of + false -> AppDirs3; + true -> ["../$(notdir $(CURDIR))"|AppDirs3] + end, + [{ok, _} = xref:add_application(Xref, AppDir, [{builtins, true}]) || AppDir <- AppDirs], + ExtraDirs = [$(call comma_list,$(foreach d,$(XREF_EXTRA_DIRS),"$d"))], + [{ok, _} = xref:add_directory(Xref, ExtraDir, [{builtins, true}]) || ExtraDir <- ExtraDirs], + ok = xref:set_library_path(Xref, code:get_path() -- (["ebin", "."] ++ AppDirs ++ ExtraDirs)), + Checks = case {$1, is_list($2)} of + {check, true} -> $2; + {check, false} -> [$2]; + {query, _} -> [$2] + end, + FinalRes = [begin + IsInformational = case $1 of + query -> true; + check -> + is_tuple(Check) andalso + lists:member(element(1, Check), + [call, use, module_call, module_use, application_call, application_use]) + end, + {ok, Res0} = case $1 of + check -> xref:analyze(Xref, Check); + query -> xref:q(Xref, Check) + end, + Res = case IsInformational of + true -> Res0; + false -> + lists:filter(fun(R) -> + {Mod, InMFA, MFA} = case R of + {InMFA0 = {M, _, _}, MFA0} -> {M, InMFA0, MFA0}; + {M, _, _} -> {M, R, R} + end, + Attrs = try + Mod:module_info(attributes) + catch error:undef -> + [] + end, + InlineIgnores = lists:flatten([ + [case V of + M when is_atom(M) -> {M, '_', '_'}; + {F, A} -> {Mod, F, A}; + _ -> V + end || V <- Values] + || {ignore_xref, Values} <- Attrs]), + BuiltinIgnores = [ + {eunit_test, wrapper_test_exported_, 0} + ], + DoCallbackIgnores = case {Check, "$(strip $(XREF_IGNORE_CALLBACKS))"} of + {exports_not_used, ""} -> true; + {_, "0"} -> false; + _ -> true + end, + CallbackIgnores = case DoCallbackIgnores of + false -> []; + true -> + Behaviors = lists:flatten([ + [BL || {behavior, BL} <- Attrs], + [BL || {behaviour, BL} <- Attrs] + ]), + [{Mod, CF, CA} || B <- Behaviors, {CF, CA} <- B:behaviour_info(callbacks)] + end, + WideIgnores = if + is_list($(XREF_IGNORE)) -> + [if is_atom(I) -> {I, '_', '_'}; true -> I end + || I <- $(XREF_IGNORE)]; + true -> [$(XREF_IGNORE)] + end, + Ignores = InlineIgnores ++ BuiltinIgnores ++ CallbackIgnores ++ WideIgnores, + not (lists:member(InMFA, Ignores) + orelse lists:member(MFA, Ignores) + orelse lists:member({Mod, '_', '_'}, Ignores)) + end, Res0) + end, + case Res of + [] -> ok; + _ when IsInformational -> + case Check of + {call, {CM, CF, CA}} -> + io:format("Functions that ~s:~s/~b calls:~n", [CM, CF, CA]); + {use, {CM, CF, CA}} -> + io:format("Function ~s:~s/~b is called by:~n", [CM, CF, CA]); + {module_call, CMod} -> + io:format("Modules that ~s calls:~n", [CMod]); + {module_use, CMod} -> + io:format("Module ~s is used by:~n", [CMod]); + {application_call, CApp} -> + io:format("Applications that ~s calls:~n", [CApp]); + {application_use, CApp} -> + io:format("Application ~s is used by:~n", [CApp]); + _ when $1 =:= query -> + io:format("Query ~s returned:~n", [Check]) + end, + [case R of + {{InM, InF, InA}, {M, F, A}} -> + io:format("- ~s:~s/~b called by ~s:~s/~b~n", + [M, F, A, InM, InF, InA]); + {M, F, A} -> + io:format("- ~s:~s/~b~n", [M, F, A]); + ModOrApp -> + io:format("- ~s~n", [ModOrApp]) + end || R <- Res], + ok; + _ -> + [case {Check, R} of + {undefined_function_calls, {{InM, InF, InA}, {M, F, A}}} -> + io:format("Undefined function ~s:~s/~b called by ~s:~s/~b~n", + [M, F, A, InM, InF, InA]); + {undefined_functions, {M, F, A}} -> + io:format("Undefined function ~s:~s/~b~n", [M, F, A]); + {locals_not_used, {M, F, A}} -> + io:format("Unused local function ~s:~s/~b~n", [M, F, A]); + {exports_not_used, {M, F, A}} -> + io:format("Unused exported function ~s:~s/~b~n", [M, F, A]); + {deprecated_function_calls, {{InM, InF, InA}, {M, F, A}}} -> + io:format("Deprecated function ~s:~s/~b called by ~s:~s/~b~n", + [M, F, A, InM, InF, InA]); + {deprecated_functions, {M, F, A}} -> + io:format("Deprecated function ~s:~s/~b~n", [M, F, A]); + _ -> + io:format("~p: ~p~n", [Check, R]) + end || R <- Res], + error + end + end || Check <- Checks], + stopped = xref:stop(Xref), + case lists:usort(FinalRes) of + [ok] -> halt(0); + _ -> halt(1) + end +endef -distclean-xref: - $(gen_verbose) rm -rf $(XREFR) +xref: deps app +ifdef q + $(verbose) $(call erlang,$(call xref.erl,query,"$q"),-pa ebin/) +else + $(verbose) $(call erlang,$(call xref.erl,check,$(XREF_CHECKS)),-pa ebin/) +endif |