aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--CHANGELOG.asciidoc8
-rw-r--r--Makefile11
-rw-r--r--build.config3
-rw-r--r--doc/src/guide/external_plugins.asciidoc28
-rw-r--r--doc/src/guide/getting_started.asciidoc14
-rw-r--r--plugins/bootstrap.mk473
-rw-r--r--templates/application.app.src13
-rw-r--r--templates/application.erl11
-rw-r--r--templates/apps_Makefile10
-rw-r--r--templates/cowboy_http_h.erl19
-rw-r--r--templates/cowboy_loop_h.erl18
-rw-r--r--templates/cowboy_rest_h.erl14
-rw-r--r--templates/cowboy_websocket_h.erl31
-rw-r--r--templates/gen_fsm.erl50
-rw-r--r--templates/gen_server.erl42
-rw-r--r--templates/gen_statem.erl42
-rw-r--r--templates/library.app.src11
-rw-r--r--templates/module.erl2
-rw-r--r--templates/ranch_protocol.erl25
-rw-r--r--templates/relx.config6
-rw-r--r--templates/supervisor.erl12
-rw-r--r--templates/sys.config2
-rw-r--r--templates/top_Makefile5
-rw-r--r--templates/vm.args3
-rw-r--r--test/core_plugins.mk39
-rw-r--r--test/core_upgrade.mk2
-rw-r--r--test/plugin_bootstrap.mk18
28 files changed, 488 insertions, 425 deletions
diff --git a/.gitignore b/.gitignore
index 9f6d960..206aa1f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
doc/guide.pdf
doc/html
+templates.mk
test/logs/
test/packages/
test/test_hex_core_git/
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 83cdf68..ea10ada 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -47,3 +47,11 @@
without an explicit fetch method). Adding "git"
at the beginning of the dep line is the modern
equivalent.
+
+2024/11/22: Templates no longer use Make variables for
+ substitution. Instead the strings template_name
+ and project_name get replaced by their equivalent.
+ Note that using variables in your own templates
+ should still work, but that this way of doing
+ things is deprecated. This change makes it
+ possible to have templates outside of Makefiles.
diff --git a/Makefile b/Makefile
index f6d4287..b69bfb8 100644
--- a/Makefile
+++ b/Makefile
@@ -29,7 +29,7 @@ ERLANG_MK_VERSION = $(shell git describe --dirty --tags --always)
.PHONY: all check
-all:
+all: templates.mk
# Temporarily force the printing of the CHANGELOG.
# The required variable hadn't been introduced yet.
#ifdef UPGRADE
@@ -40,6 +40,15 @@ all:
| sed 's/^ERLANG_MK_VERSION =.*/ERLANG_MK_VERSION = $(ERLANG_MK_VERSION)/' \
| sed 's:^ERLANG_MK_WITHOUT =.*:ERLANG_MK_WITHOUT = $(WITHOUT):' > $(ERLANG_MK)
+# Templates that end with .erl have the suffix removed. It is implied.
+templates.mk: Makefile templates/*
+ for f in templates/*; do \
+ echo define tpl_`basename $$f .erl`; \
+ cat $$f; \
+ echo endef; \
+ echo; \
+ done > $@
+
lint: all
$(MAKE) -f erlang.mk --warn-undefined-variables
diff --git a/build.config b/build.config
index 5ef60f8..5f1c193 100644
--- a/build.config
+++ b/build.config
@@ -20,6 +20,9 @@ core/rel
core/test
core/compat
+# Built-in templates.
+templates
+
# Plugins.
plugins/asciidoc
plugins/bootstrap
diff --git a/doc/src/guide/external_plugins.asciidoc b/doc/src/guide/external_plugins.asciidoc
index 5f165f5..bb42f1a 100644
--- a/doc/src/guide/external_plugins.asciidoc
+++ b/doc/src/guide/external_plugins.asciidoc
@@ -90,7 +90,7 @@ help-plugins::
Plugins declared in `DEP_PLUGINS` are loaded near the end of Erlang.mk.
That's why you have access to all previously initialized variables.
However, if you want your plugin to add common dependencies to
-your applications, a regular is loaded too late in the process.
+your applications, a regular plugin is loaded too late in the process.
You need to use "Early-stage plugins". They are declared using the
`DEP_EARLY_PLUGINS` variable instead. Plugins listed in this variable
are loaded near the beginning of Erlang.mk Otherwise, they work exactly
@@ -111,7 +111,7 @@ DEPS += cowboy
TEST_DEPS = ct_helper
dep_ct_helper = git https://github.com/ninenines/ct_helper master
-=== Loading plugins local to the application
+=== Loading plugins local to the application
If the Erlang.mk plugin lives in the same directory or repository as your
application or library, then you can load it exactly like an external
@@ -138,3 +138,27 @@ your application:
DEP_EARLY_PLUGINS = $(PROJECT)
# Loads ./plugins.mk
DEP_PLUGINS = $(PROJECT)
+
+=== Adding templates via plugins
+
+Plugins may add templates either from within their Makefile or from
+an external file. The recommended method is to use an external file;
+however do note it only works for Make 4 and above:
+
+[source,make]
+THIS := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))
+tpl_test_mk = $(file < $(THIS)/templates/my_template.erl)
+
+With 'templates/my_template.erl' containing:
+
+[source,erlang]
+-module(template_name).
+
+Erlang.mk will do string substitution replacing the following
+strings with their equivalent:
+
+* `template_name`: the name provided by the user
+* `project_name`: either `$(PROJECT)` or the target application's name
+* `template_sp`: internal; propagates whitespace settings to Makefiles
+* `rel_deps_dir`: internal; path to deps/* from within an apps/* Makefile
+* `rel_root_dir`: internal; path to top-level directory from within an apps/* Makefile
diff --git a/doc/src/guide/getting_started.asciidoc b/doc/src/guide/getting_started.asciidoc
index 5f782ca..c1e7589 100644
--- a/doc/src/guide/getting_started.asciidoc
+++ b/doc/src/guide/getting_started.asciidoc
@@ -271,8 +271,20 @@ You can list all available templates with the `list-templates`
target:
[source,bash]
+----
$ make list-templates
-Available templates: cowboy_http cowboy_loop cowboy_rest cowboy_ws gen_fsm gen_server gen_statem ranch_protocol supervisor
+Available templates:
+ cowboy_http_h
+ cowboy_loop_h
+ cowboy_rest_h
+ cowboy_websocket_h
+ gen_fsm
+ gen_server
+ gen_statem
+ module
+ ranch_protocol
+ supervisor
+----
To generate a module, let's say a `gen_server`, all you need to
do is to call `make new` with the appropriate arguments:
diff --git a/plugins/bootstrap.mk b/plugins/bootstrap.mk
index 3ece184..5c3610a 100644
--- a/plugins/bootstrap.mk
+++ b/plugins/bootstrap.mk
@@ -17,387 +17,6 @@ help::
" new t=T n=N in=APP Generate a module NAME based on the template TPL in APP" \
" list-templates List available templates"
-# Bootstrap templates.
-
-define bs_appsrc
-{application, $p, [
- {description, ""},
- {vsn, "0.1.0"},
- {id, "git"},
- {modules, []},
- {registered, []},
- {applications, [
- kernel,
- stdlib
- ]},
- {mod, {$p_app, []}},
- {env, []}
-]}.
-endef
-
-define bs_appsrc_lib
-{application, $p, [
- {description, ""},
- {vsn, "0.1.0"},
- {id, "git"},
- {modules, []},
- {registered, []},
- {applications, [
- kernel,
- stdlib
- ]}
-]}.
-endef
-
-# To prevent autocompletion issues with ZSH, we add "include erlang.mk"
-# separately during the actual bootstrap.
-define bs_Makefile
-PROJECT = $p
-PROJECT_DESCRIPTION = New project
-PROJECT_VERSION = 0.1.0
-$(if $(SP),
-# Whitespace to be used when creating files from templates.
-SP = $(SP)
-)
-endef
-
-define bs_apps_Makefile
-PROJECT = $p
-PROJECT_DESCRIPTION = New project
-PROJECT_VERSION = 0.1.0
-$(if $(SP),
-# Whitespace to be used when creating files from templates.
-SP = $(SP)
-)
-# Make sure we know where the applications are located.
-ROOT_DIR ?= $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)
-APPS_DIR ?= ..
-DEPS_DIR ?= $(call core_relpath,$(DEPS_DIR),$(APPS_DIR)/app)
-
-include $$(ROOT_DIR)/erlang.mk
-endef
-
-define bs_app
--module($p_app).
--behaviour(application).
-
--export([start/2]).
--export([stop/1]).
-
-start(_Type, _Args) ->
- $p_sup:start_link().
-
-stop(_State) ->
- ok.
-endef
-
-define bs_relx_config
-{release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
-{dev_mode, false}.
-{include_erts, true}.
-{extended_start_script, true}.
-{sys_config, "config/sys.config"}.
-{vm_args, "config/vm.args"}.
-endef
-
-define bs_sys_config
-[
-].
-endef
-
-define bs_vm_args
--setcookie $p
--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_module
--module($(n)).
--export([]).
-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_gen_statem
--module($(n)).
--behaviour(gen_statem).
-
-%% API.
--export([start_link/0]).
-
-%% gen_statem.
--export([callback_mode/0]).
--export([init/1]).
--export([state_name/3]).
--export([handle_event/4]).
--export([terminate/3]).
--export([code_change/4]).
-
--record(state, {
-}).
-
-%% API.
-
--spec start_link() -> {ok, pid()}.
-start_link() ->
- gen_statem:start_link(?MODULE, [], []).
-
-%% gen_statem.
-
-callback_mode() ->
- state_functions.
-
-init([]) ->
- {ok, state_name, #state{}}.
-
-state_name(_EventType, _EventData, StateData) ->
- {next_state, state_name, StateData}.
-
-handle_event(_EventType, _EventData, 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.
ifndef WS
@@ -408,6 +27,26 @@ WS = $(tab)
endif
endif
+ifdef SP
+define template_sp
+
+# By default templates indent with a single tab per indentation
+# level. Set this variable to the number of spaces you prefer:
+SP = $(SP)
+
+endef
+else
+template_sp =
+endif
+
+# @todo Additional template placeholders could be added.
+subst_template = $(subst rel_root_dir,$(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app),$(subst rel_deps_dir,$(call core_relpath,$(DEPS_DIR),$(APPS_DIR)/app),$(subst template_sp,$(template_sp),$(subst project_name,$p,$(subst template_name,$n,$1)))))
+
+define core_render_template
+ $(eval define _tpl_$(1)$(newline)$(call subst_template,$(tpl_$(1)))$(newline)endef)
+ $(verbose) $(call core_render,_tpl_$(1),$2)
+endef
+
bootstrap:
ifneq ($(wildcard src/),)
$(error Error: src/ directory already exists)
@@ -416,14 +55,13 @@ endif
$(if $(shell echo $p | LC_ALL=C grep -x "[a-z0-9_]*"),,\
$(error Error: Invalid characters in the application name))
$(eval n := $(PROJECT)_sup)
- $(verbose) $(call core_render,bs_Makefile,Makefile)
- $(verbose) echo "include erlang.mk" >> Makefile
+ $(verbose) $(call core_render_template,top_Makefile,Makefile)
$(verbose) mkdir src/
ifdef LEGACY
- $(verbose) $(call core_render,bs_appsrc,src/$(PROJECT).app.src)
+ $(verbose) $(call core_render_template,application.app.src,src/$(PROJECT).app.src)
endif
- $(verbose) $(call core_render,bs_app,src/$(PROJECT)_app.erl)
- $(verbose) $(call core_render,tpl_supervisor,src/$(PROJECT)_sup.erl)
+ $(verbose) $(call core_render_template,application,src/$(PROJECT)_app.erl)
+ $(verbose) $(call core_render_template,supervisor,src/$(PROJECT)_sup.erl)
bootstrap-lib:
ifneq ($(wildcard src/),)
@@ -432,11 +70,10 @@ endif
$(eval p := $(PROJECT))
$(if $(shell echo $p | LC_ALL=C grep -x "[a-z0-9_]*"),,\
$(error Error: Invalid characters in the application name))
- $(verbose) $(call core_render,bs_Makefile,Makefile)
- $(verbose) echo "include erlang.mk" >> Makefile
+ $(verbose) $(call core_render_template,top_Makefile,Makefile)
$(verbose) mkdir src/
ifdef LEGACY
- $(verbose) $(call core_render,bs_appsrc_lib,src/$(PROJECT).app.src)
+ $(verbose) $(call core_render_template,library.app.src,src/$(PROJECT).app.src)
endif
bootstrap-rel:
@@ -447,10 +84,10 @@ ifneq ($(wildcard config/),)
$(error Error: config/ directory already exists)
endif
$(eval p := $(PROJECT))
- $(verbose) $(call core_render,bs_relx_config,relx.config)
+ $(verbose) $(call core_render_template,relx.config,relx.config)
$(verbose) mkdir config/
- $(verbose) $(call core_render,bs_sys_config,config/sys.config)
- $(verbose) $(call core_render,bs_vm_args,config/vm.args)
+ $(verbose) $(call core_render_template,sys.config,config/sys.config)
+ $(verbose) $(call core_render_template,vm.args,config/vm.args)
$(verbose) awk '/^include erlang.mk/ && !ins {print "REL_DEPS += relx";ins=1};{print}' Makefile > Makefile.bak
$(verbose) mv Makefile.bak Makefile
@@ -466,12 +103,12 @@ endif
$(error Error: Invalid characters in the application name))
$(eval n := $(in)_sup)
$(verbose) mkdir -p $(APPS_DIR)/$p/src/
- $(verbose) $(call core_render,bs_apps_Makefile,$(APPS_DIR)/$p/Makefile)
+ $(verbose) $(call core_render_template,apps_Makefile,$(APPS_DIR)/$p/Makefile)
ifdef LEGACY
- $(verbose) $(call core_render,bs_appsrc,$(APPS_DIR)/$p/src/$p.app.src)
+ $(verbose) $(call core_render_template,application.app.src,$(APPS_DIR)/$p/src/$p.app.src)
endif
- $(verbose) $(call core_render,bs_app,$(APPS_DIR)/$p/src/$p_app.erl)
- $(verbose) $(call core_render,tpl_supervisor,$(APPS_DIR)/$p/src/$p_sup.erl)
+ $(verbose) $(call core_render_template,application,$(APPS_DIR)/$p/src/$p_app.erl)
+ $(verbose) $(call core_render_template,supervisor,$(APPS_DIR)/$p/src/$p_sup.erl)
new-lib:
ifndef in
@@ -484,27 +121,37 @@ endif
$(if $(shell echo $p | LC_ALL=C grep -x "[a-z0-9_]*"),,\
$(error Error: Invalid characters in the application name))
$(verbose) mkdir -p $(APPS_DIR)/$p/src/
- $(verbose) $(call core_render,bs_apps_Makefile,$(APPS_DIR)/$p/Makefile)
+ $(verbose) $(call core_render_template,apps_Makefile,$(APPS_DIR)/$p/Makefile)
ifdef LEGACY
- $(verbose) $(call core_render,bs_appsrc_lib,$(APPS_DIR)/$p/src/$p.app.src)
+ $(verbose) $(call core_render_template,library.app.src,$(APPS_DIR)/$p/src/$p.app.src)
endif
+# These are not necessary because we don't expose those as "normal" templates.
+BOOTSTRAP_TEMPLATES = apps_Makefile top_Makefile \
+ application.app.src library.app.src application \
+ relx.config sys.config vm.args
+
+# Templates may override the path they will be written to when using 'new'.
+# Only special template paths must be listed. Default is src/template_name.erl
+# Substitution is also applied to the paths. Examples:
+#
+#tplp_top_Makefile = Makefile
+#tplp_application.app.src = src/project_name.app.src
+#tplp_application = src/project_name_app.erl
+#tplp_relx.config = relx.config
+
+# Erlang.mk bundles its own templates at build time into the erlang.mk file.
+
new:
-ifeq ($(wildcard src/)$(in),)
- $(error Error: src/ directory does not exist)
-endif
-ifndef t
- $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
-endif
-ifndef n
- $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
-endif
-ifdef in
- $(verbose) $(call core_render,tpl_$(t),$(APPS_DIR)/$(in)/src/$(n).erl)
-else
- $(verbose) $(call core_render,tpl_$(t),src/$(n).erl)
-endif
+ $(if $(t),,$(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP]))
+ $(if $(n),,$(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP]))
+ $(if $(tpl_$(t)),,$(error Error: $t template does not exist; try $(Make) list-templates))
+ $(eval dest := $(if $(in),$(APPS_DIR)/$(in)/)$(call subst_template,$(if $(tplp_$(t)),$(tplp_$(t)),src/template_name.erl)))
+ $(if $(wildcard $(dir $(dest))),,$(error Error: $(dir $(dest)) directory does not exist))
+ $(if $(wildcard $(dest)),$(error Error: The file $(dest) already exists))
+ $(eval p := $(PROJECT))
+ $(call core_render_template,$(t),$(dest))
list-templates:
$(verbose) @echo Available templates:
- $(verbose) printf " %s\n" $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
+ $(verbose) printf " %s\n" $(sort $(filter-out $(BOOTSTRAP_TEMPLATES),$(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES)))))
diff --git a/templates/application.app.src b/templates/application.app.src
new file mode 100644
index 0000000..3bd1d6b
--- /dev/null
+++ b/templates/application.app.src
@@ -0,0 +1,13 @@
+{application, project_name, [
+ {description, ""},
+ {vsn, "0.1.0"},
+ {id, "git"},
+ {modules, []},
+ {registered, []},
+ {applications, [
+ kernel,
+ stdlib
+ ]},
+ {mod, {project_name_app, []}},
+ {env, []}
+]}.
diff --git a/templates/application.erl b/templates/application.erl
new file mode 100644
index 0000000..2c8d27b
--- /dev/null
+++ b/templates/application.erl
@@ -0,0 +1,11 @@
+-module(project_name_app).
+-behaviour(application).
+
+-export([start/2]).
+-export([stop/1]).
+
+start(_Type, _Args) ->
+ project_name_sup:start_link().
+
+stop(_State) ->
+ ok.
diff --git a/templates/apps_Makefile b/templates/apps_Makefile
new file mode 100644
index 0000000..093287f
--- /dev/null
+++ b/templates/apps_Makefile
@@ -0,0 +1,10 @@
+PROJECT = project_name
+PROJECT_DESCRIPTION = New project
+PROJECT_VERSION = 0.1.0
+template_sp
+# Make sure we know where the applications are located.
+ROOT_DIR ?= rel_root_dir
+APPS_DIR ?= ..
+DEPS_DIR ?= rel_deps_dir
+
+include rel_root_dir/erlang.mk
diff --git a/templates/cowboy_http_h.erl b/templates/cowboy_http_h.erl
new file mode 100644
index 0000000..a2c3200
--- /dev/null
+++ b/templates/cowboy_http_h.erl
@@ -0,0 +1,19 @@
+-module(template_name).
+-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.
diff --git a/templates/cowboy_loop_h.erl b/templates/cowboy_loop_h.erl
new file mode 100644
index 0000000..ce301c9
--- /dev/null
+++ b/templates/cowboy_loop_h.erl
@@ -0,0 +1,18 @@
+-module(template_name).
+-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.
diff --git a/templates/cowboy_rest_h.erl b/templates/cowboy_rest_h.erl
new file mode 100644
index 0000000..a15f4a5
--- /dev/null
+++ b/templates/cowboy_rest_h.erl
@@ -0,0 +1,14 @@
+-module(template_name).
+
+-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}.
diff --git a/templates/cowboy_websocket_h.erl b/templates/cowboy_websocket_h.erl
new file mode 100644
index 0000000..538f020
--- /dev/null
+++ b/templates/cowboy_websocket_h.erl
@@ -0,0 +1,31 @@
+-module(template_name).
+-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.
diff --git a/templates/gen_fsm.erl b/templates/gen_fsm.erl
new file mode 100644
index 0000000..73505df
--- /dev/null
+++ b/templates/gen_fsm.erl
@@ -0,0 +1,50 @@
+-module(template_name).
+-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}.
diff --git a/templates/gen_server.erl b/templates/gen_server.erl
new file mode 100644
index 0000000..d297edf
--- /dev/null
+++ b/templates/gen_server.erl
@@ -0,0 +1,42 @@
+-module(template_name).
+-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}.
diff --git a/templates/gen_statem.erl b/templates/gen_statem.erl
new file mode 100644
index 0000000..afdf20f
--- /dev/null
+++ b/templates/gen_statem.erl
@@ -0,0 +1,42 @@
+-module(template_name).
+-behaviour(gen_statem).
+
+%% API.
+-export([start_link/0]).
+
+%% gen_statem.
+-export([callback_mode/0]).
+-export([init/1]).
+-export([state_name/3]).
+-export([handle_event/4]).
+-export([terminate/3]).
+-export([code_change/4]).
+
+-record(state, {
+}).
+
+%% API.
+
+-spec start_link() -> {ok, pid()}.
+start_link() ->
+ gen_statem:start_link(?MODULE, [], []).
+
+%% gen_statem.
+
+callback_mode() ->
+ state_functions.
+
+init([]) ->
+ {ok, state_name, #state{}}.
+
+state_name(_EventType, _EventData, StateData) ->
+ {next_state, state_name, StateData}.
+
+handle_event(_EventType, _EventData, StateName, StateData) ->
+ {next_state, StateName, StateData}.
+
+terminate(_Reason, _StateName, _StateData) ->
+ ok.
+
+code_change(_OldVsn, StateName, StateData, _Extra) ->
+ {ok, StateName, StateData}.
diff --git a/templates/library.app.src b/templates/library.app.src
new file mode 100644
index 0000000..e7f7d75
--- /dev/null
+++ b/templates/library.app.src
@@ -0,0 +1,11 @@
+{application, project_name, [
+ {description, ""},
+ {vsn, "0.1.0"},
+ {id, "git"},
+ {modules, []},
+ {registered, []},
+ {applications, [
+ kernel,
+ stdlib
+ ]}
+]}.
diff --git a/templates/module.erl b/templates/module.erl
new file mode 100644
index 0000000..30a3b1a
--- /dev/null
+++ b/templates/module.erl
@@ -0,0 +1,2 @@
+-module(template_name).
+-export([]).
diff --git a/templates/ranch_protocol.erl b/templates/ranch_protocol.erl
new file mode 100644
index 0000000..1458815
--- /dev/null
+++ b/templates/ranch_protocol.erl
@@ -0,0 +1,25 @@
+-module(template_name).
+-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).
diff --git a/templates/relx.config b/templates/relx.config
new file mode 100644
index 0000000..3a23af7
--- /dev/null
+++ b/templates/relx.config
@@ -0,0 +1,6 @@
+{release, {project_name_release, "1"}, [project_name, sasl, runtime_tools]}.
+{dev_mode, false}.
+{include_erts, true}.
+{extended_start_script, true}.
+{sys_config, "config/sys.config"}.
+{vm_args, "config/vm.args"}.
diff --git a/templates/supervisor.erl b/templates/supervisor.erl
new file mode 100644
index 0000000..b4ddf91
--- /dev/null
+++ b/templates/supervisor.erl
@@ -0,0 +1,12 @@
+-module(template_name).
+-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}}.
diff --git a/templates/sys.config b/templates/sys.config
new file mode 100644
index 0000000..0dbd0fd
--- /dev/null
+++ b/templates/sys.config
@@ -0,0 +1,2 @@
+[
+].
diff --git a/templates/top_Makefile b/templates/top_Makefile
new file mode 100644
index 0000000..137993f
--- /dev/null
+++ b/templates/top_Makefile
@@ -0,0 +1,5 @@
+PROJECT = project_name
+PROJECT_DESCRIPTION = New project
+PROJECT_VERSION = 0.1.0
+template_sp
+include erlang.mk
diff --git a/templates/vm.args b/templates/vm.args
new file mode 100644
index 0000000..99a5076
--- /dev/null
+++ b/templates/vm.args
@@ -0,0 +1,3 @@
+-setcookie project_name
+-heart
diff --git a/test/core_plugins.mk b/test/core_plugins.mk
index dcffb9a..8544945 100644
--- a/test/core_plugins.mk
+++ b/test/core_plugins.mk
@@ -194,7 +194,7 @@ core-plugins-templates: init
$t mkdir -p $(APP)/plugin_dep
$t printf "%s\n" \
"define tpl_test_mk" \
- "-module(test_mk)." \
+ "-module(template_name)." \
"endef" > $(APP)/plugin_dep/plugins.mk
$t cd $(APP)/plugin_dep && \
git init -q && \
@@ -263,6 +263,43 @@ core-plugins-templates-apps-only: init
$i "Check that the file was compiled correctly"
$t test -f $(APP)/apps/my_app/ebin/test_mk.beam
+core-plugins-templates-file: 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 a local git repository with a plugin containing a template file"
+ $t mkdir -p $(APP)/plugin_dep/templates
+ $t printf "%s\n" "-module(template_name)." > $(APP)/plugin_dep/templates/test_mk.erl
+ $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 config user.email "[email protected]" && \
+ git config user.name "test suite" && \
+ git add . && \
+ git commit -q --no-gpg-sign -m "Tests"
+
+ $i "Add dependency and plugins to the Makefile"
+ $t perl -ni.bak -e 'print;if ($$.==1) {print "DEPS = plugin_dep\ndep_plugin_dep = git file://$(abspath $(APP)/plugin_dep) master\nDEP_PLUGINS = plugin_dep\n"}' $(APP)/Makefile
+
+ $i "Run 'make list-templates' and check that it prints test_mk"
+ $t $(MAKE) --no-print-directory -C $(APP) list-templates | grep -qw test_mk
+
+ $i "Create a new file using the template"
+ $t $(MAKE) --no-print-directory -C $(APP) new t=test_mk n=test_mk $v
+
+ $i "Confirm the file exists"
+ $t test -f $(APP)/src/test_mk.erl
+
+ $i "Build the application"
+ $t $(MAKE) -C $(APP) $v
+
+ $i "Check that the file was compiled correctly"
+ $t test -f $(APP)/ebin/test_mk.beam
+
core-plugins-test: init
$i "Bootstrap a new OTP library named $(APP)"
diff --git a/test/core_upgrade.mk b/test/core_upgrade.mk
index f165098..c9fea3d 100644
--- a/test/core_upgrade.mk
+++ b/test/core_upgrade.mk
@@ -16,8 +16,6 @@ core-upgrade-changelog: init
$i "Fork erlang.mk locally and set a test CHANGELOG.asciidoc"
$t git clone -q https://github.com/ninenines/erlang.mk $(APP)/alt-erlangmk-repo
$t echo "$(APP)$(APP)" > $(APP)/alt-erlangmk-repo/CHANGELOG.asciidoc
-# Since part of this functionality needs the main Makefile, copy it.
- $t cp ../Makefile $(APP)/alt-erlangmk-repo/
$t (cd $(APP)/alt-erlangmk-repo && \
git config user.email "[email protected]" && \
git config user.name "test suite" && \
diff --git a/test/plugin_bootstrap.mk b/test/plugin_bootstrap.mk
index ac10ca0..aa873a8 100644
--- a/test/plugin_bootstrap.mk
+++ b/test/plugin_bootstrap.mk
@@ -303,13 +303,16 @@ bootstrap-templates: init
$t $(MAKE) -C $(APP) --no-print-directory new t=gen_statem n=my_statem
$t $(MAKE) -C $(APP) --no-print-directory new t=gen_server n=my_server
$t $(MAKE) -C $(APP) --no-print-directory new t=supervisor n=my_sup
- $t $(MAKE) -C $(APP) --no-print-directory new t=cowboy_http n=my_http
- $t $(MAKE) -C $(APP) --no-print-directory new t=cowboy_loop n=my_loop
- $t $(MAKE) -C $(APP) --no-print-directory new t=cowboy_rest n=my_rest
- $t $(MAKE) -C $(APP) --no-print-directory new t=cowboy_ws n=my_ws
+ $t $(MAKE) -C $(APP) --no-print-directory new t=cowboy_http_h n=my_http_h
+ $t $(MAKE) -C $(APP) --no-print-directory new t=cowboy_loop_h n=my_loop_h
+ $t $(MAKE) -C $(APP) --no-print-directory new t=cowboy_rest_h n=my_rest_h
+ $t $(MAKE) -C $(APP) --no-print-directory new t=cowboy_websocket_h n=my_ws_h
$t $(MAKE) -C $(APP) --no-print-directory new t=ranch_protocol n=my_protocol
$t $(MAKE) -C $(APP) --no-print-directory new t=module n=my_module
+ $i "Confirm we can't overwrite existing files"
+ $t ! $(MAKE) -C $(APP) --no-print-directory new t=gen_server n=my_server $v
+
# Here we disable warnings because templates contain missing behaviors.
$i "Build the application"
$t $(MAKE) -C $(APP) ERLC_OPTS=+debug_info $v
@@ -320,12 +323,17 @@ bootstrap-templates: init
$t test -f $(APP)/ebin/my_statem.beam
$t test -f $(APP)/ebin/my_server.beam
$t test -f $(APP)/ebin/my_sup.beam
+ $t test -f $(APP)/ebin/my_http_h.beam
+ $t test -f $(APP)/ebin/my_loop_h.beam
+ $t test -f $(APP)/ebin/my_rest_h.beam
+ $t test -f $(APP)/ebin/my_ws_h.beam
+ $t test -f $(APP)/ebin/my_protocol.beam
$t test -f $(APP)/ebin/my_module.beam
$i "Check that all the modules can be loaded"
$t $(ERL) -pa $(APP)/ebin/ -eval " \
ok = application:start($(APP)), \
- {ok, Mods = [my_fsm, my_http, my_loop, my_module, my_protocol, my_rest, my_server, my_statem, my_sup, my_ws]} \
+ {ok, Mods = [my_fsm, my_http_h, my_loop_h, my_module, my_protocol, my_rest_h, my_server, my_statem, my_sup, my_ws_h]} \
= application:get_key($(APP), modules), \
[{module, M} = code:load_file(M) || M <- Mods], \
halt()"