diff options
author | alisdair sullivan <alisdairsullivan@yahoo.ca> | 2016-09-20 02:27:41 -0700 |
---|---|---|
committer | alisdair sullivan <alisdairsullivan@yahoo.ca> | 2016-09-20 02:27:41 -0700 |
commit | dad7900d31fc2744c9d6b877f1a859f1f940cc39 (patch) | |
tree | 3b2845ecab4d07edce308f5835d026736b689e18 | |
parent | fb363cd8de68e9113e407ac0be049cacdd9ddd07 (diff) |
recompile all files when a parse transform given as an opt needs updating
there's no way to detect which files actually rely on a parse transform
passed to the compiler via the options (as opposed to `-compile(..)`
so if any parse transforms are in modules that need recompiling just
recompile the world
fixes #1328
-rw-r--r-- | src/rebar_erlc_compiler.erl | 16 | ||||
-rw-r--r-- | test/rebar_compile_SUITE.erl | 105 |
2 files changed, 118 insertions, 3 deletions
diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl index 167f2bb..b148172 100644 --- a/src/rebar_erlc_compiler.erl +++ b/src/rebar_erlc_compiler.erl @@ -202,7 +202,13 @@ compile_dirs(RebarOpts, BaseDir, SrcDirs, OutDir, Opts) -> G = init_erlcinfo(include_abs_dirs(ErlOpts, BaseDir), AllErlFiles, BaseDir, OutDir), - NeededErlFiles = needed_files(G, ErlOpts, BaseDir, OutDir, AllErlFiles), + {ParseTransforms, Rest} = split_source_files(AllErlFiles, ErlOpts), + NeededErlFiles = case needed_files(G, ErlOpts, BaseDir, OutDir, ParseTransforms) of + [] -> needed_files(G, ErlOpts, BaseDir, OutDir, Rest); + %% at least one parse transform in the opts needs updating, so recompile all + _ -> AllErlFiles + end, + {ErlFirstFiles, ErlOptsFirst} = erl_first_files(RebarOpts, ErlOpts, BaseDir, NeededErlFiles), {DepErls, OtherErls} = lists:partition( fun(Source) -> digraph:in_degree(G, Source) > 0 end, @@ -296,6 +302,14 @@ erl_first_files(Opts, ErlOpts, Dir, NeededErlFiles) -> end, ErlOpts), {ErlFirstFiles ++ ParseTransformsErls, ErlOptsFirst}. +split_source_files(SourceFiles, ErlOpts) -> + ParseTransforms = proplists:get_all_values(parse_transform, ErlOpts), + lists:partition(fun(Source) -> + lists:member(filename_to_atom(Source), ParseTransforms) + end, SourceFiles). + +filename_to_atom(F) -> list_to_atom(filename:rootname(filename:basename(F))). + %% Get subset of SourceFiles which need to be recompiled, respecting %% dependencies induced by given graph G. needed_files(G, ErlOpts, Dir, OutDir, SourceFiles) -> diff --git a/test/rebar_compile_SUITE.erl b/test/rebar_compile_SUITE.erl index d9b75e4..cb16304 100644 --- a/test/rebar_compile_SUITE.erl +++ b/test/rebar_compile_SUITE.erl @@ -42,7 +42,9 @@ deps_build_in_prod/1, include_file_relative_to_working_directory/1, include_file_in_src/1, - always_recompile_when_erl_compiler_options_set/1]). + always_recompile_when_erl_compiler_options_set/1, + recompile_when_parse_transform_inline_changes/1, + recompile_when_parse_transform_as_opt_changes/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -65,7 +67,9 @@ all() -> parse_transform_test, erl_first_files_test, mib_test, umbrella_mib_first_test, only_default_transitive_deps, clean_all, override_deps, profile_override_deps, deps_build_in_prod, - include_file_relative_to_working_directory, include_file_in_src] ++ + include_file_relative_to_working_directory, include_file_in_src, + recompile_when_parse_transform_as_opt_changes, + recompile_when_parse_transform_inline_changes] ++ case erlang:function_exported(os, unsetenv, 1) of true -> [always_recompile_when_erl_compiler_options_set]; false -> [] @@ -1348,5 +1352,102 @@ always_recompile_when_erl_compiler_options_set(Config) -> _ -> os:putenv("ERL_COMPILER_OPTIONS", ExistingEnv) end. +recompile_when_parse_transform_inline_changes(Config) -> + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("parse_transform_inline_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + ok = filelib:ensure_dir(filename:join([AppDir, "src", "dummy"])), + + ModSrc = <<"-module(example).\n" + "-export([foo/2]).\n" + "-compile([{parse_transform, example_parse_transform}]).\n" + "foo(_, _) -> ok.">>, + + ok = file:write_file(filename:join([AppDir, "src", "example.erl"]), + ModSrc), + + ParseTransform = <<"-module(example_parse_transform).\n" + "-export([parse_transform/2]).\n" + "parse_transform(AST, _) -> AST.\n">>, + + ok = file:write_file(filename:join([AppDir, "src", "example_parse_transform.erl"]), + ParseTransform), + + rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}), + + EbinDir = filename:join([AppDir, "_build", "default", "lib", Name, "ebin"]), + {ok, Files} = rebar_utils:list_dir(EbinDir), + ModTime = [filelib:last_modified(filename:join([EbinDir, F])) + || F <- Files, filename:basename(F, ".beam") == "example"], + + timer:sleep(1000), + + NewParseTransform = <<"-module(example_parse_transform).\n" + "-export([parse_transform/2]).\n" + "parse_transform(AST, _) -> identity(AST).\n" + "identity(AST) -> AST.\n">>, + + ok = file:write_file(filename:join([AppDir, "src", "example_parse_transform.erl"]), + NewParseTransform), + + rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}), + + {ok, NewFiles} = rebar_utils:list_dir(EbinDir), + NewModTime = [filelib:last_modified(filename:join([EbinDir, F])) + || F <- NewFiles, filename:basename(F, ".beam") == "example"], + ?assert(ModTime =/= NewModTime). + +recompile_when_parse_transform_as_opt_changes(Config) -> + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("parse_transform_opt_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + ok = filelib:ensure_dir(filename:join([AppDir, "src", "dummy"])), + + ModSrc = <<"-module(example).\n" + "-export([foo/2]).\n" + "foo(_, _) -> ok.">>, + + ok = file:write_file(filename:join([AppDir, "src", "example.erl"]), + ModSrc), + + ParseTransform = <<"-module(example_parse_transform).\n" + "-export([parse_transform/2]).\n" + "parse_transform(AST, _) -> AST.">>, + + ok = file:write_file(filename:join([AppDir, "src", "example_parse_transform.erl"]), + ParseTransform), + + RebarConfig = [{erl_opts, [{parse_transform, example_parse_transform}]}], + + rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], {ok, [{app, Name}]}), + + EbinDir = filename:join([AppDir, "_build", "default", "lib", Name, "ebin"]), + {ok, Files} = rebar_utils:list_dir(EbinDir), + ModTime = [filelib:last_modified(filename:join([EbinDir, F])) + || F <- Files, filename:basename(F, ".beam") == "example"], + + timer:sleep(1000), + + NewParseTransform = <<"-module(example_parse_transform).\n" + "-export([parse_transform/2]).\n" + "parse_transform(AST, _) -> identity(AST).\n" + "identity(AST) -> AST.">>, + + ok = file:write_file(filename:join([AppDir, "src", "example_parse_transform.erl"]), + NewParseTransform), + + rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], {ok, [{app, Name}]}), + + {ok, NewFiles} = rebar_utils:list_dir(EbinDir), + NewModTime = [filelib:last_modified(filename:join([EbinDir, F])) + || F <- NewFiles, filename:basename(F, ".beam") == "example"], + + ?assert(ModTime =/= NewModTime). |