From 74a2fa083c24b32c0e38627adfde6ffe97d23a8e Mon Sep 17 00:00:00 2001 From: Tyler Hughes Date: Fri, 12 May 2023 21:58:37 +0100 Subject: [PATCH] Implement 'hex' for erlang.mk --- build.config | 3 +- core/core.mk | 2 + core/deps-tools.mk | 3 +- core/deps.mk | 24 +- core/docs.mk | 2 +- core/elixir.mk | 334 ++++++++++++++++++++++++++++ core/erlc.mk | 2 +- {plugins => core}/hex.mk | 2 +- core/rel.mk | 2 +- core/test.mk | 2 +- plugins/ct.mk | 2 +- plugins/escript.mk | 4 + plugins/eunit.mk | 2 +- plugins/shell.mk | 2 +- test/core_elixir.mk | 150 +++++++++++++ test/{plugin_hex.mk => core_hex.mk} | 0 test/log/console.log | 0 test/log/crash.log | 0 test/log/error.log | 0 19 files changed, 516 insertions(+), 20 deletions(-) create mode 100644 core/elixir.mk rename {plugins => core}/hex.mk (99%) create mode 100644 test/core_elixir.mk rename test/{plugin_hex.mk => core_hex.mk} (100%) create mode 100644 test/log/console.log create mode 100644 test/log/crash.log create mode 100644 test/log/error.log diff --git a/build.config b/build.config index b363ef07b..1f12cb3d3 100644 --- a/build.config +++ b/build.config @@ -12,6 +12,8 @@ core/deps # Core modules, continued. core/erlc +core/hex +core/elixir core/docs core/rel core/test @@ -29,7 +31,6 @@ plugins/edoc plugins/erlydtl plugins/escript plugins/eunit -plugins/hex plugins/proper plugins/protobuffs plugins/relx diff --git a/core/core.mk b/core/core.mk index 250a2eb3e..ff1a7a56f 100644 --- a/core/core.mk +++ b/core/core.mk @@ -98,6 +98,8 @@ endif export PLATFORM endif +UNIQUE = $(if $1,$(firstword $1) $(call UNIQUE,$(filter-out $(firstword $1),$1))) + # Core targets. all:: deps app rel diff --git a/core/deps-tools.mk b/core/deps-tools.mk index 262d045d5..e0c4aba1a 100644 --- a/core/deps-tools.mk +++ b/core/deps-tools.mk @@ -71,6 +71,7 @@ endif $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \ $(MAKE) -C $$dep fetch-deps \ IS_DEP=1 \ + ELIXIR_USE_SYSTEM=$(ELIXIR_USE_SYSTEM) \ ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \ fi \ fi \ @@ -119,7 +120,7 @@ endif :; \ else \ echo $$$$dep >> $(ERLANG_MK_TMP)/query.log; \ - $(MAKE) -C $(DEPS_DIR)/$$$$dep $$@ QUERY="$(QUERY)" IS_DEP=1 || true; \ + $(MAKE) -C $(DEPS_DIR)/$$$$dep $$@ QUERY="$(QUERY)" IS_DEP=1 ELIXIR_USE_SYSTEM=$$(ELIXIR_USE_SYSTEM) || true; \ fi \ done) ifeq ($(IS_APP)$(IS_DEP),) diff --git a/core/deps.mk b/core/deps.mk index 99f9ed102..273c984c2 100644 --- a/core/deps.mk +++ b/core/deps.mk @@ -148,7 +148,7 @@ endif # Core targets. -ALL_APPS_DIRS_TO_BUILD = $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) +ALL_APPS_DIRS_TO_BUILD = $(if $(filter-out $(ELIXIR_BUILTINS),$(LOCAL_DEPS_DIRS))$(IS_APP),$(filter-out $(ELIXIR_BUILTINS),$(LOCAL_DEPS_DIRS)),$(ALL_APPS_DIRS)) apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log | $(ERLANG_MK_TMP) # Create ebin directory for all apps to make sure Erlang recognizes them @@ -170,7 +170,7 @@ ifneq ($(ALL_APPS_DIRS_TO_BUILD),) :; \ else \ echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \ - $(MAKE) -C $$dep $(if $(IS_TEST),test-build-app) IS_APP=1; \ + $(MAKE) -C $$dep $(if $(IS_TEST),test-build-app) IS_APP=1 ELIXIR_USE_SYSTEM=$(ELIXIR_USE_SYSTEM); \ fi \ done endif @@ -212,10 +212,10 @@ ifneq ($(ALL_DEPS_DIRS),) if [ -z "$(strip $(FULL))" ] $(if $(force_rebuild_dep),&& ! ($(call force_rebuild_dep,$$dep)),) && [ ! -L $$dep ] && [ -f $$dep/ebin/dep_built ]; then \ :; \ elif [ "$$dep" = "$(DEPS_DIR)/hut" -a "$(HUT_PATCH)" ]; then \ - $(MAKE) -C $$dep app IS_DEP=1; \ + $(MAKE) -C $$dep app IS_DEP=1 ELIXIR_USE_SYSTEM=$(ELIXIR_USE_SYSTEM); \ if [ ! -L $$dep ] && [ -d $$dep/ebin ]; then touch $$dep/ebin/dep_built; fi; \ elif [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \ - $(MAKE) -C $$dep IS_DEP=1; \ + $(MAKE) -C $$dep IS_DEP=1 ELIXIR_USE_SYSTEM=$(ELIXIR_USE_SYSTEM); \ if [ ! -L $$dep ] && [ -d $$dep/ebin ]; then touch $$dep/ebin/dep_built; fi; \ else \ echo "Error: No Makefile to build dependency $$dep." >&2; \ @@ -236,6 +236,8 @@ define dep_autopatch rm -rf $(DEPS_DIR)/$1/ebin/; \ $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \ $(call dep_autopatch_erlang_mk,$(1)); \ + elif [ -f $(DEPS_DIR)/$1/mix.exs ]; then \ + $(call dep_autopatch_mix,$(1)); \ elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \ if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \ $(call dep_autopatch2,$1); \ @@ -648,7 +650,7 @@ define dep_autopatch_rebar.erl _ -> Path = "$(call core_native_path,$(DEPS_DIR)/)" ++ atom_to_list(P), io:format("~s", [os:cmd("$(MAKE) -C $(call core_native_path,$(DEPS_DIR)/$1) " ++ Path)]), - io:format("~s", [os:cmd("$(MAKE) -C " ++ Path ++ " IS_DEP=1")]), + io:format("~s", [os:cmd("$(MAKE) -C " ++ Path ++ " IS_DEP=1 ELIXIR_USE_SYSTEM=$(ELIXIR_USE_SYSTEM)")]), code:add_patha(Path ++ "/ebin") end end @@ -778,6 +780,8 @@ define dep_fetch_ln ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1)); endef +HEX_REPO = https://repo.hex.pm + ifeq ($(CACHE_DEPS),1) # Hex only has a package version. No need to look in the Erlang.mk packages. @@ -785,7 +789,7 @@ define dep_fetch_hex mkdir -p $(CACHE_DIR)/hex $(DEPS_DIR)/$1; \ $(eval hex_tar_name=$(if $(word 3,$(dep_$1)),$(word 3,$(dep_$1)),$1)-$(strip $(word 2,$(dep_$1))).tar) \ $(if $(wildcard $(CACHE_DIR)/hex/$(hex_tar_name)),,$(call core_http_get,$(CACHE_DIR)/hex/$(hex_tar_name),\ - https://repo.hex.pm/tarballs/$(hex_tar_name);)) \ + $(HEX_REPO)/tarballs/$(hex_tar_name);)) \ tar -xOf $(CACHE_DIR)/hex/$(hex_tar_name) contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -; endef @@ -795,7 +799,7 @@ else define dep_fetch_hex mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \ $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\ - https://repo.hex.pm/tarballs/$(if $(word 3,$(dep_$1)),$(word 3,$(dep_$1)),$1)-$(strip $(word 2,$(dep_$1))).tar); \ + $(HEX_REPO)/tarballs/$(if $(word 3,$(dep_$1)),$(word 3,$(dep_$1)),$1)-$(strip $(word 2,$(dep_$1))).tar); \ tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -; endef @@ -839,7 +843,7 @@ endif .PHONY: autopatch-$(call dep_name,$1) autopatch-$(call dep_name,$1):: - $(verbose) if [ "$1" = "elixir" -a "$(ELIXIR_PATCH)" ]; then \ + $(verbose) if [ "$1" = "elixir" ]; then \ ln -s lib/elixir/ebin $(DEPS_DIR)/elixir/; \ else \ $$(call dep_autopatch,$(call dep_name,$1)) \ @@ -853,14 +857,14 @@ clean:: clean-apps clean-apps: $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \ - $(MAKE) -C $$dep clean IS_APP=1; \ + $(MAKE) -C $$dep clean IS_APP=1 ELIXIR_USE_SYSTEM=$(ELIXIR_USE_SYSTEM); \ done distclean:: distclean-apps distclean-apps: $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \ - $(MAKE) -C $$dep distclean IS_APP=1; \ + $(MAKE) -C $$dep distclean IS_APP=1 ELIXIR_USE_SYSTEM=$(ELIXIR_USE_SYSTEM); \ done endif diff --git a/core/docs.mk b/core/docs.mk index 6ef2ce6fe..cb9471447 100644 --- a/core/docs.mk +++ b/core/docs.mk @@ -16,5 +16,5 @@ ifneq ($(SKIP_DEPS),) doc-deps: else doc-deps: $(ALL_DOC_DEPS_DIRS) - $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done + $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1 ELIXIR_USE_SYSTEM=$(ELIXIR_USE_SYSTEM); done endif diff --git a/core/elixir.mk b/core/elixir.mk new file mode 100644 index 000000000..a329e2721 --- /dev/null +++ b/core/elixir.mk @@ -0,0 +1,334 @@ +ifeq ($(pkg_elixir_commit),master) +pkg_elixir_commit = main +endif + +ELIXIR_USE_SYSTEM ?= 1 + +ifeq ($(ELIXIR_USE_SYSTEM),1) +ELIXIRC := $(shell which elixirc) +ifneq ($(ELIXIRC),) +ELIXIR_PATH := $(shell $(dir $(ELIXIRC))/elixir -e 'IO.puts(:code.lib_dir(:elixir))')/../../ +endif +endif + +ifeq ($(ELIXIRC),) +ELIXIR_USE_SYSTEM = 0 +ELIXIRC := $(DEPS_DIR)/$(call dep_name,elixir)/bin/elixirc +ELIXIR_PATH = $(DEPS_DIR)/$(call dep_name,elixir) +endif + +ELIXIRC := $(abspath $(ELIXIRC)) +ELIXIR_PATH := $(abspath $(ELIXIR_PATH)) + +ELIXIR_COMPILE_FIRST_PATHS = $(addprefix src/,$(addsuffix .ex,$(COMPILE_FIRST))) $(addprefix lib/,$(addsuffix .ex,$(COMPILE_FIRST))) +ELIXIRC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .ex,$(ERLC_EXCLUDE))) $(addprefix lib/,$(addsuffix .ex,$(ERLC_EXCLUDE))) +ELIXIRC_OPTS += $(ERLC_OPTS) + +elixirc_verbose_0 = @echo " ELIXIRC " $(filter-out $(patsubst %,%.ex,$(ERLC_EXCLUDE)),\ + $(filter %.ex %.core,$(?F))); +elixirc_verbose_2 = set -x; +elixirc_verbose = $(elixirc_verbose_$(V)) + +ALL_LIB_FILES := $(sort $(call core_find,lib/,*)) + +EX_FILES := $(filter-out lib/mix/%,$(filter %.ex,$(ALL_SRC_FILES) $(ALL_LIB_FILES))) +ELIXIR_BUILTINS = $(addprefix $(ELIXIR_PATH)/lib/,eex elixir logger mix) +USES_ELIXIR = $(if $(EX_FILES)$(shell find $(DEPS_DIR) -name '*.ex' 2>/dev/null),1,) + +ifneq ($(USES_ELIXIR),) +ERL_LIBS := $(ERL_LIBS):$(ELIXIR_PATH)/lib/ +export ERL_LIBS + +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))))]}, + $(if $(PROJECT_MOD),{mod$(comma) {'$(PROJECT_MOD)'$(comma) []}}$(comma),) + {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),) +]}. +endef + +app:: $(if $(wildcard ebin/test),clean) deps + $(verbose) $(MAKE) --no-print-directory $(PROJECT).d + $(verbose) $(MAKE) --no-print-directory app-build + +app-build:: ebin/$(PROJECT).app + $(verbose) : + +$(PROJECT).d:: ebin/$(PROJECT).app + +define validate_app_file + case file:consult("ebin/$(PROJECT).app") of + {ok, _} -> halt(); + _ -> halt(1) + end +endef +endif + +define Mix_Makefile.erl +{ok, _} = application:ensure_all_started(elixir), +{ok, _} = application:ensure_all_started(mix), +File = <<"$(DEPS_DIR)/$(1)/mix.exs">>, +[{Mod, Bin}] = elixir_compiler:file(File, fun(_File, _LexerPid) -> ok end), +{module, Mod} = code:load_binary(Mod, binary_to_list(File), Bin), +Project = Mod:project(), +Application = try Mod:application() catch error:undef -> [] end, +Fmt = + "PROJECT = ~p~n" + "PROJECT_DESCRIPTION = ~s~n" + "PROJECT_VERSION = ~s~n" + "PROJECT_MOD = ~s~n" + "define PROJECT_ENV~n" + "~p~n" + "endef~n" + "~n~n~s~n~n" + "~n~n~s~n~n" + "~n~s~n" + "ERLC_OPTS = +debug_info~n" + "include ../../erlang.mk", +LFirst = fun + F([], Pred) -> + false; + F([H|T], Pred) -> + case catch Pred(H) of + true -> + {ok, H}; + false -> + F(T, Pred) + end +end, +GetDeps = + fun(Deps) -> + GetVer = fun(Name, Req) -> + application:ensure_all_started(ssl), + application:ensure_all_started(inets), + {ok, PackageInfo} = + case hex_repo:get_package(hex_core:default_config(), atom_to_binary(Name)) of + {ok, {200, _RespHeaders, Decoded}} -> + LFirst(Decoded, fun(#{version := Vsn}) -> 'Elixir.Version':'match?'(Vsn, Req) end); + Other -> + io:format(standard_error, "Unexpected response for Dep ~p", [Name]), + erlang:halt(1) + end, + maps:get(version, PackageInfo) + end, + (fun + F([], DEPS_Acc0, DEP_Acc0) -> + [DEPS_Acc0, "\n", DEP_Acc0]; + F([H|T], DEPS_Acc0, DEP_Acc0) -> + {DEPS_Acc1, DEP_Acc1} = + case H of + {Name, Req} when is_binary(Req) -> + { + [DEPS_Acc0, io_lib:format("DEPS += ~p~n", [Name])], + [DEP_Acc0, io_lib:format("dep_~p = hex ~s ~p~n", [Name, GetVer(Name, Req), Name])] + }; + {Name, Opts} when is_list(Opts) -> + Path = proplists:get_value(path, Opts), + IsRequired = proplists:get_value(optional, Opts) =/= true, + IsProdOnly = case proplists:get_value(only, Opts, prod) of + prod -> true; + L when is_list(L) -> lists:member(prod, L); + _ -> false + end, + case IsRequired andalso IsProdOnly of + true when Path =/= undefined -> + { + [DEPS_Acc0, io_lib:format("DEPS += ~p~n", [Name])], + [DEP_Acc0, io_lib:format("dep_~p = ln ~s~n", [Name, Path])] + }; + true when Path =:= undefined -> + io:format(standard_error, "Skipping 'dep_~p' as no vsn given.", [Name]), + { + [DEPS_Acc0, io_lib:format("DEPS += ~p~n", [Name])], + DEP_Acc0 + }; + false -> + {DEPS_Acc0, DEP_Acc0} + end; + {Name, Req, Opts} -> + IsRequired = proplists:get_value(optional, Opts) =/= true, + IsProdOnly = case proplists:get_value(only, Opts, prod) of + prod -> true; + L when is_list(L) -> lists:member(prod, L); + _ -> false + end, + case IsRequired andalso IsProdOnly of + true -> + { + [DEPS_Acc0, io_lib:format("DEPS += ~p~n", [Name])], + [DEP_Acc0, io_lib:format("dep_~p = hex ~s ~p~n", [Name, GetVer(Name, Req), Name])] + }; + false -> + {DEPS_Acc0, DEP_Acc0} + end; + _ -> + {DEPS_Acc0, DEP_Acc0} + end, + F(T, DEPS_Acc1, DEP_Acc1) + end)(Deps, [], []) + end, +StartMod = + case lists:keyfind(mod, 1, Application) of + {mod, {StartMod_, _StartArgs}} -> + atom_to_list(StartMod_); + _ -> + "" + end, +ExtraApps = [io_lib:format("LOCAL_DEPS += ~p~n", [App]) || App <- proplists:get_value(extra_applications, Application, [])], +ProjectCompilers = proplists:get_value(compilers, Project, []), +"https://hexdocs.pm/elixir_make/Mix.Tasks.Compile.ElixirMake.html", +ExtraMakeLines = + case lists:member(elixir_make, ProjectCompilers) of + false -> + ""; + true -> + Fetch = fun(Key, Proplist, DefaultVal, DefaultReplacement) -> + case proplists:get_value(Key, Proplist, DefaultVal) of + DefaultVal -> DefaultReplacement; + Value -> Value + end + end, + case file:copy("$(DEPS_DIR)/$(1)/" ++ Fetch(make_makefile, Project, default, "Makefile"), "$(DEPS_DIR)/$(1)/elixir_make.mk") of + {ok, _} -> ok; + Err = {error, _} -> + io:format(standard_error, "Failed to copy Makefile with error ~p~n", [Err]), + halt(1) + end, + [ + io_lib:format("app::~n\t~s -C \"~s\" -f \"$(DEPS_DIR)/$(1)/elixir_make.mk\" ~s ~s~n", [ + Fetch(make_executable, Project, default, "$(MAKE)"), + Fetch(make_cwd, Project, undefined, <<".">>), + lists:join(" ", Fetch(make_targets, Project, [], [])), + lists:join(" ", Fetch(make_args, Project, undefined, [])) + ]), + "\n", + case Fetch(make_clean, Project, nil, undefined) of + undefined -> + ""; + Clean -> + io_lib:format("clean::~n\t~s~n", [Clean]) + end + ] + end, +Deps = + lists:foldl(fun(DepToRemove, Acc) -> + lists:keydelete(DepToRemove, 1, Acc) + end, proplists:get_value(deps, Project, []), [elixir_make]), +Args = [ + proplists:get_value(app, Project), + proplists:get_value(description, Project, ""), + proplists:get_value(version, Project, ""), + StartMod, + proplists:get_value(env, Application, []), + ExtraApps, + GetDeps(Deps), + ExtraMakeLines +], +Str = io_lib:format(Fmt, Args), +case file:write_file("$(DEPS_DIR)/$(1)/Makefile", Str) of + ok -> + halt(0); + {error, Reason} -> + io:format(standard_error, "Failed to create '$(DEPS_DIR)/$(1)/Makefile' with reason ~p~n", [Reason]), + halt(1) +end +endef + +define SHELL_ASSERT_ +if ! $(1); then \ + echo "$(2)" >&2; \ + exit 1; \ +fi +endef + +define SHELL_ASSERT +$(if $(verbose),,$(info [SHELL_ASSERT] $(call SHELL_ASSERT_,$(1),$(2)))) $(call SHELL_ASSERT_,$(1),$(2)) +endef + +define dep_autopatch_mix + $(MAKE) $(ELIXIRC) hex-core; \ + sed 's|\(defmodule.*do\)|\1\nCode.compiler_options(on_undefined_variable: :warn)\n|g' -i $(DEPS_DIR)/$(1)/mix.exs; \ + MIX_ENV="$(if $(MIX_ENV),$(strip $(MIX_ENV)),prod)" $(ERL) -pa $(DEPS_DIR)/hex_core $(addprefix -pa ,$(addsuffix /ebin,$(ELIXIR_BUILTINS))) \ + -eval "$(subst ",\",$(subst $(newline), ,$(subst $$,\$$,$(call Mix_Makefile.erl,$(1)))))." \ + -eval "halt(0)." || exit 1 \ + mkdir $(DEPS_DIR)/$1/src || exit 1 +endef + +ifneq ($(USES_ELIXIR),) +LOCAL_DEPS_DIRS += $(ELIXIR_BUILTINS) +LOCAL_DEPS += eex elixir logger mix + +ebin/$(PROJECT).app:: $(ELIXIR_PATH)/lib/elixir/ebin/elixir.app +endif + +$(ELIXIRC): $(ELIXIR_PATH)/lib/elixir/ebin/elixir.app + +$(addsuffix /ebin,$(ELIXIR_BUILTINS)): $(ELIXIR_PATH)/lib/elixir/ebin/elixir.app + $(verbose) $(if $(ELIXIR_USE_SYSTEM),@,$(MAKE) -C $(DEPS_DIR)/elixir IS_DEP=1D) + +define compile_ex +ERL_COMPILER_OPTIONS="[$(call comma_list,$(patsubst '%',%,$(patsubst +%,%,$(filter +%,$(ELIXIRC_OPTS)))))]" $(ELIXIRC) \ + --verbose $(if $(IS_DEP),,$(if $(filter -Werror,$(ELIXIRC_OPTS)),--warnings-as-errors)) -o ebin/ $(filter-out $(ELIXIRC_EXCLUDE_PATHS),$(ELIXIR_COMPILE_FIRST_PATHS)) $(1) -- -pa ebin/ -I include/ +endef + +# Currently doesn't account for nested modules +define get_elixir_mod +$(foreach module,$(strip \ + $(patsubst %do,%,$(patsubst defmodule%,%,\ + $(shell grep 'defmodule' $(1))\ + ))),'Elixir.$(module)') +endef + +ebin/$(PROJECT).app:: $(EX_FILES) + $(gen_verbose) $(if $(strip $(EX_FILES)),$(call compile_ex,$(EX_FILES))) +# 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 $(ELIXIRC_EXCLUDE_PATHS), $(ERL_FILES) $(CORE_FILES) $(BEAM_FILES))))))) + $(eval MODULES := $(call UNIQUE,$(MODULES) $(foreach file, \ + $(EX_FILES), \ + $(call get_elixir_mod,$(file)) \ + ))) +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 + +$(ELIXIR_PATH)/lib/elixir/ebin/elixir.app: $(ELIXIR_PATH) +ifeq ($(ELIXIR_USE_SYSTEM),1) + @ +else + $(verbose) $(MAKE) -C $(DEPS_DIR)/elixir -f Makefile.orig compile Q="$(verbose)" +endif + +# We need the original makefile so that we can compile the elixir compiler +autopatch-elixir:: + $(verbose) cp $(DEPS_DIR)/elixir/Makefile $(DEPS_DIR)/elixir/Makefile.orig + $(verbose) sed 's|"$$(MAKE)"|"$$(MAKE)" -f $$(CURDIR)/Makefile.orig|g' -i $(DEPS_DIR)/elixir/Makefile.orig + +ifneq ($(USES_ELIXIR),) +ebin/$(PROJECT).app:: $(eval $(call dep_target,elixir)) $(addsuffix /ebin,$(ELIXIR_BUILTINS)) + @ +endif diff --git a/core/erlc.mk b/core/erlc.mk index 7c9490738..d5be3a746 100644 --- a/core/erlc.mk +++ b/core/erlc.mk @@ -86,7 +86,7 @@ define app_file endef endif -app-build: ebin/$(PROJECT).app +app-build:: ebin/$(PROJECT).app $(verbose) : # Source files. diff --git a/plugins/hex.mk b/core/hex.mk similarity index 99% rename from plugins/hex.mk rename to core/hex.mk index b0a5339be..b73c8698b 100644 --- a/plugins/hex.mk +++ b/core/hex.mk @@ -18,7 +18,7 @@ $(if $(filter hex_core,$(DEPS) $(BUILD_DEPS) $(DOC_DEPS) $(REL_DEPS) $(TEST_DEPS hex-core: $(DEPS_DIR)/hex_core $(verbose) if [ ! -e $(DEPS_DIR)/hex_core/ebin/dep_built ]; then \ - $(MAKE) -C $(DEPS_DIR)/hex_core IS_DEP=1; \ + $(MAKE) -C $(DEPS_DIR)/hex_core IS_DEP=1 ELIXIR_USE_SYSTEM=$(ELIXIR_USE_SYSTEM); \ touch $(DEPS_DIR)/hex_core/ebin/dep_built; \ fi diff --git a/core/rel.mk b/core/rel.mk index f64278c2a..451c989c9 100644 --- a/core/rel.mk +++ b/core/rel.mk @@ -15,5 +15,5 @@ ifneq ($(SKIP_DEPS),) rel-deps: else rel-deps: $(ALL_REL_DEPS_DIRS) - $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done + $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep ELIXIR_USE_SYSTEM=$(ELIXIR_USE_SYSTEM); done endif diff --git a/core/test.mk b/core/test.mk index 8054abcc5..424061de9 100644 --- a/core/test.mk +++ b/core/test.mk @@ -24,7 +24,7 @@ test-deps: $(ALL_TEST_DEPS_DIRS) if [ -z "$(strip $(FULL))" ] && [ ! -L $$dep ] && [ -f $$dep/ebin/dep_built ]; then \ :; \ else \ - $(MAKE) -C $$dep IS_DEP=1; \ + $(MAKE) -C $$dep IS_DEP=1 ELIXIR_USE_SYSTEM=$(ELIXIR_USE_SYSTEM); \ if [ ! -L $$dep ] && [ -d $$dep/ebin ]; then touch $$dep/ebin/dep_built; fi; \ fi \ done diff --git a/plugins/ct.mk b/plugins/ct.mk index 820af1608..b11ecdba2 100644 --- a/plugins/ct.mk +++ b/plugins/ct.mk @@ -54,7 +54,7 @@ endif ifneq ($(ALL_APPS_DIRS),) define ct_app_target apps-ct-$1: test-build - $$(MAKE) -C $1 ct IS_APP=1 + $$(MAKE) -C $1 ct IS_APP=1 ELIXIR_USE_SYSTEM=$(ELIXIR_USE_SYSTEM) endef $(foreach app,$(ALL_APPS_DIRS),$(eval $(call ct_app_target,$(app)))) diff --git a/plugins/escript.mk b/plugins/escript.mk index 1790dcbcf..b80482b2e 100644 --- a/plugins/escript.mk +++ b/plugins/escript.mk @@ -37,6 +37,10 @@ ifneq ($(DEPS),) $(subst $(DEPS_DIR)/,,$(addsuffix /*,$(wildcard \ $(addsuffix /ebin,$(shell cat $(ERLANG_MK_TMP)/deps.log))))) endif +ifneq ($(USES_ELIXIR),) + $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(abspath $(ESCRIPT_ZIP_FILE)) \ + $(addsuffix /*,$(wildcard $(addsuffix /ebin,$(ELIXIR_BUILTINS)))) +endif escript:: escript-zip $(gen_verbose) printf "%s\n" \ diff --git a/plugins/eunit.mk b/plugins/eunit.mk index 211a7442d..f7fc81ed0 100644 --- a/plugins/eunit.mk +++ b/plugins/eunit.mk @@ -56,7 +56,7 @@ endif ifneq ($(ALL_APPS_DIRS),) apps-eunit: test-build - $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \ + $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1 ELIXIR_USE_SYSTEM=$(ELIXIR_USE_SYSTEM); \ [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \ exit $$eunit_retcode endif diff --git a/plugins/shell.mk b/plugins/shell.mk index 7c1f8e6e9..985ab70e9 100644 --- a/plugins/shell.mk +++ b/plugins/shell.mk @@ -31,7 +31,7 @@ build-shell-deps: $(ALL_SHELL_DEPS_DIRS) if [ -z "$(strip $(FULL))" ] && [ ! -L $$dep ] && [ -f $$dep/ebin/dep_built ]; then \ :; \ else \ - $(MAKE) -C $$dep IS_DEP=1; \ + $(MAKE) -C $$dep IS_DEP=1 ELIXIR_USE_SYSTEM=$(ELIXIR_USE_SYSTEM); \ if [ ! -L $$dep ] && [ -d $$dep/ebin ]; then touch $$dep/ebin/dep_built; fi; \ fi \ done diff --git a/test/core_elixir.mk b/test/core_elixir.mk new file mode 100644 index 000000000..4475334ab --- /dev/null +++ b/test/core_elixir.mk @@ -0,0 +1,150 @@ +# Core: Miscellaneous. +# +# The miscellaneous tests use the prefix "core-", not "core-misc-". + +CORE_ELIXIR_TARGETS = $(call list_targets,core-elixir) + +.PHONY: core-elixir $(CORE_ELIXIR_TARGETS) + +core-elixir: $(CORE_ELIXIR_TARGETS) + +core-elixir-test-project_library: init + $i "Bootstrap a new OTP library named $(APP)" + $t mkdir $(APP)/ + $t cp ../erlang.mk $(APP)/ + $t $(MAKE) -C $(APP) -f erlang.mk bootstrap $v + + $i "Configure Makefile" + $t echo "ERLC_OPTS += +debug_info +'{parse_transform,lager_transform}'" >> $(APP)/Makefile + $t echo "ELIXIR_USE_SYSTEM = 0" >> $(APP)/Makefile + $t echo "DEPS += lager" >> $(APP)/Makefile + $t echo "DEPS += jason" >> $(APP)/Makefile + $t echo "DEPS += phoenix" >> $(APP)/Makefile + $t echo "dep_jason = git https://github.com/michalmuskala/jason.git master" >> $(APP)/Makefile + $t echo "dep_phoenix = hex 1.7.2" >> $(APP)/Makefile + $t echo "$$(grep -v 'include erlang.mk' $(APP)/Makefile)" > $(APP)/Makefile + $t echo "include erlang.mk" >> $(APP)/Makefile + + $i "Make deps" + $t $(MAKE) -C $(APP) deps $v + + $i "Check deps have compiled" + $t test -d $(APP)/deps/lager/ebin + $t test -d $(APP)/deps/jason/ebin + $t test -d $(APP)/deps/phoenix/ebin + + $i "Make the app" + $t $(MAKE) -C $(APP) app $v + + $i "Get started apps" + $t $(MAKE) -C $(APP) shell SHELL_OPTS="$(filter-out erl,$(ERL)) -pa $(APP)/deps/*/ebin/ $(APP)/ebin/ $(APP)/apps/*/ebin/ -eval \" \ + {ok, Apps} = application:ensure_all_started('$(APP)'), \ + true = lists:member(lager, Apps), \ + true = lists:member(jason, Apps), \ + true = lists:member(phoenix, Apps), \ + halt()\"" + + $i "Check modules aren't duplicated" + $t $(MAKE) -C $(APP) shell SHELL_OPTS="$(filter-out erl,$(ERL)) -pa $(APP)/deps/*/ebin/ $(APP)/ebin/ $(APP)/apps/*/ebin/ -eval \" \ + {ok, Apps} = application:ensure_all_started('$(APP)'), \ + [begin \ + {ok, Mods} = application:get_key(App, modules), \ + true = lists:sort(Mods) =:= lists:usort(Mods) \ + end || App <- Apps], \ + halt()\"" + +core-elixir-test-project_system: init +ifneq ($(shell which elixirc),) + $i "Bootstrap a new OTP library named $(APP)" + $t mkdir $(APP)/ + $t cp ../erlang.mk $(APP)/ + $t $(MAKE) -C $(APP) -f erlang.mk bootstrap $v + + $i "Configure Makefile" + $t echo "ERLC_OPTS += +debug_info +'{parse_transform,lager_transform}'" >> $(APP)/Makefile + $t echo "ELIXIR_USE_SYSTEM = 1" >> $(APP)/Makefile + $t echo "DEPS += lager" >> $(APP)/Makefile + $t echo "DEPS += jason" >> $(APP)/Makefile + $t echo "DEPS += phoenix" >> $(APP)/Makefile + $t echo "dep_jason = git https://github.com/michalmuskala/jason.git master" >> $(APP)/Makefile + $t echo "dep_phoenix = hex 1.7.2" >> $(APP)/Makefile + $t echo "$$(grep -v 'include erlang.mk' $(APP)/Makefile)" > $(APP)/Makefile + $t echo "include erlang.mk" >> $(APP)/Makefile + + $i "Make deps" + $t $(MAKE) -C $(APP) deps $v + + $i "Check deps have compiled" + $t test -d $(APP)/deps/lager/ebin + $t test -d $(APP)/deps/jason/ebin + $t test -d $(APP)/deps/phoenix/ebin + + $i "Make the app" + $t $(MAKE) -C $(APP) app $v + + $i "Get started apps" + $t $(MAKE) -C $(APP) shell SHELL_OPTS="$(filter-out erl,$(ERL)) -pa $(APP)/deps/*/ebin/ $(APP)/ebin/ $(APP)/apps/*/ebin/ -eval \" \ + {ok, Apps} = application:ensure_all_started('$(APP)'), \ + true = lists:member(lager, Apps), \ + true = lists:member(jason, Apps), \ + true = lists:member(phoenix, Apps), \ + halt()\"" + + $i "Check modules aren't duplicated" + $t $(MAKE) -C $(APP) shell SHELL_OPTS="$(filter-out erl,$(ERL)) -pa $(APP)/deps/*/ebin/ $(APP)/ebin/ $(APP)/apps/*/ebin/ -eval \" \ + {ok, Apps} = application:ensure_all_started('$(APP)'), \ + [begin \ + {ok, Mods} = application:get_key(App, modules), \ + true = lists:sort(Mods) =:= lists:usort(Mods) \ + end || App <- Apps], \ + halt()\"" +else + $i "Test depends on a System Install of Elixir, skipping." +endif + +core-elixir-test-project-rel: init + $i "Bootstrap a new OTP library named $(APP)" + $t mkdir $(APP)/ + $t cp ../erlang.mk $(APP)/ + $t $(MAKE) -C $(APP) -f erlang.mk bootstrap $v + + $i "Configure Makefile" + $t echo "ERLC_OPTS += +debug_info +'{parse_transform,lager_transform}'" >> $(APP)/Makefile + $t echo "ELIXIR_USE_SYSTEM = 1" >> $(APP)/Makefile + $t echo "DEPS += lager" >> $(APP)/Makefile + $t echo "DEPS += jason" >> $(APP)/Makefile + $t echo "DEPS += phoenix" >> $(APP)/Makefile + $t echo "dep_jason = git https://github.com/michalmuskala/jason.git master" >> $(APP)/Makefile + $t echo "dep_phoenix = hex 1.7.2" >> $(APP)/Makefile + $t echo "$$(grep -v 'include erlang.mk' $(APP)/Makefile)" > $(APP)/Makefile + $t echo "include erlang.mk" >> $(APP)/Makefile + + $i "Make deps" + $t $(MAKE) -C $(APP) deps $v + + $i "Check a release can be made" + $t $(MAKE) -C $(APP) bootstrap-rel + $t $(MAKE) -C $(APP) rel + +core-elixir-nif: init +ifneq ($(shell which cpp >/dev/null && echo '#include "sodium.h"' | cpp -H -o /dev/null 2>&1 | head -n1 | grep -v 'No such file or directory'),) + $i "Bootstrap a new OTP library named $(APP)" + $t mkdir $(APP)/ + $t cp ../erlang.mk $(APP)/ + $t $(MAKE) -C $(APP) -f erlang.mk bootstrap $v + + $i "Configure Makefile" + $t echo "DEPS += libsalty2" >> $(APP)/Makefile + $t echo "dep_libsalty2 = git https://github.com/Ianleeclark/libsalty2.git b11e544" >> $(APP)/Makefile + $t echo "$$(grep -v 'include erlang.mk' $(APP)/Makefile)" > $(APP)/Makefile + $t echo "include erlang.mk" >> $(APP)/Makefile + + $i "Make deps" + $t $(MAKE) -C $(APP) deps $v + + $i "Check libsalty2 has compiled" + $t test -f $(APP)/deps/libsalty2/ebin/libsalty2.app + $t test -f $(APP)/deps/libsalty2/priv/salty_nif.so +else + $i "Test depends on libsodium-dev, skipping." +endif \ No newline at end of file diff --git a/test/plugin_hex.mk b/test/core_hex.mk similarity index 100% rename from test/plugin_hex.mk rename to test/core_hex.mk diff --git a/test/log/console.log b/test/log/console.log new file mode 100644 index 000000000..e69de29bb diff --git a/test/log/crash.log b/test/log/crash.log new file mode 100644 index 000000000..e69de29bb diff --git a/test/log/error.log b/test/log/error.log new file mode 100644 index 000000000..e69de29bb