%%--------------------------------------------------------------------
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%-----------------------------------------------------------------
%% File: erlresolvelinks.erl
%%
%% Description:
%% This file generates the javascript that resolves documentation links.
%%
%%-----------------------------------------------------------------
-module(erlresolvelinks).
-export([make/1]).
-include_lib("kernel/include/file.hrl").
-define(JAVASCRIPT_NAME, "erlresolvelinks.js").
make([ErlTop, RootDir, DestDir]) ->
make(ErlTop, RootDir, DestDir).
make(ErlTop, RootDir, DestDir) ->
%% doc/Dir
%% erts-Vsn
%% lib/App-Vsn
Name = ?JAVASCRIPT_NAME,
DocDirs0 = get_dirs(filename:join([ErlTop, "system/doc"])),
DocDirs = lists:map(fun({Dir, _DirPath}) ->
D = filename:join(["doc", Dir]),
{D, D} end, DocDirs0),
Released = ErlTop /= RootDir,
ErtsDirs = latest_app_dirs(Released, RootDir, ""),
AppDirs = latest_app_dirs(Released, RootDir, "lib"),
AllAppDirs =
lists:map(
fun({App, AppVsn}) -> {App, filename:join([AppVsn, "doc", "html"])}
end, ErtsDirs ++ AppDirs),
AllDirs = DocDirs ++ AllAppDirs,
{ok, Fd} = file:open(filename:join([DestDir, Name]), [write]),
UTC = calendar:universal_time(),
io:fwrite(Fd, "/* Generated by ~s at ~w UTC */\n",
[atom_to_list(?MODULE), UTC]),
io:fwrite(Fd, "function erlhref(ups, app, rest) {\n", []),
io:fwrite(Fd, " switch(app) {\n", []),
lists:foreach(
fun({Tag, Dir}) ->
io:fwrite(Fd, " case ~p:\n", [Tag]),
io:fwrite(Fd, " location.href=ups + \"~s/\" + rest;\n",
[Dir]),
io:fwrite(Fd, " break;\n", [])
end, AllDirs),
io:fwrite(Fd, " default:\n", []),
io:fwrite(Fd, " location.href=ups + \"Unresolved\";\n", []),
io:fwrite(Fd, " }\n", []),
io:fwrite(Fd, "}\n", []),
file:close(Fd),
ok.
get_dirs(Dir) ->
{ok, Files} = file:list_dir(Dir),
AFiles =
lists:map(fun(File) -> {File, filename:join([Dir, File])} end, Files),
lists:zf(fun is_dir/1, AFiles).
is_dir({File, AFile}) ->
{ok, FileInfo} = file:read_file_info(AFile),
case FileInfo#file_info.type of
directory ->
{true, {File, AFile}};
_ ->
false
end.
released_app_vsns([]) ->
[];
released_app_vsns([{AppVsn, Dir} | AVDirs]) ->
try
{ok, _} = file:read_file_info(filename:join([Dir, "doc", "html"])),
[App, Vsn] = string:tokens(AppVsn, "-"),
VsnNumList = vsnstr_to_numlist(Vsn),
[_Maj, _Min | _] = VsnNumList,
[{{App, VsnNumList}, AppVsn} | released_app_vsns(AVDirs)]
catch
_:_ -> released_app_vsns(AVDirs)
end.
latest_app_dirs(Release, RootDir, Dir) ->
ADir = filename:join(RootDir, Dir),
RDirs0 = get_dirs(ADir),
SDirs0 = case Release of
true ->
released_app_vsns(RDirs0);
false ->
lists:map(fun({App, Dir1}) ->
File = filename:join(Dir1, "vsn.mk"),
case file:read_file(File) of
{ok, Bin} ->
case re:run(Bin, ".*VSN\s*=\s*([0-9\.]+).*",[{capture,[1],list}]) of
{match, [VsnStr]} ->
VsnNumList = vsnstr_to_numlist(VsnStr),
{{App, VsnNumList}, App++"-"++VsnStr};
nomatch ->
io:format("No VSN variable found in ~s\n", [File]),
error
end;
{error, Reason} ->
io:format("~p : ~s\n", [Reason, File]),
error
end
end,
lists:filter(fun is_app_dir/1, RDirs0))
end,
SDirs1 = lists:keysort(1, SDirs0),
App2Dirs = lists:foldr(fun({{App, _VsnNumList}, AppVsn}, Acc) ->
case lists:keymember(App, 1, Acc) of
true ->
Acc;
false ->
[{App, AppVsn}| Acc]
end
end, [], SDirs1),
lists:map(fun({App, AppVsn}) -> {App, filename:join([Dir, AppVsn])} end,
App2Dirs).
is_app_dir({_Dir, DirPath}) ->
case file:read_file_info(filename:join(DirPath, "vsn.mk")) of
{ok, FileInfo} ->
case FileInfo#file_info.type of
regular ->
true;
_ ->
false
end;
{error, _Reason} ->
false
end.
%% is_vsnstr(Str) ->
%% case string:tokens(Str, ".") of
%% [_] ->
%% false;
%% Toks ->
%% lists:all(fun is_numstr/1, Toks)
%% end.
%% is_numstr(Cs) ->
%% lists:all(fun(C) when $0 =< C, C =< $9 ->
%% true;
%% (_) ->
%% false
%% end, Cs).
%% We know:
vsnstr_to_numlist(VsnStr) ->
lists:map(fun(NumStr) -> list_to_integer(NumStr) end,
string:tokens(VsnStr, ".")).