aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yaml132
-rw-r--r--CHANGELOG.asciidoc8
-rw-r--r--build.config1
-rw-r--r--core/core.mk2
-rw-r--r--core/deps-tools.mk4
-rw-r--r--core/deps.mk146
-rw-r--r--core/elixir.mk202
-rw-r--r--core/erlc.mk45
-rw-r--r--doc/src/guide/book.asciidoc2
-rw-r--r--doc/src/guide/elixir.asciidoc86
-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_app.mk16
-rw-r--r--test/core_compat.mk4
-rw-r--r--test/core_deps.mk36
-rw-r--r--test/core_elixir.mk340
-rw-r--r--test/core_plugins.mk10
-rw-r--r--test/core_query.mk2
-rw-r--r--test/plugin_eunit.mk23
-rw-r--r--test/plugin_hex.mk2
22 files changed, 998 insertions, 137 deletions
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 36325fe..e51ef0b 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,14 +98,23 @@ 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 }}
+ run: |
+ erl -sname init_cookie -run erlang halt
+ make check c=${{ matrix.suite }} -j4 -k ${{ matrix.extra }}
- name: Run tests (Windows)
if: matrix.os == 'windows-latest'
shell: msys2 {0}
- run: PATH=$INSTALL_DIR_FOR_OTP/bin:$PATH make check c=${{ matrix.suite }} -j4 -k ${{ matrix.extra }}
+ run: |
+ export PATH=$INSTALL_DIR_FOR_OTP/bin:$PATH
+ erl -sname init_cookie -run erlang halt
+ make check c=${{ matrix.suite }} -j4 -k ${{ matrix.extra }}
- name: Upload artifacts
if: failure()
@@ -111,6 +124,36 @@ jobs:
path: |
test/test_*/
+ check-asdf-elixir:
+ runs-on: ubuntu-latest
+ steps:
+
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Install Erlang/OTP
+ uses: erlef/setup-beam@v1
+ with:
+ otp-version: 27
+ version-type: loose
+
+ - name: Install asdf along with Elixir
+ run: |
+ wget https://github.com/asdf-vm/asdf/releases/download/v0.18.0/asdf-v0.18.0-linux-amd64.tar.gz
+ tar xf asdf-v*-linux-amd64.tar.gz
+ ./asdf plugin add elixir
+ ./asdf install elixir 1.18.4-otp-27
+ ./asdf set elixir 1.18.4-otp-27
+
+ - name: Install libsodium
+ run: sudo apt-get -y install libsodium-dev
+
+ - name: Run tests
+ run: |
+ export PATH=$PWD:/home/runner/.asdf/shims:$PATH
+ erl -sname init_cookie -run erlang halt
+ make check c=core-elixir -j4 -k
+
check-hex:
strategy:
fail-fast: false
@@ -143,7 +186,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)
@@ -170,12 +213,17 @@ jobs:
- name: Run tests (Linux)
if: matrix.os == 'ubuntu-latest'
- run: make check c=hex -j4 -k ${{ matrix.extra }}
+ run: |
+ erl -sname init_cookie -run erlang halt
+ make check c=hex -j4 -k ${{ matrix.extra }}
- name: Run tests (Windows)
if: matrix.os == 'windows-latest'
shell: msys2 {0}
- run: PATH=$INSTALL_DIR_FOR_OTP/bin:$PATH make check c=hex -j4 -k ${{ matrix.extra }}
+ run: |
+ export PATH=$INSTALL_DIR_FOR_OTP/bin:$PATH
+ erl -sname init_cookie -run erlang halt
+ make check c=hex -j4 -k ${{ matrix.extra }}
- name: Upload artifacts
if: failure()
@@ -324,3 +372,77 @@ jobs:
- name: Check templates
run: make check-templates
+
+ check-in-vm:
+ strategy:
+ fail-fast: false
+ matrix:
+ os:
+ - freebsd
+ suite:
+ - core-app
+ - core-apps
+ - core-autopatch
+ - core-compat
+ # Trick GH runners into picking this slower job early.
+ - ACME=1 c=core-deps
+ - core-elixir
+ - core-makedep
+ - core-misc
+ - core-plugins
+ - core-query
+ - core-upgrade
+ - asciidoc
+ - bootstrap
+ - concuerror
+ - cover
+ - c-src
+ - ct
+ - dialyzer SET=1
+ - dialyzer SET=2
+ - dialyzer SET=3
+ - dialyzer SET=4
+ - edoc
+ - erlydtl
+ - escript
+ - eunit
+ - proper
+ - protobuffs
+ # Trick GH runners into picking this slower job early.
+ - ACME=1 c=relx
+ - shell
+ - sphinx
+ - triq
+ - xref
+ # Don't run tests with CACHE_DEPS or LEGACY. Running them on Linux is
+ # good enough.
+ # extra: ['', 'CACHE_DEPS=1', 'LEGACY=1']
+ runs-on: ubuntu-latest
+ steps:
+
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Run tests (FreeBSD)
+ uses: cross-platform-actions/[email protected]
+ timeout-minutes: 60
+ with:
+ operating_system: ${{ matrix.os }}
+ version: '14.3'
+ run: |
+ sudo pkg update
+ sudo pkg upgrade -y
+ sudo pkg install -y erlang elixir bash ca_root_nss gmake git libsodium perl5 7-zip
+
+ git config --global safe.directory '*'
+ erl -sname init_cookie -run erlang halt
+
+ gmake check c=${{ matrix.suite }} -j4 USE_NODETOOL=1
+
+ - name: Upload artifacts
+ if: failure()
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ matrix.os }} ${{ matrix.suite }}
+ path: |
+ test/test_*/
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index fd127de..c12cbe6 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -64,3 +64,11 @@
future update. Erlang.mk will always assume
that a Rebar project uses Rebar3 from that
point forward.
+
+2025/03/18: Native support for Elixir was added. Erlang.mk
+ will now properly handle most Elixir dependencies,
+ as well as compile Elixir code in Erlang.mk projects,
+ from both src/ and lib/ directories. This allows
+ mixing Erlang and Elixir in the same project.
+ Please refer to the user guide for more information.
+ Native Elixir support is considered experimental.
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 d557957..da7f7c4 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
@@ -140,6 +142,14 @@ export ERL_LIBS
export NO_AUTOPATCH
+# Elixir.
+
+# 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
+
# Verbosity.
dep_verbose_0 = @echo " DEP $1 ($(call query_version,$1))";
@@ -210,9 +220,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 +248,49 @@ 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; \
+ elif [ -f $(DEPS_DIR)/$1/Makefile ]; then \
+ echo noop; \
+ 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 +306,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 +348,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 +432,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 +445,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
@@ -798,7 +830,7 @@ define hex_get_tarball.erl
Config = $(hex_config.erl),
case hex_repo:get_tarball(Config, <<"$1">>, <<"$(strip $2)">>) of
{ok, {200, _, Tarball}} ->
- ok = file:write_file("$3", Tarball),
+ ok = file:write_file("$(call core_native_path,$3)", Tarball),
halt(0);
{ok, {Status, _, Errors}} ->
io:format("Error ~b: ~0p~n", [Status, Errors]),
@@ -835,7 +867,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 +886,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..ece705d
--- /dev/null
+++ b/core/elixir.mk
@@ -0,0 +1,202 @@
+# 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.
+
+ifeq ($(ELIXIR),system)
+# We expect 'elixir' to be on the path.
+ELIXIR_BIN ?= $(shell readlink -f `which elixir`)
+ELIXIR_LIBS ?= $(abspath $(dir $(ELIXIR_BIN))/../lib)
+# Fallback in case 'elixir' is a shim.
+ifeq ($(wildcard $(ELIXIR_LIBS)/elixir/),)
+ELIXIR_LIBS = $(abspath $(shell elixir -e 'IO.puts(:code.lib_dir(:elixir))')/../)
+endif
+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' $(DEPS_DIR)/$(1)/mix.exs > $(DEPS_DIR)/$(1)/mix.exs.new; \
+ mv $(DEPS_DIR)/$(1)/mix.exs.new $(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),
+ $(foreach dep,$(LOCAL_DEPS),_ = application:load($(dep)),)
+ 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/doc/src/guide/book.asciidoc b/doc/src/guide/book.asciidoc
index 7c4ee68..65c09b9 100644
--- a/doc/src/guide/book.asciidoc
+++ b/doc/src/guide/book.asciidoc
@@ -36,6 +36,8 @@ include::cross_compiling.asciidoc[Cross compiling]
include::compat.asciidoc[Compatibility with other build tools]
+include::elixir.asciidoc[Elixir modules and dependencies]
+
[[docs]]
= Documentation
diff --git a/doc/src/guide/elixir.asciidoc b/doc/src/guide/elixir.asciidoc
new file mode 100644
index 0000000..b868825
--- /dev/null
+++ b/doc/src/guide/elixir.asciidoc
@@ -0,0 +1,86 @@
+[[elixir]]
+== Elixir modules and dependencies
+
+Erlang.mk has experimental support for building Elixir
+modules as well as dependencies. In this chapter we will
+cover the details and gotchas of the Elixir support.
+
+=== Selecting Elixir
+
+By default Elixir is disabled. This is to ensure that
+there's no negative impact to normal users of Erlang.mk.
+
+Erlang.mk can use either an Elixir installed in the
+system; or use Elixir as a dependency.
+
+Elixir will be automatically enabled when Elixir is
+used as a dependency. In that case all that is required
+is to depend on Elixir:
+
+[source,make]
+----
+DEPS = elixir
+dep_elixir_commit = v1.17.3
+----
+
+Alternatively, Erlang.mk will enable the system Elixir
+installation when Elixir files are found in the top-level
+project. In that case, Elixir is assumed to be in the path.
+
+In other cases, the system Elixir installation must be
+enabled manually in order to depend on Elixir applications.
+Note that this is only required for Elixir-only applications,
+not for applications that have both Erlang and Elixir code
+(as long as you only care about the Erlang side of things).
+The `ELIXIR` variable must be defined before including
+Erlang.mk:
+
+[source,make]
+ELIXIR = system
+
+Elixir can be explicitly disabled. In that case trying to
+depend on Elixir applications will result in failure
+during autopatch, unless the Elixir application has both
+Erlang and Elixir code.
+
+[source,make]
+ELIXIR = disable
+
+=== Elixir compilation
+
+There are currently no options.
+
+=== Elixir dependencies
+
+Erlang.mk will automatically autopatch Elixir dependencies
+by running Mix on the mix.exs file and producing a Makefile
+using the generated metadata.
+
+The following is an example of depending on Elixir
+applications from an Erlang-only application:
+
+[source,make]
+----
+DEPS = jose
+dep_jose = hex 1.11.10
+
+ELIXIR = system
+
+include erlang.mk
+----
+
+=== Dialyzer
+
+Dialyzer requires Elixir to be available in order to access
+the AST of the Elixir beam files. In most cases it will just
+work. When only enabling Elixir in a sub-application, Elixir
+will not always be available, so in that case we must tell
+Dialyzer where to find Elixir libraries. This can be done
+by adding the following rules to the relevant Makefiles,
+either as a plugin or after including Erlang.mk:
+
+[source,make]
+----
+dialyze: ELIXIR_LIBS = $(dir $(shell readlink -f `which elixir`))/../lib
+dialyze: ERL_LIBS = $(APPS_DIR):$(DEPS_DIR):$(ELIXIR_LIBS)
+----
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_app.mk b/test/core_app.mk
index 7c9c046..3eaf3bb 100644
--- a/test/core_app.mk
+++ b/test/core_app.mk
@@ -235,7 +235,7 @@ core-app-auto-git-id: init
$i "Make it a git repository"
$t cd $(APP) && \
- git init -q && \
+ git init -q -b master && \
git config user.email "[email protected]" && \
git config user.name "test suite" && \
git add . && \
@@ -1315,7 +1315,7 @@ core-app-pt: init
"-compile({parse_transform, my_pt})." > $(APP)/src/my_user.erl
$i "Build the application; confirm the parse_transform is used"
- $t $(MAKE) -C $(APP) | grep "Running my_pt parse_transform."
+ $t $(MAKE) -C $(APP) 2>&1 | grep "Running my_pt parse_transform."
$i "Check that the application was compiled correctly"
$t $(ERL) -pa $(APP)/ebin/ -eval " \
@@ -1355,7 +1355,7 @@ core-app-pt-erlc-opts: init
$t echo "ERLC_OPTS += +'{parse_transform, my_pt}'" >> $(APP)/Makefile
$i "Build the application; confirm the parse_transform is used"
- $t $(MAKE) -C $(APP) | grep "Running my_pt parse_transform."
+ $t $(MAKE) -C $(APP) 2>&1 | grep "Running my_pt parse_transform."
$i "Check that the application was compiled correctly"
$t $(ERL) -pa $(APP)/ebin/ -eval " \
@@ -1495,11 +1495,11 @@ core-app-xrl-include: init
$t $(MAKE) -C $(APP) -f erlang.mk bootstrap-lib $v
$i "Download a .xrl file with numerous includes from Gordon"
- $t curl -s -o $(APP)/src/xfl_lexer.xrl https://raw.githubusercontent.com/hypernumbers/hypernumbers/master/lib/formula_engine-1.0/priv/xfl_lexer.xrl
- $t curl -s -o $(APP)/src/errvals.hrl https://raw.githubusercontent.com/hypernumbers/hypernumbers/master/lib/hypernumbers-1.0/include/errvals.hrl
- $t curl -s -o $(APP)/src/muin_proc_dict.hrl https://raw.githubusercontent.com/hypernumbers/hypernumbers/master/lib/hypernumbers-1.0/include/muin_proc_dict.hrl
- $t curl -s -o $(APP)/src/muin_records.hrl https://raw.githubusercontent.com/hypernumbers/hypernumbers/master/lib/hypernumbers-1.0/include/muin_records.hrl
- $t curl -s -o $(APP)/src/typechecks.hrl https://raw.githubusercontent.com/hypernumbers/hypernumbers/master/lib/hypernumbers-1.0/include/typechecks.hrl
+ $t curl -s -o $(APP)/src/xfl_lexer.xrl https://raw.githubusercontent.com/essen/hypernumbers/master/lib/formula_engine-1.0/priv/xfl_lexer.xrl
+ $t curl -s -o $(APP)/src/errvals.hrl https://raw.githubusercontent.com/essen/hypernumbers/master/lib/hypernumbers-1.0/include/errvals.hrl
+ $t curl -s -o $(APP)/src/muin_proc_dict.hrl https://raw.githubusercontent.com/essen/hypernumbers/master/lib/hypernumbers-1.0/include/muin_proc_dict.hrl
+ $t curl -s -o $(APP)/src/muin_records.hrl https://raw.githubusercontent.com/essen/hypernumbers/master/lib/hypernumbers-1.0/include/muin_records.hrl
+ $t curl -s -o $(APP)/src/typechecks.hrl https://raw.githubusercontent.com/essen/hypernumbers/master/lib/hypernumbers-1.0/include/typechecks.hrl
$i "Generate unrelated .erl files"
$t echo "-module(boy)." > $(APP)/src/boy.erl
diff --git a/test/core_compat.mk b/test/core_compat.mk
index c8c570d..0e5a89e 100644
--- a/test/core_compat.mk
+++ b/test/core_compat.mk
@@ -163,7 +163,7 @@ core-compat-rebar-deps-git-tag: init
$t $(MAKE) -C $(APP) -f erlang.mk bootstrap-lib $v
$i "Add Cowboy as a dependency"
- $t perl -ni.bak -e 'print;if ($$.==1) {print "DEPS = cowboy\ndep_cowboy = git https://github.com/ninenines/cowboy 2.9.0\n"}' $(APP)/Makefile
+ $t perl -ni.bak -e 'print;if ($$.==1) {print "DEPS = cowboy\ndep_cowboy = git https://github.com/ninenines/cowboy 2.13.0\n"}' $(APP)/Makefile
$i "Run 'make rebar.config'"
$t $(MAKE) -C $(APP) rebar.config $v
@@ -174,7 +174,7 @@ core-compat-rebar-deps-git-tag: init
$i "Check that Cowboy is listed in rebar.config with a tag"
$t $(ERL) -eval " \
{ok, C} = file:consult(\"$(APP)/rebar.config\"), \
- {_, [{cowboy, _, {git, _, {tag, \"2.9.0\"}}}]} = lists:keyfind(deps, 1, C), \
+ {_, [{cowboy, _, {git, _, {tag, \"2.13.0\"}}}]} = lists:keyfind(deps, 1, C), \
halt()"
$i "Distclean the application"
diff --git a/test/core_deps.mk b/test/core_deps.mk
index dd052e5..02eff53 100644
--- a/test/core_deps.mk
+++ b/test/core_deps.mk
@@ -161,8 +161,8 @@ core-deps-cache-git-reuse: init
$t cp ../erlang.mk $(APP)_2/
$t $(MAKE) -C $(APP)_2 -f erlang.mk bootstrap-lib $v
- $i "Add Cowlib 2.0.0 to the list of dependencies"
- $t perl -ni.bak -e 'print;if ($$.==1) {print "DEPS = cowlib\ndep_cowlib = git \$$(pkg_cowlib_repo) 2.0.0\n"}' $(APP)_2/Makefile
+ $i "Add Cowlib 2.15.0 to the list of dependencies"
+ $t perl -ni.bak -e 'print;if ($$.==1) {print "DEPS = cowlib\ndep_cowlib = git \$$(pkg_cowlib_repo) 2.15.0\n"}' $(APP)_2/Makefile
$i "Add CACHE_DEPS = 1 to the Makefile"
$t perl -ni.bak -e 'print;if ($$.==1) {print "CACHE_DEPS = 1\n"}' $(APP)_2/Makefile
@@ -182,8 +182,8 @@ core-deps-cache-git-reuse: init
$i "Check that $(APP)_1 cloned Cowlib 1.0.0"
$t test "$$(cat $(APP)_1/deps/cowlib/.git/HEAD)" = "d544a494af4dbc810fc9c15eaf5cc050cced1501"
- $i "Check that $(APP)_2 cloned Cowlib 2.0.0"
- $t test "$$(cat $(APP)_2/deps/cowlib/.git/HEAD)" = "bd37be4d3b065600c3b76b492535e76e5d413fc1"
+ $i "Check that $(APP)_2 cloned Cowlib 2.15.0"
+ $t test "$$(cat $(APP)_2/deps/cowlib/.git/HEAD)" = "f8d0ad7f19b5dddd33cbfb089ebd2e2be2a81a5d"
core-deps-cache-hex: init
@@ -624,7 +624,7 @@ core-deps-fetch-git-subfolder: init
# Create an empty file so src/ gets committed.
$t touch $(APP)/git_repo/my_dep/src/README
$t cd $(APP)/git_repo && \
- git init -q && \
+ git init -q -b master && \
git config user.email "[email protected]" && \
git config user.name "test suite" && \
git add . && \
@@ -665,7 +665,7 @@ core-deps-fetch-git-submodule: init
# Create an empty file so src/ gets committed.
$t touch $(APP)/my_dep/src/README
$t cd $(APP)/my_dep && \
- git init -q && \
+ git init -q -b master && \
git config user.email "[email protected]" && \
git config user.name "test suite" && \
git add . && \
@@ -674,7 +674,7 @@ core-deps-fetch-git-submodule: init
$i "Add the submodule to my_dep"
$t mkdir $(APP)/deps
$t cd $(APP) && \
- git init -q && \
+ git init -q -b master && \
git -c protocol.file.allow=always submodule -q add file://$(abspath $(APP)/my_dep) deps/my_dep && \
git config user.email "[email protected]" && \
git config user.name "test suite" && \
@@ -870,7 +870,7 @@ define add_dep_and_subdep
$i "Create a Git repository for $(APP)_$(1)subdep"
$t (cd $(APP)_$(1)subdep && \
- git init -q && \
+ git init -q -b master && \
git config user.name "Testsuite" && \
git config user.email "[email protected]" && \
git add . && \
@@ -889,7 +889,7 @@ define add_dep_and_subdep
$i "Create a Git repository for $(APP)_$(1)dep"
$t (cd $(APP)_$(1)dep && \
- git init -q && \
+ git init -q -b master && \
git config user.name "Testsuite" && \
git config user.email "[email protected]" && \
git add . && \
@@ -928,7 +928,7 @@ dep_shelldep = git file://$(abspath $(APP)_shelldep) master\
$i "Create a Git repository for $(APP)"
$t (cd $(APP) && \
- git init -q && \
+ git init -q -b master && \
git config user.name "Testsuite" && \
git config user.email "[email protected]" && \
git add . && \
@@ -1033,7 +1033,7 @@ dep_dep = git file://$(abspath $(APP)_dep) master\
$i "Create a Git repository for $(APP)"
$t (cd $(APP) && \
- git init -q && \
+ git init -q -b master && \
git config user.name "Testsuite" && \
git config user.email "[email protected]" && \
git add . && \
@@ -1079,7 +1079,7 @@ dep_dep = git file://$(abspath $(APP)_dep) master\
$i "Create a Git repository for $(APP)"
$t (cd $(APP) && \
- git init -q && \
+ git init -q -b master && \
git config user.name "Testsuite" && \
git config user.email "[email protected]" && \
git add . && \
@@ -1264,8 +1264,8 @@ core-deps-order-first: init
$t cp ../erlang.mk $(APP)/my_dep/
$t $(MAKE) -C $(APP)/my_dep/ -f erlang.mk bootstrap-lib $v
- $i "Add Cowlib 2.0.0 to the list of dependencies for my_dep"
- $t perl -ni.bak -e 'print;if ($$.==1) {print "DEPS = cowlib\ndep_cowlib = git https://github.com/ninenines/cowlib 2.0.0\n"}' $(APP)/my_dep/Makefile
+ $i "Add Cowlib 2.15.0 to the list of dependencies for my_dep"
+ $t perl -ni.bak -e 'print;if ($$.==1) {print "DEPS = cowlib\ndep_cowlib = git https://github.com/ninenines/cowlib 2.15.0\n"}' $(APP)/my_dep/Makefile
ifdef LEGACY
$i "Add Cowboy and my_dep to the applications key in the .app.src file"
@@ -1286,7 +1286,7 @@ endif
[ok = application:load(App) || App <- [$(APP), cowboy, cowlib, my_dep, ranch]], \
{ok, Deps} = application:get_key($(APP), applications), \
true = lists:member(cowboy, Deps), \
- {ok, \"2.0.0\"} = application:get_key(cowlib, vsn), \
+ {ok, \"2.15.0\"} = application:get_key(cowlib, vsn), \
halt()"
# A higher-level dependency always wins.
@@ -1297,8 +1297,8 @@ core-deps-order-top: init
$t cp ../erlang.mk $(APP)/
$t $(MAKE) -C $(APP) -f erlang.mk bootstrap-lib $v
- $i "Add Cowboy package and Cowlib 2.0.0 to the list of dependencies"
- $t perl -ni.bak -e 'print;if ($$.==1) {print "DEPS = cowboy cowlib\ndep_cowlib = git https://github.com/ninenines/cowlib 2.0.0\n"}' $(APP)/Makefile
+ $i "Add Cowboy package and Cowlib 2.15.0 to the list of dependencies"
+ $t perl -ni.bak -e 'print;if ($$.==1) {print "DEPS = cowboy cowlib\ndep_cowlib = git https://github.com/ninenines/cowlib 2.15.0\n"}' $(APP)/Makefile
ifdef LEGACY
$i "Add Cowboy to the applications key in the .app.src file"
@@ -1318,7 +1318,7 @@ endif
[ok = application:load(App) || App <- [$(APP), cowboy, cowlib, ranch]], \
{ok, Deps} = application:get_key($(APP), applications), \
true = lists:member(cowboy, Deps), \
- {ok, \"2.0.0\"} = application:get_key(cowlib, vsn), \
+ {ok, \"2.15.0\"} = application:get_key(cowlib, vsn), \
halt()"
ifndef LEGACY
diff --git a/test/core_elixir.mk b/test/core_elixir.mk
new file mode 100644
index 0000000..8c25964
--- /dev/null
+++ b/test/core_elixir.mk
@@ -0,0 +1,340 @@
+# 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-erlang-mk: 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 Erlang.mk-compatible"
+ $t $(MAKE) -C $(APP) $v
+
+core-elixir-disable-autopatch-make: 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 Reloader to the list of dependencies"
+ $t perl -ni.bak -e 'print;if ($$.==1) {print "DEPS = reloader\ndep_reloader = git https://github.com/2600hz/erlang-reloader de1e6c74204b61ccf3b3652f05c6a7dec9e8257d\n"}' $(APP)/Makefile
+
+ $i "Disable Elixir in the Makefile"
+ $t perl -ni.bak -e 'print;if ($$.==1) {print "ELIXIR = disable\n"}' $(APP)/Makefile
+
+ $i "Fetch dependencies to patch 'reloader'"
+ $t $(MAKE) -C $(APP) fetch-deps $v
+
+# Patch `reloader` Makefile to be compatible with BSD sed. Its Makefile called
+# sed(1) in a way that was only compatible with GNU sed. As a consequence, the
+# build failed with BSD sed.
+ $i "Patch sed(1) use in Makefile"
+ $t test -f $(APP)/deps/reloader/Makefile
+ $t perl -pi.bak -e 's/\@sed/\@sed -E/;' -e 'if (/sed/) { s/{/\\{/g; s/}/\\}/g; s/\\s\*//; }' $(APP)/deps/reloader/Makefile
+
+ $i "Building the application should work as Reloader contains a proper Makefile"
+ $t $(MAKE) -C $(APP) $v
+
+ $i "Confirm Reloader was built"
+ $t test -f $(APP)/deps/reloader/ebin/dep_built
+ $t test -f $(APP)/deps/reloader/ebin/reloader.app
+ $t test -f $(APP)/deps/reloader/ebin/reloader.beam
+
+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 OpenTelemetry_API to the list of dependencies"
+ $t perl -ni.bak -e 'print;if ($$.==1) {print "DEPS = opentelemetry_api\ndep_opentelemetry_api = hex 1.3.0\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 OpenTelemetry_API is Rebar3-compatible"
+ $t $(MAKE) -C $(APP) $v
+
+core-elixir-disable-by-default-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 OpenTelemetry_API to the list of dependencies"
+ $t perl -ni.bak -e 'print;if ($$.==1) {print "DEPS = opentelemetry_api\ndep_opentelemetry_api = hex 1.3.0\n"}' $(APP)/Makefile
+
+ $i "Building the application should work as OpenTelemetry_API 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
+
+# Specify CFLAGS when building the Elixir NIF. On FreeBSD, libsodium's
+# `sodium.h` header is installed in `/usr/local/local`. The Makefile already
+# adds `/usr/local/include/sodium` to the compiler's `-I` search path, but it
+# doesn't cover the FreeBSD case.
+ $i "Build the application"
+ $t $(MAKE) -C $(APP) $v CFLAGS=-I/usr/local/include
+
+ $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/core_plugins.mk b/test/core_plugins.mk
index 8544945..76e2899 100644
--- a/test/core_plugins.mk
+++ b/test/core_plugins.mk
@@ -23,7 +23,7 @@ core-plugins-all: init
# We check that overriding THIS doesn't cause an error.
$t echo "THIS :=" >> $(APP)/plugin_dep/plugins.mk
$t cd $(APP)/plugin_dep && \
- git init -q && \
+ git init -q -b master && \
git config user.email "[email protected]" && \
git config user.name "test suite" && \
git add . && \
@@ -168,7 +168,7 @@ core-plugins-one: init
# We check that overriding THIS doesn't cause an error.
$t echo "THIS :=" >> $(APP)/plugin_dep/plugins.mk
$t cd $(APP)/plugin_dep && \
- git init -q && \
+ git init -q -b master && \
git config user.email "[email protected]" && \
git config user.name "test suite" && \
git add . && \
@@ -197,7 +197,7 @@ core-plugins-templates: init
"-module(template_name)." \
"endef" > $(APP)/plugin_dep/plugins.mk
$t cd $(APP)/plugin_dep && \
- git init -q && \
+ git init -q -b master && \
git config user.email "[email protected]" && \
git config user.name "test suite" && \
git add . && \
@@ -238,7 +238,7 @@ core-plugins-templates-apps-only: init
"-module(test_mk)." \
"endef" > $(APP)/plugin_dep/plugins.mk
$t cd $(APP)/plugin_dep && \
- git init -q && \
+ git init -q -b master && \
git config user.email "[email protected]" && \
git config user.name "test suite" && \
git add . && \
@@ -276,7 +276,7 @@ core-plugins-templates-file: init
$t echo "THIS := \$$(dir \$$(realpath \$$(lastword \$$(MAKEFILE_LIST))))" > $(APP)/plugin_dep/plugins.mk
$t printf "%s\n" "tpl_test_mk = \$$(file < \$$(THIS)/templates/test_mk.erl)" >> $(APP)/plugin_dep/plugins.mk
$t cd $(APP)/plugin_dep && \
- git init -q && \
+ git init -q -b master && \
git config user.email "[email protected]" && \
git config user.name "test suite" && \
git add . && \
diff --git a/test/core_query.mk b/test/core_query.mk
index 394652a..3dc7a24 100644
--- a/test/core_query.mk
+++ b/test/core_query.mk
@@ -154,7 +154,7 @@ endif
"farwest: cowlib git https://github.com/ninenines/cowlib master" \
"farwest: cowboy git https://github.com/ninenines/cowboy master" \
"farwest: gun git https://github.com/ninenines/gun master" \
- "gun: cowlib git https://github.com/ninenines/cowlib master" \
+ "gun: cowlib git https://github.com/ninenines/cowlib 2.15.0" \
> $(APP)/expected-deps.txt
$t cmp $(APP)/expected-deps.txt $(APP)/.erlang.mk/query-deps.log
diff --git a/test/plugin_eunit.mk b/test/plugin_eunit.mk
index ba3b192..532f699 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)"
@@ -254,7 +275,7 @@ eunit-erl-opts: init
"-endif." > $(APP)/src/$(APP).erl
$i "Check that EUnit uses EUNIT_ERL_OPTS"
- $t $(MAKE) -C $(APP) eunit | grep -c "hello" | grep -q 1
+ $t $(MAKE) -C $(APP) eunit | grep -c "^hello" | grep -q 1
eunit-fun: init
diff --git a/test/plugin_hex.mk b/test/plugin_hex.mk
index 27060b6..26d3f94 100644
--- a/test/plugin_hex.mk
+++ b/test/plugin_hex.mk
@@ -10,7 +10,7 @@ hex_TARGETS = $(call list_targets,hex)
hex: $(hex_TARGETS)
-ifeq ($(shell netcat -z localhost 4000 && echo ok),ok)
+ifeq ($(shell nc -z localhost 4000 && echo ok),ok)
hex-user-create: init
$i "Bootstrap a new OTP application named $(APP)"