aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJordan Wilberding <[email protected]>2014-04-07 10:50:10 +0200
committerJordan Wilberding <[email protected]>2014-04-07 10:50:10 +0200
commit5561818c5620066899a6bf74ae59a873d94d8aef (patch)
tree2614d3b766862978523eb49e6dd3f6e71c73c21a
parent0d292ff4c1be173a775628c09d1ac9991fdd8e67 (diff)
parent879108a2aa4e463df1b14830661c0820fcdccb11 (diff)
downloadrelx-5561818c5620066899a6bf74ae59a873d94d8aef.tar.gz
relx-5561818c5620066899a6bf74ae59a873d94d8aef.tar.bz2
relx-5561818c5620066899a6bf74ae59a873d94d8aef.zip
Merge pull request #159 from nuex/windows
Windows Support
-rw-r--r--README.md13
-rw-r--r--bootstrap.cmd12
-rw-r--r--priv/templates/bin_windows.dtl75
-rw-r--r--priv/templates/erl_ini.dtl4
-rw-r--r--priv/templates/extended_bin_windows.dtl176
-rw-r--r--src/rlx_prv_assembler.erl88
6 files changed, 352 insertions, 16 deletions
diff --git a/README.md b/README.md
index 96fe0c3..32eaa3b 100644
--- a/README.md
+++ b/README.md
@@ -33,6 +33,19 @@ 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
-----------
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/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_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/src/rlx_prv_assembler.erl b/src/rlx_prv_assembler.erl
index 1a24ab6..3772611 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().
@@ -396,16 +416,39 @@ include_erts(State, Release, OutputDir, RelDir) ->
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;
_ ->
@@ -648,13 +691,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).