diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | README.md | 16 | ||||
-rw-r--r-- | bootstrap.cmd | 12 | ||||
-rw-r--r-- | examples/relx.config | 4 | ||||
-rwxr-xr-x[-rw-r--r--] | priv/templates/bin.dtl | 68 | ||||
-rw-r--r-- | priv/templates/bin_windows.dtl | 75 | ||||
-rw-r--r-- | priv/templates/erl_ini.dtl | 4 | ||||
-rw-r--r-- | priv/templates/extended_bin.dtl | 248 | ||||
-rw-r--r-- | priv/templates/extended_bin_windows.dtl | 176 | ||||
-rw-r--r-- | priv/templates/nodetool.dtl | 2 | ||||
-rw-r--r-- | src/relx.erl | 1 | ||||
-rw-r--r-- | src/rlx_app_discovery.erl | 67 | ||||
-rw-r--r-- | src/rlx_cmd_args.erl | 8 | ||||
-rw-r--r-- | src/rlx_prv_assembler.erl | 126 | ||||
-rw-r--r-- | src/rlx_prv_config.erl | 7 | ||||
-rw-r--r-- | src/rlx_state.erl | 1 | ||||
-rw-r--r-- | test/rlx_discover_SUITE.erl | 41 |
17 files changed, 687 insertions, 170 deletions
@@ -12,3 +12,4 @@ test/*_data _rel/* .* erl_crash.dump +rebar @@ -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 %%%=================================================================== |