diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/rebar_base_compiler.erl | 13 | ||||
| -rw-r--r-- | src/rebar_erlc_compiler.erl | 466 | 
2 files changed, 177 insertions, 302 deletions
| diff --git a/src/rebar_base_compiler.erl b/src/rebar_base_compiler.erl index 265ea80..df950d7 100644 --- a/src/rebar_base_compiler.erl +++ b/src/rebar_base_compiler.erl @@ -102,21 +102,10 @@ remove_common_path1([Part | RestFilename], [Part | RestPath]) ->  remove_common_path1(FilenameParts, _) ->      filename:join(FilenameParts). - -compile(Source, Config, CompileFn) -> -    case CompileFn(Source, Config) of -        ok -> -            ok; -        skipped -> -            skipped; -        Error -> -            Error -    end. -  compile_each([], _Config, _CompileFn) ->      ok;  compile_each([Source | Rest], Config, CompileFn) -> -    case compile(Source, Config, CompileFn) of +    case CompileFn(Source, Config) of          ok ->              ?DEBUG("~sCompiled ~s", [rebar_utils:indent(1), filename:basename(Source)]);          {ok, Warnings} -> diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl index 0ea09dc..5bd04d2 100644 --- a/src/rebar_erlc_compiler.erl +++ b/src/rebar_erlc_compiler.erl @@ -33,15 +33,15 @@  -include("rebar.hrl").  -include_lib("stdlib/include/erl_compile.hrl"). --define(ERLCINFO_VSN, 1). +-define(ERLCINFO_VSN, 2).  -define(ERLCINFO_FILE, "erlcinfo").  -type erlc_info_v() :: {digraph:vertex(), term()} | 'false'.  -type erlc_info_e() :: {digraph:vertex(), digraph:vertex()}. --type erlc_info() :: {list(erlc_info_v()), list(erlc_info_e())}. +-type erlc_info() :: {list(erlc_info_v()), list(erlc_info_e()), list(string())}.  -record(erlcinfo,          {            vsn = ?ERLCINFO_VSN :: pos_integer(), -          info = {[], []} :: erlc_info() +          info = {[], [], []} :: erlc_info()          }).  -define(RE_PREFIX, "^[^._]"). @@ -104,7 +104,7 @@ compile(Config, Dir, OutDir) ->      doterl_compile(Config, Dir, OutDir).  -spec clean(rebar_state:t(), file:filename()) -> 'ok'. -clean(Config, AppDir) -> +clean(_Config, AppDir) ->      MibFiles = rebar_utils:find_files(filename:join(AppDir, "mibs"), ?RE_PREFIX".*\\.mib\$"),      MIBs = [filename:rootname(filename:basename(MIB)) || MIB <- MibFiles],      rebar_file_utils:delete_each( @@ -118,7 +118,7 @@ clean(Config, AppDir) ->          || F <- YrlFiles ]),      %% Delete the build graph, if any -    rebar_file_utils:rm_rf(erlcinfo_file(Config)), +    rebar_file_utils:rm_rf(erlcinfo_file()),      %% Erlang compilation is recursive, so it's possible that we have a nested      %% directory structure in ebin with .beam files within. As such, we want @@ -139,7 +139,6 @@ doterl_compile(State, Dir, ODir) ->      doterl_compile(State, Dir, ODir, [], ErlOpts).  doterl_compile(Config, Dir, OutDir, MoreSources, ErlOpts) -> -    ErlFirstFilesConf = rebar_state:get(Config, erl_first_files, []),      ?DEBUG("erl_opts ~p", [ErlOpts]),      %% Support the src_dirs option allowing multiple directories to      %% contain erlang source. This might be used, for example, should @@ -147,164 +146,81 @@ doterl_compile(Config, Dir, OutDir, MoreSources, ErlOpts) ->      SrcDirs = [filename:join(Dir, X) || X <- proplists:get_value(src_dirs, ErlOpts, ["src"])],      AllErlFiles = gather_src(SrcDirs, []) ++ MoreSources, -    %% Issue: rebar/rebar3#140 (fix matching based on same path + order of -    %% erl_first_files) -    ErlFirstFiles = get_erl_first_files(ErlFirstFilesConf, AllErlFiles), -    RestErls = [ File || File <- AllErlFiles, -                         not lists:member(File, ErlFirstFiles) ], -      %% Make sure that ebin/ exists and is on the path      ok = filelib:ensure_dir(filename:join(OutDir, "dummy.beam")),      CurrPath = code:get_path(),      true = code:add_path(filename:absname(OutDir)),      OutDir1 = proplists:get_value(outdir, ErlOpts, OutDir), -    G = init_erlcinfo(Config, AllErlFiles), -    %% Split RestErls so that files which are depended on are treated -    %% like erl_first_files. -    {OtherFirstErls, OtherErls} = -        lists:partition( -          fun(F) -> -                  Children = get_children(G, F), -                  log_files(?FMT("Files dependent on ~s", [F]), Children), - -                  case erls(Children) of -                      [] -> -                          %% There are no files dependent on this file. -                          false; -                      _ -> -                          %% There are some files dependent on the file. -                          %% Thus the file has higher priority -                          %% and should be compiled in the first place. -                          true -                  end -          end, RestErls), -    %% Dependencies of OtherFirstErls that must be compiled first. -    OtherFirstErlsDeps = lists:flatmap( -                           fun(Erl) -> erls(get_parents(G, Erl)) end, -                           OtherFirstErls), -    %% NOTE: In case the way we retrieve OtherFirstErlsDeps or merge -    %% it with OtherFirstErls does not result in the correct compile -    %% priorities, or the method in use proves to be too slow for -    %% certain projects, consider using a more elaborate method (maybe -    %% digraph_utils) or alternatively getting and compiling the .erl -    %% parents of an individual Source in internal_erl_compile. By not -    %% handling this in internal_erl_compile, we also avoid extra -    %% needs_compile/2 calls. -    FirstErls = ErlFirstFiles ++ uo_merge(OtherFirstErlsDeps, OtherFirstErls), + +    G = init_erlcinfo(proplists:get_all_values(i, ErlOpts), AllErlFiles), +    NeededErlFiles = needed_files(G, ErlOpts, Dir, OutDir1, AllErlFiles), +    ErlFirstFiles = erl_first_files(Config, NeededErlFiles), +    {DepErls, OtherErls} = lists:partition( +                             fun(Source) -> digraph:in_degree(G, Source) > 0 end, +                             [File || File <- NeededErlFiles, not lists:member(File, ErlFirstFiles)]), +    DepErlsOrdered = digraph_utils:topsort(digraph_utils:subgraph(G, DepErls)), +    FirstErls = ErlFirstFiles ++ lists:reverse(DepErlsOrdered),      ?DEBUG("Files to compile first: ~p", [FirstErls]),      rebar_base_compiler:run(        Config, FirstErls, OtherErls,        fun(S, C) -> -              internal_erl_compile(C, Dir, S, OutDir1, ErlOpts, G) +              internal_erl_compile(C, Dir, S, OutDir1, ErlOpts)        end),      true = code:set_path(CurrPath),      ok. -%% -%% Return all .erl files from a list of files -%% -erls(Files) -> -    [Erl || Erl <- Files, filename:extension(Erl) =:= ".erl"]. - -%% -%% Return a list of erl_first_files in order as specified in rebar.config -%% using the path from AllFiles. -%% -get_erl_first_files(FirstFiles, AllFiles) -> -    BaseFirstFiles = [filename:basename(F) || F <- FirstFiles], -    IndexedAllFiles = [{filename:basename(F), F} || F <- AllFiles ], -    [ proplists:get_value(FileName, IndexedAllFiles) || -        FileName <- BaseFirstFiles, -        proplists:is_defined(FileName, IndexedAllFiles) ]. - -%% -%% Return a list without duplicates while preserving order -%% -ulist(L) -> -    ulist(L, []). - -ulist([H|T], Acc) -> -    case lists:member(H, T) of -        true -> -            ulist(T, Acc); -        false -> -            ulist(T, [H|Acc]) -    end; -ulist([], Acc) -> -    lists:reverse(Acc). - -%% -%% Merge two lists without duplicates while preserving order -%% -uo_merge(L1, L2) -> -    lists:foldl(fun(E, Acc) -> u_add_element(E, Acc) end, ulist(L1), L2). - -u_add_element(Elem, [Elem|_]=Set) -> Set; -u_add_element(Elem, [E1|Set])     -> [E1|u_add_element(Elem, Set)]; -u_add_element(Elem, [])           -> [Elem]. - --spec include_path(file:filename(), -                   rebar_state:t()) -> [file:filename(), ...]. -include_path(Source, Config) -> -    ErlOpts = rebar_state:get(Config, erl_opts, []), -    Dir = filename:join(rebar_utils:droplast(filename:split(filename:dirname(Source)))), -    lists:usort([filename:join(Dir, "include"), filename:dirname(Source)] -                ++ proplists:get_all_values(i, ErlOpts)). - --spec needs_compile(file:filename(), file:filename(), -                    list(), [string()]) -> boolean(). -needs_compile(Source, Target, Opts, Parents) -> -    TargetLastMod = filelib:last_modified(Target), -    F = fun(I) -> source_changed(TargetLastMod, I) end, -    lists:any(F, [Source] ++ Parents) orelse opts_changed(Opts, Target). - -source_changed(TargetLastMod, I) -> TargetLastMod < filelib:last_modified(I). - -opts_changed(NewOpts, Target) -> -    case compile_info(Target) of -        {ok, Opts} -> lists:sort(Opts) =/= lists:sort(NewOpts); -        _          -> true -    end. - -compile_info(Target) -> -    case beam_lib:chunks(Target, [compile_info]) of -        {ok, {_mod, Chunks}} -> -            CompileInfo = proplists:get_value(compile_info, Chunks, []), -            {ok, proplists:get_value(options, CompileInfo, [])}; -        {error, beam_lib, Reason} -> -            ?WARN("Couldn't read debug info from ~p for reason: ~p", [Target, Reason]), -            {error, Reason} +erl_first_files(Config, NeededErlFiles) -> +    ErlFirstFilesConf = rebar_state:get(Config, erl_first_files, []), +    %% NOTE: order of files in ErlFirstFiles is important! +    [File || File <- ErlFirstFilesConf, lists:member(File, NeededErlFiles)]. + +%% Get subset of SourceFiles which need to be recompiled, respecting +%% dependencies induced by given graph G. +needed_files(G, ErlOpts, Dir, OutDir, SourceFiles) -> +    lists:filter(fun(Source) -> +                         TargetBase = target_base(OutDir, Source), +                         Target = TargetBase ++ ".beam", +                         Opts = [{outdir, filename:dirname(Target)} +                                ,{i, filename:join(Dir, "include")}] ++ ErlOpts, +                         digraph:vertex(G, Source) > {Source, filelib:last_modified(Target)} +                              orelse opts_changed(Opts, TargetBase) +                 end, SourceFiles). + +opts_changed(Opts, ObjectFile) -> +    case code:load_abs(ObjectFile) of +        {module, Mod} -> +            Compile = Mod:module_info(compile), +            lists:sort(Opts) =/= lists:sort(proplists:get_value(options, +                                                                Compile, +                                                                [])); +        {error, _} -> true      end. -check_erlcinfo(_Config, #erlcinfo{vsn=?ERLCINFO_VSN}) -> -    ok; -check_erlcinfo(Config, #erlcinfo{vsn=Vsn}) -> -    ?ABORT("~s file version is incompatible. expected: ~b got: ~b", -           [erlcinfo_file(Config), ?ERLCINFO_VSN, Vsn]); -check_erlcinfo(Config, _) -> -    ?ABORT("~s file is invalid. Please delete before next run.", -           [erlcinfo_file(Config)]). - -erlcinfo_file(_Config) -> +erlcinfo_file() ->      filename:join(rebar_dir:local_cache_dir(), ?ERLCINFO_FILE). -init_erlcinfo(Config, Erls) -> -    G = restore_erlcinfo(Config), -    %% Get a unique list of dirs based on the source files' locations. -    %% This is used for finding files in sub dirs of the configured -    %% src_dirs. For example, src/sub_dir/foo.erl. -    Dirs = sets:to_list(lists:foldl( -                          fun(Erl, Acc) -> -                                  Dir = filename:dirname(Erl), -                                  sets:add_element(Dir, Acc) -                          end, sets:new(), Erls)), -    Updates = [update_erlcinfo(G, Erl, include_path(Erl, Config) ++ Dirs) -               || Erl <- Erls], -    Modified = lists:member(modified, Updates), -    ok = store_erlcinfo(G, Config, Modified), +%% Get dependency graph of given Erls files and their dependencies (header files, +%% parse transforms, behaviours etc.) located in their directories or given +%% InclDirs. Note that last modification times stored in vertices already respect +%% dependencies induced by given graph G. +init_erlcinfo(InclDirs, Erls) -> +    G = digraph:new([acyclic]), +    try restore_erlcinfo(G, InclDirs) +    catch +        _:_ -> +            ?WARN("Failed to restore ~s file. Discarding it.~n", [erlcinfo_file()]), +            file:delete(erlcinfo_file()) +    end, +    Dirs = source_and_include_dirs(InclDirs, Erls), +    Modified = lists:foldl(update_erlcinfo_fun(G, Dirs), false, Erls), +    if Modified -> store_erlcinfo(G, InclDirs); not Modified -> ok end,      G. -update_erlcinfo(G, Source, Dirs) -> +source_and_include_dirs(InclDirs, Erls) -> +    SourceDirs = lists:map(fun filename:dirname/1, Erls), +    lists:usort(["include" | InclDirs ++ SourceDirs]). + +update_erlcinfo(G, Dirs, Source) ->      case digraph:vertex(G, Source) of          {_, LastUpdated} ->              case filelib:last_modified(Source) of @@ -315,77 +231,75 @@ update_erlcinfo(G, Source, Dirs) ->                      digraph:del_vertex(G, Source),                      modified;                  LastModified when LastUpdated < LastModified -> -                    modify_erlcinfo(G, Source, Dirs), -                    modified; +                    modify_erlcinfo(G, Source, LastModified, filename:dirname(Source), Dirs);                  _ -> -                    unmodified +                    Modified = lists:foldl( +                        update_erlcinfo_fun(G, Dirs), +                        false, digraph:out_neighbours(G, Source)), +                    MaxModified = update_max_modified_deps(G, Source), +                    case Modified orelse MaxModified > LastUpdated of +                        true -> modified; +                        false -> unmodified +                    end              end;          false -> -            modify_erlcinfo(G, Source, Dirs), -            modified +            modify_erlcinfo(G, Source, filelib:last_modified(Source), filename:dirname(Source), Dirs) +    end. + +update_erlcinfo_fun(G, Dirs) -> +    fun(Erl, Modified) -> +        case update_erlcinfo(G, Dirs, Erl) of +            modified -> true; +            unmodified -> Modified +        end      end. -modify_erlcinfo(G, Source, Dirs) -> +update_max_modified_deps(G, Source) -> +    MaxModified = lists:max(lists:map( +        fun(File) -> {_, MaxModified} = digraph:vertex(G, File), MaxModified end, +        [Source|digraph:out_neighbours(G, Source)])), +    digraph:add_vertex(G, Source, MaxModified), +    MaxModified. + +modify_erlcinfo(G, Source, LastModified, Dir, Dirs) ->      {ok, Fd} = file:open(Source, [read]), -    Incls = parse_attrs(Fd, []), +    Incls = parse_attrs(Fd, [], Dir),      AbsIncls = expand_file_names(Incls, Dirs),      ok = file:close(Fd), -    LastUpdated = {date(), time()}, -    digraph:add_vertex(G, Source, LastUpdated), +    digraph:add_vertex(G, Source, LastModified), +    digraph:del_edges(G, digraph:out_edges(G, Source)),      lists:foreach(        fun(Incl) -> -              update_erlcinfo(G, Incl, Dirs), +              update_erlcinfo(G, Dirs, Incl),                digraph:add_edge(G, Source, Incl) -      end, AbsIncls). +      end, AbsIncls), +    modified. -restore_erlcinfo(Config) -> -    File = erlcinfo_file(Config), -    G = digraph:new(), -    case file:read_file(File) of +restore_erlcinfo(G, InclDirs) -> +    case file:read_file(erlcinfo_file()) of          {ok, Data} -> -            try binary_to_term(Data) of -                Erlcinfo -> -                    ok = check_erlcinfo(Config, Erlcinfo), -                    #erlcinfo{info=ErlcInfo} = Erlcinfo, -                    {Vs, Es} = ErlcInfo, -                    lists:foreach( -                      fun({V, LastUpdated}) -> -                              digraph:add_vertex(G, V, LastUpdated) -                      end, Vs), -                    lists:foreach( -                      fun({V1, V2}) -> -                              digraph:add_edge(G, V1, V2) -                      end, Es) -            catch -                error:badarg -> -                    ?ERROR( -                       "Failed (binary_to_term) to restore rebar info file." -                       " Discard file.", []), -                    ok -            end; -        _Err -> +            % Since externally passed InclDirs can influence erlcinfo graph (see +            % modify_erlcinfo), we have to check here that they didn't change. +            #erlcinfo{vsn=?ERLCINFO_VSN, info={Vs, Es, InclDirs}} = +                binary_to_term(Data), +            lists:foreach( +              fun({V, LastUpdated}) -> +                      digraph:add_vertex(G, V, LastUpdated) +              end, Vs), +            lists:foreach( +              fun({_, V1, V2, _}) -> +                      digraph:add_edge(G, V1, V2) +              end, Es); +        {error, _} ->              ok -    end, -    G. +    end. -store_erlcinfo(_G, _Config, _Modified = false) -> -    ok; -store_erlcinfo(G, Config, _Modified) -> -    Vs = lists:map( -           fun(V) -> -                   digraph:vertex(G, V) -           end, digraph:vertices(G)), -    Es = lists:flatmap( -           fun({V, _}) -> -                   lists:map( -                     fun(E) -> -                             {_, V1, V2, _} = digraph:edge(G, E), -                             {V1, V2} -                     end, digraph:out_edges(G, V)) -           end, Vs), -    File = erlcinfo_file(Config), +store_erlcinfo(G, InclDirs) -> +    Vs = lists:map(fun(V) -> digraph:vertex(G, V) end, digraph:vertices(G)), +    Es = lists:map(fun(E) -> digraph:edge(G, E) end, digraph:edges(G)), +    File = erlcinfo_file(),      ok = filelib:ensure_dir(File), -    Data = term_to_binary(#erlcinfo{info={Vs, Es}}, [{compressed, 9}]), +    Data = term_to_binary(#erlcinfo{info={Vs, Es, InclDirs}}, [{compressed, 2}]),      file:write_file(File, Data).  %% NOTE: If, for example, one of the entries in Files, refers to @@ -420,50 +334,26 @@ expand_file_names(Files, Dirs) ->                end        end, Files). --spec get_parents(rebar_digraph(), file:filename()) -> [file:filename()]. -get_parents(G, Source) -> -    %% Return all files which the Source depends upon. -    digraph_utils:reachable_neighbours([Source], G). - --spec get_children(rebar_digraph(), file:filename()) -> [file:filename()]. -get_children(G, Source) -> -    %% Return all files dependent on the Source. -    digraph_utils:reaching_neighbours([Source], G). - --spec internal_erl_compile(rebar_state:t(), file:filename(), file:filename(), -                           file:filename(), list(), -                           rebar_digraph()) -> 'ok' | 'skipped'. -internal_erl_compile(Config, Dir, Source, OutDir, ErlOpts, G) -> -    %% Determine the target name and includes list by inspecting the source file -    Module = filename:basename(Source, ".erl"), -    Parents = get_parents(G, Source), -    log_files(?FMT("Dependencies of ~s", [Source]), Parents), - -    %% Construct the target filename -    Target = filename:join([OutDir | string:tokens(Module, ".")]) ++ ".beam", -    ok = filelib:ensure_dir(Target), - -    %% Construct the compile opts -    Opts = [{outdir, filename:dirname(Target)}] ++ -        ErlOpts ++ [{i, filename:join(Dir, "include")}], -    %% If the file needs compilation, based on last mod date of includes or -    %% the target -    case needs_compile(Source, Target, Opts, Parents) of -        true -> -            case compile:file(Source, Opts ++ [return]) of -                {ok, _Mod} -> -                    ok; -                {ok, _Mod, Ws} -> -                    rebar_base_compiler:ok_tuple(Config, Source, Ws); -                {error, Es, Ws} -> -                    rebar_base_compiler:error_tuple(Config, Source, -                                                    Es, Ws, Opts) -            end; -        false -> -            skipped +-spec internal_erl_compile(rebar_config:config(), file:filename(), file:filename(), +    file:filename(), list()) -> ok | {ok, any()} | {error, any(), any()}. +internal_erl_compile(Config, Dir, Module, OutDir, ErlOpts) -> +    Target = target_base(OutDir, Module) ++ ".beam", +    ok = filelib:ensure_dir(Target), +    Opts = [{outdir, filename:dirname(Target)}] ++ ErlOpts ++ +        [{i, filename:join(Dir, "include")}, return], +    case compile:file(Module, Opts) of +        {ok, _Mod} -> +            ok; +        {ok, _Mod, Ws} -> +            rebar_base_compiler:ok_tuple(Config, Module, Ws); +        {error, Es, Ws} -> +            rebar_base_compiler:error_tuple(Config, Module, Es, Ws, Opts)      end. +target_base(OutDir, Source) -> +    filename:join(OutDir, filename:basename(Source, ".erl")). +  -spec compile_mib(file:filename(), file:filename(),                    rebar_state:t()) -> 'ok'.  compile_mib(Source, Target, Config) -> @@ -507,7 +397,7 @@ compile_xrl_yrl(Config, Source, Target, Opts, Mod) ->      Dir = rebar_state:dir(Config),      Opts1 = [{includefile, filename:join(Dir, I)} || {includefile, I} <- Opts,                                                       filename:pathtype(I) =:= relative], -    case needs_compile(Source, Target, Opts1, []) of +    case needs_compile(Source, Target) of          true ->              case Mod:file(Source, Opts1 ++ [{return, true}]) of                  {ok, _} -> @@ -522,6 +412,9 @@ compile_xrl_yrl(Config, Source, Target, Opts, Mod) ->              skipped      end. +needs_compile(Source, Target) -> +    filelib:last_modified(Source) > filelib:last_modified(Target). +  gather_src([], Srcs) ->      Srcs;  gather_src([Dir|Rest], Srcs) -> @@ -539,80 +432,81 @@ delete_dir(Dir, Subdirs) ->      lists:foreach(fun(D) -> delete_dir(D, dirs(D)) end, Subdirs),      file:del_dir(Dir). -parse_attrs(Fd, Includes) -> +parse_attrs(Fd, Includes, Dir) ->      case io:parse_erl_form(Fd, "") of          {ok, Form, _Line} ->              case erl_syntax:type(Form) of                  attribute -> -                    NewIncludes = process_attr(Form, Includes), -                    parse_attrs(Fd, NewIncludes); +                    NewIncludes = process_attr(Form, Includes, Dir), +                    parse_attrs(Fd, NewIncludes, Dir);                  _ -> -                    parse_attrs(Fd, Includes) +                    parse_attrs(Fd, Includes, Dir)              end;          {eof, _} ->              Includes;          _Err -> -            parse_attrs(Fd, Includes) +            parse_attrs(Fd, Includes, Dir)      end. -process_attr(Form, Includes) -> -    try -        AttrName = erl_syntax:atom_value(erl_syntax:attribute_name(Form)), -        process_attr(AttrName, Form, Includes) -    catch _:_ -> -            %% TODO: We should probably try to be more specific here -            %% and not suppress all errors. -            Includes -    end. +process_attr(Form, Includes, Dir) -> +    AttrName = erl_syntax:atom_value(erl_syntax:attribute_name(Form)), +    process_attr(AttrName, Form, Includes, Dir). -process_attr(import, Form, Includes) -> +process_attr(import, Form, Includes, _Dir) ->      case erl_syntax_lib:analyze_import_attribute(Form) of          {Mod, _Funs} -> -            [atom_to_list(Mod) ++ ".erl"|Includes]; +            [module_to_erl(Mod)|Includes];          Mod -> -            [atom_to_list(Mod) ++ ".erl"|Includes] +            [module_to_erl(Mod)|Includes]      end; -process_attr(file, Form, Includes) -> +process_attr(file, Form, Includes, _Dir) ->      {File, _} = erl_syntax_lib:analyze_file_attribute(Form),      [File|Includes]; -process_attr(include, Form, Includes) -> +process_attr(include, Form, Includes, _Dir) ->      [FileNode] = erl_syntax:attribute_arguments(Form),      File = erl_syntax:string_value(FileNode),      [File|Includes]; -process_attr(include_lib, Form, Includes) -> +process_attr(include_lib, Form, Includes, Dir) ->      [FileNode] = erl_syntax:attribute_arguments(Form),      RawFile = erl_syntax:string_value(FileNode), -    File = maybe_expand_include_lib_path(RawFile), -    [File|Includes]; -process_attr(behaviour, Form, Includes) -> +    maybe_expand_include_lib_path(RawFile, Dir) ++ Includes; +process_attr(behaviour, Form, Includes, _Dir) ->      [FileNode] = erl_syntax:attribute_arguments(Form), -    File = erl_syntax:atom_name(FileNode) ++ ".erl", +    File = module_to_erl(erl_syntax:atom_value(FileNode)),      [File|Includes]; -process_attr(compile, Form, Includes) -> +process_attr(compile, Form, Includes, _Dir) ->      [Arg] = erl_syntax:attribute_arguments(Form),      case erl_syntax:concrete(Arg) of          {parse_transform, Mod} -> -            [atom_to_list(Mod) ++ ".erl"|Includes]; +            [module_to_erl(Mod)|Includes];          {core_transform, Mod} -> -            [atom_to_list(Mod) ++ ".erl"|Includes]; +            [module_to_erl(Mod)|Includes];          L when is_list(L) ->              lists:foldl( -              fun({parse_transform, M}, Acc) -> -                      [atom_to_list(M) ++ ".erl"|Acc]; -                 ({core_transform, M}, Acc) -> -                      [atom_to_list(M) ++ ".erl"|Acc]; +              fun({parse_transform, Mod}, Acc) -> +                      [module_to_erl(Mod)|Acc]; +                 ({core_transform, Mod}, Acc) -> +                      [module_to_erl(Mod)|Acc];                   (_, Acc) ->                        Acc -              end, Includes, L) -    end. +              end, Includes, L); +        _ -> +            Includes +    end; +process_attr(_, _Form, Includes, _Dir) -> +    Includes. + +module_to_erl(Mod) -> +    atom_to_list(Mod) ++ ".erl". +  %% Given the filename from an include_lib attribute, if the path  %% exists, return unmodified, or else get the absolute ERL_LIBS  %% path. -maybe_expand_include_lib_path(File) -> -    case filelib:is_regular(File) of +maybe_expand_include_lib_path(File, Dir) -> +    case filelib:is_regular(filename:join(Dir, File)) of          true -> -            File; +            [filename:join(Dir, File)];          false ->              expand_include_lib_path(File)      end. @@ -627,8 +521,10 @@ expand_include_lib_path(File) ->      Split = filename:split(filename:dirname(File)),      Lib = hd(Split),      SubDir = filename:join(tl(Split)), -    Dir = code:lib_dir(list_to_atom(Lib), list_to_atom(SubDir)), -    filename:join(Dir, File1). +    case code:lib_dir(list_to_atom(Lib), list_to_atom(SubDir)) of +        {error, bad_name} -> []; +        Dir -> [filename:join(Dir, File1)] +    end.  %%  %% Ensure all files in a list are present and abort if one is missing @@ -642,13 +538,3 @@ check_file(File) ->          false -> ?ABORT("File ~p is missing, aborting\n", [File]);          true -> File      end. - -%% Print prefix followed by list of files. If the list is empty, print -%% on the same line, otherwise use a separate line. -log_files(Prefix, Files) -> -    case Files of -        [] -> -            ?DEBUG("~s: ~p", [Prefix, Files]); -        _ -> -            ?DEBUG("~s:~n~p", [Prefix, Files]) -    end. | 
