Merge pull request #241 from mochi/rebar3

Start on rebar3 support
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f04e537..46fc870 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -5,34 +5,24 @@
   pull_request:
 jobs:
   test:
-    name: test
+    name: test ${{matrix.otp}} on ${{matrix.os}}
     runs-on: ${{matrix.os}}
+    container:
+      image: erlang:${{matrix.otp}}
     strategy:
       fail-fast: false
       matrix:
         os:
           - ubuntu-latest
         otp:
-          - "24.3.3"
-          - "23.3.4.5"
-          - "22.3.4.9"
-          - "21.3.8.17"
+          - "24.3.4.0"
+          - "23.3.4.14"
+          - "22.3.4.26"
+          - "21.3.8.24"
           - "20.3.8.26"
-        include:
-          - os: ubuntu-18.04
-            otp: "19.3.6.13"
-          - os: ubuntu-18.04
-            otp: "18.3.4.11"
+          - "19.3.6.13"
+          - "18.3.4.11"
     steps:
       - uses: actions/checkout@v2.3.2
-      - run: |
-          VERSION=${{matrix.otp}}
-          RELEASE=$(lsb_release -cs)
-          DIR=$(mktemp -d)
-          pushd $DIR
-          FILE=esl-erlang_$VERSION-1~ubuntu~$RELEASE\_amd64.deb
-          wget https://packages.erlang-solutions.com/erlang/debian/pool/$FILE
-          sudo dpkg -i $FILE
-          popd
-          rm -r $DIR
       - run: make test
+      - run: REBAR=rebar make test
diff --git a/.gitignore b/.gitignore
index b9606c1..292935b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,5 @@
 *.beam
 *.dump
 rebar.lock
+/_build
+/rebar3.crashdump
diff --git a/CHANGES.md b/CHANGES.md
index 3216a7e..16d14f7 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,8 @@
-Version 3.0.0 released 2022-XX-XX
+Version 3.0.0 released 2022-05-09
 
+* rebar3 is now the preferred build tool (finally)
+  https://github.com/mochi/mochiweb/pull/241
+  https://github.com/mochi/mochiweb/pull/243
 * Minimum OTP version is now 18, which
   allows us to remove a number of backwards
   compatibility hacks while still supporting
@@ -14,6 +17,7 @@
   that expression might have to be updated to
   handle the `{shutdown, Error}` error reason.
   https://github.com/mochi/mochiweb/pull/238
+  https://github.com/mochi/mochiweb/pull/242
 
 Version 2.22.0 released 2021-08-23
 
diff --git a/Makefile b/Makefile
index 983c304..a537d61 100644
--- a/Makefile
+++ b/Makefile
@@ -1,24 +1,18 @@
-PREFIX:=../
-DEST:=$(PREFIX)$(PROJECT)
+REBAR?=rebar3
 
-REBAR=./rebar
+.PHONY: all edoc test clean build
 
-.PHONY: all edoc test clean build_plt dialyzer app
+all: build
 
-all:
-	@$(REBAR) prepare-deps
+build:
+	@$(REBAR) get-deps # rebar2 compatibility, it's no-op on rebar3
+	@$(REBAR) compile
 
-edoc: all
-	@$(REBAR) doc
-
-test:
-	@rm -rf .eunit
-	@mkdir -p .eunit
+test: build
 	@$(REBAR) eunit
 
+edoc: build
+	@$(REBAR) edoc
+
 clean:
 	@$(REBAR) clean
-
-app:
-	@[ -z "$(PROJECT)" ] && echo "ERROR: required variable PROJECT missing" 1>&2 && exit 1 || true
-	@$(REBAR) -r create template=mochiwebapp dest=$(DEST) appid=$(PROJECT)
diff --git a/README.md b/README.md
index 78a4d81..f827f88 100644
--- a/README.md
+++ b/README.md
@@ -6,13 +6,9 @@
 
 Erlang OTP is required for setting up the MochiWeb environment and is available at https://www.erlang.org/
 
-To create a new mochiweb using project:
-   make app PROJECT=project_name
+To create a new mochiweb using project see the `example_project` in the `examples/` folder.
 
-To create a new mochiweb using project in a specific directory:
-   make app PROJECT=project_name PREFIX=$HOME/projects/
-
-Information about Rebar (Erlang build tool) is available at https://github.com/rebar/rebar
+Information about Rebar (Erlang build tool) is available at https://github.com/erlang/rebar3
 
 MochiWeb is currently tested with Erlang/OTP 18.3 through 24.0,
 versions older than 3.0.0 may still be compatible back to R15B-03.
diff --git a/examples/example_project/.gitignore b/examples/example_project/.gitignore
new file mode 100644
index 0000000..b01cd06
--- /dev/null
+++ b/examples/example_project/.gitignore
@@ -0,0 +1,14 @@
+/ebin
+/doc
+/_test
+/.eunit
+/docs
+.DS_Store
+/TEST-*.xml
+/deps
+/.rebar
+*.swp
+*.beam
+*.dump
+_build/
+rebar.lock
diff --git a/examples/example_project/Makefile b/examples/example_project/Makefile
new file mode 100644
index 0000000..2b42efc
--- /dev/null
+++ b/examples/example_project/Makefile
@@ -0,0 +1,16 @@
+REBAR?=rebar3
+
+.PHONY: all edoc test clean app
+
+build:
+	@$(REBAR) get-deps # rebar2 compatibility, it's no-op on rebar3
+	@$(REBAR) compile
+
+test:
+	@$(REBAR) eunit
+
+edoc:
+	@$(REBAR) edoc
+
+clean:
+	@$(REBAR) clean
diff --git a/support/templates/mochiwebapp_skel/bench.sh b/examples/example_project/bench.sh
similarity index 92%
rename from support/templates/mochiwebapp_skel/bench.sh
rename to examples/example_project/bench.sh
index eb6e9c9..e24e327 100755
--- a/support/templates/mochiwebapp_skel/bench.sh
+++ b/examples/example_project/bench.sh
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 # workaround for rebar mustache template bug
-DEFAULT_PORT={{port}}
+DEFAULT_PORT=8080
 HOST=${HOST:-127.0.0.1}
 PORT=${PORT:-${DEFAULT_PORT}}
 
diff --git a/support/templates/mochiwebapp_skel/priv/www/index.html b/examples/example_project/priv/www/index.html
similarity index 73%
rename from support/templates/mochiwebapp_skel/priv/www/index.html
rename to examples/example_project/priv/www/index.html
index 40ac0c8..424ae4f 100644
--- a/support/templates/mochiwebapp_skel/priv/www/index.html
+++ b/examples/example_project/priv/www/index.html
@@ -3,6 +3,6 @@
 <title>It Worked</title>
 </head>
 <body>
-{{appid}} running.
+example_project running.
 </body>
 </html>
diff --git a/support/templates/mochiwebapp_skel/rebar.config b/examples/example_project/rebar.config
similarity index 68%
rename from support/templates/mochiwebapp_skel/rebar.config
rename to examples/example_project/rebar.config
index da560eb..de98cca 100644
--- a/support/templates/mochiwebapp_skel/rebar.config
+++ b/examples/example_project/rebar.config
@@ -2,6 +2,6 @@
 {erl_opts, [debug_info]}.
 {deps, [
   {mochiweb, ".*",
-   {git, "git://github.com/mochi/mochiweb.git", {branch, "main"}}}]}.
+   {git, "https://github.com/mochi/mochiweb.git", {branch, "main"}}}]}.
 {cover_enabled, true}.
 {eunit_opts, [verbose, {report,{eunit_surefire,[{dir,"."}]}}]}.
diff --git a/examples/example_project/src/example_project.app.src b/examples/example_project/src/example_project.app.src
new file mode 100644
index 0000000..92006cd
--- /dev/null
+++ b/examples/example_project/src/example_project.app.src
@@ -0,0 +1,9 @@
+%% -*- erlang -*-
+{application, example_project,
+ [{description, "example_project"},
+  {vsn, "0.1"},
+  {modules, []},
+  {registered, []},
+  {mod, {'example_project_app', []}},
+  {env, []},
+  {applications, [kernel, stdlib, crypto]}]}.
diff --git a/examples/example_project/src/example_project.erl b/examples/example_project/src/example_project.erl
new file mode 100644
index 0000000..ef995d6
--- /dev/null
+++ b/examples/example_project/src/example_project.erl
@@ -0,0 +1,30 @@
+%% @author Mochi Media <dev@mochimedia.com>
+%% @copyright 2010 Mochi Media <dev@mochimedia.com>
+
+%% @doc example_project.
+
+-module(example_project).
+-author("Mochi Media <dev@mochimedia.com>").
+-export([start/0, stop/0]).
+
+ensure_started(App) ->
+    case application:start(App) of
+        ok ->
+            ok;
+        {error, {already_started, App}} ->
+            ok
+    end.
+
+
+%% @spec start() -> ok
+%% @doc Start the example_project server.
+start() ->
+    example_project_deps:ensure(),
+    ensure_started(crypto),
+    application:start(example_project).
+
+
+%% @spec stop() -> ok
+%% @doc Stop the example_project server.
+stop() ->
+    application:stop(example_project).
diff --git a/examples/example_project/src/example_project_app.erl b/examples/example_project/src/example_project_app.erl
new file mode 100644
index 0000000..09bca5c
--- /dev/null
+++ b/examples/example_project/src/example_project_app.erl
@@ -0,0 +1,22 @@
+%% @author Mochi Media <dev@mochimedia.com>
+%% @copyright example_project Mochi Media <dev@mochimedia.com>
+
+%% @doc Callbacks for the example_project application.
+
+-module(example_project_app).
+-author("Mochi Media <dev@mochimedia.com>").
+
+-behaviour(application).
+-export([start/2,stop/1]).
+
+
+%% @spec start(_Type, _StartArgs) -> ServerRet
+%% @doc application start callback for example_project.
+start(_Type, _StartArgs) ->
+    example_project_deps:ensure(),
+    example_project_sup:start_link().
+
+%% @spec stop(_State) -> ServerRet
+%% @doc application stop callback for example_project.
+stop(_State) ->
+    ok.
diff --git a/support/templates/mochiwebapp_skel/src/mochiapp_deps.erl b/examples/example_project/src/example_project_deps.erl
similarity index 93%
rename from support/templates/mochiwebapp_skel/src/mochiapp_deps.erl
rename to examples/example_project/src/example_project_deps.erl
index ad151bc..804a66b 100644
--- a/support/templates/mochiwebapp_skel/src/mochiapp_deps.erl
+++ b/examples/example_project/src/example_project_deps.erl
@@ -1,12 +1,12 @@
-%% @author {{author}}
-%% @copyright {{year}} {{author}}
+%% @author Mochi Media <dev@mochimedia.com>
+%% @copyright 2010 Mochi Media <dev@mochimedia.com>
 
 %% @doc Ensure that the relatively-installed dependencies are on the code
 %%      loading path, and locate resources relative
 %%      to this application's path.
 
--module({{appid}}_deps).
--author("{{author}}").
+-module(example_project_deps).
+-author("Mochi Media <dev@mochimedia.com>").
 
 -export([ensure/0, ensure/1]).
 -export([get_base_dir/0, get_base_dir/1]).
diff --git a/support/templates/mochiwebapp_skel/src/mochiapp_sup.erl b/examples/example_project/src/example_project_sup.erl
similarity index 77%
rename from support/templates/mochiwebapp_skel/src/mochiapp_sup.erl
rename to examples/example_project/src/example_project_sup.erl
index 39db395..2e799cf 100644
--- a/support/templates/mochiwebapp_skel/src/mochiapp_sup.erl
+++ b/examples/example_project/src/example_project_sup.erl
@@ -1,10 +1,10 @@
-%% @author {{author}}
-%% @copyright {{year}} {{author}}
+%% @author Mochi Media <dev@mochimedia.com>
+%% @copyright 2010 Mochi Media <dev@mochimedia.com>
 
-%% @doc Supervisor for the {{appid}} application.
+%% @doc Supervisor for the example_project application.
 
--module({{appid}}_sup).
--author("{{author}}").
+-module(example_project_sup).
+-author("Mochi Media <dev@mochimedia.com>").
 
 -behaviour(supervisor).
 
@@ -41,7 +41,7 @@
 %% @spec init([]) -> SupervisorTree
 %% @doc supervisor callback.
 init([]) ->
-    Web = web_specs({{appid}}_web, {{port}}),
+    Web = web_specs(example_project_web, 8080),
     Processes = [Web],
     Strategy = {one_for_one, 10, 10},
     {ok,
@@ -50,7 +50,7 @@
 web_specs(Mod, Port) ->
     WebConfig = [{ip, {0,0,0,0}},
                  {port, Port},
-                 {docroot, {{appid}}_deps:local_path(["priv", "www"])}],
+                 {docroot, example_project_deps:local_path(["priv", "www"])}],
     {Mod,
      {Mod, start, [WebConfig]},
      permanent, 5000, worker, dynamic}.
diff --git a/support/templates/mochiwebapp_skel/src/mochiapp_web.erl b/examples/example_project/src/example_project_web.erl
similarity index 89%
rename from support/templates/mochiwebapp_skel/src/mochiapp_web.erl
rename to examples/example_project/src/example_project_web.erl
index 0b586b0..83e6560 100644
--- a/support/templates/mochiwebapp_skel/src/mochiapp_web.erl
+++ b/examples/example_project/src/example_project_web.erl
@@ -1,11 +1,11 @@
-%% @author {{author}}
-%% @copyright {{year}} {{author}}
+%% @author Mochi Media <dev@mochimedia.com>
+%% @copyright 2010 Mochi Media <dev@mochimedia.com>
 
-%% @doc Web server for {{appid}}.
+%% @doc Web server for example_project.
 
--module('{{appid}}_web').
+-module('example_project_web').
 
--author("{{author}}").
+-author("Mochi Media <dev@mochimedia.com>").
 
 -export([loop/2, start/1, stop/0]).
 
diff --git a/examples/example_project/start-dev.sh b/examples/example_project/start-dev.sh
new file mode 100755
index 0000000..44c65c7
--- /dev/null
+++ b/examples/example_project/start-dev.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+exec erl \
+    -pa _build/default/lib/example_project/ebin \
+    -pa _build/default/lib/mochiweb/ebin \
+    -pa ebin deps/*/ebin \
+    -boot start_sasl \
+    -sname example_project_dev \
+    -s example_project \
+    -s reloader
diff --git a/rebar b/rebar
deleted file mode 100755
index c57f7ad..0000000
--- a/rebar
+++ /dev/null
Binary files differ
diff --git a/src/mochiweb_multipart.erl b/src/mochiweb_multipart.erl
index 21b23bb..906b723 100644
--- a/src/mochiweb_multipart.erl
+++ b/src/mochiweb_multipart.erl
@@ -348,9 +348,13 @@
 -include_lib("eunit/include/eunit.hrl").
 
 ssl_cert_opts() ->
-    EbinDir = filename:dirname(code:which(?MODULE)),
-    CertDir = filename:join([EbinDir, "..", "support",
-			     "test-materials"]),
+    {ok, Cwd} = file:get_cwd(),
+    CertDir = filename:join(
+        case filename:basename(Cwd) of
+            %% rebar2 compatibility
+            ".eunit" -> [".."];
+            _ -> []
+        end ++ ["support", "test-materials"]),
     CertFile = filename:join(CertDir, "test_ssl_cert.pem"),
     KeyFile = filename:join(CertDir, "test_ssl_key.pem"),
     [{certfile, CertFile}, {keyfile, KeyFile}].
diff --git a/support/templates/.gitignore b/support/templates/.gitignore
deleted file mode 100644
index 05ee58a..0000000
--- a/support/templates/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-/ebin
-/edit
-/deps
-/erl_crash.dump
-/.eunit
diff --git a/support/templates/mochiwebapp.template b/support/templates/mochiwebapp.template
deleted file mode 100644
index c56314c..0000000
--- a/support/templates/mochiwebapp.template
+++ /dev/null
@@ -1,24 +0,0 @@
-%% -*- erlang -*-
-{variables, [{appid, "mochiwebapp"},
-             {author, "Mochi Media <dev@mochimedia.com>"},
-             {year, "2010"},
-             {version, "0.1"},
-             {port, 8080},
-             {dest, "{{appid}}"}]}.
-{dir, "{{dest}}"}.
-{template, "mochiwebapp_skel/src/mochiapp.app.src", "{{dest}}/src/{{appid}}.app.src"}.
-{template, "mochiwebapp_skel/src/mochiapp.erl", "{{dest}}/src/{{appid}}.erl"}.
-{template, "mochiwebapp_skel/src/mochiapp_app.erl", "{{dest}}/src/{{appid}}_app.erl"}.
-{template, "mochiwebapp_skel/src/mochiapp_deps.erl", "{{dest}}/src/{{appid}}_deps.erl"}.
-{template, "mochiwebapp_skel/src/mochiapp_sup.erl", "{{dest}}/src/{{appid}}_sup.erl"}.
-{template, "mochiwebapp_skel/src/mochiapp_web.erl", "{{dest}}/src/{{appid}}_web.erl"}.
-{template, "mochiwebapp_skel/start-dev.sh", "{{dest}}/start-dev.sh"}.
-{template, "mochiwebapp_skel/bench.sh", "{{dest}}/bench.sh"}.
-{template, "mochiwebapp_skel/priv/www/index.html", "{{dest}}/priv/www/index.html"}.
-{file, "../../.gitignore", "{{dest}}/.gitignore"}.
-{file, "../../Makefile", "{{dest}}/Makefile"}.
-{file, "mochiwebapp_skel/rebar.config", "{{dest}}/rebar.config"}.
-{file, "../../rebar", "{{dest}}/rebar"}.
-{chmod, 8#755, "{{dest}}/rebar"}.
-{chmod, 8#755, "{{dest}}/start-dev.sh"}.
-{chmod, 8#755, "{{dest}}/bench.sh"}.
diff --git a/support/templates/mochiwebapp_skel/src/mochiapp.app.src b/support/templates/mochiwebapp_skel/src/mochiapp.app.src
deleted file mode 100644
index c0bb11f..0000000
--- a/support/templates/mochiwebapp_skel/src/mochiapp.app.src
+++ /dev/null
@@ -1,9 +0,0 @@
-%% -*- erlang -*-
-{application, {{appid}},
- [{description, "{{appid}}"},
-  {vsn, "{{version}}"},
-  {modules, []},
-  {registered, []},
-  {mod, {'{{appid}}_app', []}},
-  {env, []},
-  {applications, [kernel, stdlib, crypto]}]}.
diff --git a/support/templates/mochiwebapp_skel/src/mochiapp.erl b/support/templates/mochiwebapp_skel/src/mochiapp.erl
deleted file mode 100644
index 9770f2c..0000000
--- a/support/templates/mochiwebapp_skel/src/mochiapp.erl
+++ /dev/null
@@ -1,30 +0,0 @@
-%% @author {{author}}
-%% @copyright {{year}} {{author}}
-
-%% @doc {{appid}}.
-
--module({{appid}}).
--author("{{author}}").
--export([start/0, stop/0]).
-
-ensure_started(App) ->
-    case application:start(App) of
-        ok ->
-            ok;
-        {error, {already_started, App}} ->
-            ok
-    end.
-
-
-%% @spec start() -> ok
-%% @doc Start the {{appid}} server.
-start() ->
-    {{appid}}_deps:ensure(),
-    ensure_started(crypto),
-    application:start({{appid}}).
-
-
-%% @spec stop() -> ok
-%% @doc Stop the {{appid}} server.
-stop() ->
-    application:stop({{appid}}).
diff --git a/support/templates/mochiwebapp_skel/src/mochiapp_app.erl b/support/templates/mochiwebapp_skel/src/mochiapp_app.erl
deleted file mode 100644
index 6bbb255..0000000
--- a/support/templates/mochiwebapp_skel/src/mochiapp_app.erl
+++ /dev/null
@@ -1,22 +0,0 @@
-%% @author {{author}}
-%% @copyright {{appid}} {{author}}
-
-%% @doc Callbacks for the {{appid}} application.
-
--module({{appid}}_app).
--author("{{author}}").
-
--behaviour(application).
--export([start/2,stop/1]).
-
-
-%% @spec start(_Type, _StartArgs) -> ServerRet
-%% @doc application start callback for {{appid}}.
-start(_Type, _StartArgs) ->
-    {{appid}}_deps:ensure(),
-    {{appid}}_sup:start_link().
-
-%% @spec stop(_State) -> ServerRet
-%% @doc application stop callback for {{appid}}.
-stop(_State) ->
-    ok.
diff --git a/support/templates/mochiwebapp_skel/start-dev.sh b/support/templates/mochiwebapp_skel/start-dev.sh
deleted file mode 100755
index 65c1692..0000000
--- a/support/templates/mochiwebapp_skel/start-dev.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/sh
-exec erl \
-    -pa ebin deps/*/ebin \
-    -boot start_sasl \
-    -sname {{appid}}_dev \
-    -s {{appid}} \
-    -s reloader
diff --git a/test/mochiweb_test_util.erl b/test/mochiweb_test_util.erl
index c56faf7..c343384 100644
--- a/test/mochiweb_test_util.erl
+++ b/test/mochiweb_test_util.erl
@@ -5,8 +5,13 @@
 -include_lib("eunit/include/eunit.hrl").
 
 ssl_cert_opts() ->
-    EbinDir = filename:dirname(code:which(?MODULE)),
-    CertDir = filename:join([EbinDir, "..", "support", "test-materials"]),
+    {ok, Cwd} = file:get_cwd(),
+    CertDir = filename:join(
+        case filename:basename(Cwd) of
+            %% rebar2 compatibility
+            ".eunit" -> [".."];
+            _ -> []
+        end ++ ["support", "test-materials"]),
     CertFile = filename:join(CertDir, "test_ssl_cert.pem"),
     KeyFile = filename:join(CertDir, "test_ssl_key.pem"),
     [{certfile, CertFile}, {keyfile, KeyFile}].