diff options
| -rw-r--r-- | src/rebar_compiler.erl | 74 | ||||
| -rw-r--r-- | src/rebar_prv_shell.erl | 24 | ||||
| -rw-r--r-- | test/rebar_compile_SUITE.erl | 44 | 
3 files changed, 117 insertions, 25 deletions
| diff --git a/src/rebar_compiler.erl b/src/rebar_compiler.erl index 7da265c..02c74db 100644 --- a/src/rebar_compiler.erl +++ b/src/rebar_compiler.erl @@ -28,7 +28,8 @@  -callback clean([file:filename()], rebar_app_info:t()) -> _.  -define(DAG_VSN, 2). --define(DAG_FILE, "source.dag"). +-define(DAG_ROOT, "source"). +-define(DAG_EXT, ".dag").  -type dag_v() :: {digraph:vertex(), term()} | 'false'.  -type dag_e() :: {digraph:vertex(), digraph:vertex()}.  -type dag() :: {list(dag_v()), list(dag_e()), list(string())}. @@ -49,12 +50,13 @@ compile_all(Compilers, AppInfo) ->      _ = code:ensure_loaded(compile),      lists:foreach(fun(CompilerMod) -> -                          run(CompilerMod, AppInfo), -                          run_on_extra_src_dirs(CompilerMod, AppInfo, fun run/2) -                  end, Compilers), +        run(CompilerMod, AppInfo, undefined), +        run_on_extra_src_dirs(CompilerMod, AppInfo, +                              fun(Mod, App) -> run(Mod, App, "extra") end) +    end, Compilers),      ok. -run(CompilerMod, AppInfo) -> +run(CompilerMod, AppInfo, Label) ->      #{src_dirs := SrcDirs,        include_dirs := InclDirs,        src_ext := SrcExt, @@ -69,7 +71,7 @@ run(CompilerMod, AppInfo) ->      OutDir = rebar_app_info:out_dir(AppInfo),      AbsSrcDirs = [filename:join(BaseDir, SrcDir) || SrcDir <- SrcDirs], -    G = init_dag(CompilerMod, AbsInclDirs, AbsSrcDirs, FoundFiles, OutDir, EbinDir), +    G = init_dag(CompilerMod, AbsInclDirs, AbsSrcDirs, FoundFiles, OutDir, EbinDir, Label),      {{FirstFiles, FirstFileOpts}, {RestFiles, Opts}} = CompilerMod:needed_files(G, FoundFiles,                                                                                  Mappings, AppInfo),      true = digraph:delete(G), @@ -101,11 +103,12 @@ compile_each([Source | Rest], Opts, Config, Outs, CompilerMod) ->  -spec clean([module()], rebar_app_info:t()) -> 'ok'.  clean(Compilers, AppInfo) ->      lists:foreach(fun(CompilerMod) -> -                          clean_(CompilerMod, AppInfo), -                          run_on_extra_src_dirs(CompilerMod, AppInfo, fun clean_/2) -                  end, Compilers). +        clean_(CompilerMod, AppInfo, undefined), +        run_on_extra_src_dirs(CompilerMod, AppInfo, +                              fun(Mod, App) -> clean_(Mod, App, "extra") end) +    end, Compilers). -clean_(CompilerMod, AppInfo) -> +clean_(CompilerMod, AppInfo, Label) ->      #{src_dirs := SrcDirs,        src_ext := SrcExt} = CompilerMod:context(AppInfo),      BaseDir = rebar_app_info:dir(AppInfo), @@ -114,7 +117,7 @@ clean_(CompilerMod, AppInfo) ->      FoundFiles = find_source_files(BaseDir, SrcExt, SrcDirs, Opts),      CompilerMod:clean(FoundFiles, AppInfo), -    rebar_file_utils:rm_rf(dag_file(CompilerMod, EbinDir)). +    rebar_file_utils:rm_rf(dag_file(CompilerMod, EbinDir, Label)).  -spec needs_compile(filename:all(), extension(), [{extension(), file:dirname()}]) -> boolean().  needs_compile(Source, OutExt, Mappings) -> @@ -133,11 +136,17 @@ run_on_extra_src_dirs([], _CompilerMod, _AppInfo, _Fun) ->  run_on_extra_src_dirs([Dir | Rest], CompilerMod, AppInfo, Fun) ->      case filelib:is_dir(filename:join(rebar_app_info:dir(AppInfo), Dir)) of          true -> +            OldSrcDirs = rebar_app_info:get(AppInfo, src_dirs, ["src"]), +            AppDir = rebar_app_info:dir(AppInfo),              EbinDir = filename:join(rebar_app_info:out_dir(AppInfo), Dir),              AppInfo1 = rebar_app_info:ebin_dir(AppInfo, EbinDir),              AppInfo2 = rebar_app_info:set(AppInfo1, src_dirs, [Dir]), -            AppInfo3 = rebar_app_info:set(AppInfo2, extra_src_dirs, ["src"]), -            Fun(CompilerMod, AppInfo3); +            AppInfo3 = rebar_app_info:set(AppInfo2, extra_src_dirs, OldSrcDirs), +            AppInfo4 = add_to_includes( % give access to .hrl in app's src/ +                AppInfo3, +                [filename:join([AppDir, D]) || D <- OldSrcDirs] +            ), +            Fun(CompilerMod, AppInfo4);          _ ->              ok      end, @@ -170,8 +179,16 @@ find_source_files(BaseDir, SrcExt, SrcDirs, Opts) ->                        rebar_utils:find_files_in_dirs([filename:join(BaseDir, SrcDir)], SourceExtRe, Recursive)                    end, SrcDirs). -dag_file(CompilerMod, Dir) -> -    filename:join([rebar_dir:local_cache_dir(Dir), CompilerMod, ?DAG_FILE]). +%% @private generate the name for the DAG based on the compiler module and +%% a custom label, both of which are used to prevent various compiler runs +%% from clobbering each other. The label `undefined' is kept for a default +%% run of the compiler, to keep in line with previous versions of the file. +dag_file(CompilerMod, Dir, undefined) -> +    filename:join([rebar_dir:local_cache_dir(Dir), CompilerMod, +                   ?DAG_ROOT ++ ?DAG_EXT]); +dag_file(CompilerMod, Dir, Label) -> +    filename:join([rebar_dir:local_cache_dir(Dir), CompilerMod, +                   ?DAG_ROOT ++ "_" ++ Label ++ ?DAG_EXT]).  %% private graph functions @@ -179,20 +196,22 @@ dag_file(CompilerMod, Dir) ->  %% 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_dag(Compiler, InclDirs, SrcDirs, Erls, Dir, EbinDir) -> +init_dag(Compiler, InclDirs, SrcDirs, Erls, Dir, EbinDir, Label) ->      G = digraph:new([acyclic]), -    try restore_dag(Compiler, G, InclDirs, Dir) +    try restore_dag(Compiler, G, InclDirs, Dir, Label)      catch          _:_ -> -            ?WARN("Failed to restore ~ts file. Discarding it.~n", [dag_file(Compiler, Dir)]), -            file:delete(dag_file(Compiler, Dir)) +            ?WARN("Failed to restore ~ts file. Discarding it.~n", [dag_file(Compiler, Dir, Label)]), +            file:delete(dag_file(Compiler, Dir, Label))      end,      Dirs = lists:usort(InclDirs ++ SrcDirs),      %% A source file may have been renamed or deleted. Remove it from the graph      %% and remove any beam file for that source if it exists.      Modified = maybe_rm_beams_and_edges(G, EbinDir, Erls),      Modified1 = lists:foldl(update_dag_fun(G, Compiler, Dirs), Modified, Erls), -    if Modified1 -> store_dag(Compiler, G, InclDirs, Dir); not Modified1 -> ok end, +    if Modified1 -> store_dag(Compiler, G, InclDirs, Dir, Label); +       not Modified1 -> ok +    end,      G.  maybe_rm_beams_and_edges(G, Dir, Files) -> @@ -229,8 +248,8 @@ maybe_rm_beam_and_edge(G, OutDir, Source) ->  target_base(OutDir, Source) ->      filename:join(OutDir, filename:basename(Source, ".erl")). -restore_dag(Compiler, G, InclDirs, Dir) -> -    case file:read_file(dag_file(Compiler, Dir)) of +restore_dag(Compiler, G, InclDirs, Dir, Label) -> +    case file:read_file(dag_file(Compiler, Dir, Label)) of          {ok, Data} ->              % Since externally passed InclDirs can influence dependency graph (see              % modify_dag), we have to check here that they didn't change. @@ -248,10 +267,10 @@ restore_dag(Compiler, G, InclDirs, Dir) ->              ok      end. -store_dag(Compiler, G, InclDirs, Dir) -> +store_dag(Compiler, G, InclDirs, Dir, Label) ->      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 = dag_file(Compiler, Dir), +    File = dag_file(Compiler, Dir, Label),      ok = filelib:ensure_dir(File),      Data = term_to_binary(#dag{info={Vs, Es, InclDirs}}, [{compressed, 2}]),      file:write_file(File, Data). @@ -313,3 +332,10 @@ update_max_modified_deps(G, Source) ->                      end, 0, [Source | digraph:out_neighbours(G, Source)]),      digraph:add_vertex(G, Source, MaxModified),      MaxModified. + +add_to_includes(AppInfo, Dirs) -> +    Opts = rebar_app_info:opts(AppInfo), +    List = rebar_opts:get(Opts, erl_opts, []), +    NewErlOpts = [{i, Dir} || Dir <- Dirs] ++ List, +    NewOpts = rebar_opts:set(Opts, erl_opts, NewErlOpts), +    rebar_app_info:opts(AppInfo, NewOpts). diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl index 5e24d90..c0cfd6d 100644 --- a/src/rebar_prv_shell.erl +++ b/src/rebar_prv_shell.erl @@ -134,6 +134,7 @@ info() ->      "Start a shell with project and deps preloaded similar to~n'erl -pa ebin -pa deps/*/ebin'.~n".  setup_shell(ShellArgs) -> +    LoggerState = maybe_remove_logger(),      OldUser = kill_old_user(),      %% Test for support here      NewUser = try erlang:open_port({spawn,"tty_sl -c -e"}, []) of @@ -144,7 +145,28 @@ setup_shell(ShellArgs) ->          error:_ ->              setup_old_shell()      end, -    rewrite_leaders(OldUser, NewUser). +    rewrite_leaders(OldUser, NewUser), +    maybe_reset_logger(LoggerState). + +%% @private starting with OTP-21.2.3, there's an oddity where the logger +%% likely tries to handle system logs while we take down the TTY, which +%% ends up hanging the default logger. This function (along with +%% `maybe_reset_logger/1') removes and re-adds the default logger before and +%% after the TTY subsystem is taken offline, which prevents such hanging. +maybe_remove_logger() -> +    case erlang:function_exported(logger, module_info, 0) of +        false -> +            ignore; +        true -> +            {ok, Cfg} = logger:get_handler_config(default), +            logger:remove_handler(default), +            {restart, Cfg} +    end. + +maybe_reset_logger(ignore) -> +    ok; +maybe_reset_logger({restart, #{module := Mod, config := Cfg}}) -> +    logger:add_handler(default, Mod, Cfg).  kill_old_user() ->      OldUser = whereis(user), diff --git a/test/rebar_compile_SUITE.erl b/test/rebar_compile_SUITE.erl index ddaad0c..0bc3ad0 100644 --- a/test/rebar_compile_SUITE.erl +++ b/test/rebar_compile_SUITE.erl @@ -16,6 +16,7 @@ all() ->       {group, basic_extras}, {group, release_extras}, {group, unbalanced_extras},       {group, root_extras},       recompile_when_hrl_changes, recompile_when_included_hrl_changes, +     recompile_extra_when_hrl_in_src_changes,       recompile_when_opts_included_hrl_changes,       recompile_when_opts_change,       dont_recompile_when_opts_dont_change, dont_recompile_yrl_or_xrl, @@ -710,6 +711,49 @@ recompile_when_included_hrl_changes(Config) ->      ?assert(ModTime =/= NewModTime). +recompile_extra_when_hrl_in_src_changes(Config) -> +    AppDir = ?config(apps, Config), + +    Name = rebar_test_utils:create_random_name("app1_"), +    Vsn = rebar_test_utils:create_random_vsn(), +    rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + +    ExtraSrc = <<"-module(test_header_include).\n" +                  "-export([main/0]).\n" +                  "-include(\"test_header_include.hrl\").\n" +                  "main() -> ?SOME_DEFINE.\n">>, + +    ExtraHeader = <<"-define(SOME_DEFINE, true).\n">>, +    HeaderFile = filename:join([AppDir, "src", "test_header_include.hrl"]), +    SrcFile = filename:join([AppDir, "extra", "test_header_include.erl"]), +    filelib:ensure_dir(SrcFile), +    ok = file:write_file(SrcFile, ExtraSrc), +    ok = file:write_file(HeaderFile, ExtraHeader), + +    RebarCfg = [{extra_src_dirs, ["extra"]}], +    rebar_test_utils:run_and_check(Config, RebarCfg, ["compile"], +                                   {ok, [{app, Name}]}), + +    EbinDir = filename:join([AppDir, "_build", "default", "lib", Name, "extra"]), +    {ok, Files} = rebar_utils:list_dir(EbinDir), +    ModTime = [filelib:last_modified(filename:join([EbinDir, F])) +               || F <- Files, filename:extension(F) == ".beam"], + +    timer:sleep(1000), + +    NewExtraHeader = <<"-define(SOME_DEFINE, false).\n">>, +    ok = file:write_file(HeaderFile, NewExtraHeader, [sync]), + +    rebar_test_utils:run_and_check(Config, RebarCfg, ["compile"], +                                   {ok, [{app, Name}]}), + +    {ok, NewFiles} = rebar_utils:list_dir(EbinDir), + +    NewModTime = [filelib:last_modified(filename:join([EbinDir, F])) +                  || F <- NewFiles, filename:extension(F) == ".beam"], + +    ?assert(ModTime =/= NewModTime). +  recompile_when_opts_included_hrl_changes(Config) ->      AppsDir = ?config(apps, Config), | 
