aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--README.md16
-rw-r--r--bootstrap.cmd12
-rw-r--r--examples/relx.config4
-rwxr-xr-x[-rw-r--r--]priv/templates/bin.dtl68
-rw-r--r--priv/templates/bin_windows.dtl75
-rw-r--r--priv/templates/erl_ini.dtl4
-rw-r--r--priv/templates/extended_bin.dtl248
-rw-r--r--priv/templates/extended_bin_windows.dtl176
-rw-r--r--priv/templates/nodetool.dtl2
-rw-r--r--src/relx.erl1
-rw-r--r--src/rlx_app_discovery.erl67
-rw-r--r--src/rlx_cmd_args.erl8
-rw-r--r--src/rlx_prv_assembler.erl126
-rw-r--r--src/rlx_prv_config.erl7
-rw-r--r--src/rlx_state.erl1
-rw-r--r--test/rlx_discover_SUITE.erl41
17 files changed, 687 insertions, 170 deletions
diff --git a/.gitignore b/.gitignore
index 79a42f3..13cbe44 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ test/*_data
_rel/*
.*
erl_crash.dump
+rebar
diff --git a/README.md b/README.md
index a064666..32eaa3b 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,21 @@ To build relx and generate a standalone escript executable:
This creates the executable `relx`.
+Note, if using your own `rebar`, it must at least be > version [2.2.0](https://github.com/rebar/rebar/releases)
+
+Building on Windows
+-------------------
+
+To build relx on Windows you'll need to have rebar installed and the path to
+the rebar binary added to the `PATH` environment variable. To start the build
+use the `bootstrap` batch file:
+
+ c:\> bootstrap
+
+This creates the executable `relx` and the `relx.cmd` shortcut script. Copy
+both of these files to a directory and make the directory available to the
+`PATH` environment variable.
+
Config File
-----------
@@ -62,6 +77,7 @@ Options
| -a | --override_app | string | | An app name and a directory to override in the form appname:dir |
| -c | --config | string | ./relx.config | Config file path |
| | --overlay_vars | string | | Path to a file of overlay variables |
+| | --sys_config | string | | Path to a file to use for sys.config |
| -d | --dev-mode | boolean | false | Symlink all applications and configuration into the release instead of copying|
Wiki
----
diff --git a/bootstrap.cmd b/bootstrap.cmd
new file mode 100644
index 0000000..86ffc7f
--- /dev/null
+++ b/bootstrap.cmd
@@ -0,0 +1,12 @@
+:: A script to build relx on Windows
+:: Requires rebar
+
+:: Get dependencies, compile and escriptize relx
+@cmd /c @rebar -r get-deps compile escriptize
+
+:: Create a shortcut file for running the relx command
+@set relx_cmd=relx.cmd
+@echo @echo off > %relx_cmd%
+@echo setlocal >> %relx_cmd%
+@echo set relx=%%~f0 >> %relx_cmd%
+@echo escript ^"%%relx:.cmd=%%^" %%* >> %relx_cmd%
diff --git a/examples/relx.config b/examples/relx.config
index f328c32..b19042b 100644
--- a/examples/relx.config
+++ b/examples/relx.config
@@ -33,12 +33,12 @@
%% automatically.
{sys_config, "./config/sys.config"}.
-%% relcool will include erts by default. However, if you don't want to include
+%% relx will include erts by default. However, if you don't want to include
%% erts you can add the `include_erts` tuple to the config and tell relx not
%% to include it.
{include_erts, false}.
-%% The default start script relcool creates is basic. For a more complete start
+%% The default start script relx creates is basic. For a more complete start
%% script add the extended_start_script option.
{extended_start_script, true}.
diff --git a/priv/templates/bin.dtl b/priv/templates/bin.dtl
index 067e9f7..4434552 100644..100755
--- a/priv/templates/bin.dtl
+++ b/priv/templates/bin.dtl
@@ -2,43 +2,61 @@
set -e
-SCRIPT_DIR=`dirname $0`
-RELEASE_ROOT_DIR=`cd $SCRIPT_DIR/.. && pwd`
-REL_NAME={{ rel_name }}
-REL_VSN={{ rel_vsn }}
-ERTS_VSN={{ erts_vsn }}
-REL_DIR=$RELEASE_ROOT_DIR/releases/$REL_VSN
-ERL_OPTS={{ erl_opts }}
+SCRIPT_DIR="$(dirname "$0")"
+RELEASE_ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
+REL_NAME="{{ rel_name }}"
+REL_VSN="{{ rel_vsn }}"
+ERTS_VSN="{{ erts_vsn }}"
+REL_DIR="$RELEASE_ROOT_DIR/releases/$REL_VSN"
+ERL_OPTS="{{ erl_opts }}"
find_erts_dir() {
- local erts_dir=$RELEASE_ROOT_DIR/erts-$ERTS_VSN
+ local erts_dir="$RELEASE_ROOT_DIR/erts-$ERTS_VSN"
if [ -d "$erts_dir" ]; then
- ERTS_DIR=$erts_dir;
- ROOTDIR=$RELEASE_ROOT_DIR
+ ERTS_DIR="$erts_dir";
+ ROOTDIR="$RELEASE_ROOT_DIR"
else
- local erl=`which erl`
- local erl_root=`$erl -noshell -eval "io:format(\\"~s\\", [code:root_dir()])." -s init stop`
- ERTS_DIR=$erl_root/erts-$ERTS_VSN
- ROOTDIR=$erl_root
+ local erl="$(which erl)"
+ local erl_root="$("$erl" -noshell -eval "io:format(\\"~s\\", [code:root_dir()])." -s init stop)"
+ ERTS_DIR="$erl_root/erts-$ERTS_VSN"
+ ROOTDIR="$erl_root"
fi
}
find_sys_config() {
- local possible_sys=$REL_DIR/sys.config
+ local possible_sys="$REL_DIR/sys.config"
if [ -f "$possible_sys" ]; then
- SYS_CONFIG="-config $possible_sys"
+ SYS_CONFIG="$possible_sys"
+ fi
+}
+
+find_vm_args() {
+ local possible_vm_args="$REL_DIR/vm.args"
+ if [ -f "$possible_vm_args" ]; then
+ VM_ARGS="$possible_vm_args"
fi
}
find_erts_dir
find_sys_config
-export ROOTDIR=$RELEASE_ROOT_DIR
-export BINDIR=$ERTS_DIR/bin
-export EMU=beam
-export PROGNAME=erl
-export LD_LIBRARY_PATH=$ERTS_DIR/lib:$LD_LIBRARY_PATH
-
-cd $ROOTDIR
-
-$BINDIR/erlexec $ERL_OPTS $SYS_CONFIG -boot $REL_DIR/$REL_NAME $@
+find_vm_args
+export ROOTDIR="$RELEASE_ROOT_DIR"
+export BINDIR="$ERTS_DIR/bin"
+export EMU="beam"
+export PROGNAME="erl"
+export LD_LIBRARY_PATH="$ERTS_DIR/lib:$LD_LIBRARY_PATH"
+
+cd "$ROOTDIR"
+
+# Save extra arguments
+ARGS="$@"
+
+# Build arguments for erlexec
+set -- "$ERL_OPTS"
+[ "$SYS_CONFIG" ] && set -- "$@" -config "$SYS_CONFIG"
+[ "$VM_ARGS" ] && set -- "$@" -args_file "$VM_ARGS"
+set -- "$@" -boot "$REL_DIR/$REL_NAME" "$ARGS"
+
+# Boot the release
+"$BINDIR/erlexec" "$@"
diff --git a/priv/templates/bin_windows.dtl b/priv/templates/bin_windows.dtl
new file mode 100644
index 0000000..170d957
--- /dev/null
+++ b/priv/templates/bin_windows.dtl
@@ -0,0 +1,75 @@
+:: This is a simple start batch file that runs the release in an Erlang shell
+
+:: Set variables that describe the release
+@set rel_name={{ rel_name }}
+@set rel_vsn={{ rel_vsn }}
+@set erts_vsn={{ erts_vsn }}
+@set erl_opts={{ erl_opts }}
+
+:: Set the root release directory based on the location of this batch file
+@set script_dir=%~dp0
+@for %%A in ("%script_dir%\..") do (
+ @set "release_root_dir=%%~fA"
+)
+@set rel_dir=%release_root_dir%\releases\%rel_vsn%
+
+@call :find_erts_dir
+@call :find_sys_config
+
+@set rootdir=%release_root_dir%
+@set bindir=%erts_dir%\bin
+@set progname=erl
+@set erl=%bindir%\erl
+
+cd %rootdir%
+
+:: Write the erl.ini file
+@set erl_ini=%erts_dir%\bin\erl.ini
+@set converted_bindir=%bindir:\=\\%
+@set converted_rootdir=%rootdir:\=\\%
+@echo [erlang] > "%erl_ini%"
+@echo Bindir=%converted_bindir% >> "%erl_ini%"
+@echo Progname=%progname% >> "%erl_ini%"
+@echo Rootdir=%converted_rootdir% >> "%erl_ini%"
+
+:: Start the release in an `erl` shell
+@%erl% %erl_opts% %sys_config% -boot "%rel_dir%\%rel_name%" %*
+
+@goto :eof
+
+:: Find the ERTS dir
+:find_erts_dir
+@set erts_dir=%release_root_dir%\erts-%erts_vsn%
+@if exist %erts_dir% (
+ @goto :set_erts_dir_from_default
+) else (
+ @goto :set_erts_dir_from_erl
+)
+@goto :eof
+
+:: Set the ERTS dir from the passed in erts_vsn
+:set_erts_dir_from_default
+@set erts_dir=%erts_dir%
+@set root_dir=%release_root_dir%
+@goto :eof
+
+:: Set the ERTS dir from erl
+:set_erts_dir_from_erl
+@for /f "delims=" %%i in ('where erl') do (
+ @set erl=%%i
+)
+@set dir_cmd="%erl%" -noshell -eval "io:format(\"~s\", [filename:nativename(code:root_dir())])." -s init stop
+@for /f %%i in ('%%dir_cmd%%') do (
+ @set erl_root=%%i
+)
+@set erts_dir=%erl_root%\erts-%erts_vsn%
+@set rootdir=%erl_root%
+@goto :eof
+
+:: Find the sys.config file
+:find_sys_config
+@set possible_sys=%rel_dir%\sys.config
+@if exist "%possible_sys%" (
+ @set sys_config=-config "%possible_sys%"
+)
+@goto :eof
diff --git a/priv/templates/erl_ini.dtl b/priv/templates/erl_ini.dtl
new file mode 100644
index 0000000..b2ce5bc
--- /dev/null
+++ b/priv/templates/erl_ini.dtl
@@ -0,0 +1,4 @@
+[erlang]
+Bindir={{ bin_dir }}
+Progname=erl
+Rootdir={{ output_dir }}
diff --git a/priv/templates/extended_bin.dtl b/priv/templates/extended_bin.dtl
index a0e46a9..59cf692 100644
--- a/priv/templates/extended_bin.dtl
+++ b/priv/templates/extended_bin.dtl
@@ -2,101 +2,113 @@
set -e
-SCRIPT_DIR=`dirname $0`
-RELEASE_ROOT_DIR=`cd $SCRIPT_DIR/.. && pwd`
-REL_NAME={{ rel_name }}
-REL_VSN={{ rel_vsn }}
-ERTS_VSN={{ erts_vsn }}
-REL_DIR=$RELEASE_ROOT_DIR/releases/$REL_VSN
-ERL_OPTS={{ erl_opts }}
-PIPE_DIR=/tmp/erl_pipes/{{ rel_name }}/
+SCRIPT_DIR="$(dirname "$0")"
+RELEASE_ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
+REL_NAME="{{ rel_name }}"
+REL_VSN="{{ rel_vsn }}"
+ERTS_VSN="{{ erts_vsn }}"
+REL_DIR="$RELEASE_ROOT_DIR/releases/$REL_VSN"
+ERL_OPTS="{{ erl_opts }}"
+PIPE_DIR="/tmp/erl_pipes/{{ rel_name }}/"
+RUNNER_LOG_DIR="${RUNNER_LOG_DIR:-$RELEASE_ROOT_DIR/log}"
find_erts_dir() {
- local erts_dir=$RELEASE_ROOT_DIR/erts-$ERTS_VSN
+ local erts_dir="$RELEASE_ROOT_DIR/erts-$ERTS_VSN"
if [ -d "$erts_dir" ]; then
- ERTS_DIR=$erts_dir;
- ROOTDIR=$RELEASE_ROOT_DIR
+ ERTS_DIR="$erts_dir";
+ ROOTDIR="$RELEASE_ROOT_DIR"
else
- local erl=`which erl`
- local erl_root=`$erl -noshell -eval "io:format(\\"~s\\", [code:root_dir()])." -s init stop`
- ERTS_DIR=$erl_root/erts-$ERTS_VSN
- ROOTDIR=$erl_root
+ local erl="$(which erl)"
+ local erl_root="$("$erl" -noshell -eval "io:format(\\"~s\\", [code:root_dir()])." -s init stop)"
+ ERTS_DIR="$erl_root/erts-$ERTS_VSN"
+ ROOTDIR="$erl_root"
fi
+}
+
+# Connect to a remote node
+relx_rem_sh() {
+ # Generate a unique id used to allow multiple remsh to the same node
+ # transparently
+ id="remsh$(date +%s`@`echo "$NAME" | awk -F@ '{print $2}')"
+ # Setup remote shell command to control node
+ exec "$BINDIR/erl" "$NAME_TYPE" "$id" -remsh "$NAME" -boot start_clean \
+ -setcookie "$COOKIE"
}
-find_sys_config() {
- local possible_sys=$REL_DIR/sys.config
- if [ -f "$possible_sys" ]; then
- SYS_CONFIG="-config $possible_sys"
- fi
+# Control a node
+relx_nodetool() {
+ command="$1"; shift
+ "erts-$ERTS_VSN/bin/escript" "$ROOTDIR/bin/nodetool" "$NAME_TYPE" "$NAME" \
+ -setcookie "$COOKIE" "$command"
+}
+
+# Output a start command for the last argument of run_erl
+relx_start_command() {
+ printf "exec \"%s\" \"%s\"" "$RELEASE_ROOT_DIR/bin/$REL_NAME" \
+ "$START_OPTION"
}
# Use $CWD/vm.args if exists, otherwise releases/APP_VSN/vm.args, or else etc/vm.args
-if [ -e "$RELEASE_ROOT_DIR/vm.args" ]; then
- VMARGS_PATH=$RELEASE_ROOT_DIR/vm.args
- USE_DIR=$RELEASE_ROOT_DIR
-else
- USE_DIR=$REL_DIR
- if [ -e "$REL_DIR/vm.args" ]; then
- VMARGS_PATH="$REL_DIR/vm.args"
+if [ -z "$VMARGS_PATH" ]; then
+ if [ -e "$RELEASE_ROOT_DIR/vm.args" ]; then
+ VMARGS_PATH="$RELEASE_ROOT_DIR/vm.args"
+ USE_DIR="$RELEASE_ROOT_DIR"
else
- VMARGS_PATH="$REL_DIR/vm.args"
+ USE_DIR="$REL_DIR"
+ if [ -e "$REL_DIR/vm.args" ]; then
+ VMARGS_PATH="$REL_DIR/vm.args"
+ else
+ VMARGS_PATH="$REL_DIR/vm.args"
+ fi
fi
fi
-RUNNER_LOG_DIR=$RELEASE_ROOT_DIR/log
# Make sure log directory exists
-mkdir -p $RUNNER_LOG_DIR
+mkdir -p "$RUNNER_LOG_DIR"
# Use releases/VSN/sys.config if it exists otherwise use etc/app.config
-if [ -e "$USE_DIR/sys.config" ]; then
- CONFIG_PATH="$USE_DIR/sys.config"
-else
- if [ -e "$REL_DIR/sys.config" ]; then
- CONFIG_PATH="$REL_DIR/sys.config"
+if [ -z "$CONFIG_PATH" ]; then
+ if [ -e "$USE_DIR/sys.config" ]; then
+ CONFIG_PATH="$USE_DIR/sys.config"
else
- CONFIG_PATH="$REL_DIR/app.config"
+ if [ -e "$REL_DIR/sys.config" ]; then
+ CONFIG_PATH="$REL_DIR/sys.config"
+ else
+ CONFIG_PATH="$REL_DIR/app.config"
+ fi
fi
fi
# Extract the target node name from node.args
-NAME_ARG=`egrep '^-s?name' $VMARGS_PATH`
+NAME_ARG=$(egrep '^-s?name' "$VMARGS_PATH")
if [ -z "$NAME_ARG" ]; then
echo "vm.args needs to have either -name or -sname parameter."
exit 1
fi
# Extract the name type and name from the NAME_ARG for REMSH
-REMSH_TYPE=`echo $NAME_ARG | awk '{print $1}'`
-REMSH_NAME=`echo $NAME_ARG | awk '{print $2}'`
-
-# Note the `date +%s`, used to allow multiple remsh to the same node transparently
-REMSH_NAME_ARG="$REMSH_TYPE remsh`date +%s`@`echo $REMSH_NAME | awk -F@ '{print $2}'`"
-REMSH_REMSH_ARG="-remsh $REMSH_NAME -boot start_clean"
+NAME_TYPE="$(echo "$NAME_ARG" | awk '{print $1}')"
+NAME="$(echo "$NAME_ARG" | awk '{print $2}')"
# Extract the target cookie
-COOKIE_ARG=`grep '^-setcookie' $VMARGS_PATH`
+COOKIE_ARG="$(grep '^-setcookie' "$VMARGS_PATH")"
if [ -z "$COOKIE_ARG" ]; then
echo "vm.args needs to have a -setcookie parameter."
exit 1
fi
-find_erts_dir
-find_sys_config
-export ROOTDIR=$RELEASE_ROOT_DIR
-export BINDIR=$ERTS_DIR/bin
-export EMU=beam
-export PROGNAME=erl
-export LD_LIBRARY_PATH=$ERTS_DIR/lib:$LD_LIBRARY_PATH
-
-cd $ROOTDIR
+# Extract cookie name from COOKIE_ARG
+COOKIE="$(echo "$COOKIE_ARG" | awk '{print $2}')"
-# Setup remote shell command to control node
-REMSH="$BINDIR/erl $REMSH_NAME_ARG $REMSH_REMSH_ARG $COOKIE_ARG"
+find_erts_dir
+export ROOTDIR="$RELEASE_ROOT_DIR"
+export BINDIR="$ERTS_DIR/bin"
+export EMU="beam"
+export PROGNAME="erl"
+export LD_LIBRARY_PATH="$ERTS_DIR/lib:$LD_LIBRARY_PATH"
-# Setup command to control the node
-NODETOOL="$BINDIR/escript $ROOTDIR/bin/nodetool $NAME_ARG $COOKIE_ARG"
+cd "$ROOTDIR"
# Check the first argument for instructions
case "$1" in
@@ -120,37 +132,46 @@ case "$1" in
HEART_OPTION="start_boot"
;;
esac
- RUN_PARAM=$(printf "'%s' " "$@")
- HEART_COMMAND="$SCRIPT_DIR/bin/$REL_NAME $HEART_OPTION $RUN_PARAM"
+ RUN_PARAM="$@"
+
+ # Set arguments for the heart command
+ set -- "$SCRIPT_DIR/$REL_NAME" "$HEART_OPTION"
+ [ "$RUN_PARAM" ] && set -- "$@" "$RUN_PARAM"
+
+ # Export the HEART_COMMAND
+ HEART_COMMAND="$@"
export HEART_COMMAND
- mkdir -p $PIPE_DIR
- $BINDIR/run_erl -daemon $PIPE_DIR $RUNNER_LOG_DIR "exec $RELEASE_ROOT_DIR/bin/$REL_NAME $START_OPTION $RUN_PARAM" 2>&1
+
+ mkdir -p "$PIPE_DIR"
+
+ "$BINDIR/run_erl" -daemon "$PIPE_DIR" "$RUNNER_LOG_DIR" \
+ "$(relx_start_command)"
;;
stop)
# Wait for the node to completely stop...
- case `uname -s` in
+ case $(uname -s) in
Linux|Darwin|FreeBSD|DragonFly|NetBSD|OpenBSD)
# PID COMMAND
- PID=`ps ax -o pid= -o command=|
- grep "$SCRIPT_DIR/.*/[b]eam"|awk '{print $1}'`
+ PID=$(ps ax -o pid= -o command=|
+ grep "$SCRIPT_DIR/.*/[b]eam"|awk '{print $1}')
;;
SunOS)
# PID COMMAND
- PID=`ps -ef -o pid= -o args=|
- grep "$SCRIPT_DIR/.*/[b]eam"|awk '{print $1}'`
+ PID=$(ps -ef -o pid= -o args=|
+ grep "$SCRIPT_DIR/.*/[b]eam"|awk '{print $1}')
;;
CYGWIN*)
# UID PID PPID TTY STIME COMMAND
- PID=`ps -efW|grep "$SCRIPT_DIR/.*/[b]eam"|awk '{print $2}'`
+ PID=$(ps -efw|grep "$SCRIPT_DIR/.*/[b]eam"|awk '{print $2}')
;;
esac
- $NODETOOL stop
- ES=$?
+ relx_nodetool "stop"
+ ES="$?"
if [ "$ES" -ne 0 ]; then
- exit $ES
+ exit "$ES"
fi
- while `kill -0 $PID 2>/dev/null`;
+ while $(kill -0 "$PID" 2>/dev/null);
do
sleep 1
done
@@ -158,8 +179,8 @@ case "$1" in
restart)
## Restart the VM without exiting the process
- $NODETOOL restart
- ES=$?
+ relx_nodetool "restart"
+ ES="$?"
if [ "$ES" -ne 0 ]; then
exit $ES
fi
@@ -167,8 +188,8 @@ case "$1" in
reboot)
## Restart the VM completely (uses heart to restart it)
- $NODETOOL reboot
- ES=$?
+ relx_nodetool "reboot"
+ ES="$?"
if [ "$ES" -ne 0 ]; then
exit $ES
fi
@@ -176,8 +197,8 @@ case "$1" in
ping)
## See if the VM is alive
- $NODETOOL ping
- ES=$?
+ relx_nodetool "ping"
+ ES="$?"
if [ "$ES" -ne 0 ]; then
exit $ES
fi
@@ -185,28 +206,28 @@ case "$1" in
attach)
# Make sure a node IS running
- RES=`$NODETOOL ping`
- ES=$?
+ RES="$(relx_nodetool "ping")"
+ ES="$?"
if [ "$ES" -ne 0 ]; then
echo "Node is not running!"
exit $ES
fi
shift
- exec $BINDIR/to_erl $PIPE_DIR
+ exec "$BINDIR/to_erl" "$PIPE_DIR"
;;
remote_console)
# Make sure a node IS running
- RES=`$NODETOOL ping`
- ES=$?
+ RES="$(relx_nodetool "ping")"
+ ES="$?"
if [ "$ES" -ne 0 ]; then
echo "Node is not running!"
exit $ES
fi
shift
- exec $REMSH
+ relx_rem_sh
;;
upgrade|downgrade|install)
@@ -218,17 +239,15 @@ case "$1" in
fi
# Make sure a node IS running
- RES=`$NODETOOL ping`
- ES=$?
+ RES="$(relx_nodetool "ping")"
+ ES="$?"
if [ "$ES" -ne 0 ]; then
echo "Node is not running!"
exit $ES
fi
- node_name=`echo $NAME_ARG | awk '{print $2}'`
- erlang_cookie=`echo $COOKIE_ARG | awk '{print $2}'`
-
- exec $BINDIR/escript $ROOTDIR/bin/install_upgrade.escript $REL_NAME $node_name $erlang_cookie $2
+ exec "$BINDIR/escript" "$ROOTDIR/bin/install_upgrade.escript" \
+ "$REL_NAME" "$NAME" "$COOKIE" "$2"
;;
console|console_clean|console_boot)
@@ -236,8 +255,16 @@ case "$1" in
# however, for debugging, sometimes start_clean.boot is useful.
# For e.g. 'setup', one may even want to name another boot script.
case "$1" in
- console) [ -f $REL_DIR/$REL_NAME.boot ] && BOOTFILE=$REL_DIR/$REL_NAME || BOOTFILE=$REL_DIR/start ;;
- console_clean) BOOTFILE=$ROOTDIR/bin/start_clean ;;
+ console)
+ if [ "$REL_DIR/$REL_NAME.boot" ]; then
+ BOOTFILE="$REL_DIR/$REL_NAME"
+ else
+ BOOTFILE="$REL_DIR/start"
+ fi
+ ;;
+ console_clean)
+ BOOTFILE="$ROOTDIR/bin/start_clean"
+ ;;
console_boot)
shift
BOOTFILE="$1"
@@ -245,43 +272,62 @@ case "$1" in
;;
esac
# Setup beam-required vars
- EMU=beam
- PROGNAME=`echo $0 | sed 's/.*\\///'`
- CMD="$BINDIR/erlexec -boot $BOOTFILE -env ERL_LIBS $REL_DIR/lib -config $CONFIG_PATH -args_file $VMARGS_PATH"
+ EMU="beam"
+ PROGNAME="${0#*/}"
+
export EMU
export PROGNAME
+ # Store passed arguments since they will be erased by `set`
+ ARGS="$@"
+
+ # Build an array of arguments to pass to exec later on
+ # Build it here because this command will be used for logging.
+ set -- "$BINDIR/erlexec" -boot "$BOOTFILE" \
+ -env ERL_LIBS "$REL_DIR/lib" -config "$CONFIG_PATH" \
+ -args_file "$VMARGS_PATH"
+
# Dump environment info for logging purposes
- echo "Exec: $CMD" -- ${1+"$@"}
+ echo "Exec: $@ -- ${1+$ARGS}"
echo "Root: $ROOTDIR"
# Log the startup
+ echo "$RELEASE_ROOT_DIR"
logger -t "$REL_NAME[$$]" "Starting up"
# Start the VM
- exec $CMD -- ${1+"$@"}
+ exec "$@" -- "${1+$ARGS}"
;;
foreground)
# start up the release in the foreground for use by runit
# or other supervision services
- [ -f $REL_DIR/$REL_NAME.boot ] && BOOTFILE=$REL_NAME || BOOTFILE=start
+ [ -f "$REL_DIR/$REL_NAME.boot" ] && BOOTFILE="$REL_NAME" || BOOTFILE=start
FOREGROUNDOPTIONS="-noinput +Bd"
# Setup beam-required vars
EMU=beam
- PROGNAME=`echo $0 | sed 's/.*\\///'`
- CMD="$BINDIR/erlexec $FOREGROUNDOPTIONS -boot $REL_DIR/$BOOTFILE -mode embedded -config $CONFIG_PATH -args_file $VMARGS_PATH"
+ PROGNAME="${0#*/}"
+
export EMU
export PROGNAME
+ # Store passed arguments since they will be erased by `set`
+ ARGS="$@"
+
+ # Build an array of arguments to pass to exec later on
+ # Build it here because this command will be used for logging.
+ set -- "$BINDIR/erlexec" "$FOREGROUNDOPTIONS" \
+ -boot "$REL_DIR/$BOOTFILE" -mode embedded -config "$CONFIG_PATH" \
+ -args_file "$VMARGS_PATH"
+
# Dump environment info for logging purposes
- echo "Exec: $CMD" -- ${1+"$@"}
+ echo "Exec: $@" -- "${1+$ARGS}"
echo "Root: $ROOTDIR"
# Start the VM
- exec $CMD -- ${1+"$@"}
+ exec "$@" -- "${1+$ARGS}"
;;
*)
echo "Usage: $REL_NAME {start|start_boot <file>|foreground|stop|restart|reboot|ping|console|console_clean|console_boot <file>|attach|remote_console|upgrade}"
diff --git a/priv/templates/extended_bin_windows.dtl b/priv/templates/extended_bin_windows.dtl
new file mode 100644
index 0000000..1f94675
--- /dev/null
+++ b/priv/templates/extended_bin_windows.dtl
@@ -0,0 +1,176 @@
+:: This batch file handles managing an Erlang node as a Windows service.
+::
+:: Commands provided:
+::
+:: * install - install the release as a Windows service
+:: * start - start the service and Erlang node
+:: * stop - stop the service and Erlang node
+:: * restart - run the stop command and start command
+:: * uninstall - uninstall the service and kill a running node
+:: * ping - check if the node is running
+:: * console - start the Erlang release in a `werl` Windows shell
+:: * attach - connect to a running node and open an interactive console
+:: * list - display a listing of installed Erlang services
+:: * usage - display available commands
+
+:: Set variables that describe the release
+@set rel_name={{ rel_name }}
+@set rel_vsn={{ rel_vsn }}
+@set erts_vsn={{ erts_vsn }}
+@set erl_opts={{ erl_opts }}
+
+:: Discover the release root directory from the directory
+:: of this script
+@set script_dir=%~dp0
+@for %%A in ("%script_dir%\..") do @(
+ @set release_root_dir=%%~fA
+)
+@set rel_dir=%release_root_dir%\releases\%rel_vsn%
+
+@call :find_erts_dir
+@call :find_sys_config
+
+@set bindir=%erts_dir%\bin
+@set vm_args=%rel_dir%\vm.args
+@set progname=erl.exe
+@set boot_script=%rel_dir%\%rel_name%
+@set clean_boot_script=%release_root_dir%\bin\start_clean
+@set erlsrv="%bindir%\erlsrv.exe"
+@set epmd="%bindir%\epmd.exe"
+@set escript="%bindir%\escript.exe"
+@set werl="%bindir%\werl.exe"
+@set nodetool="%release_root_dir%\bin\nodetool"
+
+:: Extract node type and name from vm.args
+@for /f "usebackq tokens=1-2" %%I in (`findstr /b "\-name \-sname" "%vm_args%"`) do @(
+ @set node_type=%%I
+ @set node_name=%%J
+)
+
+:: Extract cookie from vm.args
+@for /f "usebackq tokens=1-2" %%I in (`findstr /b \-setcookie "%vm_args%"`) do @(
+ @set cookie=%%J
+)
+
+:: Write the erl.ini file to set up paths relative to this script
+@call :write_ini
+
+:: If a start.boot file is not present, copy one from the named .boot file
+@if not exist "%rel_dir%\start.boot" (
+ @copy "%rel_dir%\%rel_name%.boot" "%rel_dir%\start.boot" >nul
+)
+
+@if "%1"=="install" @goto install
+@if "%1"=="uninstall" @goto uninstall
+@if "%1"=="start" @goto start
+@if "%1"=="stop" @goto stop
+@if "%1"=="restart" @call :stop && @goto start
+@if "%1"=="console" @goto console
+@if "%1"=="ping" @goto ping
+@if "%1"=="list" @goto list
+@if "%1"=="attach" @goto attach
+@if "%1"=="" @goto usage
+@echo Unknown command: "%1"
+
+@goto :eof
+
+:: Find the ERTS dir
+:find_erts_dir
+@set possible_erts_dir=%release_root_dir%\erts-%erts_vsn%
+@if exist "%possible_erts_dir%" (
+ @call :set_erts_dir_from_default
+) else (
+ @call :set_erts_dir_from_erl
+)
+@goto :eof
+
+:: Set the ERTS dir from the passed in erts_vsn
+:set_erts_dir_from_default
+@set erts_dir=%possible_erts_dir%
+@set rootdir=%release_root_dir%
+@goto :eof
+
+:: Set the ERTS dir from erl
+:set_erts_dir_from_erl
+@for /f "delims=" %%i in ('where erl') do @(
+ @set erl=%%i
+)
+@set dir_cmd="%erl%" -noshell -eval "io:format(\"~s\", [filename:nativename(code:root_dir())])." -s init stop
+@for /f %%i in ('%%dir_cmd%%') do @(
+ @set erl_root=%%i
+)
+@set erts_dir=%erl_root%\erts-%erts_vsn%
+@set rootdir=%erl_root%
+@goto :eof
+
+:: Find the sys.config file
+:find_sys_config
+@set possible_sys=%rel_dir%\sys.config
+@if exist %possible_sys% (
+ @set sys_config=-config %possible_sys%
+)
+@goto :eof
+
+:: Write the erl.ini file
+:write_ini
+@set erl_ini=%erts_dir%\bin\erl.ini
+@set converted_bindir=%bindir:\=\\%
+@set converted_rootdir=%rootdir:\=\\%
+@echo [erlang] > "%erl_ini%"
+@echo Bindir=%converted_bindir% >> "%erl_ini%"
+@echo Progname=%progname% >> "%erl_ini%"
+@echo Rootdir=%converted_rootdir% >> "%erl_ini%"
+@goto :eof
+
+:: Display usage information
+:usage
+@echo usage: %~n0 ^(install^|uninstall^|start^|stop^|restart^|console^|ping^|list^|attach^)
+@goto :eof
+
+:: Install the release as a Windows service
+:install
+@set args=%erl_opts% -setcookie %cookie% ++ -rootdir \"%rootdir%\"
+@set start_erl=%erts_dir%\bin\start_erl.exe
+@set description=Erlang node %node_name% in %rootdir%
+@%erlsrv% add %rel_name% %node_type% "%node_name%" -c "%description%" ^
+ -w "%rootdir%" -m "%start_erl%" -args "%args%" ^
+ -stopaction "init:stop()."
+@goto :eof
+
+:: Uninstall the Windows service
+:uninstall
+@%erlsrv% remove %rel_name%
+@%epmd% -kill
+@goto :eof
+
+:: Start the Windows service
+:start
+@%erlsrv% start %rel_name%
+@goto :eof
+
+:: Stop the Windows service
+:stop
+@%erlsrv% stop %rel_name%
+@goto :eof
+
+:: Start a console
+:console
+@start "%rel_name% console" %werl% -boot "%boot_script%" "%sys_config%" ^
+ -args_file "%vm_args%" %node_type% %node_name%
+@goto :eof
+
+:: Ping the running node
+:ping
+@%escript% %nodetool% ping %node_type% "%node_name%" -setcookie "%cookie%"
+@goto :eof
+
+:: List installed Erlang services
+:list
+@%erlsrv% list %rel_name%
+@goto :eof
+
+:: Attach to a running node
+:attach
+@start "%node_name% attach" %werl% -boot "%clean_boot_script%" ^
+ -remsh %node_name% %node_type% console -setcookie %cookie%
+@goto :eof
diff --git a/priv/templates/nodetool.dtl b/priv/templates/nodetool.dtl
index a6faad5..80f1965 100644
--- a/priv/templates/nodetool.dtl
+++ b/priv/templates/nodetool.dtl
@@ -75,7 +75,7 @@ process_args([Arg | Rest], Acc, Opts) ->
start_epmd() ->
- [] = os:cmd(epmd_path() ++ " -daemon"),
+ [] = os:cmd("\"" ++ epmd_path() ++ "\" -daemon"),
ok.
epmd_path() ->
diff --git a/src/relx.erl b/src/relx.erl
index 359d47d..79eeae2 100644
--- a/src/relx.erl
+++ b/src/relx.erl
@@ -203,6 +203,7 @@ opt_spec_list() ->
"Provide an app name and a directory to override in the form <appname>:<app directory>"},
{config, $c, "config", {string, ""}, "The path to a config file"},
{overlay_vars, undefined, "overlay_vars", string, "Path to a file of overlay variables"},
+ {sys_config, undefined, "sys_config", string, "Path to a file to use for sys.config"},
{system_libs, undefined, "system_libs", string, "Path to dir of Erlang system libs"},
{version, undefined, "version", undefined, "Print relx version"},
{root_dir, $r, "root", string, "The project root directory"}].
diff --git a/src/rlx_app_discovery.erl b/src/rlx_app_discovery.erl
index aaa6e86..4a5620b 100644
--- a/src/rlx_app_discovery.erl
+++ b/src/rlx_app_discovery.erl
@@ -52,8 +52,56 @@ format_error(ErrorDetails)
%%%===================================================================
%%% Internal Functions
%%%===================================================================
+
+-spec app_files(list(binary())) -> list(binary()).
+app_files(LibDirs) ->
+ lists:foldl(fun(LibDir, Acc) ->
+ Files = app_files_paths(LibDir),
+ BinFiles = lists:map(fun(F) ->
+ list_to_binary(F)
+ end, Files),
+ Acc ++ BinFiles
+ end, [], LibDirs).
+
+-spec app_files_paths(binary()) -> list(string()).
+app_files_paths(LibDir) ->
+ %% Search for Erlang apps in the lib dir itself
+ Path1 = filename:join([binary_to_list(LibDir),
+ "*.app"]),
+ %% Search for Erlang apps in subdirs of lib dir
+ Path2 = filename:join([binary_to_list(LibDir),
+ "*",
+ "ebin",
+ "*.app"]),
+ lists:foldl(fun(Path, Acc) ->
+ Files = filelib:wildcard(Path),
+ Files ++ Acc
+ end, [], [Path1, Path2]).
+
+-spec get_app_metadata(rlx_state:t(), list(binary())) -> list({ok, rlx_app_info:t()}).
+get_app_metadata(State, LibDirs) ->
+ lists:foldl(fun(AppFile, Acc) ->
+ case is_valid_otp_app(AppFile) of
+ {ok, _} = AppMeta ->
+ [AppMeta|Acc];
+ {warning, W} ->
+ ec_cmd_log:warn(rlx_state:log(State), format_detail(W)),
+ Acc;
+ {error, E} ->
+ ec_cmd_log:error(rlx_state:log(State), format_detail(E)),
+ Acc;
+ _ ->
+ Acc
+ end
+ end, [], app_files(LibDirs)).
+
resolve_app_metadata(State, LibDirs) ->
- AppMeta0 = lists:flatten(rlx_dscv_util:do(fun discover_dir/2, LibDirs)),
+ AppMeta0 = case rlx_state:get(State, enable_shallow_app_discovery, false) of
+ true ->
+ get_app_metadata(State, LibDirs);
+ false ->
+ lists:flatten(rlx_dscv_util:do(fun discover_dir/2, LibDirs))
+ end,
case [case Err of
{error, Ret} ->
Ret
@@ -139,9 +187,10 @@ discover_dir(_File, directory) ->
discover_dir(File, file) ->
is_valid_otp_app(File).
--spec is_valid_otp_app(file:name()) -> {ok, rlx_app_info:t()} | {error, Reason::term()} |
+-spec is_valid_otp_app(file:name()) -> {ok, rlx_app_info:t()} |
+ {warning, Reason::term()} |
+ {error, Reason::term()} |
{noresult, false}.
-
is_valid_otp_app(File) ->
%% Is this an ebin dir?
EbinDir = filename:dirname(File),
@@ -159,7 +208,9 @@ is_valid_otp_app(File) ->
-spec gather_application_info(file:name(), file:filename()) ->
- {ok, rlx_app_info:t()} | {error, Reason::term()}.
+ {ok, rlx_app_info:t()} |
+ {warning, Reason::term()} |
+ {error, Reason::term()}.
gather_application_info(EbinDir, File) ->
AppDir = filename:dirname(EbinDir),
case file:consult(File) of
@@ -175,7 +226,9 @@ gather_application_info(EbinDir, File) ->
file:name(),
atom(),
proplists:proplist()) ->
- {ok, list()} | {error, Reason::term()}.
+ {ok, list()} |
+ {warning, Reason::term()} |
+ {error, Reason::term()}.
validate_application_info(EbinDir, AppFile, AppName, AppDetail) ->
AppDir = filename:dirname(EbinDir),
case get_modules_list(AppFile, AppDetail) of
@@ -190,7 +243,9 @@ validate_application_info(EbinDir, AppFile, AppName, AppDetail) ->
end.
-spec get_modules_list(file:name(), proplists:proplist()) ->
- {ok, list()} | {error, Reason::term()}.
+ {ok, list()} |
+ {warning, Reason::term()} |
+ {error, Reason::term()}.
get_modules_list(AppFile, AppDetail) ->
case proplists:get_value(modules, AppDetail) of
undefined ->
diff --git a/src/rlx_cmd_args.erl b/src/rlx_cmd_args.erl
index d2402ce..da8f3a0 100644
--- a/src/rlx_cmd_args.erl
+++ b/src/rlx_cmd_args.erl
@@ -253,7 +253,13 @@ create_disable_default_libs(Opts, Acc) ->
{ok, rlx_state:cmd_args()} | relx:error().
create_overlay_vars(Opts, Acc) ->
OverlayVars = proplists:get_all_values(overlay_vars, Opts),
- create_system_libs(Opts, [{overlay_vars, OverlayVars} | Acc]).
+ create_sys_config(Opts, [{overlay_vars, OverlayVars} | Acc]).
+
+-spec create_sys_config([getopt:option()], rlx_state:cmd_args()) ->
+ {ok, rlx_state:cmd_args()} | relx:error().
+create_sys_config(Opts, Acc) ->
+ SysConfig = proplists:get_value(sys_config, Opts, undefined),
+ create_system_libs(Opts, [{sys_config, SysConfig} | Acc]).
-spec create_system_libs([getopt:option()], rlx_state:cmd_args()) ->
{ok, rlx_state:cmd_args()} | relx:error().
diff --git a/src/rlx_prv_assembler.erl b/src/rlx_prv_assembler.erl
index 1a24ab6..d7bf3f1 100644
--- a/src/rlx_prv_assembler.erl
+++ b/src/rlx_prv_assembler.erl
@@ -296,9 +296,10 @@ write_bin_file(State, Release, OutputDir, RelDir) ->
VsnRel = filename:join(BinDir, rlx_release:canonical_name(Release)),
BareRel = filename:join(BinDir, RelName),
ErlOpts = rlx_state:get(State, erl_opts, ""),
+ {OsFamily, _OsName} = os:type(),
StartFile = case rlx_state:get(State, extended_start_script, false) of
false ->
- bin_file_contents(RelName, RelVsn,
+ bin_file_contents(OsFamily, RelName, RelVsn,
rlx_release:erts(Release),
ErlOpts);
true ->
@@ -321,7 +322,7 @@ write_bin_file(State, Release, OutputDir, RelDir) ->
false ->
ok
end,
- extended_bin_file_contents(RelName, RelVsn, rlx_release:erts(Release), ErlOpts)
+ extended_bin_file_contents(OsFamily, RelName, RelVsn, rlx_release:erts(Release), ErlOpts)
end,
%% We generate the start script by default, unless the user
%% tells us not too
@@ -329,15 +330,34 @@ write_bin_file(State, Release, OutputDir, RelDir) ->
false ->
ok;
_ ->
- ok = file:write_file(VsnRel, StartFile),
- ok = file:change_mode(VsnRel, 8#777),
- ok = file:write_file(BareRel, StartFile),
- ok = file:change_mode(BareRel, 8#777)
+ VsnRelStartFile = case OsFamily of
+ unix -> VsnRel;
+ win32 -> string:concat(VsnRel, ".cmd")
+ end,
+ ok = file:write_file(VsnRelStartFile, StartFile),
+ ok = file:change_mode(VsnRelStartFile, 8#777),
+ BareRelStartFile = case OsFamily of
+ unix -> BareRel;
+ win32 -> string:concat(BareRel, ".cmd")
+ end,
+ ok = file:write_file(BareRelStartFile, StartFile),
+ ok = file:change_mode(BareRelStartFile, 8#777)
end,
+ ReleasesDir = filename:join(OutputDir, "releases"),
+ generate_start_erl_data_file(Release, ReleasesDir),
copy_or_generate_vmargs_file(State, Release, RelDir),
copy_or_generate_sys_config_file(State, RelDir),
include_erts(State, Release, OutputDir, RelDir).
+%% @doc generate a start_erl.data file
+-spec generate_start_erl_data_file(rlx_release:t(), file:name()) ->
+ ok | relx:error().
+generate_start_erl_data_file(Release, ReleasesDir) ->
+ ErtsVersion = rlx_release:erts(Release),
+ ReleaseVersion = rlx_release:vsn(Release),
+ Data = ErtsVersion ++ " " ++ ReleaseVersion,
+ ok = file:write_file(filename:join(ReleasesDir, "start_erl.data"), Data).
+
%% @doc copy vm.args or generate one to releases/VSN/vm.args
-spec copy_or_generate_vmargs_file(rlx_state:t(), rlx_release:t(), file:name()) ->
{ok, rlx_state:t()} | relx:error().
@@ -390,29 +410,61 @@ copy_or_symlink_config_file(State, ConfigPath, RelConfPath) ->
-spec include_erts(rlx_state:t(), rlx_release:t(), file:name(), file:name()) ->
{ok, rlx_state:t()} | relx:error().
include_erts(State, Release, OutputDir, RelDir) ->
- case rlx_state:get(State, include_erts, true) of
- true ->
- Prefix = code:root_dir(),
+ Prefix = case rlx_state:get(State, include_erts, true) of
+ false ->
+ false;
+ true ->
+ code:root_dir();
+ P ->
+ filename:absname(P)
+ end,
+
+ case Prefix of
+ false ->
+ make_boot_script(State, Release, OutputDir, RelDir);
+ _ ->
+ ec_cmd_log:info(rlx_state:log(State),
+ "Including Erts from ~s~n", [Prefix]),
ErtsVersion = rlx_release:erts(Release),
ErtsDir = filename:join([Prefix, "erts-" ++ ErtsVersion]),
LocalErts = filename:join([OutputDir, "erts-" ++ ErtsVersion]),
+ {OsFamily, _OsName} = os:type(),
case filelib:is_dir(ErtsDir) of
false ->
?RLX_ERROR({specified_erts_does_not_exist, ErtsVersion});
true ->
ok = ec_file:mkdir_p(LocalErts),
ok = ec_file:copy(ErtsDir, LocalErts, [recursive]),
- Erl = filename:join([LocalErts, "bin", "erl"]),
- ok = ec_file:remove(Erl),
- ok = file:write_file(Erl, erl_script(ErtsVersion)),
- ok = file:change_mode(Erl, 8#755),
+ case OsFamily of
+ unix ->
+ Erl = filename:join([LocalErts, "bin", "erl"]),
+ ok = ec_file:remove(Erl),
+ ok = file:write_file(Erl, erl_script(ErtsVersion)),
+ ok = file:change_mode(Erl, 8#755);
+ win32 ->
+ ErlIni = filename:join([LocalErts, "bin", "erl.ini"]),
+ ok = ec_file:remove(ErlIni),
+ ok = file:write_file(ErlIni, erl_ini(OutputDir, ErtsVersion))
+ end,
+ case rlx_state:get(State, extended_start_script, false) of
+ true ->
+ ok = ec_file:copy(filename:join([Prefix, "bin", "start_clean.boot"]),
+ filename:join([OutputDir, "bin", "start_clean.boot"])),
+ NodeToolFile = nodetool_contents(),
+ InstallUpgradeFile = install_upgrade_escript_contents(),
+ NodeTool = filename:join([LocalErts, "bin", "nodetool"]),
+ InstallUpgrade = filename:join([LocalErts, "bin", "install_upgrade.escript"]),
+ ok = file:write_file(NodeTool, NodeToolFile),
+ ok = file:write_file(InstallUpgrade, InstallUpgradeFile),
+ ok = file:change_mode(NodeTool, 8#755),
+ ok = file:change_mode(InstallUpgrade, 8#755);
+ false ->
+ ok
+ end,
make_boot_script(State, Release, OutputDir, RelDir)
- end;
- _ ->
- make_boot_script(State, Release, OutputDir, RelDir)
+ end
end.
-
-spec make_boot_script(rlx_state:t(), rlx_release:t(), file:name(), file:name()) ->
{ok, rlx_state:t()} | relx:error().
make_boot_script(State, Release, OutputDir, RelDir) ->
@@ -475,16 +527,19 @@ make_relup(State, Release) ->
make_tar(State, Release, OutputDir) ->
Name = atom_to_list(rlx_release:name(Release)),
Vsn = rlx_release:vsn(Release),
- Prefix = code:root_dir(),
ErtsVersion = rlx_release:erts(Release),
- ErtsDir = filename:join([Prefix]),
Opts = [{path, [filename:join([OutputDir, "lib", "*", "ebin"])]},
{outdir, OutputDir} |
case rlx_state:get(State, include_erts, true) of
true ->
+ Prefix = code:root_dir(),
+ ErtsDir = filename:join([Prefix]),
[{erts, ErtsDir}];
false ->
- []
+ [];
+ Prefix ->
+ ErtsDir = filename:join([Prefix]),
+ [{erts, ErtsDir}]
end],
case systools:make_tar(filename:join([OutputDir, "releases", Vsn, Name]),
Opts) of
@@ -519,10 +574,10 @@ update_tar(State, TempDir, OutputDir, Name, Vsn, ErtsVersion) ->
filename:join([OutputDir, "releases", Vsn, "vm.args"])},
{"bin", filename:join([OutputDir, "bin"])} |
case rlx_state:get(State, include_erts, true) of
- true ->
- [{"erts-"++ErtsVersion, filename:join(OutputDir, "erts-"++ErtsVersion)}];
false ->
- []
+ [];
+ _ ->
+ [{"erts-"++ErtsVersion, filename:join(OutputDir, "erts-"++ErtsVersion)}]
end], [compressed]),
ec_cmd_log:info(rlx_state:log(State),
"tarball ~s successfully created!~n", [TarFile]),
@@ -648,13 +703,26 @@ ensure_not_exist(RelConfPath) ->
erl_script(ErtsVsn) ->
render(erl_script_dtl, [{erts_vsn, ErtsVsn}]).
-bin_file_contents(RelName, RelVsn, ErtsVsn, ErlOpts) ->
- render(bin_dtl, [{rel_name, RelName}, {rel_vsn, RelVsn},
- {erts_vsn, ErtsVsn}, {erl_opts, ErlOpts}]).
+bin_file_contents(OsFamily, RelName, RelVsn, ErtsVsn, ErlOpts) ->
+ Template = case OsFamily of
+ unix -> bin_dtl;
+ win32 -> bin_windows_dtl
+ end,
+ render(Template, [{rel_name, RelName}, {rel_vsn, RelVsn},
+ {erts_vsn, ErtsVsn}, {erl_opts, ErlOpts}]).
+
+extended_bin_file_contents(OsFamily, RelName, RelVsn, ErtsVsn, ErlOpts) ->
+ Template = case OsFamily of
+ unix -> extended_bin_dtl;
+ win32 -> extended_bin_windows_dtl
+ end,
+ render(Template, [{rel_name, RelName}, {rel_vsn, RelVsn},
+ {erts_vsn, ErtsVsn}, {erl_opts, ErlOpts}]).
-extended_bin_file_contents(RelName, RelVsn, ErtsVsn, ErlOpts) ->
- render(extended_bin_dtl, [{rel_name, RelName}, {rel_vsn, RelVsn},
- {erts_vsn, ErtsVsn}, {erl_opts, ErlOpts}]).
+erl_ini(OutputDir, ErtsVsn) ->
+ ErtsDirName = string:concat("erts-", ErtsVsn),
+ BinDir = filename:join([OutputDir, ErtsDirName, bin]),
+ render(erl_ini_dtl, [{bin_dir, BinDir}, {output_dir, OutputDir}]).
install_upgrade_escript_contents() ->
render(install_upgrade_escript_dtl).
diff --git a/src/rlx_prv_config.erl b/src/rlx_prv_config.erl
index 0379c2f..da620be 100644
--- a/src/rlx_prv_config.erl
+++ b/src/rlx_prv_config.erl
@@ -188,7 +188,12 @@ load_terms({release, {RelName, Vsn}, {erts, ErtsVsn},
load_terms({vm_args, VmArgs}, {ok, State}) ->
{ok, rlx_state:vm_args(State, filename:absname(VmArgs))};
load_terms({sys_config, SysConfig}, {ok, State}) ->
- {ok, rlx_state:sys_config(State, filename:absname(SysConfig))};
+ case rlx_state:sys_config(State) of
+ undefined ->
+ {ok, rlx_state:sys_config(State, filename:absname(SysConfig))};
+ _ ->
+ {ok, State}
+ end;
load_terms({output_dir, OutputDir}, {ok, State}) ->
{ok, rlx_state:output_dir(State, filename:absname(OutputDir))};
load_terms({overlay_vars, OverlayVars}, {ok, State}) ->
diff --git a/src/rlx_state.erl b/src/rlx_state.erl
index 2d0493b..0d4051e 100644
--- a/src/rlx_state.erl
+++ b/src/rlx_state.erl
@@ -129,6 +129,7 @@ new(PropList, Targets)
output_dir=proplists:get_value(output_dir, PropList, ""),
lib_dirs=[to_binary(Dir) || Dir <- proplists:get_value(lib_dirs, PropList, [])],
config_file=proplists:get_value(config, PropList, undefined),
+ sys_config=proplists:get_value(sys_config, PropList, undefined),
dev_mode = proplists:get_value(dev_mode, PropList),
actions = Targets,
caller = Caller,
diff --git a/test/rlx_discover_SUITE.erl b/test/rlx_discover_SUITE.erl
index 0fb1ad7..8ea95c4 100644
--- a/test/rlx_discover_SUITE.erl
+++ b/test/rlx_discover_SUITE.erl
@@ -26,7 +26,9 @@
all/0,
normal_case/1,
no_beam_case/1,
- bad_ebin_case/1]).
+ bad_ebin_case/1,
+ shallow_app_discovery/1
+ ]).
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
@@ -53,7 +55,7 @@ init_per_testcase(_, Config) ->
all() ->
- [normal_case, no_beam_case, bad_ebin_case].
+ [normal_case, no_beam_case, bad_ebin_case, shallow_app_discovery].
normal_case(Config) ->
LibDir1 = proplists:get_value(lib1, Config),
@@ -83,8 +85,7 @@ normal_case(Config) ->
lists:foreach(fun(App) ->
?assertMatch(true, lists:member(App, rlx_state:available_apps(State2)))
end, Apps2),
- Length = erlang:length(Apps2) +
- erlang:length(Apps2),
+ Length = erlang:length(Apps1) + erlang:length(Apps2),
?assertMatch(Length, erlang:length(rlx_state:available_apps(State2))).
no_beam_case(Config) ->
@@ -146,6 +147,38 @@ bad_ebin_case(Config) ->
?assertMatch([], [App || App <- rlx_state:available_apps(State2),
BadName =:= rlx_app_info:name(App)]).
+shallow_app_discovery(Config) ->
+ LibDir1 = proplists:get_value(lib1, Config),
+ Apps1 = [(fun({Name, Vsn}) ->
+ create_app(LibDir1, Name, Vsn)
+ end)(App)
+ ||
+ App <-
+ [{create_random_name("lib_app1_"), create_random_vsn()}
+ || _ <- lists:seq(1, 100)]],
+
+ LibDir2 = proplists:get_value(lib2, Config),
+ Apps2 = [(fun({Name, Vsn}) ->
+ create_app(LibDir2, Name, Vsn)
+ end)(App)
+ || App <-
+ [{create_random_name("lib_app2_"), create_random_vsn()}
+ || _ <- lists:seq(1, 100)]],
+ State0 = rlx_state:put(proplists:get_value(state, Config),
+ default_libs, false),
+ State1 = rlx_state:put(State0, enable_shallow_app_discovery, true),
+ {DiscoverProvider, {ok, State2}} = rlx_provider:new(rlx_prv_discover, State1),
+ {ok, State3} = rlx_provider:do(DiscoverProvider, State2),
+ lists:foreach(fun(App) ->
+ ?assertMatch(true, lists:member(App, rlx_state:available_apps(State3)))
+ end, Apps1),
+
+ lists:foreach(fun(App) ->
+ ?assertMatch(true, lists:member(App, rlx_state:available_apps(State3)))
+ end, Apps2),
+ Length = erlang:length(Apps1) + erlang:length(Apps2),
+ ?assertMatch(Length, erlang:length(rlx_state:available_apps(State3))).
+
%%%===================================================================
%%% Helper functions
%%%===================================================================