@@ -15,8 +15,9 @@ defmodule Castle.SyntheticRelease do
1515 # applications - kernel, stdlib, sasl, compiler, elixir and castle
1616 # started, anything else on the code path only, which is the shape
1717 # Forecastle's preboot script has,
18- # * `releases/<vsn>/<name>.rel`, which is where the emulator version comes
19- # from, and
18+ # * the release file or files, which is where the emulator version comes
19+ # from - see `write_release_files/4`, since a version directory holds one
20+ # of them or two depending on how the version got there, and
2021 # * `releases/<vsn>/sys.config`.
2122 #
2223 # The boot script is generated by `systools` from the real application
@@ -25,6 +26,9 @@ defmodule Castle.SyntheticRelease do
2526
2627 @ started [ :kernel , :stdlib , :sasl , :compiler , :elixir , :castle ]
2728
29+ # The release name, which is what the release file or files are named after.
30+ @ name "synthetic"
31+
2832 @ doc """
2933 Builds a release under `root` and returns its version directory.
3034
@@ -36,6 +40,9 @@ defmodule Castle.SyntheticRelease do
3640 * `:apps` - `{app, vsn, dir}` triples to add to the code path without
3741 starting them, which is how a provider module that lives outside the
3842 applications preboot starts is reached
43+ * `:shape` - `:unpacked`, the default, or `:assembled`; which release files
44+ the version directory holds, and so how the version is to have got there.
45+ See `write_release_files/4`
3946 """
4047 def build ( root , opts \\ [ ] ) do
4148 vsn = Keyword . get ( opts , :vsn , "1.0.0" )
@@ -49,7 +56,7 @@ defmodule Castle.SyntheticRelease do
4956 apps = apps ++ for { app , app_vsn , dir } <- extra , do: { app , app_vsn , :none , dir }
5057
5158 make_boot_script ( vsn_dir , vsn , apps , link_apps ( lib , apps , extra ) )
52- write_rel ( Path . join ( vsn_dir , "synthetic.rel" ) , vsn , apps )
59+ write_release_files ( vsn_dir , vsn , apps , Keyword . get ( opts , :shape , :unpacked ) )
5360 write_sys_config ( vsn_dir , opts )
5461
5562 vsn_dir
@@ -159,8 +166,10 @@ defmodule Castle.SyntheticRelease do
159166 end
160167
161168 # `systools` writes the `.rel` it was given back out beside the boot script,
162- # and a version directory holds exactly one release file, so preboot's goes as
163- # soon as it has been used - which is what Mix does with its own.
169+ # so preboot's goes as soon as it has been used - which is what Mix does with
170+ # the release file of every boot script but `start`. Left there it would be a
171+ # release file no release has, and one `Castle.Peer` would be right to refuse
172+ # to choose between.
164173 defp make_boot_script ( vsn_dir , vsn , apps , paths ) do
165174 path = Path . join ( vsn_dir , "preboot" )
166175 write_rel ( path <> ".rel" , vsn , apps )
@@ -178,14 +187,41 @@ defmodule Castle.SyntheticRelease do
178187 File . rm! ( path <> ".rel" )
179188 end
180189
190+ # The release file, under the name or names the shape being built has.
191+ #
192+ # `:assembled` is what `mix release` leaves: one file, `<name>.rel`, the one
193+ # belonging to the `start` script, which Mix renames after the release.
194+ #
195+ # `:unpacked` is that plus `<name>-<vsn>.rel`, and is what
196+ # `release_handler:unpack_release/1` leaves behind - so it is the shape the
197+ # peer path meets, every version it is asked to configure having been unpacked
198+ # from a tarball. The second name is the copy Forecastle puts at the top of
199+ # the tarball's `releases` directory, which `release_handler` reads to admit
200+ # the version and then copies in here, for compatibility with tarballs that
201+ # carried no release file inside the version directory. Made by copying,
202+ # because copying is how the two come to hold the same bytes in a real
203+ # release: `File.cp!` at assembly, and a read and a write of the whole file on
204+ # unpack.
205+ #
206+ # The unpacked shape is the default because it is the one reality presents. A
207+ # test that means the assembled shape asks for it.
208+ defp write_release_files ( vsn_dir , vsn , apps , shape ) do
209+ assembled = Path . join ( vsn_dir , "#{ @ name } .rel" )
210+ write_rel ( assembled , vsn , apps )
211+
212+ if shape == :unpacked do
213+ File . cp! ( assembled , Path . join ( vsn_dir , "#{ @ name } -#{ vsn } .rel" ) )
214+ end
215+ end
216+
181217 defp write_rel ( path , vsn , apps ) do
182218 entries =
183219 for app_spec <- apps do
184220 { elem ( app_spec , 0 ) , to_charlist ( elem ( app_spec , 1 ) ) , elem ( app_spec , 2 ) }
185221 end
186222
187223 rel =
188- { :release , { ~c " synthetic " , to_charlist ( vsn ) } , { :erts , :erlang . system_info ( :version ) } ,
224+ { :release , { to_charlist ( @ name ) , to_charlist ( vsn ) } , { :erts , :erlang . system_info ( :version ) } ,
189225 entries }
190226
191227 File . write! ( path , :io_lib . format ( ~c" ~tp.~n" , [ rel ] ) )
0 commit comments