Current section

Files

Jump to
gpb build find-vsn
Raw

build/find-vsn

#!/usr/bin/env escript
%% -*- erlang -*-
-include_lib("kernel/include/file.hrl").
-mode(compile).
main(["--show-vsn"]) ->
case find_vsn() of
{ok, {Vsn, _Source}} ->
io:put_chars([Vsn, "\n"]),
do_exit(0);
{error, _} ->
print_no_vsn_error(),
do_exit(1)
end;
main(["--show-vsn-source"]) ->
case find_vsn() of
{ok, {_Vsn, Source}} ->
io:put_chars([atom_to_list(Source), "\n"]),
do_exit(0);
{error, _} ->
print_no_vsn_error(),
do_exit(1)
end;
main(["--have-vsn"]) ->
case find_vsn() of
{ok, {_Vsn, _Source}} ->
io:put_chars("true\n");
{error, _} ->
io:put_chars("false\n")
end,
do_exit(0);
main(["--check-have-vsn"]) ->
case find_vsn() of
{ok, {_Vsn, _Source}} ->
do_exit(0);
{error, _} ->
do_exit(1)
end;
main(_) ->
io:format("usage: ~s --show-vsn | --show-vsn-source~n",
[escript:script_name()]),
do_exit(1).
do_exit(ExitCode) ->
halt(ExitCode).
find_vsn() ->
case find_vsn_from_file() of
{ok, Vsn} ->
{ok, {Vsn, file}};
{error, Reason1} ->
case find_vsn_from_git() of
{ok, Vsn} ->
{ok, {Vsn, git}};
{error, Reason2} ->
{error, {Reason1, Reason2}}
end
end.
find_vsn_from_file() ->
%% assume in top dir
%% else move up until either .git or gpb.vsn??
%% though not across file systems?
%% or until dir looks like top of gpb (eg src/gpb.erl exists)
case file:get_cwd() of
{ok, Cwd} ->
try_walk_root_wise(Cwd, "gpb.vsn");
{error, Reason} ->
{error, {no_cwd, Reason}}
end.
try_walk_root_wise(Dir, Base) ->
case is_at_gpb_root(Dir) of
true ->
case file:read_file(filename:join(Dir, Base)) of
{ok, Vsn} ->
{ok, first_line_no_nl(binary_to_list(Vsn))};
{error, _} ->
{error, no_file}
end;
false ->
case up_dir(Dir) of
{ok, Up} -> try_walk_root_wise(Up, Base);
{error, Reason} -> {error, Reason}
end
end.
is_at_gpb_root(Dir) ->
lists:any(
fun(F) -> F() end,
[fun() -> filelib:is_dir(filename:join(Dir, ".git")) end,
fun() -> filelib:is_regular(fjoin(Dir, ".git")) end, % a git worktree?
fun() -> filelib:is_regular(fjoin(Dir, "gpb.vsn")) end,
fun() -> filelib:is_regular(fjoin([Dir, "src", "gpb.erl"])) end]).
fjoin(Dir, Name) -> filename:join(Dir, Name).
fjoin(Comps) -> filename:join(Comps).
up_dir(Dir) ->
case filename:dirname(Dir) of
Dir ->
{error, at_file_system_root};
"." -> % windows
{error, at_file_system_root};
Up ->
%% Check if we move cross
case {file:read_file_info(Dir), file:read_file_info(Up)} of
{{ok, #file_info{major_device=SameDev}},
{ok, #file_info{major_device=SameDev}}} ->
{ok, Up};
{{ok, _},
{ok, _}} ->
{error, cross_device};
{_, {error, Reason}} ->
{error, Reason};
{{error, Reason}, _} ->
{error, Reason};
_ ->
{error, undefined}
end
end.
find_vsn_from_git() ->
Cmd = case os:type() of
{win32,_} ->
"git describe --always --tags --match [0-9]*.[0-9]*";
_ ->
"git describe --always --tags --match '[0-9]*.[0-9]*'"
end,
Output = os:cmd(Cmd),
Line1 = first_line_no_nl(Output),
case looks_like_vsn_output(Line1) of
true ->
{ok, Line1};
false ->
{error, {Cmd, Output}}
end.
-define(is_digit(C), ($0 =< C andalso C =< $9)).
looks_like_vsn_output([C1 | _]=Str) when ?is_digit(C1) ->
%% Examples of valid outputs:
%% "4.17.3"
%% "4.17.3-1-g0bfce0e"
is_vsn_str_1(Str);
looks_like_vsn_output(_) ->
false.
is_vsn_str_1([C | Rest]) when ?is_digit(C) -> is_vsn_str_1(Rest);
is_vsn_str_1("." ++ Rest) -> is_vsn_str_2(Rest);
is_vsn_str_1(_) -> false.
is_vsn_str_2([C | Rest]) when ?is_digit(C) -> is_vsn_str_2(Rest);
is_vsn_str_2("." ++ Rest) -> is_vsn_str_3(Rest);
is_vsn_str_2(_) -> false.
is_vsn_str_3([C | Rest]) when ?is_digit(C) -> is_vsn_str_3(Rest);
is_vsn_str_3("-" ++ _) -> true;
is_vsn_str_3("") -> true;
is_vsn_str_3(_) -> false.
first_line_no_nl("\r" ++ _) -> "";
first_line_no_nl("\n" ++ _) -> "";
first_line_no_nl([C | Tl]) -> [C | first_line_no_nl(Tl)];
first_line_no_nl("") -> "".
print_no_vsn_error() ->
io:put_chars(["No version information found: no gpb.vsn file "
"and not in a git repo.\n"]).