選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

219 行
8.5 KiB

  1. %% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
  2. %% ex: ts=4 sw=4 et
  3. %% -------------------------------------------------------------------
  4. %%
  5. %% rebar: Erlang Build Tools
  6. %%
  7. %% Copyright (c) 2011 Joe Williams (joe@joetify.com)
  8. %%
  9. %% Permission is hereby granted, free of charge, to any person obtaining a copy
  10. %% of this software and associated documentation files (the "Software"), to deal
  11. %% in the Software without restriction, including without limitation the rights
  12. %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. %% copies of the Software, and to permit persons to whom the Software is
  14. %% furnished to do so, subject to the following conditions:
  15. %%
  16. %% The above copyright notice and this permission notice shall be included in
  17. %% all copies or substantial portions of the Software.
  18. %%
  19. %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. %% THE SOFTWARE.
  26. %% ------------------------------------------------------------------
  27. -module(rebar_appups).
  28. -include("rebar.hrl").
  29. -export(['generate-appups'/2]).
  30. %% for internal use only
  31. -export([info/2]).
  32. -define(APPUPFILEFORMAT, "%% appup generated for ~p by rebar (~p)~n"
  33. "{~p, [{~p, ~p}], [{~p, []}]}.~n").
  34. %% ====================================================================
  35. %% Public API
  36. %% ====================================================================
  37. 'generate-appups'(Config, ReltoolFile) ->
  38. %% Get the old release path
  39. {Config1, ReltoolConfig} = rebar_rel_utils:load_config(Config, ReltoolFile),
  40. TargetParentDir = rebar_rel_utils:get_target_parent_dir(Config,
  41. ReltoolConfig),
  42. PrevRelPath = rebar_rel_utils:get_previous_release_path(Config),
  43. OldVerPath = filename:join([TargetParentDir, PrevRelPath]),
  44. ModDeps = rebar_config:get(Config, module_deps, []),
  45. %% Get the new and old release name and versions
  46. {Name, _Ver} = rebar_rel_utils:get_reltool_release_info(ReltoolConfig),
  47. NewVerPath = filename:join([TargetParentDir, Name]),
  48. {NewName, NewVer} = rebar_rel_utils:get_rel_release_info(Name, NewVerPath),
  49. {OldName, OldVer} = rebar_rel_utils:get_rel_release_info(Name, OldVerPath),
  50. %% Run some simple checks
  51. true = rebar_utils:prop_check(NewVer =/= OldVer,
  52. "New and old .rel versions match~n", []),
  53. true = rebar_utils:prop_check(
  54. NewName == OldName,
  55. "Reltool and .rel release names do not match~n", []),
  56. %% Find all the apps that have been upgraded
  57. {_Added, _Removed, Upgraded} = get_apps(Name, OldVerPath, NewVerPath),
  58. %% Get a list of any appup files that exist in the new release
  59. NewAppUpFiles = rebar_utils:find_files(
  60. filename:join([NewVerPath, "lib"]), "^.*.appup$"),
  61. %% Convert the list of appup files into app names
  62. AppUpApps = [file_to_name(File) || File <- NewAppUpFiles],
  63. %% Create a list of apps that don't already have appups
  64. UpgradeApps = genappup_which_apps(Upgraded, AppUpApps),
  65. %% Generate appup files for upgraded apps
  66. generate_appup_files(NewVerPath, OldVerPath, ModDeps, UpgradeApps),
  67. {ok, Config1}.
  68. %% ===================================================================
  69. %% Internal functions
  70. %% ===================================================================
  71. info(help, 'generate-appups') ->
  72. ?CONSOLE("Generate appup files.~n"
  73. "~n"
  74. "Valid command line options:~n"
  75. " previous_release=path~n",
  76. []).
  77. get_apps(Name, OldVerPath, NewVerPath) ->
  78. OldApps = rebar_rel_utils:get_rel_apps(Name, OldVerPath),
  79. ?DEBUG("Old Version Apps: ~p~n", [OldApps]),
  80. NewApps = rebar_rel_utils:get_rel_apps(Name, NewVerPath),
  81. ?DEBUG("New Version Apps: ~p~n", [NewApps]),
  82. Added = app_list_diff(NewApps, OldApps),
  83. ?DEBUG("Added: ~p~n", [Added]),
  84. Removed = app_list_diff(OldApps, NewApps),
  85. ?DEBUG("Removed: ~p~n", [Removed]),
  86. PossiblyUpgraded = proplists:get_keys(NewApps),
  87. UpgradedApps = [upgraded_app(AppName,
  88. proplists:get_value(AppName, OldApps),
  89. proplists:get_value(AppName, NewApps))
  90. || AppName <- PossiblyUpgraded],
  91. Upgraded = lists:dropwhile(fun(Elem) ->
  92. Elem == false
  93. end, lists:sort(UpgradedApps)),
  94. ?DEBUG("Upgraded: ~p~n", [Upgraded]),
  95. {Added, Removed, Upgraded}.
  96. upgraded_app(AppName, OldAppVer, NewAppVer) when OldAppVer /= NewAppVer ->
  97. {AppName, {OldAppVer, NewAppVer}};
  98. upgraded_app(_, _, _) ->
  99. false.
  100. app_list_diff(List1, List2) ->
  101. List3 = lists:umerge(lists:sort(proplists:get_keys(List1)),
  102. lists:sort(proplists:get_keys(List2))),
  103. List3 -- proplists:get_keys(List2).
  104. file_to_name(File) ->
  105. filename:rootname(filename:basename(File)).
  106. genappup_which_apps(UpgradedApps, [First|Rest]) ->
  107. List = proplists:delete(list_to_atom(First), UpgradedApps),
  108. genappup_which_apps(List, Rest);
  109. genappup_which_apps(Apps, []) ->
  110. Apps.
  111. generate_appup_files(NewVerPath, OldVerPath, ModDeps, [{_App, {undefined, _}}|Rest]) ->
  112. generate_appup_files(NewVerPath, OldVerPath, ModDeps, Rest);
  113. generate_appup_files(NewVerPath, OldVerPath, ModDeps, [{App, {OldVer, NewVer}}|Rest]) ->
  114. OldEbinDir = filename:join([OldVerPath, "lib",
  115. atom_to_list(App) ++ "-" ++ OldVer, "ebin"]),
  116. NewEbinDir = filename:join([NewVerPath, "lib",
  117. atom_to_list(App) ++ "-" ++ NewVer, "ebin"]),
  118. {AddedFiles, DeletedFiles, ChangedFiles} = beam_lib:cmp_dirs(NewEbinDir,
  119. OldEbinDir),
  120. ChangedNames = [list_to_atom(file_to_name(F)) || {F, _} <- ChangedFiles],
  121. ModDeps1 = [{N, [M1 || M1 <- M, lists:member(M1, ChangedNames)]}
  122. || {N, M} <- ModDeps],
  123. Added = [generate_instruction(added, File) || File <- AddedFiles],
  124. Deleted = [generate_instruction(deleted, File) || File <- DeletedFiles],
  125. Changed = [generate_instruction(changed, ModDeps1, File)
  126. || File <- ChangedFiles],
  127. Inst = lists:append([Added, Deleted, Changed]),
  128. AppUpFile = filename:join([NewEbinDir, atom_to_list(App) ++ ".appup"]),
  129. ok = file:write_file(AppUpFile,
  130. io_lib:fwrite(?APPUPFILEFORMAT,
  131. [App, rebar_utils:now_str(), NewVer,
  132. OldVer, Inst, OldVer])),
  133. ?CONSOLE("Generated appup for ~p~n", [App]),
  134. generate_appup_files(NewVerPath, OldVerPath, ModDeps, Rest);
  135. generate_appup_files(_, _, _, []) ->
  136. ?CONSOLE("Appup generation complete~n", []).
  137. generate_instruction(added, File) ->
  138. Name = list_to_atom(file_to_name(File)),
  139. {add_module, Name};
  140. generate_instruction(deleted, File) ->
  141. Name = list_to_atom(file_to_name(File)),
  142. {delete_module, Name}.
  143. generate_instruction(changed, ModDeps, {File, _}) ->
  144. {ok, {Name, List}} = beam_lib:chunks(File, [attributes, exports]),
  145. Behavior = get_behavior(List),
  146. CodeChange = is_code_change(List),
  147. Deps = proplists:get_value(Name, ModDeps, []),
  148. generate_instruction_advanced(Name, Behavior, CodeChange, Deps).
  149. generate_instruction_advanced(Name, undefined, undefined, Deps) ->
  150. %% Not a behavior or code change, assume purely functional
  151. {load_module, Name, Deps};
  152. generate_instruction_advanced(Name, [supervisor], _, _) ->
  153. %% Supervisor
  154. {update, Name, supervisor};
  155. generate_instruction_advanced(Name, _, code_change, Deps) ->
  156. %% Includes code_change export
  157. {update, Name, {advanced, []}, Deps};
  158. generate_instruction_advanced(Name, _, _, Deps) ->
  159. %% Anything else
  160. {load_module, Name, Deps}.
  161. get_behavior(List) ->
  162. Attributes = proplists:get_value(attributes, List),
  163. case proplists:get_value(behavior, Attributes) of
  164. undefined -> proplists:get_value(behaviour, Attributes);
  165. Else -> Else
  166. end.
  167. is_code_change(List) ->
  168. Exports = proplists:get_value(exports, List),
  169. case proplists:is_defined(code_change, Exports) of
  170. true ->
  171. code_change;
  172. false ->
  173. undefined
  174. end.