mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 12:20:24 +00:00

This commit is not only an early preview of HTTP/2, it is an early preview of the new Cowboy architecture that will be presented tomorrow in my talk. If you have found it before the talk, great! It's not complete so you better go watch the talk anyway.
2181 lines
57 KiB
Makefile
2181 lines
57 KiB
Makefile
# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
|
|
#
|
|
# Permission to use, copy, modify, and/or distribute this software for any
|
|
# purpose with or without fee is hereby granted, provided that the above
|
|
# copyright notice and this permission notice appear in all copies.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
.PHONY: all deps app rel docs install-docs tests check clean distclean help erlang-mk
|
|
|
|
ERLANG_MK_VERSION = 1.2.0-564-g178e214-dirty
|
|
|
|
# Core configuration.
|
|
|
|
PROJECT ?= $(notdir $(CURDIR))
|
|
PROJECT := $(strip $(PROJECT))
|
|
|
|
# Verbosity.
|
|
|
|
V ?= 0
|
|
|
|
gen_verbose_0 = @echo " GEN " $@;
|
|
gen_verbose = $(gen_verbose_$(V))
|
|
|
|
# Temporary files directory.
|
|
|
|
ERLANG_MK_TMP ?= $(CURDIR)/.erlang.mk
|
|
export ERLANG_MK_TMP
|
|
|
|
# "erl" command.
|
|
|
|
ERL = erl +A0 -noinput -boot start_clean
|
|
|
|
# Platform detection.
|
|
# @todo Add Windows/Cygwin detection eventually.
|
|
|
|
ifeq ($(PLATFORM),)
|
|
UNAME_S := $(shell uname -s)
|
|
|
|
ifeq ($(UNAME_S),Linux)
|
|
PLATFORM = linux
|
|
else ifeq ($(UNAME_S),Darwin)
|
|
PLATFORM = darwin
|
|
else ifeq ($(UNAME_S),SunOS)
|
|
PLATFORM = solaris
|
|
else ifeq ($(UNAME_S),GNU)
|
|
PLATFORM = gnu
|
|
else ifeq ($(UNAME_S),FreeBSD)
|
|
PLATFORM = freebsd
|
|
else ifeq ($(UNAME_S),NetBSD)
|
|
PLATFORM = netbsd
|
|
else ifeq ($(UNAME_S),OpenBSD)
|
|
PLATFORM = openbsd
|
|
else
|
|
$(error Unable to detect platform. Please open a ticket with the output of uname -a.)
|
|
endif
|
|
|
|
export PLATFORM
|
|
endif
|
|
|
|
# Core targets.
|
|
|
|
ifneq ($(words $(MAKECMDGOALS)),1)
|
|
.NOTPARALLEL:
|
|
endif
|
|
|
|
all:: deps
|
|
@$(MAKE) --no-print-directory app
|
|
@$(MAKE) --no-print-directory rel
|
|
|
|
# Noop to avoid a Make warning when there's nothing to do.
|
|
rel::
|
|
@echo -n
|
|
|
|
check:: clean app tests
|
|
|
|
clean:: clean-crashdump
|
|
|
|
clean-crashdump:
|
|
ifneq ($(wildcard erl_crash.dump),)
|
|
$(gen_verbose) rm -f erl_crash.dump
|
|
endif
|
|
|
|
distclean:: clean
|
|
|
|
help::
|
|
@printf "%s\n" \
|
|
"erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
|
|
"Copyright (c) 2013-2014 Loïc Hoguin <essen@ninenines.eu>" \
|
|
"" \
|
|
"Usage: [V=1] $(MAKE) [-jNUM] [target]" \
|
|
"" \
|
|
"Core targets:" \
|
|
" all Run deps, app and rel targets in that order" \
|
|
" deps Fetch dependencies (if needed) and compile them" \
|
|
" app Compile the project" \
|
|
" rel Build a release for this project, if applicable" \
|
|
" docs Build the documentation for this project" \
|
|
" install-docs Install the man pages for this project" \
|
|
" tests Run the tests for this project" \
|
|
" check Compile and run all tests and analysis for this project" \
|
|
" clean Delete temporary and output files from most targets" \
|
|
" distclean Delete all temporary and output files" \
|
|
" help Display this help and exit" \
|
|
"" \
|
|
"The target clean only removes files that are commonly removed." \
|
|
"Dependencies and releases are left untouched." \
|
|
"" \
|
|
"Setting V=1 when calling $(MAKE) enables verbose mode." \
|
|
"Parallel execution is supported through the -j $(MAKE) flag."
|
|
|
|
# Core functions.
|
|
|
|
define newline
|
|
|
|
|
|
endef
|
|
|
|
# Adding erlang.mk to make Erlang scripts who call init:get_plain_arguments() happy.
|
|
define erlang
|
|
$(ERL) -pa $(ERLANG_MK_TMP)/rebar/ebin -eval "$(subst $(newline),,$(subst ",\",$(1)))" -- erlang.mk
|
|
endef
|
|
|
|
ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
|
|
define core_http_get
|
|
wget --no-check-certificate -O $(1) $(2)|| rm $(1)
|
|
endef
|
|
else
|
|
define core_http_get.erl
|
|
ssl:start(),
|
|
inets:start(),
|
|
case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
|
|
{ok, {{_, 200, _}, _, Body}} ->
|
|
case file:write_file("$(1)", Body) of
|
|
ok -> ok;
|
|
{error, R1} -> halt(R1)
|
|
end;
|
|
{error, R2} ->
|
|
halt(R2)
|
|
end,
|
|
halt(0).
|
|
endef
|
|
|
|
define core_http_get
|
|
$(call erlang,$(call core_http_get.erl,$(1),$(2)))
|
|
endef
|
|
endif
|
|
|
|
# Automated update.
|
|
|
|
ERLANG_MK_BUILD_CONFIG ?= build.config
|
|
ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
|
|
|
|
erlang-mk:
|
|
git clone https://github.com/ninenines/erlang.mk $(ERLANG_MK_BUILD_DIR)
|
|
if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR); fi
|
|
cd $(ERLANG_MK_BUILD_DIR) && $(MAKE)
|
|
cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
|
|
rm -rf $(ERLANG_MK_BUILD_DIR)
|
|
|
|
# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
|
|
# This file is part of erlang.mk and subject to the terms of the ISC License.
|
|
|
|
.PHONY: distclean-deps distclean-pkg pkg-list pkg-search
|
|
|
|
# Configuration.
|
|
|
|
IGNORE_DEPS ?=
|
|
|
|
DEPS_DIR ?= $(CURDIR)/deps
|
|
export DEPS_DIR
|
|
|
|
REBAR_DEPS_DIR = $(DEPS_DIR)
|
|
export REBAR_DEPS_DIR
|
|
|
|
ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(filter-out $(IGNORE_DEPS),$(DEPS)))
|
|
|
|
ifeq ($(filter $(DEPS_DIR),$(subst :, ,$(ERL_LIBS))),)
|
|
ifeq ($(ERL_LIBS),)
|
|
ERL_LIBS = $(DEPS_DIR)
|
|
else
|
|
ERL_LIBS := $(ERL_LIBS):$(DEPS_DIR)
|
|
endif
|
|
endif
|
|
export ERL_LIBS
|
|
|
|
PKG_FILE2 ?= $(CURDIR)/.erlang.mk.packages.v2
|
|
export PKG_FILE2
|
|
|
|
PKG_FILE_URL ?= https://raw.githubusercontent.com/ninenines/erlang.mk/master/packages.v2.tsv
|
|
|
|
# Verbosity.
|
|
|
|
dep_verbose_0 = @echo " DEP " $(1);
|
|
dep_verbose = $(dep_verbose_$(V))
|
|
|
|
# Core targets.
|
|
|
|
ifneq ($(SKIP_DEPS),)
|
|
deps::
|
|
else
|
|
deps:: $(ALL_DEPS_DIRS)
|
|
@for dep in $(ALL_DEPS_DIRS) ; do \
|
|
if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ] ; then \
|
|
$(MAKE) -C $$dep IS_DEP=1 || exit $$? ; \
|
|
else \
|
|
echo "ERROR: No Makefile to build dependency $$dep." ; \
|
|
exit 1 ; \
|
|
fi ; \
|
|
done
|
|
endif
|
|
|
|
distclean:: distclean-deps distclean-pkg
|
|
|
|
# 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
|
|
if [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
|
|
if [ 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 [ 0 != `find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk | xargs grep -ci rebar` ]; then \
|
|
$(call dep_autopatch2,$(1)); \
|
|
else \
|
|
if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
|
|
$(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
|
|
$(call dep_autopatch_erlang_mk,$(1)); \
|
|
else \
|
|
$(call erlang,$(call dep_autopatch_app.erl,$(1))); \
|
|
fi \
|
|
fi \
|
|
else \
|
|
if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
|
|
$(call dep_autopatch_noop,$(1)); \
|
|
else \
|
|
$(call dep_autopatch2,$(1)); \
|
|
fi \
|
|
fi
|
|
endef
|
|
|
|
define dep_autopatch2
|
|
$(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
|
|
if [ -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
|
|
$(call dep_autopatch_fetch_rebar); \
|
|
$(call dep_autopatch_rebar,$(1)); \
|
|
else \
|
|
$(call dep_autopatch_gen,$(1)); \
|
|
fi
|
|
endef
|
|
|
|
define dep_autopatch_noop
|
|
printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
|
|
endef
|
|
|
|
# Overwrite erlang.mk with the current file by default.
|
|
ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
|
|
define dep_autopatch_erlang_mk
|
|
rm -f $(DEPS_DIR)/$(1)/erlang.mk; \
|
|
cd $(DEPS_DIR)/$(1)/ && ln -s ../../erlang.mk
|
|
endef
|
|
else
|
|
define dep_autopatch_erlang_mk
|
|
echo -n
|
|
endef
|
|
endif
|
|
|
|
define dep_autopatch_gen
|
|
printf "%s\n" \
|
|
"ERLC_OPTS = +debug_info" \
|
|
"include ../../erlang.mk" > $(DEPS_DIR)/$(1)/Makefile
|
|
endef
|
|
|
|
define dep_autopatch_fetch_rebar
|
|
mkdir -p $(ERLANG_MK_TMP); \
|
|
if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
|
|
git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
|
|
cd $(ERLANG_MK_TMP)/rebar; \
|
|
git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
|
|
make; \
|
|
cd -; \
|
|
fi
|
|
endef
|
|
|
|
define dep_autopatch_rebar
|
|
if [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
|
|
mv $(DEPS_DIR)/$(1)/Makefile $(DEPS_DIR)/$(1)/Makefile.orig.mk; \
|
|
fi; \
|
|
$(call erlang,$(call dep_autopatch_rebar.erl,$(1)))
|
|
endef
|
|
|
|
define dep_autopatch_rebar.erl
|
|
application:set_env(rebar, log_level, debug),
|
|
Conf1 = case file:consult("$(DEPS_DIR)/$(1)/rebar.config") of
|
|
{ok, Conf0} -> Conf0;
|
|
_ -> []
|
|
end,
|
|
{Conf, OsEnv} = fun() ->
|
|
case filelib:is_file("$(DEPS_DIR)/$(1)/rebar.config.script") of
|
|
false -> {Conf1, []};
|
|
true ->
|
|
Bindings0 = erl_eval:new_bindings(),
|
|
Bindings1 = erl_eval:add_binding('CONFIG', Conf1, Bindings0),
|
|
Bindings = erl_eval:add_binding('SCRIPT', "$(DEPS_DIR)/$(1)/rebar.config.script", Bindings1),
|
|
Before = os:getenv(),
|
|
{ok, Conf2} = file:script("$(DEPS_DIR)/$(1)/rebar.config.script", Bindings),
|
|
{Conf2, lists:foldl(fun(E, Acc) -> lists:delete(E, Acc) end, os:getenv(), Before)}
|
|
end
|
|
end(),
|
|
Write = fun (Text) ->
|
|
file:write_file("$(DEPS_DIR)/$(1)/Makefile", Text, [append])
|
|
end,
|
|
Escape = fun (Text) ->
|
|
re:replace(Text, "\\\\$$$$", "\$$$$$$$$", [global, {return, list}])
|
|
end,
|
|
Write("IGNORE_DEPS = edown eper eunit_formatters meck node_package "
|
|
"rebar_lock_deps_plugin rebar_vsn_plugin reltool_util\n"),
|
|
Write("C_SRC_DIR = /path/do/not/exist\n"),
|
|
Write("DRV_CFLAGS = -fPIC\nexport DRV_CFLAGS\n"),
|
|
Write(["ERLANG_ARCH = ", rebar_utils:wordsize(), "\nexport ERLANG_ARCH\n"]),
|
|
fun() ->
|
|
Write("ERLC_OPTS = +debug_info\nexport ERLC_OPTS\n"),
|
|
case lists:keyfind(erl_opts, 1, Conf) of
|
|
false -> ok;
|
|
{_, ErlOpts} ->
|
|
lists:foreach(fun
|
|
({d, D}) ->
|
|
Write("ERLC_OPTS += -D" ++ atom_to_list(D) ++ "=1\n");
|
|
({i, I}) ->
|
|
Write(["ERLC_OPTS += -I ", I, "\n"]);
|
|
({platform_define, Regex, D}) ->
|
|
case rebar_utils:is_arch(Regex) of
|
|
true -> Write("ERLC_OPTS += -D" ++ atom_to_list(D) ++ "=1\n");
|
|
false -> ok
|
|
end;
|
|
({parse_transform, PT}) ->
|
|
Write("ERLC_OPTS += +'{parse_transform, " ++ atom_to_list(PT) ++ "}'\n");
|
|
(_) -> ok
|
|
end, ErlOpts)
|
|
end,
|
|
Write("\n")
|
|
end(),
|
|
fun() ->
|
|
File = case lists:keyfind(deps, 1, Conf) of
|
|
false -> [];
|
|
{_, Deps} ->
|
|
[begin case case Dep of
|
|
{N, S} when is_tuple(S) -> {N, S};
|
|
{N, _, S} -> {N, S};
|
|
{N, _, S, _} -> {N, S};
|
|
_ -> false
|
|
end of
|
|
false -> ok;
|
|
{Name, Source} ->
|
|
{Method, Repo, Commit} = case Source of
|
|
{git, R} -> {git, R, master};
|
|
{M, R, {branch, C}} -> {M, R, C};
|
|
{M, R, {tag, C}} -> {M, R, C};
|
|
{M, R, C} -> {M, R, C}
|
|
end,
|
|
Write(io_lib:format("DEPS += ~s\ndep_~s = ~s ~s ~s~n", [Name, Name, Method, Repo, Commit]))
|
|
end end || Dep <- Deps]
|
|
end
|
|
end(),
|
|
fun() ->
|
|
case lists:keyfind(erl_first_files, 1, Conf) of
|
|
false -> ok;
|
|
{_, Files} ->
|
|
Names = [[" ", case lists:reverse(F) of
|
|
"lre." ++ Elif -> lists:reverse(Elif);
|
|
Elif -> lists:reverse(Elif)
|
|
end] || "src/" ++ F <- Files],
|
|
Write(io_lib:format("COMPILE_FIRST +=~s\n", [Names]))
|
|
end
|
|
end(),
|
|
FindFirst = fun(F, Fd) ->
|
|
case io:parse_erl_form(Fd, undefined) of
|
|
{ok, {attribute, _, compile, {parse_transform, PT}}, _} ->
|
|
[PT, F(F, Fd)];
|
|
{ok, {attribute, _, compile, CompileOpts}, _} when is_list(CompileOpts) ->
|
|
case proplists:get_value(parse_transform, CompileOpts) of
|
|
undefined -> [F(F, Fd)];
|
|
PT -> [PT, F(F, Fd)]
|
|
end;
|
|
{ok, {attribute, _, include, Hrl}, _} ->
|
|
case file:open("$(DEPS_DIR)/$(1)/include/" ++ Hrl, [read]) of
|
|
{ok, HrlFd} -> [F(F, HrlFd), F(F, Fd)];
|
|
_ ->
|
|
case file:open("$(DEPS_DIR)/$(1)/src/" ++ Hrl, [read]) of
|
|
{ok, HrlFd} -> [F(F, HrlFd), F(F, Fd)];
|
|
_ -> [F(F, Fd)]
|
|
end
|
|
end;
|
|
{ok, {attribute, _, include_lib, "$(1)/include/" ++ Hrl}, _} ->
|
|
{ok, HrlFd} = file:open("$(DEPS_DIR)/$(1)/include/" ++ Hrl, [read]),
|
|
[F(F, HrlFd), F(F, Fd)];
|
|
{ok, {attribute, _, include_lib, Hrl}, _} ->
|
|
case file:open("$(DEPS_DIR)/$(1)/include/" ++ Hrl, [read]) of
|
|
{ok, HrlFd} -> [F(F, HrlFd), F(F, Fd)];
|
|
_ -> [F(F, Fd)]
|
|
end;
|
|
{ok, {attribute, _, import, {Imp, _}}, _} ->
|
|
case file:open("$(DEPS_DIR)/$(1)/src/" ++ atom_to_list(Imp) ++ ".erl", [read]) of
|
|
{ok, ImpFd} -> [Imp, F(F, ImpFd), F(F, Fd)];
|
|
_ -> [F(F, Fd)]
|
|
end;
|
|
{eof, _} ->
|
|
file:close(Fd),
|
|
[];
|
|
_ ->
|
|
F(F, Fd)
|
|
end
|
|
end,
|
|
fun() ->
|
|
ErlFiles = filelib:wildcard("$(DEPS_DIR)/$(1)/src/*.erl"),
|
|
First0 = lists:usort(lists:flatten([begin
|
|
{ok, Fd} = file:open(F, [read]),
|
|
FindFirst(FindFirst, Fd)
|
|
end || F <- ErlFiles])),
|
|
First = lists:flatten([begin
|
|
{ok, Fd} = file:open("$(DEPS_DIR)/$(1)/src/" ++ atom_to_list(M) ++ ".erl", [read]),
|
|
FindFirst(FindFirst, Fd)
|
|
end || M <- First0, lists:member("$(DEPS_DIR)/$(1)/src/" ++ atom_to_list(M) ++ ".erl", ErlFiles)]) ++ First0,
|
|
Write(["COMPILE_FIRST +=", [[" ", atom_to_list(M)] || M <- First,
|
|
lists:member("$(DEPS_DIR)/$(1)/src/" ++ atom_to_list(M) ++ ".erl", ErlFiles)], "\n"])
|
|
end(),
|
|
Write("\n\nrebar_dep: preprocess pre-deps deps pre-app app\n"),
|
|
Write("\npreprocess::\n"),
|
|
Write("\npre-deps::\n"),
|
|
Write("\npre-app::\n"),
|
|
PatchHook = fun(Cmd) ->
|
|
case Cmd of
|
|
"make -C" ++ _ -> Escape(Cmd);
|
|
"gmake -C" ++ _ -> Escape(Cmd);
|
|
"make " ++ Cmd1 -> "make -f Makefile.orig.mk " ++ Escape(Cmd1);
|
|
"gmake " ++ Cmd1 -> "gmake -f Makefile.orig.mk " ++ Escape(Cmd1);
|
|
_ -> Escape(Cmd)
|
|
end
|
|
end,
|
|
fun() ->
|
|
case lists:keyfind(pre_hooks, 1, Conf) of
|
|
false -> ok;
|
|
{_, Hooks} ->
|
|
[case H of
|
|
{'get-deps', Cmd} ->
|
|
Write("\npre-deps::\n\t" ++ PatchHook(Cmd) ++ "\n");
|
|
{compile, Cmd} ->
|
|
Write("\npre-app::\n\tCC=$$$$\(CC) " ++ PatchHook(Cmd) ++ "\n");
|
|
{Regex, compile, Cmd} ->
|
|
case rebar_utils:is_arch(Regex) of
|
|
true -> Write("\npre-app::\n\tCC=$$$$\(CC) " ++ PatchHook(Cmd) ++ "\n");
|
|
false -> ok
|
|
end;
|
|
_ -> ok
|
|
end || H <- Hooks]
|
|
end
|
|
end(),
|
|
ShellToMk = fun(V) ->
|
|
re:replace(re:replace(V, "(\\\\$$$$)(\\\\w*)", "\\\\1(\\\\2)", [global]),
|
|
"-Werror\\\\b", "", [{return, list}, global])
|
|
end,
|
|
PortSpecs = fun() ->
|
|
case lists:keyfind(port_specs, 1, Conf) of
|
|
false ->
|
|
case filelib:is_dir("$(DEPS_DIR)/$(1)/c_src") of
|
|
false -> [];
|
|
true ->
|
|
[{"priv/" ++ proplists:get_value(so_name, Conf, "$(1)_drv.so"),
|
|
proplists:get_value(port_sources, Conf, ["c_src/*.c"]), []}]
|
|
end;
|
|
{_, Specs} ->
|
|
lists:flatten([case S of
|
|
{Output, Input} -> {ShellToMk(Output), Input, []};
|
|
{Regex, Output, Input} ->
|
|
case rebar_utils:is_arch(Regex) of
|
|
true -> {ShellToMk(Output), Input, []};
|
|
false -> []
|
|
end;
|
|
{Regex, Output, Input, [{env, Env}]} ->
|
|
case rebar_utils:is_arch(Regex) of
|
|
true -> {ShellToMk(Output), Input, Env};
|
|
false -> []
|
|
end
|
|
end || S <- Specs])
|
|
end
|
|
end(),
|
|
PortSpecWrite = fun (Text) ->
|
|
file:write_file("$(DEPS_DIR)/$(1)/c_src/Makefile.erlang.mk", Text, [append])
|
|
end,
|
|
case PortSpecs of
|
|
[] -> ok;
|
|
_ ->
|
|
Write("\npre-app::\n\t$$$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
|
|
PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I ~s/erts-~s/include -I ~s\n",
|
|
[code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
|
|
PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L ~s -lerl_interface -lei\n",
|
|
[code:lib_dir(erl_interface, lib)])),
|
|
[PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
|
|
FilterEnv = fun(Env) ->
|
|
lists:flatten([case E of
|
|
{_, _} -> E;
|
|
{Regex, K, V} ->
|
|
case rebar_utils:is_arch(Regex) of
|
|
true -> {K, V};
|
|
false -> []
|
|
end
|
|
end || E <- Env])
|
|
end,
|
|
MergeEnv = fun(Env) ->
|
|
lists:foldl(fun ({K, V}, Acc) ->
|
|
case lists:keyfind(K, 1, Acc) of
|
|
false -> [{K, rebar_utils:expand_env_variable(V, K, "")}|Acc];
|
|
{_, V0} -> [{K, rebar_utils:expand_env_variable(V, K, V0)}|Acc]
|
|
end
|
|
end, [], Env)
|
|
end,
|
|
PortEnv = case lists:keyfind(port_env, 1, Conf) of
|
|
false -> [];
|
|
{_, PortEnv0} -> FilterEnv(PortEnv0)
|
|
end,
|
|
PortSpec = fun ({Output, Input0, Env}) ->
|
|
filelib:ensure_dir("$(DEPS_DIR)/$(1)/" ++ Output),
|
|
Input = [[" ", I] || I <- Input0],
|
|
PortSpecWrite([
|
|
[["\n", K, " = ", ShellToMk(V)] || {K, V} <- lists:reverse(MergeEnv(PortEnv))],
|
|
case $(PLATFORM) of
|
|
darwin -> "\n\nLDFLAGS += -flat_namespace -undefined suppress";
|
|
_ -> ""
|
|
end,
|
|
"\n\nall:: ", Output, "\n\n",
|
|
"%.o: %.c\n\t$$$$\(CC) -c -o $$$$\@ $$$$\< $$$$\(CFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n",
|
|
"%.o: %.C\n\t$$$$\(CXX) -c -o $$$$\@ $$$$\< $$$$\(CXXFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n",
|
|
"%.o: %.cc\n\t$$$$\(CXX) -c -o $$$$\@ $$$$\< $$$$\(CXXFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n",
|
|
"%.o: %.cpp\n\t$$$$\(CXX) -c -o $$$$\@ $$$$\< $$$$\(CXXFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n",
|
|
[[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
|
|
Output, ": $$$$\(foreach ext,.c .C .cc .cpp,",
|
|
"$$$$\(patsubst %$$$$\(ext),%.o,$$$$\(filter %$$$$\(ext),$$$$\(wildcard", Input, "))))\n",
|
|
"\t$$$$\(CC) -o $$$$\@ $$$$\? $$$$\(LDFLAGS) $$$$\(ERL_LDFLAGS) $$$$\(DRV_LDFLAGS) $$$$\(EXE_LDFLAGS)",
|
|
case filename:extension(Output) of
|
|
[] -> "\n";
|
|
_ -> " -shared\n"
|
|
end])
|
|
end,
|
|
[PortSpec(S) || S <- PortSpecs]
|
|
end,
|
|
Write("\ninclude ../../erlang.mk"),
|
|
RunPlugin = fun(Plugin, Step) ->
|
|
case erlang:function_exported(Plugin, Step, 2) of
|
|
false -> ok;
|
|
true ->
|
|
c:cd("$(DEPS_DIR)/$(1)/"),
|
|
Ret = Plugin:Step({config, "", Conf, dict:new(), dict:new(), dict:new(),
|
|
dict:store(base_dir, "", dict:new())}, undefined),
|
|
io:format("rebar plugin ~p step ~p ret ~p~n", [Plugin, Step, Ret])
|
|
end
|
|
end,
|
|
fun() ->
|
|
case lists:keyfind(plugins, 1, Conf) of
|
|
false -> ok;
|
|
{_, Plugins} ->
|
|
[begin
|
|
case lists:keyfind(deps, 1, Conf) of
|
|
false -> ok;
|
|
{_, Deps} ->
|
|
case lists:keyfind(P, 1, Deps) of
|
|
false -> ok;
|
|
_ ->
|
|
Path = "$(DEPS_DIR)/" ++ atom_to_list(P),
|
|
io:format("~s", [os:cmd("$(MAKE) -C $(DEPS_DIR)/$(1) " ++ Path)]),
|
|
io:format("~s", [os:cmd("$(MAKE) -C " ++ Path ++ " IS_DEP=1")]),
|
|
code:add_patha(Path ++ "/ebin")
|
|
end
|
|
end
|
|
end || P <- Plugins],
|
|
[case code:load_file(P) of
|
|
{module, P} -> ok;
|
|
_ ->
|
|
case lists:keyfind(plugin_dir, 1, Conf) of
|
|
false -> ok;
|
|
{_, PluginsDir} ->
|
|
ErlFile = "$(DEPS_DIR)/$(1)/" ++ PluginsDir ++ "/" ++ atom_to_list(P) ++ ".erl",
|
|
{ok, P, Bin} = compile:file(ErlFile, [binary]),
|
|
{module, P} = code:load_binary(P, ErlFile, Bin)
|
|
end
|
|
end || P <- Plugins],
|
|
[RunPlugin(P, preprocess) || P <- Plugins],
|
|
[RunPlugin(P, pre_compile) || P <- Plugins]
|
|
end
|
|
end(),
|
|
halt()
|
|
endef
|
|
|
|
define dep_autopatch_app.erl
|
|
UpdateModules = fun(App) ->
|
|
case filelib:is_regular(App) of
|
|
false -> ok;
|
|
true ->
|
|
{ok, [{application, $(1), L0}]} = file:consult(App),
|
|
Mods = filelib:fold_files("$(DEPS_DIR)/$(1)/src", "\\\\.erl$$$$", true,
|
|
fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
|
|
L = lists:keystore(modules, 1, L0, {modules, Mods}),
|
|
ok = file:write_file(App, io_lib:format("~p.~n", [{application, $(1), L}]))
|
|
end
|
|
end,
|
|
UpdateModules("$(DEPS_DIR)/$(1)/ebin/$(1).app"),
|
|
halt()
|
|
endef
|
|
|
|
define dep_autopatch_appsrc.erl
|
|
AppSrcOut = "$(DEPS_DIR)/$(1)/src/$(1).app.src",
|
|
AppSrcIn = case filelib:is_regular(AppSrcOut) of false -> "$(DEPS_DIR)/$(1)/ebin/$(1).app"; true -> AppSrcOut end,
|
|
case filelib:is_regular(AppSrcIn) of
|
|
false -> ok;
|
|
true ->
|
|
{ok, [{application, $(1), L0}]} = file:consult(AppSrcIn),
|
|
L1 = lists:keystore(modules, 1, L0, {modules, []}),
|
|
L2 = case lists:keyfind(vsn, 1, L1) of {vsn, git} -> lists:keyreplace(vsn, 1, L1, {vsn, "git"}); _ -> L1 end,
|
|
ok = file:write_file(AppSrcOut, io_lib:format("~p.~n", [{application, $(1), L2}])),
|
|
case AppSrcOut of AppSrcIn -> ok; _ -> ok = file:delete(AppSrcIn) end
|
|
end,
|
|
halt()
|
|
endef
|
|
|
|
define dep_fetch
|
|
if [ "$$$$VS" = "git" ]; then \
|
|
git clone -q -n -- $$$$REPO $(DEPS_DIR)/$(1); \
|
|
cd $(DEPS_DIR)/$(1) && git checkout -q $$$$COMMIT; \
|
|
elif [ "$$$$VS" = "hg" ]; then \
|
|
hg clone -q -U $$$$REPO $(DEPS_DIR)/$(1); \
|
|
cd $(DEPS_DIR)/$(1) && hg update -q $$$$COMMIT; \
|
|
elif [ "$$$$VS" = "svn" ]; then \
|
|
svn checkout -q $$$$REPO $(DEPS_DIR)/$(1); \
|
|
elif [ "$$$$VS" = "cp" ]; then \
|
|
cp -R $$$$REPO $(DEPS_DIR)/$(1); \
|
|
else \
|
|
echo "Unknown or invalid dependency: $(1). Please consult the erlang.mk README for instructions." >&2; \
|
|
exit 78; \
|
|
fi
|
|
endef
|
|
|
|
define dep_target
|
|
$(DEPS_DIR)/$(1):
|
|
@mkdir -p $(DEPS_DIR)
|
|
ifeq (,$(dep_$(1)))
|
|
@if [ ! -f $(PKG_FILE2) ]; then $(call core_http_get,$(PKG_FILE2),$(PKG_FILE_URL)); fi
|
|
$(dep_verbose) DEPPKG=$$$$(awk 'BEGIN { FS = "\t" }; $$$$1 == "$(1)" { print $$$$2 " " $$$$3 " " $$$$4 }' $(PKG_FILE2);); \
|
|
VS=$$$$(echo $$$$DEPPKG | cut -d " " -f1); \
|
|
REPO=$$$$(echo $$$$DEPPKG | cut -d " " -f2); \
|
|
COMMIT=$$$$(echo $$$$DEPPKG | cut -d " " -f3); \
|
|
$(call dep_fetch,$(1))
|
|
else
|
|
ifeq (1,$(words $(dep_$(1))))
|
|
$(dep_verbose) VS=git; \
|
|
REPO=$(dep_$(1)); \
|
|
COMMIT=master; \
|
|
$(call dep_fetch,$(1))
|
|
else
|
|
ifeq (2,$(words $(dep_$(1))))
|
|
$(dep_verbose) VS=git; \
|
|
REPO=$(word 1,$(dep_$(1))); \
|
|
COMMIT=$(word 2,$(dep_$(1))); \
|
|
$(call dep_fetch,$(1))
|
|
else
|
|
$(dep_verbose) VS=$(word 1,$(dep_$(1))); \
|
|
REPO=$(word 2,$(dep_$(1))); \
|
|
COMMIT=$(word 3,$(dep_$(1))); \
|
|
$(call dep_fetch,$(1))
|
|
endif
|
|
endif
|
|
endif
|
|
@if [ -f $(DEPS_DIR)/$(1)/configure.ac -o -f $(DEPS_DIR)/$(1)/configure.in ]; then \
|
|
echo " AUTO " $(1); \
|
|
cd $(DEPS_DIR)/$(1) && autoreconf -Wall -vif -I m4; \
|
|
fi
|
|
-@if [ -f $(DEPS_DIR)/$(1)/configure ]; then \
|
|
echo " CONF " $(1); \
|
|
cd $(DEPS_DIR)/$(1) && ./configure; \
|
|
fi
|
|
ifeq ($(filter $(1),$(NO_AUTOPATCH)),)
|
|
@$(call dep_autopatch,$(1))
|
|
endif
|
|
endef
|
|
|
|
$(foreach dep,$(DEPS),$(eval $(call dep_target,$(dep))))
|
|
|
|
distclean-deps:
|
|
$(gen_verbose) rm -rf $(DEPS_DIR)
|
|
|
|
# Packages related targets.
|
|
|
|
$(PKG_FILE2):
|
|
@$(call core_http_get,$(PKG_FILE2),$(PKG_FILE_URL))
|
|
|
|
pkg-list: $(PKG_FILE2)
|
|
@cat $(PKG_FILE2) | awk 'BEGIN { FS = "\t" }; { print \
|
|
"Name:\t\t" $$1 "\n" \
|
|
"Repository:\t" $$3 "\n" \
|
|
"Website:\t" $$5 "\n" \
|
|
"Description:\t" $$6 "\n" }'
|
|
|
|
ifdef q
|
|
pkg-search: $(PKG_FILE2)
|
|
@cat $(PKG_FILE2) | grep -i ${q} | awk 'BEGIN { FS = "\t" }; { print \
|
|
"Name:\t\t" $$1 "\n" \
|
|
"Repository:\t" $$3 "\n" \
|
|
"Website:\t" $$5 "\n" \
|
|
"Description:\t" $$6 "\n" }'
|
|
else
|
|
pkg-search:
|
|
$(error Usage: $(MAKE) pkg-search q=STRING)
|
|
endif
|
|
|
|
ifeq ($(PKG_FILE2),$(CURDIR)/.erlang.mk.packages.v2)
|
|
distclean-pkg:
|
|
$(gen_verbose) rm -f $(PKG_FILE2)
|
|
endif
|
|
|
|
help::
|
|
@printf "%s\n" "" \
|
|
"Package-related targets:" \
|
|
" pkg-list List all known packages" \
|
|
" pkg-search q=STRING Search for STRING in the package index"
|
|
|
|
# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
|
|
# This file is part of erlang.mk and subject to the terms of the ISC License.
|
|
|
|
# Verbosity.
|
|
|
|
proto_verbose_0 = @echo " PROTO " $(filter %.proto,$(?F));
|
|
proto_verbose = $(proto_verbose_$(V))
|
|
|
|
# Core targets.
|
|
|
|
define compile_proto
|
|
@mkdir -p ebin/ include/
|
|
$(proto_verbose) $(call erlang,$(call compile_proto.erl,$(1)))
|
|
$(proto_verbose) erlc +debug_info -o ebin/ ebin/*.erl
|
|
@rm ebin/*.erl
|
|
endef
|
|
|
|
define compile_proto.erl
|
|
[begin
|
|
Dir = filename:dirname(filename:dirname(F)),
|
|
protobuffs_compile:generate_source(F,
|
|
[{output_include_dir, Dir ++ "/include"},
|
|
{output_src_dir, Dir ++ "/ebin"}])
|
|
end || F <- string:tokens("$(1)", " ")],
|
|
halt().
|
|
endef
|
|
|
|
ifneq ($(wildcard src/),)
|
|
ebin/$(PROJECT).app:: $(shell find src -type f -name \*.proto 2>/dev/null)
|
|
$(if $(strip $?),$(call compile_proto,$?))
|
|
endif
|
|
|
|
# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
|
|
# This file is part of erlang.mk and subject to the terms of the ISC License.
|
|
|
|
.PHONY: clean-app
|
|
|
|
# Configuration.
|
|
|
|
ERLC_OPTS ?= -Werror +debug_info +warn_export_vars +warn_shadow_vars \
|
|
+warn_obsolete_guard # +bin_opt_info +warn_export_all +warn_missing_spec
|
|
COMPILE_FIRST ?=
|
|
COMPILE_FIRST_PATHS = $(addprefix src/,$(addsuffix .erl,$(COMPILE_FIRST)))
|
|
ERLC_EXCLUDE ?=
|
|
ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
|
|
|
|
ERLC_MIB_OPTS ?=
|
|
COMPILE_MIB_FIRST ?=
|
|
COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
|
|
|
|
# Verbosity.
|
|
|
|
appsrc_verbose_0 = @echo " APP " $(PROJECT).app.src;
|
|
appsrc_verbose = $(appsrc_verbose_$(V))
|
|
|
|
erlc_verbose_0 = @echo " ERLC " $(filter-out $(patsubst %,%.erl,$(ERLC_EXCLUDE)),\
|
|
$(filter %.erl %.core,$(?F)));
|
|
erlc_verbose = $(erlc_verbose_$(V))
|
|
|
|
xyrl_verbose_0 = @echo " XYRL " $(filter %.xrl %.yrl,$(?F));
|
|
xyrl_verbose = $(xyrl_verbose_$(V))
|
|
|
|
asn1_verbose_0 = @echo " ASN1 " $(filter %.asn1,$(?F));
|
|
asn1_verbose = $(asn1_verbose_$(V))
|
|
|
|
mib_verbose_0 = @echo " MIB " $(filter %.bin %.mib,$(?F));
|
|
mib_verbose = $(mib_verbose_$(V))
|
|
|
|
# Targets.
|
|
|
|
ifeq ($(wildcard ebin/test),)
|
|
app:: app-build
|
|
else
|
|
app:: clean app-build
|
|
endif
|
|
|
|
app-build: erlc-include ebin/$(PROJECT).app
|
|
$(eval MODULES := $(shell find ebin -type f -name \*.beam \
|
|
| sed "s/ebin\//'/;s/\.beam/',/" | sed '$$s/.$$//'))
|
|
@if [ -z "$$(grep -E '^[^%]*{modules,' src/$(PROJECT).app.src)" ]; then \
|
|
echo "Empty modules entry not found in $(PROJECT).app.src. Please consult the erlang.mk README for instructions." >&2; \
|
|
exit 1; \
|
|
fi
|
|
$(eval GITDESCRIBE := $(shell git describe --dirty --abbrev=7 --tags --always --first-parent 2>/dev/null || true))
|
|
$(appsrc_verbose) cat src/$(PROJECT).app.src \
|
|
| sed "s/{modules,[[:space:]]*\[\]}/{modules, \[$(MODULES)\]}/" \
|
|
| sed "s/{id,[[:space:]]*\"git\"}/{id, \"$(GITDESCRIBE)\"}/" \
|
|
> ebin/$(PROJECT).app
|
|
|
|
erlc-include:
|
|
-@if [ -d ebin/ ]; then \
|
|
find include/ src/ -type f -name \*.hrl -newer ebin -exec touch $(shell find src/ -type f -name "*.erl") \; 2>/dev/null || printf ''; \
|
|
fi
|
|
|
|
define compile_erl
|
|
$(erlc_verbose) erlc -v $(ERLC_OPTS) -o ebin/ \
|
|
-pa ebin/ -I include/ $(filter-out $(ERLC_EXCLUDE_PATHS),\
|
|
$(COMPILE_FIRST_PATHS) $(1))
|
|
endef
|
|
|
|
define compile_xyrl
|
|
$(xyrl_verbose) erlc -v -o ebin/ $(1)
|
|
$(xyrl_verbose) erlc $(ERLC_OPTS) -o ebin/ ebin/*.erl
|
|
@rm ebin/*.erl
|
|
endef
|
|
|
|
define compile_asn1
|
|
$(asn1_verbose) erlc -v -I include/ -o ebin/ $(1)
|
|
@mv ebin/*.hrl include/
|
|
@mv ebin/*.asn1db include/
|
|
@rm ebin/*.erl
|
|
endef
|
|
|
|
define compile_mib
|
|
$(mib_verbose) erlc -v $(ERLC_MIB_OPTS) -o priv/mibs/ \
|
|
-I priv/mibs/ $(COMPILE_MIB_FIRST_PATHS) $(1)
|
|
$(mib_verbose) erlc -o include/ -- priv/mibs/*.bin
|
|
endef
|
|
|
|
ifneq ($(wildcard src/),)
|
|
ebin/$(PROJECT).app::
|
|
@mkdir -p ebin/
|
|
|
|
ifneq ($(wildcard asn1/),)
|
|
ebin/$(PROJECT).app:: $(shell find asn1 -type f -name \*.asn1)
|
|
@mkdir -p include
|
|
$(if $(strip $?),$(call compile_asn1,$?))
|
|
endif
|
|
|
|
ifneq ($(wildcard mibs/),)
|
|
ebin/$(PROJECT).app:: $(shell find mibs -type f -name \*.mib)
|
|
@mkdir -p priv/mibs/ include
|
|
$(if $(strip $?),$(call compile_mib,$?))
|
|
endif
|
|
|
|
ebin/$(PROJECT).app:: $(shell find src -type f -name \*.erl -o -name \*.core)
|
|
$(if $(strip $?),$(call compile_erl,$?))
|
|
|
|
ebin/$(PROJECT).app:: $(shell find src -type f -name \*.xrl -o -name \*.yrl)
|
|
$(if $(strip $?),$(call compile_xyrl,$?))
|
|
endif
|
|
|
|
clean:: clean-app
|
|
|
|
clean-app:
|
|
$(gen_verbose) rm -rf ebin/ priv/mibs/ \
|
|
$(addprefix include/,$(addsuffix .hrl,$(notdir $(basename $(wildcard mibs/*.mib)))))
|
|
|
|
# Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
|
|
# This file is part of erlang.mk and subject to the terms of the ISC License.
|
|
|
|
.PHONY: docs-deps
|
|
|
|
# Configuration.
|
|
|
|
ALL_DOC_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(DOC_DEPS))
|
|
|
|
# Targets.
|
|
|
|
$(foreach dep,$(DOC_DEPS),$(eval $(call dep_target,$(dep))))
|
|
|
|
ifneq ($(SKIP_DEPS),)
|
|
doc-deps:
|
|
else
|
|
doc-deps: $(ALL_DOC_DEPS_DIRS)
|
|
@for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
|
|
endif
|
|
|
|
# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
|
|
# This file is part of erlang.mk and subject to the terms of the ISC License.
|
|
|
|
.PHONY: test-deps test-dir test-build clean-test-dir
|
|
|
|
# Configuration.
|
|
|
|
TEST_DIR ?= $(CURDIR)/test
|
|
|
|
ALL_TEST_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(TEST_DEPS))
|
|
|
|
TEST_ERLC_OPTS ?= +debug_info +warn_export_vars +warn_shadow_vars +warn_obsolete_guard
|
|
TEST_ERLC_OPTS += -DTEST=1
|
|
|
|
# Targets.
|
|
|
|
$(foreach dep,$(TEST_DEPS),$(eval $(call dep_target,$(dep))))
|
|
|
|
ifneq ($(SKIP_DEPS),)
|
|
test-deps:
|
|
else
|
|
test-deps: $(ALL_TEST_DEPS_DIRS)
|
|
@for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
|
|
endif
|
|
|
|
ifneq ($(strip $(TEST_DIR)),)
|
|
test-dir:
|
|
$(gen_verbose) erlc -v $(TEST_ERLC_OPTS) -I include/ -o $(TEST_DIR) \
|
|
$(wildcard $(TEST_DIR)/*.erl $(TEST_DIR)/*/*.erl) -pa ebin/
|
|
endif
|
|
|
|
ifeq ($(wildcard ebin/test),)
|
|
test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS)
|
|
test-build:: clean deps test-deps
|
|
@$(MAKE) --no-print-directory app-build test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)"
|
|
$(gen_verbose) touch ebin/test
|
|
else
|
|
test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS)
|
|
test-build:: deps test-deps
|
|
@$(MAKE) --no-print-directory app-build test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)"
|
|
endif
|
|
|
|
clean:: clean-test-dir
|
|
|
|
clean-test-dir:
|
|
ifneq ($(wildcard $(TEST_DIR)/*.beam),)
|
|
$(gen_verbose) rm -f $(TEST_DIR)/*.beam
|
|
endif
|
|
|
|
# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
|
|
# This file is part of erlang.mk and subject to the terms of the ISC License.
|
|
|
|
.PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
|
|
|
|
MAN_INSTALL_PATH ?= /usr/local/share/man
|
|
MAN_SECTIONS ?= 3 7
|
|
|
|
docs:: asciidoc
|
|
|
|
asciidoc: distclean-asciidoc doc-deps asciidoc-guide asciidoc-manual
|
|
|
|
ifeq ($(wildcard doc/src/guide/book.asciidoc),)
|
|
asciidoc-guide:
|
|
else
|
|
asciidoc-guide:
|
|
a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
|
|
a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
|
|
endif
|
|
|
|
ifeq ($(wildcard doc/src/manual/*.asciidoc),)
|
|
asciidoc-manual:
|
|
else
|
|
asciidoc-manual:
|
|
for f in doc/src/manual/*.asciidoc ; do \
|
|
a2x -v -f manpage $$f ; \
|
|
done
|
|
for s in $(MAN_SECTIONS); do \
|
|
mkdir -p doc/man$$s/ ; \
|
|
mv doc/src/manual/*.$$s doc/man$$s/ ; \
|
|
gzip doc/man$$s/*.$$s ; \
|
|
done
|
|
|
|
install-docs:: install-asciidoc
|
|
|
|
install-asciidoc: asciidoc-manual
|
|
for s in $(MAN_SECTIONS); do \
|
|
mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
|
|
install -g 0 -o 0 -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
|
|
done
|
|
endif
|
|
|
|
distclean:: distclean-asciidoc
|
|
|
|
distclean-asciidoc:
|
|
$(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
|
|
|
|
# Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
|
|
# This file is part of erlang.mk and subject to the terms of the ISC License.
|
|
|
|
.PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
|
|
|
|
# Core targets.
|
|
|
|
help::
|
|
@printf "%s\n" "" \
|
|
"Bootstrap targets:" \
|
|
" bootstrap Generate a skeleton of an OTP application" \
|
|
" bootstrap-lib Generate a skeleton of an OTP library" \
|
|
" bootstrap-rel Generate the files needed to build a release" \
|
|
" new t=TPL n=NAME Generate a module NAME based on the template TPL" \
|
|
" list-templates List available templates"
|
|
|
|
# Bootstrap templates.
|
|
|
|
define bs_appsrc
|
|
{application, $(PROJECT), [
|
|
{description, ""},
|
|
{vsn, "0.1.0"},
|
|
{id, "git"},
|
|
{modules, []},
|
|
{registered, []},
|
|
{applications, [
|
|
kernel,
|
|
stdlib
|
|
]},
|
|
{mod, {$(PROJECT)_app, []}},
|
|
{env, []}
|
|
]}.
|
|
endef
|
|
|
|
define bs_appsrc_lib
|
|
{application, $(PROJECT), [
|
|
{description, ""},
|
|
{vsn, "0.1.0"},
|
|
{id, "git"},
|
|
{modules, []},
|
|
{registered, []},
|
|
{applications, [
|
|
kernel,
|
|
stdlib
|
|
]}
|
|
]}.
|
|
endef
|
|
|
|
define bs_Makefile
|
|
PROJECT = $(PROJECT)
|
|
include erlang.mk
|
|
endef
|
|
|
|
define bs_app
|
|
-module($(PROJECT)_app).
|
|
-behaviour(application).
|
|
|
|
-export([start/2]).
|
|
-export([stop/1]).
|
|
|
|
start(_Type, _Args) ->
|
|
$(PROJECT)_sup:start_link().
|
|
|
|
stop(_State) ->
|
|
ok.
|
|
endef
|
|
|
|
define bs_relx_config
|
|
{release, {$(PROJECT)_release, "1"}, [$(PROJECT)]}.
|
|
{extended_start_script, true}.
|
|
{sys_config, "rel/sys.config"}.
|
|
{vm_args, "rel/vm.args"}.
|
|
endef
|
|
|
|
define bs_sys_config
|
|
[
|
|
].
|
|
endef
|
|
|
|
define bs_vm_args
|
|
-name $(PROJECT)@127.0.0.1
|
|
-setcookie $(PROJECT)
|
|
-heart
|
|
endef
|
|
|
|
# Normal templates.
|
|
|
|
define tpl_supervisor
|
|
-module($(n)).
|
|
-behaviour(supervisor).
|
|
|
|
-export([start_link/0]).
|
|
-export([init/1]).
|
|
|
|
start_link() ->
|
|
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
|
|
|
init([]) ->
|
|
Procs = [],
|
|
{ok, {{one_for_one, 1, 5}, Procs}}.
|
|
endef
|
|
|
|
define tpl_gen_server
|
|
-module($(n)).
|
|
-behaviour(gen_server).
|
|
|
|
%% API.
|
|
-export([start_link/0]).
|
|
|
|
%% gen_server.
|
|
-export([init/1]).
|
|
-export([handle_call/3]).
|
|
-export([handle_cast/2]).
|
|
-export([handle_info/2]).
|
|
-export([terminate/2]).
|
|
-export([code_change/3]).
|
|
|
|
-record(state, {
|
|
}).
|
|
|
|
%% API.
|
|
|
|
-spec start_link() -> {ok, pid()}.
|
|
start_link() ->
|
|
gen_server:start_link(?MODULE, [], []).
|
|
|
|
%% gen_server.
|
|
|
|
init([]) ->
|
|
{ok, #state{}}.
|
|
|
|
handle_call(_Request, _From, State) ->
|
|
{reply, ignored, State}.
|
|
|
|
handle_cast(_Msg, State) ->
|
|
{noreply, State}.
|
|
|
|
handle_info(_Info, State) ->
|
|
{noreply, State}.
|
|
|
|
terminate(_Reason, _State) ->
|
|
ok.
|
|
|
|
code_change(_OldVsn, State, _Extra) ->
|
|
{ok, State}.
|
|
endef
|
|
|
|
define tpl_cowboy_http
|
|
-module($(n)).
|
|
-behaviour(cowboy_http_handler).
|
|
|
|
-export([init/3]).
|
|
-export([handle/2]).
|
|
-export([terminate/3]).
|
|
|
|
-record(state, {
|
|
}).
|
|
|
|
init(_, Req, _Opts) ->
|
|
{ok, Req, #state{}}.
|
|
|
|
handle(Req, State=#state{}) ->
|
|
{ok, Req2} = cowboy_req:reply(200, Req),
|
|
{ok, Req2, State}.
|
|
|
|
terminate(_Reason, _Req, _State) ->
|
|
ok.
|
|
endef
|
|
|
|
define tpl_gen_fsm
|
|
-module($(n)).
|
|
-behaviour(gen_fsm).
|
|
|
|
%% API.
|
|
-export([start_link/0]).
|
|
|
|
%% gen_fsm.
|
|
-export([init/1]).
|
|
-export([state_name/2]).
|
|
-export([handle_event/3]).
|
|
-export([state_name/3]).
|
|
-export([handle_sync_event/4]).
|
|
-export([handle_info/3]).
|
|
-export([terminate/3]).
|
|
-export([code_change/4]).
|
|
|
|
-record(state, {
|
|
}).
|
|
|
|
%% API.
|
|
|
|
-spec start_link() -> {ok, pid()}.
|
|
start_link() ->
|
|
gen_fsm:start_link(?MODULE, [], []).
|
|
|
|
%% gen_fsm.
|
|
|
|
init([]) ->
|
|
{ok, state_name, #state{}}.
|
|
|
|
state_name(_Event, StateData) ->
|
|
{next_state, state_name, StateData}.
|
|
|
|
handle_event(_Event, StateName, StateData) ->
|
|
{next_state, StateName, StateData}.
|
|
|
|
state_name(_Event, _From, StateData) ->
|
|
{reply, ignored, state_name, StateData}.
|
|
|
|
handle_sync_event(_Event, _From, StateName, StateData) ->
|
|
{reply, ignored, StateName, StateData}.
|
|
|
|
handle_info(_Info, StateName, StateData) ->
|
|
{next_state, StateName, StateData}.
|
|
|
|
terminate(_Reason, _StateName, _StateData) ->
|
|
ok.
|
|
|
|
code_change(_OldVsn, StateName, StateData, _Extra) ->
|
|
{ok, StateName, StateData}.
|
|
endef
|
|
|
|
define tpl_cowboy_loop
|
|
-module($(n)).
|
|
-behaviour(cowboy_loop_handler).
|
|
|
|
-export([init/3]).
|
|
-export([info/3]).
|
|
-export([terminate/3]).
|
|
|
|
-record(state, {
|
|
}).
|
|
|
|
init(_, Req, _Opts) ->
|
|
{loop, Req, #state{}, 5000, hibernate}.
|
|
|
|
info(_Info, Req, State) ->
|
|
{loop, Req, State, hibernate}.
|
|
|
|
terminate(_Reason, _Req, _State) ->
|
|
ok.
|
|
endef
|
|
|
|
define tpl_cowboy_rest
|
|
-module($(n)).
|
|
|
|
-export([init/3]).
|
|
-export([content_types_provided/2]).
|
|
-export([get_html/2]).
|
|
|
|
init(_, _Req, _Opts) ->
|
|
{upgrade, protocol, cowboy_rest}.
|
|
|
|
content_types_provided(Req, State) ->
|
|
{[{{<<"text">>, <<"html">>, '*'}, get_html}], Req, State}.
|
|
|
|
get_html(Req, State) ->
|
|
{<<"<html><body>This is REST!</body></html>">>, Req, State}.
|
|
endef
|
|
|
|
define tpl_cowboy_ws
|
|
-module($(n)).
|
|
-behaviour(cowboy_websocket_handler).
|
|
|
|
-export([init/3]).
|
|
-export([websocket_init/3]).
|
|
-export([websocket_handle/3]).
|
|
-export([websocket_info/3]).
|
|
-export([websocket_terminate/3]).
|
|
|
|
-record(state, {
|
|
}).
|
|
|
|
init(_, _, _) ->
|
|
{upgrade, protocol, cowboy_websocket}.
|
|
|
|
websocket_init(_, Req, _Opts) ->
|
|
Req2 = cowboy_req:compact(Req),
|
|
{ok, Req2, #state{}}.
|
|
|
|
websocket_handle({text, Data}, Req, State) ->
|
|
{reply, {text, Data}, Req, State};
|
|
websocket_handle({binary, Data}, Req, State) ->
|
|
{reply, {binary, Data}, Req, State};
|
|
websocket_handle(_Frame, Req, State) ->
|
|
{ok, Req, State}.
|
|
|
|
websocket_info(_Info, Req, State) ->
|
|
{ok, Req, State}.
|
|
|
|
websocket_terminate(_Reason, _Req, _State) ->
|
|
ok.
|
|
endef
|
|
|
|
define tpl_ranch_protocol
|
|
-module($(n)).
|
|
-behaviour(ranch_protocol).
|
|
|
|
-export([start_link/4]).
|
|
-export([init/4]).
|
|
|
|
-type opts() :: [].
|
|
-export_type([opts/0]).
|
|
|
|
-record(state, {
|
|
socket :: inet:socket(),
|
|
transport :: module()
|
|
}).
|
|
|
|
start_link(Ref, Socket, Transport, Opts) ->
|
|
Pid = spawn_link(?MODULE, init, [Ref, Socket, Transport, Opts]),
|
|
{ok, Pid}.
|
|
|
|
-spec init(ranch:ref(), inet:socket(), module(), opts()) -> ok.
|
|
init(Ref, Socket, Transport, _Opts) ->
|
|
ok = ranch:accept_ack(Ref),
|
|
loop(#state{socket=Socket, transport=Transport}).
|
|
|
|
loop(State) ->
|
|
loop(State).
|
|
endef
|
|
|
|
# Plugin-specific targets.
|
|
|
|
define render_template
|
|
@echo "$${$(1)}" > $(2)
|
|
endef
|
|
|
|
$(foreach template,$(filter bs_%,$(.VARIABLES)),$(eval export $(template)))
|
|
$(foreach template,$(filter tpl_%,$(.VARIABLES)),$(eval export $(template)))
|
|
|
|
bootstrap:
|
|
ifneq ($(wildcard src/),)
|
|
$(error Error: src/ directory already exists)
|
|
endif
|
|
$(call render_template,bs_Makefile,Makefile)
|
|
@mkdir src/
|
|
$(call render_template,bs_appsrc,src/$(PROJECT).app.src)
|
|
$(call render_template,bs_app,src/$(PROJECT)_app.erl)
|
|
$(eval n := $(PROJECT)_sup)
|
|
$(call render_template,tpl_supervisor,src/$(PROJECT)_sup.erl)
|
|
|
|
bootstrap-lib:
|
|
ifneq ($(wildcard src/),)
|
|
$(error Error: src/ directory already exists)
|
|
endif
|
|
$(call render_template,bs_Makefile,Makefile)
|
|
@mkdir src/
|
|
$(call render_template,bs_appsrc_lib,src/$(PROJECT).app.src)
|
|
|
|
bootstrap-rel:
|
|
ifneq ($(wildcard relx.config),)
|
|
$(error Error: relx.config already exists)
|
|
endif
|
|
ifneq ($(wildcard rel/),)
|
|
$(error Error: rel/ directory already exists)
|
|
endif
|
|
$(call render_template,bs_relx_config,relx.config)
|
|
@mkdir rel/
|
|
$(call render_template,bs_sys_config,rel/sys.config)
|
|
$(call render_template,bs_vm_args,rel/vm.args)
|
|
|
|
new:
|
|
ifeq ($(wildcard src/),)
|
|
$(error Error: src/ directory does not exist)
|
|
endif
|
|
ifndef t
|
|
$(error Usage: $(MAKE) new t=TEMPLATE n=NAME)
|
|
endif
|
|
ifndef tpl_$(t)
|
|
$(error Unknown template)
|
|
endif
|
|
ifndef n
|
|
$(error Usage: $(MAKE) new t=TEMPLATE n=NAME)
|
|
endif
|
|
$(call render_template,tpl_$(t),src/$(n).erl)
|
|
|
|
list-templates:
|
|
@echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
|
|
|
|
# Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
|
|
# This file is part of erlang.mk and subject to the terms of the ISC License.
|
|
|
|
.PHONY: clean-c_src distclean-c_src-env
|
|
|
|
# Configuration.
|
|
|
|
C_SRC_DIR ?= $(CURDIR)/c_src
|
|
C_SRC_ENV ?= $(C_SRC_DIR)/env.mk
|
|
C_SRC_OUTPUT ?= $(CURDIR)/priv/$(PROJECT).so
|
|
C_SRC_TYPE ?= shared
|
|
|
|
# System type and C compiler/flags.
|
|
|
|
ifeq ($(PLATFORM),darwin)
|
|
CC ?= cc
|
|
CFLAGS ?= -O3 -std=c99 -arch x86_64 -finline-functions -Wall -Wmissing-prototypes
|
|
CXXFLAGS ?= -O3 -arch x86_64 -finline-functions -Wall
|
|
LDFLAGS ?= -arch x86_64 -flat_namespace -undefined suppress
|
|
else ifeq ($(PLATFORM),freebsd)
|
|
CC ?= cc
|
|
CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
|
|
CXXFLAGS ?= -O3 -finline-functions -Wall
|
|
else ifeq ($(PLATFORM),linux)
|
|
CC ?= gcc
|
|
CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
|
|
CXXFLAGS ?= -O3 -finline-functions -Wall
|
|
endif
|
|
|
|
CFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR)
|
|
CXXFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR)
|
|
|
|
LDLIBS += -L $(ERL_INTERFACE_LIB_DIR) -lerl_interface -lei
|
|
|
|
ifeq ($(C_SRC_TYPE),shared)
|
|
LDFLAGS += -shared
|
|
endif
|
|
|
|
# Verbosity.
|
|
|
|
c_verbose_0 = @echo " C " $(?F);
|
|
c_verbose = $(c_verbose_$(V))
|
|
|
|
cpp_verbose_0 = @echo " CPP " $(?F);
|
|
cpp_verbose = $(cpp_verbose_$(V))
|
|
|
|
link_verbose_0 = @echo " LD " $(@F);
|
|
link_verbose = $(link_verbose_$(V))
|
|
|
|
# Targets.
|
|
|
|
ifeq ($(wildcard $(C_SRC_DIR)),)
|
|
else ifneq ($(wildcard $(C_SRC_DIR)/Makefile),)
|
|
app:: app-c_src
|
|
|
|
test-build:: app-c_src
|
|
|
|
app-c_src:
|
|
$(MAKE) -C $(C_SRC_DIR)
|
|
|
|
clean::
|
|
$(MAKE) -C $(C_SRC_DIR) clean
|
|
|
|
else
|
|
|
|
ifeq ($(SOURCES),)
|
|
SOURCES := $(shell find $(C_SRC_DIR) -type f \( -name "*.c" -o -name "*.C" -o -name "*.cc" -o -name "*.cpp" \))
|
|
endif
|
|
OBJECTS = $(addsuffix .o, $(basename $(SOURCES)))
|
|
|
|
COMPILE_C = $(c_verbose) $(CC) $(CFLAGS) $(CPPFLAGS) -c
|
|
COMPILE_CPP = $(cpp_verbose) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c
|
|
|
|
app:: $(C_SRC_ENV) $(C_SRC_OUTPUT)
|
|
|
|
test-build:: $(C_SRC_ENV) $(C_SRC_OUTPUT)
|
|
|
|
$(C_SRC_OUTPUT): $(OBJECTS)
|
|
@mkdir -p priv/
|
|
$(link_verbose) $(CC) $(OBJECTS) $(LDFLAGS) $(LDLIBS) -o $(C_SRC_OUTPUT)
|
|
|
|
%.o: %.c
|
|
$(COMPILE_C) $(OUTPUT_OPTION) $<
|
|
|
|
%.o: %.cc
|
|
$(COMPILE_CPP) $(OUTPUT_OPTION) $<
|
|
|
|
%.o: %.C
|
|
$(COMPILE_CPP) $(OUTPUT_OPTION) $<
|
|
|
|
%.o: %.cpp
|
|
$(COMPILE_CPP) $(OUTPUT_OPTION) $<
|
|
|
|
clean:: clean-c_src
|
|
|
|
clean-c_src:
|
|
$(gen_verbose) rm -f $(C_SRC_OUTPUT) $(OBJECTS)
|
|
|
|
endif
|
|
|
|
ifneq ($(wildcard $(C_SRC_DIR)),)
|
|
$(C_SRC_ENV):
|
|
@$(ERL) -eval "file:write_file(\"$(C_SRC_ENV)\", \
|
|
io_lib:format( \
|
|
\"ERTS_INCLUDE_DIR ?= ~s/erts-~s/include/~n\" \
|
|
\"ERL_INTERFACE_INCLUDE_DIR ?= ~s~n\" \
|
|
\"ERL_INTERFACE_LIB_DIR ?= ~s~n\", \
|
|
[code:root_dir(), erlang:system_info(version), \
|
|
code:lib_dir(erl_interface, include), \
|
|
code:lib_dir(erl_interface, lib)])), \
|
|
halt()."
|
|
|
|
distclean:: distclean-c_src-env
|
|
|
|
distclean-c_src-env:
|
|
$(gen_verbose) rm -f $(C_SRC_ENV)
|
|
|
|
-include $(C_SRC_ENV)
|
|
endif
|
|
|
|
# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
|
|
# This file is part of erlang.mk and subject to the terms of the ISC License.
|
|
|
|
.PHONY: ci ci-setup distclean-kerl
|
|
|
|
KERL ?= $(CURDIR)/kerl
|
|
export KERL
|
|
|
|
KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
|
|
|
|
OTP_GIT ?= https://github.com/erlang/otp
|
|
|
|
CI_INSTALL_DIR ?= $(HOME)/erlang
|
|
CI_OTP ?=
|
|
|
|
ifeq ($(strip $(CI_OTP)),)
|
|
ci::
|
|
else
|
|
ci:: $(KERL) $(addprefix ci-,$(CI_OTP))
|
|
|
|
ci-setup::
|
|
|
|
ci_verbose_0 = @echo " CI " $(1);
|
|
ci_verbose = $(ci_verbose_$(V))
|
|
|
|
define ci_target
|
|
ci-$(1): $(CI_INSTALL_DIR)/$(1)
|
|
-$(ci_verbose) \
|
|
PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
|
|
CI_OTP_RELEASE="$(1)" \
|
|
CT_OPTS="-label $(1)" \
|
|
$(MAKE) clean ci-setup tests
|
|
endef
|
|
|
|
$(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
|
|
|
|
define ci_otp_target
|
|
$(CI_INSTALL_DIR)/$(1):
|
|
$(KERL) build git $(OTP_GIT) $(1) $(1)
|
|
$(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
|
|
endef
|
|
|
|
$(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
|
|
|
|
define kerl_fetch
|
|
$(call core_http_get,$(KERL),$(KERL_URL))
|
|
chmod +x $(KERL)
|
|
endef
|
|
|
|
$(KERL):
|
|
@$(call kerl_fetch)
|
|
|
|
help::
|
|
@printf "%s\n" "" \
|
|
"Continuous Integration targets:" \
|
|
" ci Run '$(MAKE) tests' on all configured Erlang versions." \
|
|
"" \
|
|
"The CI_OTP variable must be defined with the Erlang versions" \
|
|
"that must be tested. For example: CI_OTP = OTP-17.3.4 OTP-17.5.3"
|
|
|
|
distclean:: distclean-kerl
|
|
|
|
distclean-kerl:
|
|
$(gen_verbose) rm -rf $(KERL)
|
|
endif
|
|
|
|
# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
|
|
# This file is part of erlang.mk and subject to the terms of the ISC License.
|
|
|
|
.PHONY: ct distclean-ct
|
|
|
|
# Configuration.
|
|
|
|
CT_OPTS ?=
|
|
ifneq ($(wildcard $(TEST_DIR)),)
|
|
CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(shell find $(TEST_DIR) -type f -name \*_SUITE.erl -exec basename {} \;)))
|
|
else
|
|
CT_SUITES ?=
|
|
endif
|
|
|
|
# Core targets.
|
|
|
|
tests:: ct
|
|
|
|
distclean:: distclean-ct
|
|
|
|
help::
|
|
@printf "%s\n" "" \
|
|
"Common_test targets:" \
|
|
" ct Run all the common_test suites for this project" \
|
|
"" \
|
|
"All your common_test suites have their associated targets." \
|
|
"A suite named http_SUITE can be ran using the ct-http target."
|
|
|
|
# Plugin-specific targets.
|
|
|
|
CT_RUN = ct_run \
|
|
-no_auto_compile \
|
|
-noinput \
|
|
-pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(TEST_DIR) \
|
|
-dir $(TEST_DIR) \
|
|
-logdir $(CURDIR)/logs
|
|
|
|
ifeq ($(CT_SUITES),)
|
|
ct:
|
|
else
|
|
ct: test-build
|
|
@mkdir -p $(CURDIR)/logs/
|
|
$(gen_verbose) $(CT_RUN) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
|
|
endif
|
|
|
|
define ct_suite_target
|
|
ct-$(1): test-build
|
|
@mkdir -p $(CURDIR)/logs/
|
|
$(gen_verbose) $(CT_RUN) -suite $(addsuffix _SUITE,$(1)) $(CT_OPTS)
|
|
endef
|
|
|
|
$(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
|
|
|
|
distclean-ct:
|
|
$(gen_verbose) rm -rf $(CURDIR)/logs/
|
|
|
|
# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
|
|
# This file is part of erlang.mk and subject to the terms of the ISC License.
|
|
|
|
.PHONY: plt distclean-plt dialyze
|
|
|
|
# Configuration.
|
|
|
|
DIALYZER_PLT ?= $(CURDIR)/.$(PROJECT).plt
|
|
export DIALYZER_PLT
|
|
|
|
PLT_APPS ?=
|
|
DIALYZER_DIRS ?= --src -r src
|
|
DIALYZER_OPTS ?= -Werror_handling -Wrace_conditions \
|
|
-Wunmatched_returns # -Wunderspecs
|
|
|
|
# Core targets.
|
|
|
|
check:: dialyze
|
|
|
|
distclean:: distclean-plt
|
|
|
|
help::
|
|
@printf "%s\n" "" \
|
|
"Dialyzer targets:" \
|
|
" plt Build a PLT file for this project" \
|
|
" dialyze Analyze the project using Dialyzer"
|
|
|
|
# Plugin-specific targets.
|
|
|
|
$(DIALYZER_PLT): deps app
|
|
@dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(ALL_DEPS_DIRS)
|
|
|
|
plt: $(DIALYZER_PLT)
|
|
|
|
distclean-plt:
|
|
$(gen_verbose) rm -f $(DIALYZER_PLT)
|
|
|
|
ifneq ($(wildcard $(DIALYZER_PLT)),)
|
|
dialyze:
|
|
else
|
|
dialyze: $(DIALYZER_PLT)
|
|
endif
|
|
@dialyzer --no_native $(DIALYZER_DIRS) $(DIALYZER_OPTS)
|
|
|
|
# Copyright (c) 2014, Juan Facorro <juan@inaka.net>
|
|
# This file is part of erlang.mk and subject to the terms of the ISC License.
|
|
|
|
.PHONY: elvis distclean-elvis
|
|
|
|
# Configuration.
|
|
|
|
ELVIS_CONFIG ?= $(CURDIR)/elvis.config
|
|
|
|
ELVIS ?= $(CURDIR)/elvis
|
|
export ELVIS
|
|
|
|
ELVIS_URL ?= https://github.com/inaka/elvis/releases/download/0.2.3/elvis
|
|
ELVIS_CONFIG_URL ?= https://github.com/inaka/elvis/releases/download/0.2.3/elvis.config
|
|
ELVIS_OPTS ?=
|
|
|
|
# Core targets.
|
|
|
|
help::
|
|
@printf "%s\n" "" \
|
|
"Elvis targets:" \
|
|
" elvis Run Elvis using the local elvis.config or download the default otherwise"
|
|
|
|
distclean:: distclean-elvis
|
|
|
|
# Plugin-specific targets.
|
|
|
|
$(ELVIS):
|
|
@$(call core_http_get,$(ELVIS),$(ELVIS_URL))
|
|
@chmod +x $(ELVIS)
|
|
|
|
$(ELVIS_CONFIG):
|
|
@$(call core_http_get,$(ELVIS_CONFIG),$(ELVIS_CONFIG_URL))
|
|
|
|
elvis: $(ELVIS) $(ELVIS_CONFIG)
|
|
@$(ELVIS) rock -c $(ELVIS_CONFIG) $(ELVIS_OPTS)
|
|
|
|
distclean-elvis:
|
|
$(gen_verbose) rm -rf $(ELVIS)
|
|
|
|
# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
|
|
# This file is part of erlang.mk and subject to the terms of the ISC License.
|
|
|
|
# Configuration.
|
|
|
|
DTL_FULL_PATH ?= 0
|
|
|
|
# Verbosity.
|
|
|
|
dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
|
|
dtl_verbose = $(dtl_verbose_$(V))
|
|
|
|
# Core targets.
|
|
|
|
define compile_erlydtl
|
|
$(dtl_verbose) $(ERL) -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/ -eval ' \
|
|
Compile = fun(F) -> \
|
|
S = fun (1) -> re:replace(filename:rootname(string:sub_string(F, 11), ".dtl"), "/", "_", [{return, list}, global]); \
|
|
(0) -> filename:basename(F, ".dtl") \
|
|
end, \
|
|
Module = list_to_atom(string:to_lower(S($(DTL_FULL_PATH))) ++ "_dtl"), \
|
|
{ok, _} = erlydtl:compile(F, Module, [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) \
|
|
end, \
|
|
_ = [Compile(F) || F <- string:tokens("$(1)", " ")], \
|
|
halt().'
|
|
endef
|
|
|
|
ifneq ($(wildcard src/),)
|
|
ebin/$(PROJECT).app:: $(shell find templates -type f -name \*.dtl 2>/dev/null)
|
|
$(if $(strip $?),$(call compile_erlydtl,$?))
|
|
endif
|
|
|
|
# Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
|
|
# This file is part of erlang.mk and subject to the terms of the ISC License.
|
|
|
|
.PHONY: distclean-escript escript
|
|
|
|
# Configuration.
|
|
|
|
ESCRIPT_NAME ?= $(PROJECT)
|
|
ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
|
|
|
|
ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
|
|
ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
|
|
ESCRIPT_EMU_ARGS ?= -pa . \
|
|
-sasl errlog_type error \
|
|
-escript main $(ESCRIPT_NAME)
|
|
ESCRIPT_SHEBANG ?= /usr/bin/env escript
|
|
ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
|
|
|
|
# Core targets.
|
|
|
|
distclean:: distclean-escript
|
|
|
|
help::
|
|
@printf "%s\n" "" \
|
|
"Escript targets:" \
|
|
" escript Build an executable escript archive" \
|
|
|
|
# Plugin-specific targets.
|
|
|
|
# Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
|
|
# Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
|
|
# Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
|
|
# Software may only be used for the great good and the true happiness of all
|
|
# sentient beings.
|
|
|
|
define ESCRIPT_RAW
|
|
'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
|
|
'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
|
|
' [F || F <- A, not filelib:is_dir(F) ] end,'\
|
|
'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
|
|
'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
|
|
'Ez = fun(Escript) ->'\
|
|
' Static = Files([$(ESCRIPT_STATIC)]),'\
|
|
' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
|
|
' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
|
|
' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
|
|
' {archive, Archive, [memory]},'\
|
|
' {shebang, "$(ESCRIPT_SHEBANG)"},'\
|
|
' {comment, "$(ESCRIPT_COMMENT)"},'\
|
|
' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
|
|
' ]),'\
|
|
' file:change_mode(Escript, 8#755)'\
|
|
'end,'\
|
|
'Ez("$(ESCRIPT_NAME)"),'\
|
|
'halt().'
|
|
endef
|
|
|
|
ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
|
|
|
|
escript:: distclean-escript deps app
|
|
$(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
|
|
|
|
distclean-escript:
|
|
$(gen_verbose) rm -f $(ESCRIPT_NAME)
|
|
|
|
# Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
|
|
# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
|
|
# This file is contributed to erlang.mk and subject to the terms of the ISC License.
|
|
|
|
.PHONY: eunit
|
|
|
|
# Configuration
|
|
|
|
# All modules in TEST_DIR
|
|
ifeq ($(strip $(TEST_DIR)),)
|
|
TEST_DIR_MODS =
|
|
else
|
|
TEST_DIR_MODS = $(notdir $(basename $(shell find $(TEST_DIR) -type f -name *.beam)))
|
|
endif
|
|
|
|
# All modules in 'ebin'
|
|
EUNIT_EBIN_MODS = $(notdir $(basename $(shell find ebin -type f -name *.beam)))
|
|
# Only those modules in TEST_DIR with no matching module in 'ebin'.
|
|
# This is done to avoid some tests being executed twice.
|
|
EUNIT_MODS = $(filter-out $(patsubst %,%_tests,$(EUNIT_EBIN_MODS)),$(TEST_DIR_MODS))
|
|
TAGGED_EUNIT_TESTS = $(foreach mod,$(EUNIT_EBIN_MODS) $(EUNIT_MODS),{module,$(mod)})
|
|
|
|
EUNIT_OPTS ?=
|
|
|
|
# Utility functions
|
|
|
|
define str-join
|
|
$(shell echo '$(strip $(1))' | sed -e "s/ /,/g")
|
|
endef
|
|
|
|
# Core targets.
|
|
|
|
tests:: eunit
|
|
|
|
help::
|
|
@printf "%s\n" "" \
|
|
"EUnit targets:" \
|
|
" eunit Run all the EUnit tests for this project"
|
|
|
|
# Plugin-specific targets.
|
|
|
|
EUNIT_RUN_BEFORE ?=
|
|
EUNIT_RUN_AFTER ?=
|
|
EUNIT_RUN = $(ERL) \
|
|
-pa $(TEST_DIR) $(DEPS_DIR)/*/ebin \
|
|
-pz ebin \
|
|
$(EUNIT_RUN_BEFORE) \
|
|
-eval 'case eunit:test([$(call str-join,$(TAGGED_EUNIT_TESTS))],\
|
|
[$(EUNIT_OPTS)]) of ok -> ok; error -> halt(1) end.' \
|
|
$(EUNIT_RUN_AFTER) \
|
|
-eval 'halt(0).'
|
|
|
|
eunit: test-build
|
|
$(gen_verbose) $(EUNIT_RUN)
|
|
|
|
# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
|
|
# This file is part of erlang.mk and subject to the terms of the ISC License.
|
|
|
|
.PHONY: relx-rel distclean-relx-rel distclean-relx run
|
|
|
|
# Configuration.
|
|
|
|
RELX_CONFIG ?= $(CURDIR)/relx.config
|
|
|
|
RELX ?= $(CURDIR)/relx
|
|
export RELX
|
|
|
|
RELX_URL ?= https://github.com/erlware/relx/releases/download/v2.0.0/relx
|
|
RELX_OPTS ?=
|
|
RELX_OUTPUT_DIR ?= _rel
|
|
|
|
ifeq ($(firstword $(RELX_OPTS)),-o)
|
|
RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
|
|
else
|
|
RELX_OPTS += -o $(RELX_OUTPUT_DIR)
|
|
endif
|
|
|
|
# Core targets.
|
|
|
|
ifeq ($(IS_DEP),)
|
|
ifneq ($(wildcard $(RELX_CONFIG)),)
|
|
rel:: distclean-relx-rel relx-rel
|
|
endif
|
|
endif
|
|
|
|
distclean:: distclean-relx-rel distclean-relx
|
|
|
|
# Plugin-specific targets.
|
|
|
|
define relx_fetch
|
|
$(call core_http_get,$(RELX),$(RELX_URL))
|
|
chmod +x $(RELX)
|
|
endef
|
|
|
|
$(RELX):
|
|
@$(call relx_fetch)
|
|
|
|
relx-rel: $(RELX)
|
|
@$(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
|
|
|
|
distclean-relx-rel:
|
|
$(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
|
|
|
|
distclean-relx:
|
|
$(gen_verbose) rm -rf $(RELX)
|
|
|
|
# Run target.
|
|
|
|
ifeq ($(wildcard $(RELX_CONFIG)),)
|
|
run:
|
|
else
|
|
|
|
define get_relx_release.erl
|
|
{ok, Config} = file:consult("$(RELX_CONFIG)"),
|
|
{release, {Name, _}, _} = lists:keyfind(release, 1, Config),
|
|
io:format("~s", [Name]),
|
|
halt(0).
|
|
endef
|
|
|
|
RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
|
|
|
|
run: all
|
|
@$(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
|
|
|
|
help::
|
|
@printf "%s\n" "" \
|
|
"Relx targets:" \
|
|
" run Compile the project, build the release and run it"
|
|
|
|
endif
|
|
|
|
# Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
|
|
# This file is contributed to erlang.mk and subject to the terms of the ISC License.
|
|
|
|
.PHONY: shell
|
|
|
|
# Configuration.
|
|
|
|
SHELL_PATH ?= -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin
|
|
SHELL_OPTS ?=
|
|
|
|
ALL_SHELL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(SHELL_DEPS))
|
|
|
|
# Core targets
|
|
|
|
help::
|
|
@printf "%s\n" "" \
|
|
"Shell targets:" \
|
|
" shell Run an erlang shell with SHELL_OPTS or reasonable default"
|
|
|
|
# Plugin-specific targets.
|
|
|
|
$(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
|
|
|
|
build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
|
|
@for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
|
|
|
|
shell: build-shell-deps
|
|
$(gen_verbose) erl $(SHELL_PATH) $(SHELL_OPTS)
|
|
|
|
# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
|
|
# This file is part of erlang.mk and subject to the terms of the ISC License.
|
|
|
|
ifneq ($(wildcard $(DEPS_DIR)/triq),)
|
|
.PHONY: triq
|
|
|
|
# Targets.
|
|
|
|
tests:: triq
|
|
|
|
define triq_check.erl
|
|
code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
|
|
try
|
|
case $(1) of
|
|
all -> [true] =:= lists:usort([triq:check(M) || M <- [$(MODULES)]]);
|
|
module -> triq:check($(2));
|
|
function -> triq:check($(2))
|
|
end
|
|
of
|
|
true -> halt(0);
|
|
_ -> halt(1)
|
|
catch error:undef ->
|
|
io:format("Undefined property or module~n"),
|
|
halt(0)
|
|
end.
|
|
endef
|
|
|
|
ifdef t
|
|
ifeq (,$(findstring :,$(t)))
|
|
triq: test-build
|
|
@$(call erlang,$(call triq_check.erl,module,$(t)))
|
|
else
|
|
triq: test-build
|
|
@echo Testing $(t)/0
|
|
@$(call erlang,$(call triq_check.erl,function,$(t)()))
|
|
endif
|
|
else
|
|
triq: test-build
|
|
$(eval MODULES := $(shell find ebin -type f -name \*.beam \
|
|
| sed "s/ebin\//'/;s/\.beam/',/" | sed '$$s/.$$//'))
|
|
$(gen_verbose) $(call erlang,$(call triq_check.erl,all,undefined))
|
|
endif
|
|
endif
|
|
|
|
# Copyright (c) 2015, Euen Lopez <euen@inakanetworks.com>
|
|
# This file is part of erlang.mk and subject to the terms of the ISC License.
|
|
|
|
.PHONY: xref distclean-xref
|
|
|
|
# Configuration.
|
|
|
|
ifeq ($(XREF_CONFIG),)
|
|
XREF_ARGS :=
|
|
else
|
|
XREF_ARGS := -c $(XREF_CONFIG)
|
|
endif
|
|
|
|
XREFR ?= $(CURDIR)/xrefr
|
|
export XREFR
|
|
|
|
XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.0/xrefr
|
|
|
|
# Core targets.
|
|
|
|
help::
|
|
@printf "%s\n" "" \
|
|
"Xref targets:" \
|
|
" xref Run Xrefr using $XREF_CONFIG as config file if defined"
|
|
|
|
distclean:: distclean-xref
|
|
|
|
# Plugin-specific targets.
|
|
|
|
$(XREFR):
|
|
@$(call core_http_get,$(XREFR),$(XREFR_URL))
|
|
@chmod +x $(XREFR)
|
|
|
|
xref: deps app $(XREFR)
|
|
$(gen_verbose) $(XREFR) $(XREFR_ARGS)
|
|
|
|
distclean-xref:
|
|
$(gen_verbose) rm -rf $(XREFR)
|
|
|
|
# Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
|
|
# This file is part of erlang.mk and subject to the terms of the ISC License.
|
|
|
|
COVER_REPORT_DIR = cover
|
|
|
|
# utility variables for representing special symbols
|
|
empty :=
|
|
space := $(empty) $(empty)
|
|
comma := ,
|
|
|
|
# Hook in coverage to eunit
|
|
|
|
ifdef COVER
|
|
ifdef EUNIT_RUN
|
|
EUNIT_RUN_BEFORE += -eval \
|
|
'case cover:compile_beam_directory("ebin") of \
|
|
{error, _} -> halt(1); \
|
|
_ -> ok \
|
|
end.'
|
|
EUNIT_RUN_AFTER += -eval 'cover:export("eunit.coverdata").'
|
|
endif
|
|
endif
|
|
|
|
# Hook in coverage to ct
|
|
|
|
ifdef COVER
|
|
ifdef CT_RUN
|
|
|
|
# All modules in 'ebin'
|
|
COVER_MODS = $(notdir $(basename $(shell echo ebin/*.beam)))
|
|
|
|
test-build:: $(TEST_DIR)/ct.cover.spec
|
|
|
|
$(TEST_DIR)/ct.cover.spec:
|
|
@echo Cover mods: $(COVER_MODS)
|
|
$(gen_verbose) printf "%s\n" \
|
|
'{incl_mods,[$(subst $(space),$(comma),$(COVER_MODS))]}.' \
|
|
'{export,"$(CURDIR)/ct.coverdata"}.' > $@
|
|
|
|
CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
|
|
endif
|
|
endif
|
|
|
|
# Core targets
|
|
|
|
ifdef COVER
|
|
ifneq ($(COVER_REPORT_DIR),)
|
|
tests::
|
|
@$(MAKE) --no-print-directory cover-report
|
|
endif
|
|
endif
|
|
|
|
clean:: coverdata-clean
|
|
|
|
ifneq ($(COVER_REPORT_DIR),)
|
|
distclean:: cover-report-clean
|
|
endif
|
|
|
|
help::
|
|
@printf "%s\n" "" \
|
|
"Cover targets:" \
|
|
" cover-report Generate a HTML coverage report from previously collected" \
|
|
" cover data." \
|
|
" all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
|
|
"" \
|
|
"If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
|
|
"target tests additionally generates a HTML coverage report from the combined" \
|
|
"coverdata files from each of these testing tools. HTML reports can be disabled" \
|
|
"by setting COVER_REPORT_DIR to empty."
|
|
|
|
# Plugin specific targets
|
|
|
|
COVERDATA = $(filter-out all.coverdata,$(wildcard *.coverdata))
|
|
|
|
.PHONY: coverdata-clean
|
|
coverdata-clean:
|
|
$(gen_verbose) rm -f *.coverdata ct.cover.spec
|
|
|
|
# Merge all coverdata files into one.
|
|
all.coverdata: $(COVERDATA)
|
|
$(gen_verbose) $(ERL) -eval ' \
|
|
$(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
|
|
cover:export("$@"), halt(0).'
|
|
|
|
# These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
|
|
# empty if you want the coverdata files but not the HTML report.
|
|
ifneq ($(COVER_REPORT_DIR),)
|
|
|
|
.PHONY: cover-report-clean cover-report
|
|
|
|
cover-report-clean:
|
|
$(gen_verbose) rm -rf $(COVER_REPORT_DIR)
|
|
|
|
ifeq ($(COVERDATA),)
|
|
cover-report:
|
|
else
|
|
|
|
# Modules which include eunit.hrl always contain one line without coverage
|
|
# because eunit defines test/0 which is never called. We compensate for this.
|
|
EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
|
|
grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
|
|
| sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
|
|
|
|
define cover_report.erl
|
|
$(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
|
|
Ms = cover:imported_modules(),
|
|
[cover:analyse_to_file(M, "$(COVER_REPORT_DIR)/" ++ atom_to_list(M)
|
|
++ ".COVER.html", [html]) || M <- Ms],
|
|
Report = [begin {ok, R} = cover:analyse(M, module), R end || M <- Ms],
|
|
EunitHrlMods = [$(EUNIT_HRL_MODS)],
|
|
Report1 = [{M, {Y, case lists:member(M, EunitHrlMods) of
|
|
true -> N - 1; false -> N end}} || {M, {Y, N}} <- Report],
|
|
TotalY = lists:sum([Y || {_, {Y, _}} <- Report1]),
|
|
TotalN = lists:sum([N || {_, {_, N}} <- Report1]),
|
|
TotalPerc = round(100 * TotalY / (TotalY + TotalN)),
|
|
{ok, F} = file:open("$(COVER_REPORT_DIR)/index.html", [write]),
|
|
io:format(F, "<!DOCTYPE html><html>~n"
|
|
"<head><meta charset=\"UTF-8\">~n"
|
|
"<title>Coverage report</title></head>~n"
|
|
"<body>~n", []),
|
|
io:format(F, "<h1>Coverage</h1>~n<p>Total: ~p%</p>~n", [TotalPerc]),
|
|
io:format(F, "<table><tr><th>Module</th><th>Coverage</th></tr>~n", []),
|
|
[io:format(F, "<tr><td><a href=\"~p.COVER.html\">~p</a></td>"
|
|
"<td>~p%</td></tr>~n",
|
|
[M, M, round(100 * Y / (Y + N))]) || {M, {Y, N}} <- Report1],
|
|
How = "$(subst $(space),$(comma)$(space),$(basename $(COVERDATA)))",
|
|
Date = "$(shell date -u "+%Y-%m-%dT%H:%M:%SZ")",
|
|
io:format(F, "</table>~n"
|
|
"<p>Generated using ~s and erlang.mk on ~s.</p>~n"
|
|
"</body></html>", [How, Date]),
|
|
halt().
|
|
endef
|
|
|
|
cover-report:
|
|
$(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
|
|
$(gen_verbose) $(call erlang,$(cover_report.erl))
|
|
|
|
endif
|
|
endif # ifneq ($(COVER_REPORT_DIR),)
|