aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTyler Hughes <[email protected]>2023-05-12 21:58:37 +0100
committerLoïc Hoguin <[email protected]>2025-03-17 15:23:40 +0100
commit39160fbf248ae6576e87847d9c33659190a476e6 (patch)
treed90d838214fe9d6013b540ede37687dc48260c47
parent3f7955bad270767f87272f1066ecb0a7ae0c7914 (diff)
downloaderlang.mk-39160fbf248ae6576e87847d9c33659190a476e6.tar.gz
erlang.mk-39160fbf248ae6576e87847d9c33659190a476e6.tar.bz2
erlang.mk-39160fbf248ae6576e87847d9c33659190a476e6.zip
Native Elixir support
This commit also includes a way to completely disable Eunit as that is generally desirable for Elixir-only projects.
-rw-r--r--.github/workflows/ci.yaml10
-rw-r--r--build.config1
-rw-r--r--core/core.mk2
-rw-r--r--core/deps-tools.mk4
-rw-r--r--core/deps.mk134
-rw-r--r--core/elixir.mk201
-rw-r--r--core/erlc.mk45
-rw-r--r--index/elixir.mk7
-rw-r--r--plugins/escript.mk36
-rw-r--r--plugins/eunit.mk7
-rw-r--r--plugins/hex.mk24
-rw-r--r--test/core_elixir.mk276
-rw-r--r--test/plugin_eunit.mk21
13 files changed, 672 insertions, 96 deletions
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 36325fe..0ff2e4d 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -20,6 +20,8 @@ jobs:
matrix:
erlang:
- '27'
+ elixir:
+ - '1.17'
os:
# - macos-latest
- ubuntu-latest
@@ -31,6 +33,7 @@ jobs:
- core-compat
# Trick GH runners into picking this slower job early.
- ACME=1 c=core-deps
+ - core-elixir
- core-makedep
- core-misc
- core-plugins
@@ -78,6 +81,7 @@ jobs:
uses: erlef/setup-beam@v1
with:
otp-version: ${{ matrix.erlang }}
+ elixir-version: ${{ matrix.elixir }}
version-type: loose
- name: Setup MSYS2 (Windows)
@@ -94,6 +98,10 @@ jobs:
gnu-netcat
diffutils
+ - name: Install libsodium (Linux, Elixir)
+ if: matrix.os == 'ubuntu-latest' && matrix.suite == 'core-elixir'
+ run: sudo apt-get -y install libsodium-dev
+
- name: Run tests (Linux)
if: matrix.os == 'ubuntu-latest'
run: make check c=${{ matrix.suite }} -j4 -k ${{ matrix.extra }}
@@ -143,7 +151,7 @@ jobs:
uses: erlef/setup-beam@v1
with:
otp-version: ${{ matrix.erlang }}
- elixir-version: 1.18
+ elixir-version: '1.18'
version-type: loose
- name: Setup MSYS2 (Windows)
diff --git a/build.config b/build.config
index 5f1c193..27b3dc9 100644
--- a/build.config
+++ b/build.config
@@ -15,6 +15,7 @@ core/deps
# Core modules, continued.
core/beam-cache
core/erlc
+core/elixir
core/docs
core/rel
core/test
diff --git a/core/core.mk b/core/core.mk
index 5d5eca0..222faeb 100644
--- a/core/core.mk
+++ b/core/core.mk
@@ -36,7 +36,7 @@ PROJECT ?= $(notdir $(CURDIR))
PROJECT := $(strip $(PROJECT))
PROJECT_VERSION ?= rolling
-PROJECT_MOD ?= $(PROJECT)_app
+PROJECT_MOD ?=
PROJECT_ENV ?= []
# Verbosity.
diff --git a/core/deps-tools.mk b/core/deps-tools.mk
index 47f3b8e..b29bf2a 100644
--- a/core/deps-tools.mk
+++ b/core/deps-tools.mk
@@ -78,9 +78,7 @@ endif
ifeq ($(IS_APP)$(IS_DEP),)
$(verbose) sort < $(ERLANG_MK_RECURSIVE_TMP_LIST) | \
uniq > $(ERLANG_MK_RECURSIVE_TMP_LIST).sorted
- $(verbose) cmp -s $(ERLANG_MK_RECURSIVE_TMP_LIST).sorted $@ \
- || mv $(ERLANG_MK_RECURSIVE_TMP_LIST).sorted $@
- $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST).sorted
+ $(verbose) mv $(ERLANG_MK_RECURSIVE_TMP_LIST).sorted $@
$(verbose) rm $(ERLANG_MK_RECURSIVE_TMP_LIST)
endif
endif # ifneq ($(SKIP_DEPS),)
diff --git a/core/deps.mk b/core/deps.mk
index eb4685c..d2719e5 100644
--- a/core/deps.mk
+++ b/core/deps.mk
@@ -118,6 +118,8 @@ dep_name = $(call query_name,$(1))
# Application directories.
LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$a),$(APPS_DIR)/$a))
+# Elixir is handled specially as it must be built before all other deps
+# when Mix autopatching is necessary.
ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call query_name,$(dep))))
# When we are calling an app directly we don't want to include it here
@@ -210,9 +212,11 @@ endif
ifneq ($(SKIP_DEPS),)
deps::
else
-deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log | $(ERLANG_MK_TMP)
-ifneq ($(ALL_DEPS_DIRS),)
- $(verbose) set -e; for dep in $(ALL_DEPS_DIRS); do \
+ALL_DEPS_DIRS_TO_BUILD = $(if $(filter-out $(DEPS_DIR)/elixir,$(ALL_DEPS_DIRS)),$(filter-out $(DEPS_DIR)/elixir,$(ALL_DEPS_DIRS)),$(ALL_DEPS_DIRS))
+
+deps:: $(ALL_DEPS_DIRS_TO_BUILD) apps clean-tmp-deps.log | $(ERLANG_MK_TMP)
+ifneq ($(ALL_DEPS_DIRS_TO_BUILD),)
+ $(verbose) set -e; for dep in $(ALL_DEPS_DIRS_TO_BUILD); do \
if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
:; \
else \
@@ -236,34 +240,47 @@ endif
# Deps related targets.
-# @todo rename GNUmakefile and makefile into Makefile first, if they exist
-# While Makefile file could be GNUmakefile or makefile,
-# in practice only Makefile is needed so far.
-define dep_autopatch
+autopatch_verbose_0 = @echo " PATCH " $(subst autopatch-,,$@) "(method: $(AUTOPATCH_METHOD))";
+autopatch_verbose_2 = set -x;
+autopatch_verbose = $(autopatch_verbose_$(V))
+
+define dep_autopatch_detect
if [ -f $(DEPS_DIR)/$1/erlang.mk ]; then \
- rm -rf $(DEPS_DIR)/$1/ebin/; \
- $(call erlang,$(call dep_autopatch_appsrc.erl,$1)); \
- $(call dep_autopatch_erlang_mk,$1); \
+ echo erlang.mk; \
+ elif [ -f $(DEPS_DIR)/$1/mix.exs -a -d $(DEPS_DIR)/$1/lib ]; then \
+ if [ "$(ELIXIR)" != "disable" ]; then \
+ echo mix; \
+ elif [ -f $(DEPS_DIR)/$1/rebar.lock -o -f $(DEPS_DIR)/$1/rebar.config ]; then \
+ echo rebar3; \
+ else \
+ exit 99; \
+ fi \
elif [ -f $(DEPS_DIR)/$1/Makefile ]; then \
if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
- $(call dep_autopatch2,$1); \
- elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$1/Makefile` ]; then \
- $(call dep_autopatch2,$1); \
- elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$1/Makefile` ]; then \
- $(call dep_autopatch2,$1); \
- elif [ -n "`find $(DEPS_DIR)/$1/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
- $(call dep_autopatch2,$1); \
- fi \
- else \
- if [ ! -d $(DEPS_DIR)/$1/src/ ]; then \
- $(call dep_autopatch_noop,$1); \
+ echo rebar3; \
+ elif [ 0 != \`grep -c "include ../\w*\.mk" $(DEPS_DIR)/$1/Makefile\` ]; then \
+ echo rebar3; \
+ elif [ 0 != \`grep -ci "^[^#].*rebar" $(DEPS_DIR)/$1/Makefile\` ]; then \
+ echo rebar3; \
+ elif [ -n "\`find $(DEPS_DIR)/$1/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;\`" ]; then \
+ echo rebar3; \
else \
- $(call dep_autopatch2,$1); \
+ echo noop; \
fi \
+ elif [ ! -d $(DEPS_DIR)/$1/src/ ]; then \
+ echo noop; \
+ else \
+ echo rebar3; \
fi
endef
-define dep_autopatch2
+define dep_autopatch_for_erlang.mk
+ rm -rf $(DEPS_DIR)/$1/ebin/; \
+ $(call erlang,$(call dep_autopatch_appsrc.erl,$1)); \
+ $(call dep_autopatch_erlang_mk,$1)
+endef
+
+define dep_autopatch_for_rebar3
! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
@@ -279,8 +296,22 @@ define dep_autopatch2
fi
endef
-define dep_autopatch_noop
- printf "noop:\n" > $(DEPS_DIR)/$1/Makefile
+define dep_autopatch_for_mix
+ $(call dep_autopatch_mix,$1)
+endef
+
+define dep_autopatch_for_noop
+ test -f $(DEPS_DIR)/$1/Makefile || printf "noop:\n" > $(DEPS_DIR)/$1/Makefile
+endef
+
+define maybe_flock
+ if command -v flock >/dev/null; then \
+ flock $1 sh -c "$2"; \
+ elif command -v lockf >/dev/null; then \
+ lockf $1 sh -c "$2"; \
+ else \
+ $2; \
+ fi
endef
# Replace "include erlang.mk" with a line that will load the parent Erlang.mk
@@ -307,13 +338,7 @@ endef
# We use flock/lockf when available to avoid concurrency issues.
define dep_autopatch_fetch_rebar
- if command -v flock >/dev/null; then \
- flock $(ERLANG_MK_TMP)/rebar.lock sh -c "$(call dep_autopatch_fetch_rebar2)"; \
- elif command -v lockf >/dev/null; then \
- lockf $(ERLANG_MK_TMP)/rebar.lock sh -c "$(call dep_autopatch_fetch_rebar2)"; \
- else \
- $(call dep_autopatch_fetch_rebar2); \
- fi
+ $(call maybe_flock,$(ERLANG_MK_TMP)/rebar.lock,$(call dep_autopatch_fetch_rebar2))
endef
define dep_autopatch_fetch_rebar2
@@ -397,7 +422,6 @@ define dep_autopatch_rebar.erl
GetHexVsn2 = fun(N, NP) ->
case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.lock)") of
{ok, Lock} ->
- io:format("~p~n", [Lock]),
LockPkgs = case lists:keyfind("1.2.0", 1, Lock) of
{_, LP} ->
LP;
@@ -411,10 +435,8 @@ define dep_autopatch_rebar.erl
end,
if
is_list(LockPkgs) ->
- io:format("~p~n", [LockPkgs]),
case lists:keyfind(atom_to_binary(N, latin1), 1, LockPkgs) of
{_, {pkg, _, Vsn}, _} ->
- io:format("~p~n", [Vsn]),
{N, {hex, NP, binary_to_list(Vsn)}};
_ ->
false
@@ -835,7 +857,7 @@ define dep_fetch_fail
endef
define dep_target
-$(DEPS_DIR)/$(call query_name,$1): | $(if $(filter hex,$(call query_fetch_method,$1)),hex-core) $(ERLANG_MK_TMP)
+$(DEPS_DIR)/$(call query_name,$1): $(if $(filter elixir,$(BUILD_DEPS) $(DEPS)),$(if $(filter-out elixir,$1),$(DEPS_DIR)/elixir/ebin/dep_built)) $(if $(filter hex,$(call query_fetch_method,$1)),$(if $(wildcard $(DEPS_DIR)/$(call query_name,$1)),,$(DEPS_DIR)/hex_core/ebin/dep_built)) | $(ERLANG_MK_TMP)
$(eval DEP_NAME := $(call query_name,$1))
$(eval DEP_STR := $(if $(filter $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
$(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
@@ -854,30 +876,44 @@ $(DEPS_DIR)/$(call query_name,$1): | $(if $(filter hex,$(call query_fetch_method
cd $(DEPS_DIR)/$(DEP_NAME) && ./configure; \
fi
ifeq ($(filter $1,$(NO_AUTOPATCH)),)
- $(verbose) $$(MAKE) --no-print-directory autopatch-$(DEP_NAME)
+ $(verbose) AUTOPATCH_METHOD=`$(call dep_autopatch_detect,$1)`; \
+ if [ $$$$? -eq 99 ]; then \
+ echo "Elixir is currently disabled. Please set 'ELIXIR = system' in the Makefile to enable"; \
+ exit 99; \
+ fi; \
+ $$(MAKE) --no-print-directory autopatch-$(DEP_NAME) AUTOPATCH_METHOD=$$$$AUTOPATCH_METHOD
endif
.PHONY: autopatch-$(call query_name,$1)
+ifeq ($1,elixir)
+autopatch-elixir::
+ $$(verbose) ln -s lib/elixir/ebin $(DEPS_DIR)/elixir/
+else
autopatch-$(call query_name,$1)::
- $(verbose) if [ "$1" = "elixir" -a "$(ELIXIR_PATCH)" ]; then \
- ln -s lib/elixir/ebin $(DEPS_DIR)/elixir/; \
- else \
- $$(call dep_autopatch,$(call query_name,$1)) \
- fi
+ $$(autopatch_verbose) $$(call dep_autopatch_for_$(AUTOPATCH_METHOD),$(call query_name,$1))
+endif
endef
# We automatically depend on hex_core when the project isn't already.
$(if $(filter hex_core,$(DEPS) $(BUILD_DEPS) $(DOC_DEPS) $(REL_DEPS) $(TEST_DEPS)),,\
$(eval $(call dep_target,hex_core)))
-.PHONY: hex-core
-
-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; \
- touch $(DEPS_DIR)/hex_core/ebin/dep_built; \
- fi
+$(DEPS_DIR)/hex_core/ebin/dep_built: | $(ERLANG_MK_TMP)
+ $(verbose) $(call maybe_flock,$(ERLANG_MK_TMP)/hex_core.lock,\
+ if [ ! -e $(DEPS_DIR)/hex_core/ebin/dep_built ]; then \
+ $(MAKE) $(DEPS_DIR)/hex_core; \
+ $(MAKE) -C $(DEPS_DIR)/hex_core IS_DEP=1; \
+ touch $(DEPS_DIR)/hex_core/ebin/dep_built; \
+ fi)
+
+$(DEPS_DIR)/elixir/ebin/dep_built: | $(ERLANG_MK_TMP)
+ $(verbose) $(call maybe_flock,$(ERLANG_MK_TMP)/elixir.lock,\
+ if [ ! -e $(DEPS_DIR)/elixir/ebin/dep_built ]; then \
+ $(MAKE) $(DEPS_DIR)/elixir; \
+ $(MAKE) -C $(DEPS_DIR)/elixir; \
+ touch $(DEPS_DIR)/elixir/ebin/dep_built; \
+ fi)
$(foreach dep,$(BUILD_DEPS) $(DEPS),$(eval $(call dep_target,$(dep))))
diff --git a/core/elixir.mk b/core/elixir.mk
new file mode 100644
index 0000000..8d1dba9
--- /dev/null
+++ b/core/elixir.mk
@@ -0,0 +1,201 @@
+# Copyright (c) 2024, Tyler Hughes <[email protected]>
+# Copyright (c) 2024, Loïc Hoguin <[email protected]>
+# This file is part of erlang.mk and subject to the terms of the ISC License.
+
+# Elixir is automatically enabled in all cases except when
+# an Erlang project uses an Elixir dependency. In that case
+# $(ELIXIR) must be set explicitly.
+ELIXIR ?= $(if $(filter elixir,$(BUILD_DEPS) $(DEPS)),dep,$(if $(EX_FILES),system,disable))
+export ELIXIR
+
+ifeq ($(ELIXIR),system)
+# We expect 'elixir' to be on the path.
+ELIXIR_LIBS ?= $(dir $(shell readlink -f `which elixir`))/../lib
+ELIXIR_LIBS := $(ELIXIR_LIBS)
+export ELIXIR_LIBS
+ERL_LIBS := $(ERL_LIBS):$(ELIXIR_LIBS)
+else
+ifeq ($(ELIXIR),dep)
+ERL_LIBS := $(ERL_LIBS):$(DEPS_DIR)/elixir/lib/
+endif
+endif
+
+elixirc_verbose_0 = @echo " EXC $(words $(EX_FILES)) files";
+elixirc_verbose_2 = set -x;
+elixirc_verbose = $(elixirc_verbose_$(V))
+
+# Unfortunately this currently requires Elixir.
+# https://github.com/jelly-beam/verl is a good choice
+# for an Erlang implementation, but we already have to
+# pull hex_core and Rebar3 so adding yet another pull
+# is annoying, especially one that would be necessary
+# every time we autopatch Rebar projects. Wait and see.
+define hex_version_resolver.erl
+ HexVersionResolve = fun(Name, Req) ->
+ application:ensure_all_started(ssl),
+ application:ensure_all_started(inets),
+ Config = $(hex_config.erl),
+ case hex_repo:get_package(Config, atom_to_binary(Name)) of
+ {ok, {200, _RespHeaders, Package}} ->
+ #{releases := List} = Package,
+ {value, #{version := Version}} = lists:search(fun(#{version := Vsn}) ->
+ M = list_to_atom("Elixir.Version"),
+ F = list_to_atom("match?"),
+ M:F(Vsn, Req)
+ end, List),
+ {ok, Version};
+ {ok, {Status, _, Errors}} ->
+ {error, Status, Errors}
+ end
+ end,
+ HexVersionResolveAndPrint = fun(Name, Req) ->
+ case HexVersionResolve(Name, Req) of
+ {ok, Version} ->
+ io:format("~s", [Version]),
+ halt(0);
+ {error, Status, Errors} ->
+ io:format("Error ~b: ~0p~n", [Status, Errors]),
+ halt(77)
+ end
+ end
+endef
+
+define dep_autopatch_mix.erl
+ $(call hex_version_resolver.erl),
+ {ok, _} = application:ensure_all_started(elixir),
+ {ok, _} = application:ensure_all_started(mix),
+ MixFile = <<"$(call core_native_path,$(DEPS_DIR)/$1/mix.exs)">>,
+ {Mod, Bin} =
+ case elixir_compiler:file(MixFile, fun(_File, _LexerPid) -> ok end) of
+ [{T = {_, _}, _CheckerPid}] -> T;
+ [T = {_, _}] -> T
+ end,
+ {module, Mod} = code:load_binary(Mod, binary_to_list(MixFile), Bin),
+ Project = Mod:project(),
+ Application = try Mod:application() catch error:undef -> [] end,
+ StartMod = case lists:keyfind(mod, 1, Application) of
+ {mod, {StartMod0, _StartArgs}} ->
+ atom_to_list(StartMod0);
+ _ ->
+ ""
+ end,
+ Write = fun (Text) ->
+ file:write_file("$(call core_native_path,$(DEPS_DIR)/$1/Makefile)", Text, [append])
+ end,
+ Write([
+ "PROJECT = ", atom_to_list(proplists:get_value(app, Project)), "\n"
+ "PROJECT_DESCRIPTION = ", proplists:get_value(description, Project, ""), "\n"
+ "PROJECT_VERSION = ", proplists:get_value(version, Project, ""), "\n"
+ "PROJECT_MOD = ", StartMod, "\n"
+ "define PROJECT_ENV\n",
+ io_lib:format("~p", [proplists:get_value(env, Application, [])]), "\n"
+ "endef\n\n"]),
+ ExtraApps = lists:usort([eex, elixir, logger, mix] ++ proplists:get_value(extra_applications, Application, [])),
+ Write(["LOCAL_DEPS += ", lists:join(" ", [atom_to_list(App) || App <- ExtraApps]), "\n\n"]),
+ Deps = proplists:get_value(deps, Project, []) -- [elixir_make],
+ IsRequiredProdDep = fun(Opts) ->
+ (proplists:get_value(optional, Opts) =/= true)
+ andalso
+ case proplists:get_value(only, Opts, prod) of
+ prod -> true;
+ L when is_list(L) -> lists:member(prod, L);
+ _ -> false
+ end
+ end,
+ lists:foreach(fun
+ ({Name, Req}) when is_binary(Req) ->
+ {ok, Vsn} = HexVersionResolve(Name, Req),
+ Write(["DEPS += ", atom_to_list(Name), "\n"]),
+ Write(["dep_", atom_to_list(Name), " = hex ", Vsn, " ", atom_to_list(Name), "\n"]);
+ ({Name, Opts}) when is_list(Opts) ->
+ Path = proplists:get_value(path, Opts),
+ case IsRequiredProdDep(Opts) of
+ true when Path =/= undefined ->
+ Write(["DEPS += ", atom_to_list(Name), "\n"]),
+ Write(["dep_", atom_to_list(Name), " = ln ", Path, "\n"]);
+ true when Path =:= undefined ->
+ Write(["DEPS += ", atom_to_list(Name), "\n"]),
+ io:format(standard_error, "Warning: No version given for ~p.", [Name]);
+ false ->
+ ok
+ end;
+ ({Name, Req, Opts}) ->
+ case IsRequiredProdDep(Opts) of
+ true ->
+ {ok, Vsn} = HexVersionResolve(Name, Req),
+ Write(["DEPS += ", atom_to_list(Name), "\n"]),
+ Write(["dep_", atom_to_list(Name), " = hex ", Vsn, " ", atom_to_list(Name), "\n"]);
+ false ->
+ ok
+ end;
+ (_) ->
+ ok
+ end, Deps),
+ case lists:member(elixir_make, proplists:get_value(compilers, Project, [])) of
+ false ->
+ ok;
+ true ->
+ Write("# https://hexdocs.pm/elixir_make/Mix.Tasks.Compile.ElixirMake.html\n"),
+ MakeVal = fun(Key, Proplist, DefaultVal, DefaultReplacement) ->
+ case proplists:get_value(Key, Proplist, DefaultVal) of
+ DefaultVal -> DefaultReplacement;
+ Value -> Value
+ end
+ end,
+ MakeMakefile = binary_to_list(MakeVal(make_makefile, Project, default, <<"Makefile">>)),
+ MakeExe = MakeVal(make_executable, Project, default, "$$\(MAKE)"),
+ MakeCwd = MakeVal(make_cwd, Project, undefined, <<".">>),
+ MakeTargets = MakeVal(make_targets, Project, [], []),
+ MakeArgs = MakeVal(make_args, Project, undefined, []),
+ case file:rename("$(DEPS_DIR)/$1/" ++ MakeMakefile, "$(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(90)
+ end,
+ Write(["app::\n"
+ "\t", MakeExe, " -C ", MakeCwd, " -f $(DEPS_DIR)/$1/elixir_make.mk",
+ lists:join(" ", MakeTargets),
+ lists:join(" ", MakeArgs),
+ "\n\n"]),
+ case MakeVal(make_clean, Project, nil, undefined) of
+ undefined ->
+ ok;
+ Clean ->
+ Write(["clean::\n\t", Clean, "\n\n"])
+ end
+ end,
+ Write("ERLC_OPTS = +debug_info\n\n"),
+ Write("include $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
+ halt()
+endef
+
+define dep_autopatch_mix
+ sed 's|\(defmodule.*do\)|\1\n try do\n Code.compiler_options(on_undefined_variable: :warn)\n rescue _ -> :ok\n end\n|g' -i $(DEPS_DIR)/$(1)/mix.exs; \
+ $(MAKE) $(DEPS_DIR)/hex_core/ebin/dep_built; \
+ MIX_ENV="$(if $(MIX_ENV),$(strip $(MIX_ENV)),prod)" \
+ $(call erlang,$(call dep_autopatch_mix.erl,$1))
+endef
+
+# We change the group leader so the Elixir io:format output
+# isn't captured as we need to either print the modules on
+# success, or print _ERROR_ on failure.
+define compile_ex.erl
+ {ok, _} = application:ensure_all_started(elixir),
+ {ok, _} = application:ensure_all_started(mix),
+ ModCode = list_to_atom("Elixir.Code"),
+ ModCode:put_compiler_option(ignore_module_conflict, true),
+ ModComp = list_to_atom("Elixir.Kernel.ParallelCompiler"),
+ ModMixProject = list_to_atom("Elixir.Mix.Project"),
+ erlang:group_leader(whereis(standard_error), self()),
+ ModMixProject:in_project($(PROJECT), ".", [], fun(_MixFile) ->
+ case ModComp:compile_to_path([$(call comma_list,$(patsubst %,<<"%">>,$1))], <<"ebin/">>) of
+ {ok, Modules, _} ->
+ lists:foreach(fun(E) -> io:format(user, "~p ", [E]) end, Modules),
+ halt(0);
+ {error, _ErroredModules, _WarnedModules} ->
+ io:format(user, "_ERROR_", []),
+ halt(1)
+ end
+ end)
+endef
diff --git a/core/erlc.mk b/core/erlc.mk
index f3b499a..c956c4d 100644
--- a/core/erlc.mk
+++ b/core/erlc.mk
@@ -49,7 +49,7 @@ mib_verbose_0 = @echo " MIB " $(filter %.bin %.mib,$(?F));
mib_verbose_2 = set -x;
mib_verbose = $(mib_verbose_$(V))
-ifneq ($(wildcard src/),)
+ifneq ($(wildcard src/)$(wildcard lib/),)
# Targets.
@@ -57,34 +57,21 @@ app:: $(if $(wildcard ebin/test),beam-cache-restore-app) 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) $(OPTIONAL_DEPS) $(foreach dep,$(DEPS),$(call query_name,$(dep))))]},
- {optional_applications, [$(call comma_list,$(OPTIONAL_DEPS))]},
- {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
-]}.
-endef
-else
+PROJECT_MOD := $(if $(PROJECT_MOD),$(PROJECT_MOD),$(if $(wildcard src/$(PROJECT)_app.erl),$(PROJECT)_app))
+
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))]},
+ {registered, [$(if $(PROJECT_MOD),$(call comma_list,$(if $(filter $(PROJECT_MOD),$(PROJECT)_app),$(PROJECT)_sup) $(PROJECT_REGISTERED)))]},
{applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(OPTIONAL_DEPS) $(foreach dep,$(DEPS),$(call query_name,$(dep))))]},
- {optional_applications, [$(call comma_list,$(OPTIONAL_DEPS))]},
- {mod, {$(PROJECT_MOD), []}},
+ {optional_applications, [$(call comma_list,$(OPTIONAL_DEPS))]},$(if $(PROJECT_MOD),
+ {mod$(comma)$(space){$(patsubst %,'%',$(PROJECT_MOD))$(comma)$(space)[]}}$(comma))
{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) :
@@ -96,6 +83,9 @@ ALL_SRC_FILES := $(sort $(call core_find,src/,*))
ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
+ALL_LIB_FILES := $(sort $(call core_find,lib/,*))
+EX_FILES := $(filter-out lib/mix/%,$(filter %.ex,$(ALL_SRC_FILES) $(ALL_LIB_FILES)))
+
# ASN.1 files.
ifneq ($(wildcard asn1/),)
@@ -271,21 +261,21 @@ define makedep.erl
endef
ifeq ($(if $(NO_MAKEDEP),$(wildcard $(PROJECT).d),),)
-$(PROJECT).d:: $(ERL_FILES) $(call core_find,include/,*.hrl) $(MAKEFILE_LIST)
+$(PROJECT).d:: $(ERL_FILES) $(EX_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)
+ifneq ($(words $(ERL_FILES) $(EX_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES) $(EX_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 $(ERL_FILES) $(EX_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES) $(EX_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
+$(ERL_FILES) $(EX_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
@@ -312,13 +302,16 @@ define validate_app_file
end
endef
-ebin/$(PROJECT).app:: $(ERL_FILES) $(CORE_FILES) $(wildcard src/$(PROJECT).app.src)
- $(eval FILES_TO_COMPILE := $(filter-out src/$(PROJECT).app.src,$?))
+ebin/$(PROJECT).app:: $(ERL_FILES) $(CORE_FILES) $(wildcard src/$(PROJECT).app.src) $(EX_FILES)
+ $(eval FILES_TO_COMPILE := $(filter-out $(EX_FILES) src/$(PROJECT).app.src,$?))
$(if $(strip $(FILES_TO_COMPILE)),$(call compile_erl,$(FILES_TO_COMPILE)))
+ $(if $(filter $(ELIXIR),disable),,$(if $(filter $?,$(EX_FILES)),$(elixirc_verbose) $(eval MODULES := $(shell $(call erlang,$(call compile_ex.erl,$(EX_FILES)))))))
+ $(eval ELIXIR_COMP_FAILED := $(if $(filter _ERROR_,$(firstword $(MODULES))),true,false))
# Older git versions do not have the --first-parent flag. Do without in that case.
+ $(verbose) if $(ELIXIR_COMP_FAILED); then exit 1; fi
$(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 \
+ $(eval MODULES := $(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)))))' \
diff --git a/index/elixir.mk b/index/elixir.mk
new file mode 100644
index 0000000..86f89e7
--- /dev/null
+++ b/index/elixir.mk
@@ -0,0 +1,7 @@
+PACKAGES += elixir
+pkg_elixir_name = elixir
+pkg_elixir_description = Elixir is a dynamic, functional language for building scalable and maintainable applications.
+pkg_elixir_homepage = https://elixir-lang.org
+pkg_elixir_fetch = git
+pkg_elixir_repo = https://github.com/elixir-lang/elixir
+pkg_elixir_commit = main
diff --git a/plugins/escript.mk b/plugins/escript.mk
index 1790dcb..4eac352 100644
--- a/plugins/escript.mk
+++ b/plugins/escript.mk
@@ -27,17 +27,45 @@ help::
# Plugin-specific targets.
-escript-zip:: FULL=1
-escript-zip:: deps app
+ALL_ESCRIPT_DEPS_DIRS = $(LOCAL_DEPS_DIRS) $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(DEPS)),$(call query_name,$(dep))))
+
+ESCRIPT_RUNTIME_DEPS_FILE ?= $(ERLANG_MK_TMP)/escript-deps.log
+
+escript-list-runtime-deps:
+ifeq ($(IS_DEP),)
+ $(verbose) rm -f $(ESCRIPT_RUNTIME_DEPS_FILE)
+endif
+ $(verbose) touch $(ESCRIPT_RUNTIME_DEPS_FILE)
+ $(verbose) set -e; for dep in $(ALL_ESCRIPT_DEPS_DIRS) ; do \
+ if ! grep -qs ^$$dep$$ $(ESCRIPT_RUNTIME_DEPS_FILE); then \
+ echo $$dep >> $(ESCRIPT_RUNTIME_DEPS_FILE); \
+ if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
+ $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
+ $(MAKE) -C $$dep escript-list-runtime-deps \
+ IS_DEP=1 \
+ ESCRIPT_RUNTIME_DEPS_FILE=$(ESCRIPT_RUNTIME_DEPS_FILE); \
+ fi \
+ fi \
+ done
+ifeq ($(IS_DEP),)
+ $(verbose) sort < $(ESCRIPT_RUNTIME_DEPS_FILE) | uniq > $(ESCRIPT_RUNTIME_DEPS_FILE).sorted
+ $(verbose) mv $(ESCRIPT_RUNTIME_DEPS_FILE).sorted $(ESCRIPT_RUNTIME_DEPS_FILE)
+endif
+
+escript-prepare: deps app
+ $(MAKE) escript-list-runtime-deps
+
+escript-zip:: escript-prepare
$(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/*
+ $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(abspath $(ESCRIPT_ZIP_FILE)) $(notdir $(CURDIR))/ebin/*
ifneq ($(DEPS),)
$(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(abspath $(ESCRIPT_ZIP_FILE)) \
$(subst $(DEPS_DIR)/,,$(addsuffix /*,$(wildcard \
- $(addsuffix /ebin,$(shell cat $(ERLANG_MK_TMP)/deps.log)))))
+ $(addsuffix /ebin,$(shell cat $(ESCRIPT_RUNTIME_DEPS_FILE))))))
endif
+# @todo Only generate the zip file if there were changes.
escript:: escript-zip
$(gen_verbose) printf "%s\n" \
"#!$(ESCRIPT_SHEBANG)" \
diff --git a/plugins/eunit.mk b/plugins/eunit.mk
index 211a744..8a97688 100644
--- a/plugins/eunit.mk
+++ b/plugins/eunit.mk
@@ -4,6 +4,11 @@
.PHONY: eunit apps-eunit
+# Eunit can be disabled by setting this to any other value.
+EUNIT ?= system
+
+ifeq ($(EUNIT),system)
+
# Configuration
EUNIT_OPTS ?=
@@ -61,3 +66,5 @@ apps-eunit: test-build
exit $$eunit_retcode
endif
endif
+
+endif
diff --git a/plugins/hex.mk b/plugins/hex.mk
index 482a56a..72c0db6 100644
--- a/plugins/hex.mk
+++ b/plugins/hex.mk
@@ -19,7 +19,7 @@ define hex_user_create.erl
endef
# The $(info ) call inserts a new line after the password prompt.
-hex-user-create: hex-core
+hex-user-create: $(DEPS_DIR)/hex_core/ebin/dep_built
$(if $(HEX_USERNAME),,$(eval HEX_USERNAME := $(shell read -p "Username: " username; echo $$username)))
$(if $(HEX_PASSWORD),,$(eval HEX_PASSWORD := $(shell stty -echo; read -p "Password: " password; stty echo; echo $$password) $(info )))
$(if $(HEX_EMAIL),,$(eval HEX_EMAIL := $(shell read -p "Email: " email; echo $$email)))
@@ -49,7 +49,7 @@ define hex_key_add.erl
end
endef
-hex-key-add: hex-core
+hex-key-add: $(DEPS_DIR)/hex_core/ebin/dep_built
$(if $(HEX_USERNAME),,$(eval HEX_USERNAME := $(shell read -p "Username: " username; echo $$username)))
$(if $(HEX_PASSWORD),,$(eval HEX_PASSWORD := $(shell stty -echo; read -p "Password: " password; stty echo; echo $$password) $(info )))
$(gen_verbose) $(call erlang,$(call hex_key_add.erl,$(HEX_USERNAME),$(HEX_PASSWORD),\
@@ -127,7 +127,7 @@ hex_tar_verbose_0 = @echo " TAR $(notdir $(ERLANG_MK_TMP))/$(@F)";
hex_tar_verbose_2 = set -x;
hex_tar_verbose = $(hex_tar_verbose_$(V))
-$(HEX_TARBALL_OUTPUT_FILE): hex-core app
+$(HEX_TARBALL_OUTPUT_FILE): $(DEPS_DIR)/hex_core/ebin/dep_built app
$(hex_tar_verbose) $(call erlang,$(call hex_tarball_create.erl))
hex-tarball-create: $(HEX_TARBALL_OUTPUT_FILE)
@@ -178,14 +178,14 @@ define hex_release_publish.erl
end
endef
-hex-release-tarball: hex-core $(HEX_TARBALL_OUTPUT_FILE)
+hex-release-tarball: $(DEPS_DIR)/hex_core/ebin/dep_built $(HEX_TARBALL_OUTPUT_FILE)
$(verbose) $(call erlang,$(call hex_release_publish_summary.erl))
-hex-release-publish: hex-core hex-release-tarball
+hex-release-publish: $(DEPS_DIR)/hex_core/ebin/dep_built hex-release-tarball
$(if $(HEX_SECRET),,$(eval HEX_SECRET := $(shell stty -echo; read -p "Secret: " secret; stty echo; echo $$secret) $(info )))
$(gen_verbose) $(call erlang,$(call hex_release_publish.erl,$(HEX_SECRET),false))
-hex-release-replace: hex-core hex-release-tarball
+hex-release-replace: $(DEPS_DIR)/hex_core/ebin/dep_built hex-release-tarball
$(if $(HEX_SECRET),,$(eval HEX_SECRET := $(shell stty -echo; read -p "Secret: " secret; stty echo; echo $$secret) $(info )))
$(gen_verbose) $(call erlang,$(call hex_release_publish.erl,$(HEX_SECRET),true))
@@ -204,7 +204,7 @@ define hex_release_delete.erl
end
endef
-hex-release-delete: hex-core
+hex-release-delete: $(DEPS_DIR)/hex_core/ebin/dep_built
$(if $(HEX_SECRET),,$(eval HEX_SECRET := $(shell stty -echo; read -p "Secret: " secret; stty echo; echo $$secret) $(info )))
$(gen_verbose) $(call erlang,$(call hex_release_delete.erl,$(HEX_SECRET)))
@@ -224,7 +224,7 @@ define hex_release_retire.erl
end
endef
-hex-release-retire: hex-core
+hex-release-retire: $(DEPS_DIR)/hex_core/ebin/dep_built
$(if $(HEX_SECRET),,$(eval HEX_SECRET := $(shell stty -echo; read -p "Secret: " secret; stty echo; echo $$secret) $(info )))
$(gen_verbose) $(call erlang,$(call hex_release_retire.erl,$(HEX_SECRET),\
$(if $(HEX_VERSION),$(HEX_VERSION),$(PROJECT_VERSION)),\
@@ -246,7 +246,7 @@ define hex_release_unretire.erl
end
endef
-hex-release-unretire: hex-core
+hex-release-unretire: $(DEPS_DIR)/hex_core/ebin/dep_built
$(if $(HEX_SECRET),,$(eval HEX_SECRET := $(shell stty -echo; read -p "Secret: " secret; stty echo; echo $$secret) $(info )))
$(gen_verbose) $(call erlang,$(call hex_release_unretire.erl,$(HEX_SECRET),\
$(if $(HEX_VERSION),$(HEX_VERSION),$(PROJECT_VERSION))))
@@ -255,7 +255,7 @@ HEX_DOCS_DOC_DIR ?= doc/
HEX_DOCS_TARBALL_FILES ?= $(sort $(call core_find,$(HEX_DOCS_DOC_DIR),*))
HEX_DOCS_TARBALL_OUTPUT_FILE ?= $(ERLANG_MK_TMP)/$(PROJECT)-docs.tar.gz
-$(HEX_DOCS_TARBALL_OUTPUT_FILE): hex-core app docs
+$(HEX_DOCS_TARBALL_OUTPUT_FILE): $(DEPS_DIR)/hex_core/ebin/dep_built app docs
$(hex_tar_verbose) tar czf $(HEX_DOCS_TARBALL_OUTPUT_FILE) -C $(HEX_DOCS_DOC_DIR) \
$(HEX_DOCS_TARBALL_FILES:$(HEX_DOCS_DOC_DIR)%=%)
@@ -279,7 +279,7 @@ define hex_docs_publish.erl
end
endef
-hex-docs-publish: hex-core hex-docs-tarball-create
+hex-docs-publish: $(DEPS_DIR)/hex_core/ebin/dep_built hex-docs-tarball-create
$(if $(HEX_SECRET),,$(eval HEX_SECRET := $(shell stty -echo; read -p "Secret: " secret; stty echo; echo $$secret) $(info )))
$(gen_verbose) $(call erlang,$(call hex_docs_publish.erl,$(HEX_SECRET)))
@@ -299,7 +299,7 @@ define hex_docs_delete.erl
end
endef
-hex-docs-delete: hex-core
+hex-docs-delete: $(DEPS_DIR)/hex_core/ebin/dep_built
$(if $(HEX_SECRET),,$(eval HEX_SECRET := $(shell stty -echo; read -p "Secret: " secret; stty echo; echo $$secret) $(info )))
$(gen_verbose) $(call erlang,$(call hex_docs_delete.erl,$(HEX_SECRET),\
$(if $(HEX_VERSION),$(HEX_VERSION),$(PROJECT_VERSION))))
diff --git a/test/core_elixir.mk b/test/core_elixir.mk
new file mode 100644
index 0000000..1ed23b6
--- /dev/null
+++ b/test/core_elixir.mk
@@ -0,0 +1,276 @@
+# 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-compile-from-lib: 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-lib $v
+
+ $i "Create Elixir source file hello.ex"
+ $t mkdir $(APP)/lib
+ $t printf "%s\n" \
+ "defmodule HelloWorld do" \
+ " def hello do" \
+ ' IO.puts("Hello, world!")' \
+ " end" \
+ "end" > $(APP)/lib/hello.ex
+
+ $i "Build the application"
+ $t $(MAKE) -C $(APP) $v
+
+ $i "Check that all compiled files exist"
+ $t test -f $(APP)/ebin/$(APP).app
+ $t test -f $(APP)/ebin/Elixir.HelloWorld.beam
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -pa $(APP)/deps/*/ebin -pa $(dir $(shell elixir -e 'IO.puts(:code.lib_dir(:elixir))'))/*/ebin -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = ['Elixir.HelloWorld']} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+core-elixir-compile-from-src: 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-lib $v
+
+ $i "Create Elixir source file hello.ex"
+ $t printf "%s\n" \
+ "defmodule HelloWorld do" \
+ " def hello do" \
+ ' IO.puts("Hello, world!")' \
+ " end" \
+ "end" > $(APP)/src/hello.ex
+
+ $i "Build the application"
+ $t $(MAKE) -C $(APP) $v
+
+ $i "Check that all compiled files exist"
+ $t test -f $(APP)/ebin/$(APP).app
+ $t test -f $(APP)/ebin/Elixir.HelloWorld.beam
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -pa $(APP)/deps/*/ebin -pa $(dir $(shell elixir -e 'IO.puts(:code.lib_dir(:elixir))'))/*/ebin -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = ['Elixir.HelloWorld']} \
+ = application:get_key($(APP), modules), \
+ [{module, M} = code:load_file(M) || M <- Mods], \
+ halt()"
+
+core-elixir-disable: 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-lib $v
+
+ $i "Create Elixir source file hello.ex"
+ $t printf "%s\n" \
+ "defmodule HelloWorld do" \
+ " def hello do" \
+ ' IO.puts("Hello, world!")' \
+ " end" \
+ "end" > $(APP)/src/hello.ex
+
+ $i "Disable Elixir in the Makefile"
+ $t perl -ni.bak -e 'print;if ($$.==1) {print "ELIXIR = disable\n"}' $(APP)/Makefile
+
+ $i "Build the application"
+ $t $(MAKE) -C $(APP) $v
+
+ $i "Check that the Elixir file wasn't compiled"
+ $t test -f $(APP)/ebin/$(APP).app
+ $t test ! -e $(APP)/ebin/Elixir.HelloWorld.beam
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -pa $(APP)/deps/*/ebin -pa $(dir $(shell elixir -e 'IO.puts(:code.lib_dir(:elixir))'))/*/ebin -eval " \
+ ok = application:start($(APP)), \
+ {ok, Mods = []} \
+ = application:get_key($(APP), modules), \
+ halt()"
+
+core-elixir-disable-autopatch-fail: 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-lib $v
+
+ $i "Add Jason to the list of dependencies"
+ $t perl -ni.bak -e 'print;if ($$.==1) {print "DEPS = jason\ndep_jason = git https://github.com/michalmuskala/jason.git master\n"}' $(APP)/Makefile
+
+ $i "Disable Elixir in the Makefile"
+ $t perl -ni.bak -e 'print;if ($$.==1) {print "ELIXIR = disable\n"}' $(APP)/Makefile
+
+ $i "Building the application should fail"
+ $t ! $(MAKE) -C $(APP) $v
+
+core-elixir-disable-autopatch-rebar3: 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-lib $v
+
+ $i "Add Jose to the list of dependencies"
+ $t perl -ni.bak -e 'print;if ($$.==1) {print "DEPS = jose\ndep_jose = git https://github.com/potatosalad/erlang-jose main\n"}' $(APP)/Makefile
+
+ $i "Disable Elixir in the Makefile"
+ $t perl -ni.bak -e 'print;if ($$.==1) {print "ELIXIR = disable\n"}' $(APP)/Makefile
+
+ $i "Building the application should work as Jose is Rebar3-compatible"
+ $t $(MAKE) -C $(APP) $v
+
+core-elixir-from-dep: 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-lib $v
+
+ $i "Add Elixir, Lager, Jason, Phoenix to the list of dependencies"
+ $t perl -ni.bak -e 'print;if ($$.==1) {print "DEPS = elixir lager jason phoenix\ndep_elixir_commit = v1.17.3\ndep_lager = git https://github.com/erlang-lager/lager master\ndep_jason = git https://github.com/michalmuskala/jason.git master\ndep_phoenix = hex 1.7.2\n"}' $(APP)/Makefile
+
+ $i "Add the lager_transform parse_transform to ERLC_OPTS"
+ $t echo "ERLC_OPTS += +'{parse_transform, lager_transform}'" >> $(APP)/Makefile
+
+ifdef LEGACY
+ $i "Add Elixir, Lager, Jason and Phoenix to the applications key in the .app.src file"
+ $t perl -ni.bak -e 'print;if ($$.==7) {print "\t\telixir,\n\t\tlager,\n\t\tjason,\n\t\tphoenix,\n"}' $(APP)/src/$(APP).app.src
+endif
+
+ $i "Build the application"
+ $t $(MAKE) -C $(APP) $v
+
+ $i "Check that all dependencies were fetched and built"
+ $t test -f $(APP)/deps/elixir/ebin/dep_built
+ $t test -f $(APP)/deps/lager/ebin/dep_built
+ $t test -f $(APP)/deps/jason/ebin/dep_built
+ $t test -f $(APP)/deps/phoenix/ebin/dep_built
+
+ $i "Check that the application was compiled correctly"
+ $t cd $(APP); $(ERL) -pa ebin/ -pa deps/*/ebin -pa deps/elixir/lib/*/ebin -eval " \
+ {ok, Apps} = application:ensure_all_started('$(APP)'), \
+ true = lists:member(elixir, Apps), \
+ true = lists:member(lager, Apps), \
+ true = lists:member(jason, Apps), \
+ true = lists:member(phoenix, Apps), \
+ halt()"
+
+ $i "Check that the Jason application depends on Elixir builtins"
+ $t cd $(APP); $(ERL) -pa ebin/ -pa deps/*/ebin -pa deps/elixir/lib/*/ebin -eval " \
+ {ok, Apps} = application:ensure_all_started(jason), \
+ true = lists:member(elixir, Apps), \
+ true = lists:member(eex, Apps), \
+ true = lists:member(logger, Apps), \
+ true = lists:member(mix, Apps), \
+ halt()"
+
+core-elixir-from-system: 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-lib $v
+
+ $i "Add Lager, Jason, Phoenix to the list of dependencies"
+ $t perl -ni.bak -e 'print;if ($$.==1) {print "DEPS = lager jason phoenix\ndep_lager = git https://github.com/erlang-lager/lager master\ndep_jason = git https://github.com/michalmuskala/jason.git master\ndep_phoenix = hex 1.7.2\nELIXIR = system\n"}' $(APP)/Makefile
+
+ $i "Add the lager_transform parse_transform to ERLC_OPTS"
+ $t echo "ERLC_OPTS += +'{parse_transform, lager_transform}'" >> $(APP)/Makefile
+
+ifdef LEGACY
+ $i "Add Lager, Jason and Phoenix to the applications key in the .app.src file"
+ $t perl -ni.bak -e 'print;if ($$.==7) {print "\t\telixir,\n\t\tlager,\n\t\tjason,\n\t\tphoenix,\n"}' $(APP)/src/$(APP).app.src
+endif
+
+ $i "Build the application"
+ $t $(MAKE) -C $(APP) $v
+
+ $i "Check that all dependencies were fetched and built"
+ $t ! test -e $(APP)/deps/elixir
+ $t test -f $(APP)/deps/lager/ebin/dep_built
+ $t test -f $(APP)/deps/jason/ebin/dep_built
+ $t test -f $(APP)/deps/phoenix/ebin/dep_built
+
+ $i "Check that the application was compiled correctly"
+ $t cd $(APP); $(ERL) -pa ebin/ -pa deps/*/ebin -pa $(dir $(shell elixir -e 'IO.puts(:code.lib_dir(:elixir))'))/*/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 that the Jason application depends on Elixir builtins"
+ $t cd $(APP); $(ERL) -pa ebin/ -pa deps/*/ebin -pa $(dir $(shell elixir -e 'IO.puts(:code.lib_dir(:elixir))'))/*/ebin -eval " \
+ {ok, Apps} = application:ensure_all_started(jason), \
+ true = lists:member(elixir, Apps), \
+ true = lists:member(eex, Apps), \
+ true = lists:member(logger, Apps), \
+ true = lists:member(mix, Apps), \
+ halt()"
+
+core-elixir-nif: 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-lib $v
+
+ $i "Add Libsalty2 to the list of dependencies"
+ $t perl -ni.bak -e 'print;if ($$.==1) {print "DEPS = libsalty2\ndep_libsalty2 = git https://github.com/Ianleeclark/libsalty2.git b11e544\nELIXIR = system\n"}' $(APP)/Makefile
+
+ifdef LEGACY
+ $i "Add Libsalty2 to the applications key in the .app.src file"
+ $t perl -ni.bak -e 'print;if ($$.==7) {print "\t\tlibsalty2,\n"}' $(APP)/src/$(APP).app.src
+endif
+
+ $i "Build the application"
+ $t $(MAKE) -C $(APP) $v
+
+ $i "Check that the application was compiled correctly"
+ $t $(ERL) -pa $(APP)/ebin/ -pa $(APP)/deps/*/ebin -pa $(dir $(shell elixir -e 'IO.puts(:code.lib_dir(:elixir))'))/*/ebin -eval " \
+ {ok, Apps} = application:ensure_all_started('$(APP)'), \
+ true = lists:member(libsalty2, Apps), \
+ halt()"
+
+core-elixir-rel: init
+
+ $i "Bootstrap a new release named $(APP)"
+ $t mkdir $(APP)/
+ $t cp ../erlang.mk $(APP)/
+ $t $(MAKE) -C $(APP) -f erlang.mk bootstrap-lib bootstrap-rel $v
+
+ $i "Add Lager, Jason, Phoenix to the list of dependencies"
+ $t perl -ni.bak -e 'print;if ($$.==1) {print "DEPS = lager jason phoenix\ndep_lager = git https://github.com/erlang-lager/lager master\ndep_jason = git https://github.com/michalmuskala/jason.git master\ndep_phoenix = hex 1.7.2\nELIXIR = system\n"}' $(APP)/Makefile
+
+ $i "Add the lager_transform parse_transform to ERLC_OPTS"
+ $t echo "ERLC_OPTS += +'{parse_transform, lager_transform}'" >> $(APP)/Makefile
+
+ifdef LEGACY
+ $i "Add Lager, Jason and Phoenix to the applications key in the .app.src file"
+ $t perl -ni.bak -e 'print;if ($$.==7) {print "\t\tlager,\n\t\tjason,\n\t\tphoenix,\n"}' $(APP)/src/$(APP).app.src
+endif
+
+ $i "Build the release"
+ $t $(MAKE) -C $(APP) $v
+
+ $i "Check that the release was built"
+ $t test -d $(APP)/_rel
+ $t test -d $(APP)/_rel/$(APP)_release
+ $t test -d $(APP)/_rel/$(APP)_release/bin
+ $t test -d $(APP)/_rel/$(APP)_release/lib
+ $t test -d $(APP)/_rel/$(APP)_release/releases
+ $t test -d $(APP)/_rel/$(APP)_release/releases/1
diff --git a/test/plugin_eunit.mk b/test/plugin_eunit.mk
index ba3b192..bd1f0e0 100644
--- a/test/plugin_eunit.mk
+++ b/test/plugin_eunit.mk
@@ -235,6 +235,27 @@ eunit-check: init
$i "Check that EUnit runs on 'make check'"
$t $(MAKE) -C $(APP) check | grep -c "Test passed." | grep -q 1
+eunit-disable: init
+
+ $i "Bootstrap a new OTP application named $(APP)"
+ $t mkdir $(APP)/
+ $t cp ../erlang.mk $(APP)/
+ $t $(MAKE) -C $(APP) -f erlang.mk bootstrap $v
+
+ $i "Set EUNIT = disable in the Makefile"
+ $t perl -ni.bak -e 'print;if ($$.==1) {print "EUNIT = disable\n"}' $(APP)/Makefile
+
+ $i "Generate a module containing EUnit tests"
+ $t printf "%s\n" \
+ "-module($(APP))." \
+ "-ifdef(TEST)." \
+ "-include_lib(\"eunit/include/eunit.hrl\")." \
+ "ok_test() -> ok." \
+ "-endif." > $(APP)/src/$(APP).erl
+
+ $i "Check that EUnit is not run on 'make tests'"
+ $t $(MAKE) -C $(APP) tests | grep -c "Test passed." | grep -q 0
+
eunit-erl-opts: init
$i "Bootstrap a new OTP application named $(APP)"