aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2016-10-30 16:56:29 +0200
committerLoïc Hoguin <[email protected]>2016-10-30 16:56:29 +0200
commit01efaa6764088ee0df0d1ec6e5f561707af5ebe0 (patch)
tree20dc3fb18dd84fc703e83441c588529d67103f5e
parent373122de2f3e3a6e937e527044ae3b62ba939c96 (diff)
downloaderlang.mk-01efaa6764088ee0df0d1ec6e5f561707af5ebe0.tar.gz
erlang.mk-01efaa6764088ee0df0d1ec6e5f561707af5ebe0.tar.bz2
erlang.mk-01efaa6764088ee0df0d1ec6e5f561707af5ebe0.zip
Greatly improve the escript support
The plugin can now easily generate escripts as complex as relx or rebar/rebar3. It generates a proper structure and allows embedding extra files by extending the escript-zip target. Documentation and tests have been added.
-rw-r--r--doc/src/guide/escripts.asciidoc72
-rw-r--r--plugins/escript.mk61
-rw-r--r--test/plugin_escript.mk89
3 files changed, 180 insertions, 42 deletions
diff --git a/doc/src/guide/escripts.asciidoc b/doc/src/guide/escripts.asciidoc
index 3d68c77..eebd6d8 100644
--- a/doc/src/guide/escripts.asciidoc
+++ b/doc/src/guide/escripts.asciidoc
@@ -1,6 +1,74 @@
[[escript]]
== Escripts
-// @todo Write it.
+Escripts are an alternative to release. They are meant to be
+used for small command line executables written in Erlang.
-Placeholder chapter.
+They are not self-contained, unlike xref:relx[releases].
+Erlang must be installed for them to run. This however means
+that they are fairly small compared to releases.
+
+For self-contained executables, check xref:sfx[self-extracting releases].
+
+Erlang.mk uses `p7zip` by default to generate the escript
+archive. Make sure it is installed.
+
+=== Generating an escript
+
+Run the following command to generate an escript:
+
+[source,bash]
+$ make escript
+
+This will by default create an escript with the same name as
+the project, in the project's directory. If the project is
+called `relx` then the escript will be in `./relx`.
+
+You can run the escript as you would any executable:
+
+[source,bash]
+$ ./relx
+
+=== Configuration
+
+You can change the name of the escript by setting `ESCRIPT_NAME`.
+The name determines both the default output file name and the
+entry module containing the function `main/1`.
+
+`ESCRIPT_FILE` can be set if you need a different file name
+or location.
+
+The escript header can be entirely customized. The first line
+is the shebang, set by `ESCRIPT_SHEBANG`. The second line is
+a comment, set by `ESCRIPT_COMMENT`. The third line is the
+arguments the VM will use when running the escript, set by
+`ESCRIPT_EMU_ARGS`.
+
+Finally, `ESCRIPT_ZIP` can be set to customize the command used
+to create the zip file. Read on for more information.
+
+=== Extra files
+
+Generating an escript is a two-part process. First, a zip file
+is created with the contents of the escript. Then a header is
+added to this file to create the escript.
+
+It is possible to add commands that will be executed between
+the two steps. You can for example add extra files to the zip
+archive:
+
+[source,make]
+----
+escript-zip::
+ $(verbose) $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) priv/templates/*
+----
+
+The `ESCRIPT_ZIP` variable contains the command to run to add
+files to the zip archive `ESCRIPT_ZIP_FILE`.
+
+=== Optimizing for size
+
+Erlang.mk will by default compile BEAM files with debug
+information. You may want to disable this behavior to obtain
+smaller escript files. Simply set `ERLC_OPTS` to a value that
+does not include `+debug_info`.
diff --git a/plugins/escript.mk b/plugins/escript.mk
index 49fa989..6d70006 100644
--- a/plugins/escript.mk
+++ b/plugins/escript.mk
@@ -2,22 +2,19 @@
# Copyright (c) 2014, Dave Cottlehuber <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
-.PHONY: distclean-escript escript
+.PHONY: distclean-escript escript escript-zip
# Configuration.
ESCRIPT_NAME ?= $(PROJECT)
ESCRIPT_FILE ?= $(ESCRIPT_NAME)
+ESCRIPT_SHEBANG ?= /usr/bin/env escript
ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
+ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
-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/**"
+ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
+ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
# Core targets.
@@ -30,38 +27,22 @@ help::
# 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_FILE)"),'\
-'halt().'
-endef
-
-ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
-
-escript:: distclean-escript deps app
- $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
+escript-zip:: deps app
+ $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
+ $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
+ $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
+ifneq ($(DEPS),)
+ $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
+ `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
+endif
+
+escript:: escript-zip
+ $(gen_verbose) printf "%s\n" \
+ "#!$(ESCRIPT_SHEBANG)" \
+ "%% $(ESCRIPT_COMMENT)" \
+ "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
+ $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
+ $(verbose) chmod +x $(ESCRIPT_FILE)
distclean-escript:
$(gen_verbose) rm -f $(ESCRIPT_NAME)
diff --git a/test/plugin_escript.mk b/test/plugin_escript.mk
new file mode 100644
index 0000000..3916094
--- /dev/null
+++ b/test/plugin_escript.mk
@@ -0,0 +1,89 @@
+# Escript plugin.
+
+ESCRIPT_CASES = build deps extra
+ESCRIPT_TARGETS = $(addprefix escript-,$(ESCRIPT_CASES))
+
+.PHONY: escript $(ESCRIPT_TARGETS)
+
+escript: $(ESCRIPT_TARGETS)
+
+escript-build: build clean
+
+ $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 "Generate a module containing a function main/1"
+ $t printf "%s\n" \
+ "-module($(APP))." \
+ "-export([main/1])." \
+ 'main(_) -> io:format("good~n").' > $(APP)/src/$(APP).erl
+
+ $i "Build the escript"
+ $t $(MAKE) -C $(APP) escript $v
+
+ $i "Check that the escript exists"
+ $t test -f $(APP)/$(APP)
+
+ $i "Check that the escript runs"
+ $t $(APP)/$(APP) | grep -q good
+
+ $i "Distclean the application"
+ $t $(MAKE) -C $(APP) distclean $v
+
+ $i "Check that the escript was removed"
+ $t test ! -e $(APP)/$(APP)
+
+escript-deps: build clean
+
+ $i "Bootstrap a new OTP library named $(APP)"
+ $t mkdir $(APP)/
+ $t cp ../erlang.mk $(APP)/
+ $t $(MAKE) -C $(APP) -f erlang.mk bootstrap-lib $v
+
+ $i "Add Ranch to the list of dependencies"
+ $t perl -ni.bak -e 'print;if ($$.==1) {print "DEPS = ranch\n"}' $(APP)/Makefile
+
+ $i "Generate a module containing a function main/1"
+ $t printf "%s\n" \
+ "-module($(APP))." \
+ "-export([main/1])." \
+ 'main(_) -> io:format("good~n").' > $(APP)/src/$(APP).erl
+
+ $i "Build the escript"
+ $t $(MAKE) -C $(APP) escript $v
+
+ $i "Check that the escript runs"
+ $t $(APP)/$(APP) | grep -q good
+
+ $i "Check that the escript contains the dependency"
+ $t zipinfo $(APP)/$(APP) 2> /dev/null | grep -q ranch
+
+escript-extra: build clean
+
+ $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 "Instruct Erlang.mk to add extra files to the escript"
+ $t printf "%s\n" \
+ "escript-zip::" \
+ ' $$(verbose) $$(ESCRIPT_ZIP) $$(ESCRIPT_ZIP_FILE) Makefile erlang.mk' >> $(APP)/Makefile
+
+ $i "Generate a module containing a function main/1"
+ $t printf "%s\n" \
+ "-module($(APP))." \
+ "-export([main/1])." \
+ 'main(_) -> io:format("good~n").' > $(APP)/src/$(APP).erl
+
+ $i "Build the escript"
+ $t $(MAKE) -C $(APP) escript $v
+
+ $i "Check that the escript runs"
+ $t $(APP)/$(APP) | grep -q good
+
+ $i "Check that the escript contains the extra files"
+ $t zipinfo $(APP)/$(APP) 2> /dev/null | grep -q Makefile
+ $t zipinfo $(APP)/$(APP) 2> /dev/null | grep -q erlang.mk