diff options
| -rw-r--r-- | src/rebar_compiler_xrl.erl | 30 | ||||
| -rw-r--r-- | src/rebar_compiler_yrl.erl | 14 | ||||
| -rw-r--r-- | test/rebar_compile_SUITE.erl | 150 | 
3 files changed, 176 insertions, 18 deletions
| diff --git a/src/rebar_compiler_xrl.erl b/src/rebar_compiler_xrl.erl index 23ed1eb..35447ed 100644 --- a/src/rebar_compiler_xrl.erl +++ b/src/rebar_compiler_xrl.erl @@ -8,6 +8,8 @@           compile/4,           clean/2]). +-export([update_opts/2]). +  context(AppInfo) ->      Dir = rebar_app_info:dir(AppInfo),      Mappings = [{".erl", filename:join([Dir, "src"])}], @@ -25,28 +27,38 @@ needed_files(_, FoundFiles, Mappings, AppInfo) ->                             rebar_compiler:needs_compile(Source, ".erl", Mappings)],      Opts = rebar_opts:get(rebar_app_info:opts(AppInfo), xrl_opts, []), +    Opts1 = update_opts(Opts, AppInfo), -    {{FirstFiles, Opts}, {RestFiles, Opts}}. +    {{FirstFiles, Opts1}, {RestFiles, Opts1}}.  dependencies(_, _, _) ->      []. -compile(Source, [{_, OutDir}], _, Opts) -> -    BaseName = filename:basename(Source), -    Target = filename:join([OutDir, BaseName]), -    AllOpts = [{parserfile, Target} | Opts], -    AllOpts1 = [{includefile, filename:join(OutDir, I)} || {includefile, I} <- AllOpts, -                                                           filename:pathtype(I) =:= relative], -    case leex:file(Source, AllOpts1 ++ [{return, true}]) of +compile(Source, [{_, _}], _, Opts) -> +    case leex:file(Source, [{return, true} | Opts]) of          {ok, _} ->              ok;          {ok, _Mod, Ws} ->              rebar_compiler:ok_tuple(Source, Ws);          {error, Es, Ws} -> -            rebar_compiler:error_tuple(Source, Es, Ws, AllOpts1) +            rebar_compiler:error_tuple(Source, Es, Ws, Opts)      end.  clean(XrlFiles, _AppInfo) ->      rebar_file_utils:delete_each(        [rebar_utils:to_list(re:replace(F, "\\.xrl$", ".erl", [unicode]))         || F <- XrlFiles]). + +%% make includefile options absolute paths +update_opts(Opts, AppInfo) -> +    OutDir = rebar_app_info:out_dir(AppInfo), +    lists:map(fun({includefile, I}) -> +                      case filename:pathtype(I) =:= relative of +                          true -> +                              {includefile, filename:join(OutDir, I)}; +                          false -> +                              {includefile, I} +                      end; +                 (O) -> +                      O +              end, Opts). diff --git a/src/rebar_compiler_yrl.erl b/src/rebar_compiler_yrl.erl index 9c1767e..8e52d0e 100644 --- a/src/rebar_compiler_yrl.erl +++ b/src/rebar_compiler_yrl.erl @@ -25,24 +25,24 @@ needed_files(_, FoundFiles, Mappings, AppInfo) ->                             rebar_compiler:needs_compile(Source, ".erl", Mappings)],      Opts = rebar_opts:get(rebar_app_info:opts(AppInfo), yrl_opts, []), -    {{FirstFiles, Opts}, {RestFiles, Opts}}. +    Opts1 = rebar_compiler_xrl:update_opts(Opts, AppInfo), + +    {{FirstFiles, Opts1}, {RestFiles, Opts1}}.  dependencies(_, _, _) ->      [].  compile(Source, [{_, OutDir}], _, Opts) -> -    BaseName = filename:basename(Source), +    BaseName = filename:basename(Source, ".yrl"),      Target = filename:join([OutDir, BaseName]), -    AllOpts = [{parserfile, Target} | Opts], -    AllOpts1 = [{includefile, filename:join(OutDir, I)} || {includefile, I} <- AllOpts, -                                                           filename:pathtype(I) =:= relative], -    case yecc:file(Source, AllOpts1 ++ [{return, true}]) of +    AllOpts = [{parserfile, Target}, {return, true} | Opts], +    case yecc:file(Source, AllOpts) of          {ok, _} ->              ok;          {ok, _Mod, Ws} ->              rebar_compiler:ok_tuple(Source, Ws);          {error, Es, Ws} -> -            rebar_compiler:error_tuple(Source, Es, Ws, AllOpts1) +            rebar_compiler:error_tuple(Source, Es, Ws, AllOpts)      end.  clean(YrlFiles, _AppInfo) -> diff --git a/test/rebar_compile_SUITE.erl b/test/rebar_compile_SUITE.erl index 4d51f2a..9cbe8a2 100644 --- a/test/rebar_compile_SUITE.erl +++ b/test/rebar_compile_SUITE.erl @@ -852,7 +852,13 @@ dont_recompile_yrl_or_xrl(Config) ->      XrlBeam = filename:join([EbinDir, filename:basename(Xrl, ".xrl") ++ ".beam"]),      YrlBeam = filename:join([EbinDir, filename:basename(Yrl, ".yrl") ++ ".beam"]), -    rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}), +    Hrl = filename:join([AppDir, "include", "some_header.hrl"]), +    ok = filelib:ensure_dir(Hrl), +    HrlBody = yeccpre_hrl(), +    ok = ec_file:write(Hrl, HrlBody), +    RebarConfig = [{yrl_opts, [{includefile, "include/some_header.hrl"}]}], + +    rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], {ok, [{app, Name}]}),      XrlModTime = filelib:last_modified(XrlErl),      YrlModTime = filelib:last_modified(YrlErl), @@ -862,7 +868,7 @@ dont_recompile_yrl_or_xrl(Config) ->      timer:sleep(1000), -    rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}), +    rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], {ok, [{app, Name}]}),      NewXrlModTime = filelib:last_modified(XrlErl),      NewYrlModTime = filelib:last_modified(YrlErl), @@ -2208,3 +2214,143 @@ regex_filter_regression(Config) ->                                     {ok, [{file, Expected}]}),      ok. +%% + +%% a copy of lib/parsetools/include/yeccpre.hrl so we can test yrl includefile +yeccpre_hrl() -> +    <<"-type yecc_ret() :: {'error', _} | {'ok', _}. + +-spec parse(Tokens :: list()) -> yecc_ret(). +parse(Tokens) -> +    yeccpars0(Tokens, {no_func, no_line}, 0, [], []). + +-spec parse_and_scan({function() | {atom(), atom()}, [_]} +                     | {atom(), atom(), [_]}) -> yecc_ret(). +parse_and_scan({F, A}) -> +    yeccpars0([], {{F, A}, no_line}, 0, [], []); +parse_and_scan({M, F, A}) -> +    Arity = length(A), +    yeccpars0([], {{fun M:F/Arity, A}, no_line}, 0, [], []). + +-spec format_error(any()) -> [char() | list()]. +format_error(Message) -> +    case io_lib:deep_char_list(Message) of +        true -> +            Message; +        _ -> +            io_lib:write(Message) +    end. + +%% To be used in grammar files to throw an error message to the parser +%% toplevel. Doesn't have to be exported! +-compile({nowarn_unused_function, return_error/2}). +-spec return_error(integer(), any()) -> no_return(). +return_error(Line, Message) -> +    throw({error, {Line, ?MODULE, Message}}). + +-define(CODE_VERSION, \"1.4\"). + +yeccpars0(Tokens, Tzr, State, States, Vstack) -> +    try yeccpars1(Tokens, Tzr, State, States, Vstack) +    catch +        error:Error -> +            try yecc_error_type(Error, []) of +                Desc -> +                    erlang:raise(error, {yecc_bug, ?CODE_VERSION, Desc}, +                                 []) +            catch _:_ -> erlang:raise(error, Error, []) +            end; +        %% Probably thrown from return_error/2: +        throw: {error, {_Line, ?MODULE, _M}} = Error -> +            Error +    end. + +yecc_error_type(function_clause, _) -> +    not_implemented. + +yeccpars1([Token | Tokens], Tzr, State, States, Vstack) -> +    yeccpars2(State, element(1, Token), States, Vstack, Token, Tokens, Tzr); +yeccpars1([], {{F, A},_Line}, State, States, Vstack) -> +    case apply(F, A) of +        {ok, Tokens, Endline} -> +            yeccpars1(Tokens, {{F, A}, Endline}, State, States, Vstack); +        {eof, Endline} -> +            yeccpars1([], {no_func, Endline}, State, States, Vstack); +        {error, Descriptor, _Endline} -> +            {error, Descriptor} +    end; +yeccpars1([], {no_func, no_line}, State, States, Vstack) -> +    Line = 999999, +    yeccpars2(State, '$end', States, Vstack, yecc_end(Line), [], +              {no_func, Line}); +yeccpars1([], {no_func, Endline}, State, States, Vstack) -> +    yeccpars2(State, '$end', States, Vstack, yecc_end(Endline), [], +              {no_func, Endline}). + +%% yeccpars1/7 is called from generated code. +%% +%% When using the {includefile, Includefile} option, make sure that +%% yeccpars1/7 can be found by parsing the file without following +%% include directives. yecc will otherwise assume that an old +%% yeccpre.hrl is included (one which defines yeccpars1/5). +yeccpars1(State1, State, States, Vstack, Token0, [Token | Tokens], Tzr) -> +    yeccpars2(State, element(1, Token), [State1 | States], +              [Token0 | Vstack], Token, Tokens, Tzr); +yeccpars1(State1, State, States, Vstack, Token0, [], {{_F,_A}, _Line}=Tzr) -> +    yeccpars1([], Tzr, State, [State1 | States], [Token0 | Vstack]); +yeccpars1(State1, State, States, Vstack, Token0, [], {no_func, no_line}) -> +    Line = yecctoken_end_location(Token0), +    yeccpars2(State, '$end', [State1 | States], [Token0 | Vstack], +              yecc_end(Line), [], {no_func, Line}); +yeccpars1(State1, State, States, Vstack, Token0, [], {no_func, Line}) -> +    yeccpars2(State, '$end', [State1 | States], [Token0 | Vstack], +              yecc_end(Line), [], {no_func, Line}). + +%% For internal use only. +yecc_end({Line,_Column}) -> +    {'$end', Line}; +yecc_end(Line) -> +    {'$end', Line}. + +yecctoken_end_location(Token) -> +    try erl_anno:end_location(element(2, Token)) of +        undefined -> yecctoken_location(Token); +        Loc -> Loc +    catch _:_ -> yecctoken_location(Token) +    end. + +-compile({nowarn_unused_function, yeccerror/1}). +yeccerror(Token) -> +    Text = yecctoken_to_string(Token), +    Location = yecctoken_location(Token), +    {error, {Location, ?MODULE, [\"syntax error before: \", Text]}}. + +-compile({nowarn_unused_function, yecctoken_to_string/1}). +yecctoken_to_string(Token) -> +    try erl_scan:text(Token) of +        undefined -> yecctoken2string(Token); +        Txt -> Txt +    catch _:_ -> yecctoken2string(Token) +    end. + +yecctoken_location(Token) -> +    try erl_scan:location(Token) +    catch _:_ -> element(2, Token) +    end. + +-compile({nowarn_unused_function, yecctoken2string/1}). +yecctoken2string({atom, _, A}) -> io_lib:write_atom(A); +yecctoken2string({integer,_,N}) -> io_lib:write(N); +yecctoken2string({float,_,F}) -> io_lib:write(F); +yecctoken2string({char,_,C}) -> io_lib:write_char(C); +yecctoken2string({var,_,V}) -> io_lib:format(\"~s\", [V]); +yecctoken2string({string,_,S}) -> io_lib:write_string(S); +yecctoken2string({reserved_symbol, _, A}) -> io_lib:write(A); +yecctoken2string({_Cat, _, Val}) -> io_lib:format(\"~p\", [Val]); +yecctoken2string({dot, _}) -> \"'.'\"; +yecctoken2string({'$end', _}) -> []; +yecctoken2string({Other, _}) when is_atom(Other) -> +    io_lib:write_atom(Other); +yecctoken2string(Other) -> +    io_lib:format(\"~p\", [Other]). +">>. | 
