diff options
-rw-r--r-- | README.md | 13 | ||||
-rw-r--r-- | bootstrap.cmd | 12 | ||||
-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_windows.dtl | 176 | ||||
-rw-r--r-- | src/rlx_prv_assembler.erl | 88 |
6 files changed, 352 insertions, 16 deletions
@@ -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). |