%%----------------------------------------------------
|
|
%% Erlang模块热更新到所有线路(包括server的回调函数,如果对state有影响时慎用)
|
|
%%
|
|
%% 检查:u:c() %% 列出前5分钟内编译过的文件
|
|
%% u:c(N) %% 列出前N分钟内编译过的文件
|
|
%%
|
|
%% 更新:u:u() %% 更新前5分钟内编译过的文件
|
|
%% u:u(N) %% 更新前N分钟内编译过的文件
|
|
%% u:u([mod_xx, ...]) %% 指定模块(不带后缀名)
|
|
%% u:u(m) %% 编译并加载文件
|
|
%%
|
|
%% Tips: u - update, c - check
|
|
%%
|
|
%% @author rolong@vip.qq.com
|
|
%%----------------------------------------------------
|
|
|
|
-module(u).
|
|
-compile(export_all).
|
|
-include_lib("kernel/include/file.hrl").
|
|
|
|
c() ->
|
|
c(5).
|
|
c(S) when is_integer(S) ->
|
|
c:cd("../ebin"),
|
|
case file:list_dir(".") of
|
|
{ok, FileList} ->
|
|
Files = get_new_file(FileList, S * 60),
|
|
info("---------check modules---------~n~w~n=========check modules=========", [Files]);
|
|
Any -> info("Error Dir: ~w", [Any])
|
|
end;
|
|
c([S]) when is_atom(S) ->
|
|
S1 = tool:to_integer(tool:to_list(S)),
|
|
case is_integer(S1) of
|
|
true ->
|
|
c:cd("../ebin"),
|
|
case file:list_dir(".") of
|
|
{ok, FileList} ->
|
|
Files = get_new_file(FileList, S * 60),
|
|
info("---------check modules---------~n~w~n=========check modules=========", [Files]);
|
|
Any -> info("Error Dir: ~w", [Any])
|
|
end;
|
|
_ ->
|
|
info("ERROR======> Badarg ~p/~p ~n", [S, S1])
|
|
end;
|
|
c(S) -> info("ERROR======> Badarg ~p ~n", [S]).
|
|
|
|
admin() ->
|
|
spawn(fun() -> u(m) end),
|
|
ok.
|
|
|
|
u() ->
|
|
u(5).
|
|
u(m) ->
|
|
StartTime = util:unixtime(),
|
|
info("----------makes----------", []),
|
|
c:cd("../"),
|
|
make:all(),
|
|
c:cd("ebin"),
|
|
EndTime = util:unixtime(),
|
|
Time = EndTime - StartTime,
|
|
info("Make Time : ~w s", [Time]),
|
|
u(Time / 60);
|
|
u(S) when is_number(S) ->
|
|
case file:list_dir(".") of
|
|
{ok, FileList} ->
|
|
Files = get_new_file(FileList, util:ceil(S * 60) + 3),
|
|
load(Files);
|
|
%% AllZone = mod_node_interface:server_list(),
|
|
%% info("---------modules---------~n~w~n----------nodes----------", [Files]),
|
|
%% loads(AllZone, Files);
|
|
Any -> info("Error Dir: ~w", [Any])
|
|
end;
|
|
u(Files) when is_list(Files) ->
|
|
load(Files);
|
|
%% AllZone = mod_node_interface:server_list(),
|
|
%% info("---------modules---------~n~w~n----------nodes----------", [Files]),
|
|
%% loads(AllZone, Files);
|
|
u(_) -> info("ERROR======> Badarg", []).
|
|
|
|
%% m(['src/data/*','src/lib/lib_goods.erl'])
|
|
m(Files) when is_list(Files) ->
|
|
StartTime = util:unixtime(),
|
|
info("----------makes----------~n~w~n", [Files]),
|
|
c:cd("../"),
|
|
Res = make:files(Files, [debug_info, {i, "include"}, {outdir, "ebin"}]),
|
|
c:cd("ebin"),
|
|
EndTime = util:unixtime(),
|
|
Time = EndTime - StartTime,
|
|
info("Make Time : ~w s", [Time]),
|
|
Res.
|
|
|
|
info(V) ->
|
|
info(V, []).
|
|
info(V, P) ->
|
|
io:format(V ++ "~n", P).
|
|
|
|
%% 更新到所有线路,暂时处理单节点的情况
|
|
%% loads([], _Files) -> ok;
|
|
%% loads([H | T], Files) ->
|
|
%% info("[~w]", [H#t_server_node.node]),
|
|
%% rpc:cast(H#t_server_node.node, u, load, [Files]),
|
|
%% loads(T, Files).
|
|
|
|
get_new_file(Files, S) ->
|
|
get_new_file(Files, S, []).
|
|
get_new_file([], _S, Result) -> Result;
|
|
get_new_file([H | T], S, Result) ->
|
|
NewResult = case string:tokens(H, ".") of
|
|
[Left, Right] when Right =:= "beam" ->
|
|
case file:read_file_info(H) of
|
|
{ok, FileInfo} ->
|
|
Now = calendar:local_time(),
|
|
case calendar:time_difference(FileInfo#file_info.mtime, Now) of
|
|
{Days, Times} ->
|
|
Seconds = calendar:time_to_seconds(Times),
|
|
case Days =:= 0 andalso Seconds < S of
|
|
true ->
|
|
FileName = list_to_atom(Left),
|
|
[FileName | Result];
|
|
false -> Result
|
|
end;
|
|
_ -> Result
|
|
end;
|
|
_ -> Result
|
|
end;
|
|
_ -> Result
|
|
end,
|
|
get_new_file(T, S, NewResult).
|
|
|
|
load([]) -> ok;
|
|
load([FileName | T]) ->
|
|
c:l(FileName),
|
|
info("loaded: ~w", [FileName]),
|
|
load(T).
|
|
% case code:soft_purge(FileName) of
|
|
% true ->
|
|
% case code:load_file(FileName) of
|
|
% {module, _} ->
|
|
% info("loaded: ~w", [FileName]),
|
|
% ok;
|
|
% %% info("loaded: ~w", [FileName]);
|
|
% {error, What} -> info("ERROR======> loading: ~w (~w)", [FileName, What])
|
|
% end;
|
|
% false -> info("ERROR======> Processes lingering : ~w [zone ~w] ", [FileName, srv_kernel:zone_id()])
|
|
% end,
|
|
% load(T).
|
|
|
|
|
|
a() ->
|
|
c(),
|
|
u().
|
|
|
|
%% @spec hotswap() -> ok
|
|
%% @doc 用于远程热更新
|
|
hotswap(NodeArg) ->
|
|
Node = util_data:to_atom(hd(NodeArg)),
|
|
net_adm:ping(Node),
|
|
rpc:call(Node, ?MODULE, do_hotswap, []).
|
|
|
|
do_hotswap() ->
|
|
CommandFile = "../../hotswap/hotswap_command.txt",
|
|
case filelib:is_file(CommandFile) andalso filelib:file_size(CommandFile) > 2 of
|
|
true ->
|
|
{{Y, M, D}, {H, I, S}} = erlang:localtime(),
|
|
TimeString = io_lib:format("[~w-~w-~w ~w:~w:~w]", [Y, M, D, H, I, S]),
|
|
try
|
|
info("===> command running ..."),
|
|
case file:open(CommandFile, read) of
|
|
{ok, IoDevice} ->
|
|
case file:eval(CommandFile) of
|
|
ok -> info("Update Time: ~s", [TimeString]);
|
|
{error, EvalErr} -> info("~s Eval Error:~w", [TimeString, EvalErr])
|
|
end,
|
|
info("command:"),
|
|
ResultData = parse(IoDevice, <<>>),
|
|
case byte_size(ResultData) < 5 of
|
|
true -> info("Empty Command (<5byte): ~s", [TimeString]);
|
|
false -> info("=> ~s", [ResultData])
|
|
end,
|
|
file:close(IoDevice),
|
|
file:delete(CommandFile);
|
|
{error, OpenErr} ->
|
|
info("~s Open Error:~w\n", [TimeString, OpenErr])
|
|
end
|
|
catch T : X ->
|
|
info("~s Error: ~w : ~w\nUpdate Server Is Stopped!\n", [TimeString, T, X])
|
|
end;
|
|
false -> ignore
|
|
end,
|
|
ok.
|
|
|
|
parse(IoDevice, D) ->
|
|
case io:get_line(IoDevice, '') of
|
|
eof ->
|
|
%% info("\n--- DONE ---"),
|
|
D;
|
|
Data ->
|
|
%% io:format("=> ~s", [Data]),
|
|
parse(IoDevice, list_to_binary([D, Data]))
|
|
end.
|
|
|
|
%% @doc 保证正确的工作路径
|
|
%% @spec make_sure_working_dir() -> any().
|
|
make_sure_working_dir() ->
|
|
Cwd = config:get_cwd(),
|
|
case file:get_cwd() == Cwd of
|
|
true ->
|
|
skip;
|
|
_ ->
|
|
c:cd(Cwd)
|
|
end.
|