diff options
-rw-r--r-- | .github/workflows/ci.yaml | 132 | ||||
-rw-r--r-- | CHANGELOG.asciidoc | 8 | ||||
-rw-r--r-- | build.config | 1 | ||||
-rw-r--r-- | core/core.mk | 2 | ||||
-rw-r--r-- | core/deps-tools.mk | 4 | ||||
-rw-r--r-- | core/deps.mk | 146 | ||||
-rw-r--r-- | core/elixir.mk | 202 | ||||
-rw-r--r-- | core/erlc.mk | 45 | ||||
-rw-r--r-- | doc/src/guide/book.asciidoc | 2 | ||||
-rw-r--r-- | doc/src/guide/elixir.asciidoc | 86 | ||||
-rw-r--r-- | index/elixir.mk | 7 | ||||
-rw-r--r-- | plugins/escript.mk | 36 | ||||
-rw-r--r-- | plugins/eunit.mk | 7 | ||||
-rw-r--r-- | plugins/hex.mk | 24 | ||||
-rw-r--r-- | test/core_app.mk | 16 | ||||
-rw-r--r-- | test/core_compat.mk | 4 | ||||
-rw-r--r-- | test/core_deps.mk | 36 | ||||
-rw-r--r-- | test/core_elixir.mk | 340 | ||||
-rw-r--r-- | test/core_plugins.mk | 10 | ||||
-rw-r--r-- | test/core_query.mk | 2 | ||||
-rw-r--r-- | test/plugin_eunit.mk | 23 | ||||
-rw-r--r-- | test/plugin_hex.mk | 2 |
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)" |