diff options
| author | Tristan Sloughter <t@crashfast.com> | 2018-09-21 10:32:57 -0600 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-09-21 10:32:57 -0600 | 
| commit | 2dfba204e4dea5d1c3821fd26d22bd7201595f6c (patch) | |
| tree | 147f7ab179d3342bc06d106e188d5c62637bc3ec | |
| parent | 8e0ef83de48968fed80114cc4518004ed59fa649 (diff) | |
properly support top level app erl_opts from REBAR_CONFIG os var (#1889)
When REBAR_CONFIG was set it would not effect the top level app's
configuration because app_discover was rereading the top level
rebar.config which ignored REBAR_CONFIG. Instead this patch has
it use the existing configuration from REBAR_CONFIG.
| -rw-r--r-- | rebar.config | 3 | ||||
| -rw-r--r-- | src/rebar_app_discover.erl | 74 | ||||
| -rw-r--r-- | src/rebar_app_info.erl | 14 | ||||
| -rw-r--r-- | src/rebar_erlc_compiler.erl | 1 | ||||
| -rw-r--r-- | src/rebar_prv_plugins.erl | 4 | ||||
| -rw-r--r-- | test/rebar_compile_SUITE.erl | 28 | ||||
| -rw-r--r-- | test/rebar_hooks_SUITE.erl | 65 | ||||
| -rw-r--r-- | test/rebar_test_utils.erl | 29 | 
8 files changed, 157 insertions, 61 deletions
| diff --git a/rebar.config b/rebar.config index 2d41927..f481e28 100644 --- a/rebar.config +++ b/rebar.config @@ -34,7 +34,8 @@              {platform_define, "^(19|2)", rand_only},              {platform_define, "^2", unicode_str},              {platform_define, "^(R|1|20)", fun_stacktrace}, -            warnings_as_errors]}. +            warnings_as_errors +           ]}.  %% Use OTP 18+ when dialyzing rebar3  {dialyzer, [ diff --git a/src/rebar_app_discover.erl b/src/rebar_app_discover.erl index 9b1346d..224539b 100644 --- a/src/rebar_app_discover.erl +++ b/src/rebar_app_discover.erl @@ -7,7 +7,7 @@           find_unbuilt_apps/1,           find_apps/1,           find_apps/2, -         find_apps/3, +         find_apps/4,           find_app/2,           find_app/3]). @@ -23,7 +23,7 @@ do(State, LibDirs) ->      Dirs = [filename:join(BaseDir, LibDir) || LibDir <- LibDirs],      RebarOpts = rebar_state:opts(State),      SrcDirs = rebar_dir:src_dirs(RebarOpts, ["src"]), -    Apps = find_apps(Dirs, SrcDirs, all), +    Apps = find_apps(Dirs, SrcDirs, all, State),      ProjectDeps = rebar_state:deps_names(State),      DepsDir = rebar_dir:deps_dir(State),      CurrentProfiles = rebar_state:current_profiles(State), @@ -52,7 +52,7 @@ do(State, LibDirs) ->                          Name = rebar_app_info:name(AppInfo),                          case enable(State, AppInfo) of                              true -> -                                {AppInfo1, StateAcc1} = merge_deps(AppInfo, StateAcc), +                                {AppInfo1, StateAcc1} = merge_opts(AppInfo, StateAcc),                                  OutDir = filename:join(DepsDir, Name),                                  AppInfo2 = rebar_app_info:out_dir(AppInfo1, OutDir),                                  ProjectDeps1 = lists:delete(Name, ProjectDeps), @@ -87,33 +87,34 @@ format_error({module_list, File}) ->  format_error({missing_module, Module}) ->      io_lib:format("Module defined in app file missing: ~p~n", [Module]). -%% @doc handles the merging and application of profiles and overrides -%% for a given application, within its own context. --spec merge_deps(rebar_app_info:t(), rebar_state:t()) -> +%% @doc merges configuration of a project app and the top level state +%% some configuration like erl_opts must be merged into a subapp's opts +%% while plugins and hooks need to be kept defined to only either the +%% top level state or an individual application. +-spec merge_opts(rebar_app_info:t(), rebar_state:t()) ->      {rebar_app_info:t(), rebar_state:t()}. -merge_deps(AppInfo, State) -> +merge_opts(AppInfo, State) ->      %% These steps make sure that hooks and artifacts are run in the context of      %% the application they are defined at. If an umbrella structure is used and      %% they are defined at the top level they will instead run in the context of      %% the State and at the top level, not as part of an application.      CurrentProfiles = rebar_state:current_profiles(State), -    Default = reset_hooks(rebar_state:default(State), CurrentProfiles), -    {AppInfo0, State1} = project_app_config(AppInfo, Default, State), +    {AppInfo1, State1} = maybe_reset_hooks_plugins(AppInfo, State), -    Name = rebar_app_info:name(AppInfo0), +    Name = rebar_app_info:name(AppInfo1),      %% We reset the opts here to default so no profiles are applied multiple times -    AppInfo1 = rebar_app_info:apply_overrides(rebar_state:get(State1, overrides, []), AppInfo0), -    AppInfo2 = rebar_app_info:apply_profiles(AppInfo1, CurrentProfiles), +    AppInfo2 = rebar_app_info:apply_overrides(rebar_state:get(State1, overrides, []), AppInfo1), +    AppInfo3 = rebar_app_info:apply_profiles(AppInfo2, CurrentProfiles),      %% Will throw an exception if checks fail -    rebar_app_info:verify_otp_vsn(AppInfo2), +    rebar_app_info:verify_otp_vsn(AppInfo3),      State2 = lists:foldl(fun(Profile, StateAcc) -> -                                 handle_profile(Profile, Name, AppInfo2, StateAcc) +                                 handle_profile(Profile, Name, AppInfo3, StateAcc)                           end, State1, lists:reverse(CurrentProfiles)), -    {AppInfo2, State2}. +    {AppInfo3, State2}.  %% @doc Applies a given profile for an app, ensuring the deps  %% match the context it will require. @@ -151,25 +152,15 @@ parse_profile_deps(Profile, Name, Deps, Opts, State) ->                                ,Locks                                ,1). -%% @doc Find the app-level config and return the state updated -%% with the relevant app-level data. --spec project_app_config(rebar_app_info:t(), rebar_dict(), rebar_state:t()) -> -    {Config, rebar_state:t()} when -      Config :: [any()]. -project_app_config(AppInfo, Default, State) -> -    C = rebar_config:consult(rebar_app_info:dir(AppInfo)), -    AppInfo1 = rebar_app_info:update_opts(AppInfo, Default, C), -    {AppInfo2, State1} = maybe_reset_hooks_plugins(AppInfo1, State), -    {AppInfo2, State1}. - +%% reset the State hooks if there is a top level application  -spec maybe_reset_hooks_plugins(AppInfo, State) ->  {AppInfo, State} when        AppInfo :: rebar_app_info:t(),        State :: rebar_state:t().  maybe_reset_hooks_plugins(AppInfo, State) ->      Dir = rebar_app_info:dir(AppInfo), +    CurrentProfiles = rebar_state:current_profiles(State),      case ec_file:real_dir_path(rebar_dir:root_dir(State)) of          Dir -> -            CurrentProfiles = rebar_state:current_profiles(State),              Opts = reset_hooks(rebar_state:opts(State), CurrentProfiles),              State1 = rebar_state:opts(State, Opts), @@ -179,7 +170,11 @@ maybe_reset_hooks_plugins(AppInfo, State) ->              {AppInfo1, State1};          _ -> -            {AppInfo, State} +            %% if not in the top root directory then we need to merge in the +            %% default state opts to this subapp's opts +            Default = reset_hooks(rebar_state:default(State), CurrentProfiles), +            AppInfo1 = rebar_app_info:update_opts(AppInfo, Default), +            {AppInfo1, State}      end. @@ -211,8 +206,8 @@ reset_hooks(Opts, CurrentProfiles) ->  -spec all_app_dirs([file:name()]) -> [{file:name(), [file:name()]}].  all_app_dirs(LibDirs) ->      lists:flatmap(fun(LibDir) -> -                        {_, SrcDirs} = find_config_src(LibDir, ["src"]), -                        app_dirs(LibDir, SrcDirs) +                          {_, SrcDirs} = find_config_src(LibDir, ["src"]), +                          app_dirs(LibDir, SrcDirs)                    end, LibDirs).  %% @private find the directories for all apps based on their source dirs @@ -270,11 +265,11 @@ find_apps(LibDirs, Validate) ->  %% @doc for each directory passed, with the configured source directories,  %% find all apps according to the validity rule passed in.  %% Returns all the related app info records. --spec find_apps([file:filename_all()], [file:filename_all()], valid | invalid | all) -> [rebar_app_info:t()]. -find_apps(LibDirs, SrcDirs, Validate) -> +-spec find_apps([file:filename_all()], [file:filename_all()], valid | invalid | all, rebar_state:t()) -> [rebar_app_info:t()]. +find_apps(LibDirs, SrcDirs, Validate, State) ->      rebar_utils:filtermap(        fun({AppDir, AppSrcDirs}) -> -            find_app(rebar_app_info:new(), AppDir, AppSrcDirs, Validate) +            find_app(rebar_app_info:new(), AppDir, AppSrcDirs, Validate, State)        end,        all_app_dirs(LibDirs, SrcDirs)      ). @@ -305,8 +300,19 @@ find_app(AppInfo, AppDir, Validate) ->  %% the directories where source files can be located. Returns the related  %% app info record.  -spec find_app(rebar_app_info:t(), file:filename_all(), -               [file:filename_all()], valid | invalid | all) -> +               [file:filename_all()], valid | invalid | all, rebar_state:t()) ->      {true, rebar_app_info:t()} | false. +find_app(AppInfo, AppDir, SrcDirs, Validate, State) -> +    AppInfo1 = case ec_file:real_dir_path(rebar_dir:root_dir(State)) of +                   AppDir -> +                       Opts = rebar_state:opts(State), +                       rebar_app_info:default(rebar_app_info:opts(AppInfo, Opts), Opts); +                   _ -> +                       Config = rebar_config:consult(AppDir), +                       rebar_app_info:update_opts(AppInfo, rebar_app_info:opts(AppInfo), Config) +               end, +    find_app_(AppInfo1, AppDir, SrcDirs, Validate). +  find_app(AppInfo, AppDir, SrcDirs, Validate) ->      Config = rebar_config:consult(AppDir),      AppInfo1 = rebar_app_info:update_opts(AppInfo, rebar_app_info:opts(AppInfo), Config), diff --git a/src/rebar_app_info.erl b/src/rebar_app_info.erl index 56ae4c0..eb95311 100644 --- a/src/rebar_app_info.erl +++ b/src/rebar_app_info.erl @@ -7,6 +7,7 @@           new/4,           new/5,           update_opts/3, +         update_opts/2,           update_opts_deps/2,           discover/1,           name/1, @@ -158,7 +159,7 @@ update_opts(AppInfo, Opts, Config) ->                         %% don't set anything here.                         [];                     _ -> -                       deps_from_config(dir(AppInfo), Config) +                       deps_from_config(dir(AppInfo), proplists:get_value(deps, Config, []))                 end,      Plugins = proplists:get_value(plugins, Config, []), @@ -171,6 +172,13 @@ update_opts(AppInfo, Opts, Config) ->      AppInfo#app_info_t{opts=NewOpts,                         default=NewOpts}. +%% @doc update current app info opts by merging in a new dict of opts +-spec update_opts(t(), rebar_dict()) -> t(). +update_opts(AppInfo=#app_info_t{opts=LocalOpts}, Opts) -> +    NewOpts = rebar_opts:merge_opts(LocalOpts, Opts), +    AppInfo#app_info_t{opts=NewOpts, +                       default=NewOpts}. +  %% @doc update the opts based on new deps, usually from an app's hex registry metadata  -spec update_opts_deps(t(), [any()]) -> t().  update_opts_deps(AppInfo=#app_info_t{opts=Opts}, Deps) -> @@ -183,10 +191,10 @@ update_opts_deps(AppInfo=#app_info_t{opts=Opts}, Deps) ->  %% @private extract the deps for an app in `Dir' based on its config file data  -spec deps_from_config(file:filename(), [any()]) -> [{tuple(), any()}, ...]. -deps_from_config(Dir, Config) -> +deps_from_config(Dir, ConfigDeps) ->      case rebar_config:consult_lock_file(filename:join(Dir, ?LOCK_FILE)) of          [] -> -            [{{deps, default}, proplists:get_value(deps, Config, [])}]; +            [{{deps, default}, ConfigDeps}];          D ->              %% We want the top level deps only from the lock file.              %% This ensures deterministic overrides for configs. diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl index 57e5398..c63ca5b 100644 --- a/src/rebar_erlc_compiler.erl +++ b/src/rebar_erlc_compiler.erl @@ -99,6 +99,7 @@ compile(AppInfo, CompileOpts) when element(1, AppInfo) == app_info_t ->                 {recursive, dir_recursive(RebarOpts, "src", CompileOpts)}],      MibsOpts = [check_last_mod,                  {recursive, dir_recursive(RebarOpts, "mibs", CompileOpts)}], +      rebar_base_compiler:run(RebarOpts,                              check_files([filename:join(Dir, File)                                           || File <- rebar_opts:get(RebarOpts, xrl_first_files, [])]), diff --git a/src/rebar_prv_plugins.erl b/src/rebar_prv_plugins.erl index 4bea3b3..d66b645 100644 --- a/src/rebar_prv_plugins.erl +++ b/src/rebar_prv_plugins.erl @@ -36,7 +36,7 @@ do(State) ->      GlobalPlugins = rebar_state:get(GlobalConfig, plugins, []),      GlobalSrcDirs = rebar_state:get(GlobalConfig, src_dirs, ["src"]),      GlobalPluginsDir = filename:join([rebar_dir:global_cache_dir(rebar_state:opts(State)), "plugins", "*"]), -    GlobalApps = rebar_app_discover:find_apps([GlobalPluginsDir], GlobalSrcDirs, all), +    GlobalApps = rebar_app_discover:find_apps([GlobalPluginsDir], GlobalSrcDirs, all, State),      display_plugins("Global plugins", GlobalApps, GlobalPlugins),      RebarOpts = rebar_state:opts(State), @@ -44,7 +44,7 @@ do(State) ->      Plugins = rebar_state:get(State, plugins, []),      PluginsDirs = filelib:wildcard(filename:join(rebar_dir:plugins_dir(State), "*")),      CheckoutsDirs = filelib:wildcard(filename:join(rebar_dir:checkouts_dir(State), "*")), -    Apps = rebar_app_discover:find_apps(CheckoutsDirs++PluginsDirs, SrcDirs, all), +    Apps = rebar_app_discover:find_apps(CheckoutsDirs++PluginsDirs, SrcDirs, all, State),      display_plugins("Local plugins", Apps, Plugins),      {ok, State}. diff --git a/test/rebar_compile_SUITE.erl b/test/rebar_compile_SUITE.erl index 269413e..e97b5fb 100644 --- a/test/rebar_compile_SUITE.erl +++ b/test/rebar_compile_SUITE.erl @@ -38,7 +38,8 @@ all() ->       recursive, no_recursive,       always_recompile_when_erl_compiler_options_set,       dont_recompile_when_erl_compiler_options_env_does_not_change, -     recompile_when_erl_compiler_options_env_changes]. +     recompile_when_erl_compiler_options_env_changes, +     rebar_config_os_var].  groups() ->      [{basic_app, [], [build_basic_app, paths_basic_app, clean_basic_app]}, @@ -774,7 +775,7 @@ recompile_when_opts_change(Config) ->      rebar_test_utils:create_config(AppDir, [{erl_opts, [{d, some_define}]}]), -    rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}), +    rebar_test_utils:run_and_check(Config, [{erl_opts, [{d, some_define}]}], ["compile"], {ok, [{app, Name}]}),      {ok, NewFiles} = rebar_utils:list_dir(EbinDir),      NewModTime = [filelib:last_modified(filename:join([EbinDir, F])) @@ -1944,6 +1945,29 @@ recompile_when_erl_compiler_options_env_changes(Config) ->          _     -> os:putenv("ERL_COMPILER_OPTIONS", ExistingEnv)      end. +rebar_config_os_var(Config) -> +    AppDir = ?config(apps, Config), + +    Name = rebar_test_utils:create_random_name("rebar_config_os_var_"), +    Vsn = rebar_test_utils:create_random_vsn(), +    rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + +    rebar_test_utils:create_config(AppDir, [{erl_opts, []}]), + +    AltConfig = filename:join(AppDir, "test.rebar.config"), +    file:write_file(AltConfig, "{erl_opts, [compressed]}."), +    true = os:putenv("REBAR_CONFIG", AltConfig), + +    rebar_test_utils:run_and_check(Config, ["compile"], {ok, [{app, Name}]}), + +    Path = filename:join([AppDir, "_build", "default", "lib", Name, "ebin"]), +    code:add_patha(Path), + +    Mod = list_to_atom("not_a_real_src_" ++ Name), + +    true = lists:member(compressed, proplists:get_value(options, Mod:module_info(compile), [])), +    ok. +  %% this test sets the env var, compiles, records the file last modified  %% timestamp, recompiles and compares the file last modified timestamp to  %% ensure it has changed. this test should run on 18.x diff --git a/test/rebar_hooks_SUITE.erl b/test/rebar_hooks_SUITE.erl index 1f3edd2..aae7ea0 100644 --- a/test/rebar_hooks_SUITE.erl +++ b/test/rebar_hooks_SUITE.erl @@ -1,20 +1,6 @@  -module(rebar_hooks_SUITE). --export([suite/0, -         init_per_suite/1, -         end_per_suite/1, -         init_per_testcase/2, -         end_per_testcase/2, -         all/0, -         build_and_clean_app/1, -         escriptize_artifacts/1, -         run_hooks_once/1, -         run_hooks_once_profiles/1, -         run_hooks_for_plugins/1, -         eunit_app_hooks/1, -         deps_hook_namespace/1, -         bare_compile_hooks_default_ns/1, -         deps_clean_hook_namespace/1]). +-compile(export_all).  -include_lib("common_test/include/ct.hrl").  -include_lib("eunit/include/eunit.hrl"). @@ -38,7 +24,8 @@ end_per_testcase(_, _Config) ->  all() ->      [build_and_clean_app, run_hooks_once, run_hooks_once_profiles,       escriptize_artifacts, run_hooks_for_plugins, deps_hook_namespace, -     bare_compile_hooks_default_ns, deps_clean_hook_namespace, eunit_app_hooks]. +     bare_compile_hooks_default_ns, deps_clean_hook_namespace, eunit_app_hooks, +     sub_app_hooks, root_hooks].  %% Test post provider hook cleans compiled project app, leaving it invalid  build_and_clean_app(Config) -> @@ -97,7 +84,7 @@ run_hooks_once(Config) ->      Name = rebar_test_utils:create_random_name("app1_"),      Vsn = rebar_test_utils:create_random_vsn(), -    RebarConfig = [{pre_hooks, [{compile, "mkdir blah"}]}], +    RebarConfig = [{pre_hooks, [{compile, "mkdir  $REBAR_ROOT_DIR/blah"}]}],      rebar_test_utils:create_config(AppDir, RebarConfig),      rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]),      rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], {ok, [{app, Name, valid}]}). @@ -109,7 +96,7 @@ run_hooks_once_profiles(Config) ->      Name = rebar_test_utils:create_random_name("app1_"),      Vsn = rebar_test_utils:create_random_vsn(), -    RebarConfig = [{profiles, [{hooks, [{pre_hooks, [{compile, "mkdir blah"}]}]}]}], +    RebarConfig = [{profiles, [{hooks, [{pre_hooks, [{compile, "mkdir $REBAR_ROOT_DIR/blah"}]}]}]}],      rebar_test_utils:create_config(AppDir, RebarConfig),      rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]),      rebar_test_utils:run_and_check(Config, RebarConfig, ["as", "hooks", "compile"], {ok, [{app, Name, valid}]}). @@ -215,3 +202,45 @@ run_hooks_for_plugins(Config) ->      rebar_test_utils:run_and_check(Config, RConf, ["compile"], {ok, [{app, Name, valid},                                                                       {plugin, PluginName},                                                                       {file, filename:join([AppDir, "_build", "default", "plugins", PluginName, "randomfile"])}]}). + +%% test that a subapp of a project keeps its hooks +sub_app_hooks(Config) -> +    AppDir = ?config(apps, Config), + +    Name = rebar_test_utils:create_random_name("sub_app1_"), +    Vsn = rebar_test_utils:create_random_vsn(), + +    SubAppsDir = filename:join([AppDir, "apps", Name]), + +    rebar_test_utils:create_app(SubAppsDir, Name, Vsn, [kernel, stdlib]), +    rebar_test_utils:create_config(SubAppsDir, [{provider_hooks, [{post, [{compile, clean}]}]}]), + +    RConfFile = rebar_test_utils:create_config(AppDir, []), +    {ok, RConf} = file:consult(RConfFile), + +    %% Build with deps. +    rebar_test_utils:run_and_check( +      Config, RConf, ["compile"], +      {ok, [{app, Name, invalid}]} +     ). + +%% test that hooks at the top level don't run in the subapps +root_hooks(Config) -> +    AppDir = ?config(apps, Config), + +    Name = rebar_test_utils:create_random_name("sub_app1_"), +    Vsn = rebar_test_utils:create_random_vsn(), + +    SubAppsDir = filename:join([AppDir, "apps", Name]), + +    rebar_test_utils:create_app(SubAppsDir, Name, Vsn, [kernel, stdlib]), +    rebar_test_utils:create_config(SubAppsDir, [{provider_hooks, [{post, [{compile, clean}]}]}]), + +    RConfFile = rebar_test_utils:create_config(AppDir, [{pre_hooks, [{compile, "mkdir $REBAR_ROOT_DIR/blah"}]}]), +    {ok, RConf} = file:consult(RConfFile), + +    %% Build with deps. +    rebar_test_utils:run_and_check( +      Config, RConf, ["compile"], +      {ok, [{app, Name, invalid}]} +     ). diff --git a/test/rebar_test_utils.erl b/test/rebar_test_utils.erl index efe7467..944efa0 100644 --- a/test/rebar_test_utils.erl +++ b/test/rebar_test_utils.erl @@ -1,7 +1,7 @@  -module(rebar_test_utils).  -include_lib("common_test/include/ct.hrl").  -include_lib("eunit/include/eunit.hrl"). --export([init_rebar_state/1, init_rebar_state/2, run_and_check/4, check_results/3]). +-export([init_rebar_state/1, init_rebar_state/2, run_and_check/3, run_and_check/4, check_results/3]).  -export([expand_deps/2, flat_deps/1, top_level_deps/1]).  -export([create_app/4, create_plugin/4, create_eunit_app/4, create_empty_app/4,           create_config/2, create_config/3, package_app/4]). @@ -82,6 +82,33 @@ run_and_check(Config, RebarConfig, Command, Expect) ->          rebar_abort when Expect =:= rebar_abort -> rebar_abort      end. +run_and_check(Config, Command, Expect) -> +    %% Assumes init_rebar_state has run first +    AppDir = ?config(apps, Config), +    {ok, Cwd} = file:get_cwd(), +    try +        ok = file:set_cwd(AppDir), +        Res = rebar3:run(Command), +        case Expect of +            {error, Reason} -> +                ?assertEqual({error, Reason}, Res); +            {ok, Expected} -> +                {ok, _} = Res, +                check_results(AppDir, Expected, "*"), +                Res; +            {ok, Expected, ProfileRun} -> +                {ok, _} = Res, +                check_results(AppDir, Expected, ProfileRun), +                Res; +            return -> +                Res +        end +    catch +        rebar_abort when Expect =:= rebar_abort -> rebar_abort +    after +        ok = file:set_cwd(Cwd) +    end. +  %% @doc Creates a dummy application including:  %% - src/<file>.erl  %% - src/<file>.app.src | 
