Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

405 rader
16 KiB

Split up the compiler DAG This is another tricky commit towards replacing the current module analysis with EPP. The compiler DAG is now being shared across multiple applications being compiled, rather than a per-application basis, which promises to allow better ordering, parallelism, and more thorough invalidation of runtime dependencies when they are modified. This however required changes: - The compiler DAG is no longer private to `rebar_compiler`, and has been extracted to the `rebar_compiler_dag` module - The compiler DAG is now started by the `rebar_prv_compile` module, which oversees the calls to `rebar_compiler` for each OTP application - The compiler DAG has been refactored to use a "dirty flag" to know if it was modified, rather than just tracking modifications in a functional manner, since the scope change (going multi-app) makes it impossible to cleanly use the functional approach without much larger changes - The DAG used to be cached within each OTP application. This is no longer possible since it is shared. Instead the DAG is stored in the state's deps_dir, which allows to cleanly split caches between regular apps for the user's project and plugins - The DAG supported a "label" mode that was used to store distinct DAGs for extra_src_dir runs and regular modules; this label is now used (and extended to `rebar_prv_compile` internals) to distinguish between "compile runs", such as "project_apps", or just "apps" (deps). The label is optional (i.e. not used by plugins which have no such need) - The extra_src_dirs for each app is now compiled using the main app's DAG, but the run takes place later in the compilation process. This may need changing to detect and prevent dependencies from src_dirs into extra_src_dirs, but this should not technically be a problem for runtime anyway. - Reworked the support for extra_src_dirs that are at the root of an umbrella project (and therefore do not belong to any single app) to use the new structure, also as part of the project_apps DAG. All tests keep passing, and this puts us in a better place to use EPP with cross-app support in the near-future.
5 år sedan
Split up the compiler DAG This is another tricky commit towards replacing the current module analysis with EPP. The compiler DAG is now being shared across multiple applications being compiled, rather than a per-application basis, which promises to allow better ordering, parallelism, and more thorough invalidation of runtime dependencies when they are modified. This however required changes: - The compiler DAG is no longer private to `rebar_compiler`, and has been extracted to the `rebar_compiler_dag` module - The compiler DAG is now started by the `rebar_prv_compile` module, which oversees the calls to `rebar_compiler` for each OTP application - The compiler DAG has been refactored to use a "dirty flag" to know if it was modified, rather than just tracking modifications in a functional manner, since the scope change (going multi-app) makes it impossible to cleanly use the functional approach without much larger changes - The DAG used to be cached within each OTP application. This is no longer possible since it is shared. Instead the DAG is stored in the state's deps_dir, which allows to cleanly split caches between regular apps for the user's project and plugins - The DAG supported a "label" mode that was used to store distinct DAGs for extra_src_dir runs and regular modules; this label is now used (and extended to `rebar_prv_compile` internals) to distinguish between "compile runs", such as "project_apps", or just "apps" (deps). The label is optional (i.e. not used by plugins which have no such need) - The extra_src_dirs for each app is now compiled using the main app's DAG, but the run takes place later in the compilation process. This may need changing to detect and prevent dependencies from src_dirs into extra_src_dirs, but this should not technically be a problem for runtime anyway. - Reworked the support for extra_src_dirs that are at the root of an umbrella project (and therefore do not belong to any single app) to use the new structure, also as part of the project_apps DAG. All tests keep passing, and this puts us in a better place to use EPP with cross-app support in the near-future.
5 år sedan
Split up the compiler DAG This is another tricky commit towards replacing the current module analysis with EPP. The compiler DAG is now being shared across multiple applications being compiled, rather than a per-application basis, which promises to allow better ordering, parallelism, and more thorough invalidation of runtime dependencies when they are modified. This however required changes: - The compiler DAG is no longer private to `rebar_compiler`, and has been extracted to the `rebar_compiler_dag` module - The compiler DAG is now started by the `rebar_prv_compile` module, which oversees the calls to `rebar_compiler` for each OTP application - The compiler DAG has been refactored to use a "dirty flag" to know if it was modified, rather than just tracking modifications in a functional manner, since the scope change (going multi-app) makes it impossible to cleanly use the functional approach without much larger changes - The DAG used to be cached within each OTP application. This is no longer possible since it is shared. Instead the DAG is stored in the state's deps_dir, which allows to cleanly split caches between regular apps for the user's project and plugins - The DAG supported a "label" mode that was used to store distinct DAGs for extra_src_dir runs and regular modules; this label is now used (and extended to `rebar_prv_compile` internals) to distinguish between "compile runs", such as "project_apps", or just "apps" (deps). The label is optional (i.e. not used by plugins which have no such need) - The extra_src_dirs for each app is now compiled using the main app's DAG, but the run takes place later in the compilation process. This may need changing to detect and prevent dependencies from src_dirs into extra_src_dirs, but this should not technically be a problem for runtime anyway. - Reworked the support for extra_src_dirs that are at the root of an umbrella project (and therefore do not belong to any single app) to use the new structure, also as part of the project_apps DAG. All tests keep passing, and this puts us in a better place to use EPP with cross-app support in the near-future.
5 år sedan
Split up the compiler DAG This is another tricky commit towards replacing the current module analysis with EPP. The compiler DAG is now being shared across multiple applications being compiled, rather than a per-application basis, which promises to allow better ordering, parallelism, and more thorough invalidation of runtime dependencies when they are modified. This however required changes: - The compiler DAG is no longer private to `rebar_compiler`, and has been extracted to the `rebar_compiler_dag` module - The compiler DAG is now started by the `rebar_prv_compile` module, which oversees the calls to `rebar_compiler` for each OTP application - The compiler DAG has been refactored to use a "dirty flag" to know if it was modified, rather than just tracking modifications in a functional manner, since the scope change (going multi-app) makes it impossible to cleanly use the functional approach without much larger changes - The DAG used to be cached within each OTP application. This is no longer possible since it is shared. Instead the DAG is stored in the state's deps_dir, which allows to cleanly split caches between regular apps for the user's project and plugins - The DAG supported a "label" mode that was used to store distinct DAGs for extra_src_dir runs and regular modules; this label is now used (and extended to `rebar_prv_compile` internals) to distinguish between "compile runs", such as "project_apps", or just "apps" (deps). The label is optional (i.e. not used by plugins which have no such need) - The extra_src_dirs for each app is now compiled using the main app's DAG, but the run takes place later in the compilation process. This may need changing to detect and prevent dependencies from src_dirs into extra_src_dirs, but this should not technically be a problem for runtime anyway. - Reworked the support for extra_src_dirs that are at the root of an umbrella project (and therefore do not belong to any single app) to use the new structure, also as part of the project_apps DAG. All tests keep passing, and this puts us in a better place to use EPP with cross-app support in the near-future.
5 år sedan
Fix directory recursion in compiler This patch contains two behaviour changes and reasserts other behaviours that now line things up with user and documentation expectations: 1. The src directories remain recursive. We turned it on by accident at some point in the past and now people rely on it, so we're stuck with it. However a new test ensures that the feature can be turned off on-demand as documented on the website. 2. The test directories are no longer recursive by default. The fix is done by properly fixing how rebar3.erl does its feature injection by mandating the default value there. I'm somewhat nervous that this change could negatively impact some users and older compiler module versions, but if users stick to the rebar_dir interface, everything should keep working transparently. 3. The test directories' configuration is no longer silently dropped. Due to how rebar3.erl injected test state without looking for what the user may have specified, multiple extra_src_dirs entries existed at once and were run; one with the recursion set to true and one with whatever the user specified. If the user disabled recursion of the "test" extra_src_dir, then the injected value still ran it once... 4. The handling of extra files in the compiler module is fixed to actually use the rebar_dir interface properly, and reinjects non-default directory recursion settings into the swapped options for the shimmed extra apps. Not doing this annotation step resulted in the write for swapped opts to actually drop the configured recursion value and make everything recursive all the time. A single new test actually validates all of that behaviour and seems to work fine.
5 år sedan
Split up the compiler DAG This is another tricky commit towards replacing the current module analysis with EPP. The compiler DAG is now being shared across multiple applications being compiled, rather than a per-application basis, which promises to allow better ordering, parallelism, and more thorough invalidation of runtime dependencies when they are modified. This however required changes: - The compiler DAG is no longer private to `rebar_compiler`, and has been extracted to the `rebar_compiler_dag` module - The compiler DAG is now started by the `rebar_prv_compile` module, which oversees the calls to `rebar_compiler` for each OTP application - The compiler DAG has been refactored to use a "dirty flag" to know if it was modified, rather than just tracking modifications in a functional manner, since the scope change (going multi-app) makes it impossible to cleanly use the functional approach without much larger changes - The DAG used to be cached within each OTP application. This is no longer possible since it is shared. Instead the DAG is stored in the state's deps_dir, which allows to cleanly split caches between regular apps for the user's project and plugins - The DAG supported a "label" mode that was used to store distinct DAGs for extra_src_dir runs and regular modules; this label is now used (and extended to `rebar_prv_compile` internals) to distinguish between "compile runs", such as "project_apps", or just "apps" (deps). The label is optional (i.e. not used by plugins which have no such need) - The extra_src_dirs for each app is now compiled using the main app's DAG, but the run takes place later in the compilation process. This may need changing to detect and prevent dependencies from src_dirs into extra_src_dirs, but this should not technically be a problem for runtime anyway. - Reworked the support for extra_src_dirs that are at the root of an umbrella project (and therefore do not belong to any single app) to use the new structure, also as part of the project_apps DAG. All tests keep passing, and this puts us in a better place to use EPP with cross-app support in the near-future.
5 år sedan
Fix directory recursion in compiler This patch contains two behaviour changes and reasserts other behaviours that now line things up with user and documentation expectations: 1. The src directories remain recursive. We turned it on by accident at some point in the past and now people rely on it, so we're stuck with it. However a new test ensures that the feature can be turned off on-demand as documented on the website. 2. The test directories are no longer recursive by default. The fix is done by properly fixing how rebar3.erl does its feature injection by mandating the default value there. I'm somewhat nervous that this change could negatively impact some users and older compiler module versions, but if users stick to the rebar_dir interface, everything should keep working transparently. 3. The test directories' configuration is no longer silently dropped. Due to how rebar3.erl injected test state without looking for what the user may have specified, multiple extra_src_dirs entries existed at once and were run; one with the recursion set to true and one with whatever the user specified. If the user disabled recursion of the "test" extra_src_dir, then the injected value still ran it once... 4. The handling of extra files in the compiler module is fixed to actually use the rebar_dir interface properly, and reinjects non-default directory recursion settings into the swapped options for the shimmed extra apps. Not doing this annotation step resulted in the write for swapped opts to actually drop the configured recursion value and make everything recursive all the time. A single new test actually validates all of that behaviour and seems to work fine.
5 år sedan
Split up the compiler DAG This is another tricky commit towards replacing the current module analysis with EPP. The compiler DAG is now being shared across multiple applications being compiled, rather than a per-application basis, which promises to allow better ordering, parallelism, and more thorough invalidation of runtime dependencies when they are modified. This however required changes: - The compiler DAG is no longer private to `rebar_compiler`, and has been extracted to the `rebar_compiler_dag` module - The compiler DAG is now started by the `rebar_prv_compile` module, which oversees the calls to `rebar_compiler` for each OTP application - The compiler DAG has been refactored to use a "dirty flag" to know if it was modified, rather than just tracking modifications in a functional manner, since the scope change (going multi-app) makes it impossible to cleanly use the functional approach without much larger changes - The DAG used to be cached within each OTP application. This is no longer possible since it is shared. Instead the DAG is stored in the state's deps_dir, which allows to cleanly split caches between regular apps for the user's project and plugins - The DAG supported a "label" mode that was used to store distinct DAGs for extra_src_dir runs and regular modules; this label is now used (and extended to `rebar_prv_compile` internals) to distinguish between "compile runs", such as "project_apps", or just "apps" (deps). The label is optional (i.e. not used by plugins which have no such need) - The extra_src_dirs for each app is now compiled using the main app's DAG, but the run takes place later in the compilation process. This may need changing to detect and prevent dependencies from src_dirs into extra_src_dirs, but this should not technically be a problem for runtime anyway. - Reworked the support for extra_src_dirs that are at the root of an umbrella project (and therefore do not belong to any single app) to use the new structure, also as part of the project_apps DAG. All tests keep passing, and this puts us in a better place to use EPP with cross-app support in the near-future.
5 år sedan
Fix directory recursion in compiler This patch contains two behaviour changes and reasserts other behaviours that now line things up with user and documentation expectations: 1. The src directories remain recursive. We turned it on by accident at some point in the past and now people rely on it, so we're stuck with it. However a new test ensures that the feature can be turned off on-demand as documented on the website. 2. The test directories are no longer recursive by default. The fix is done by properly fixing how rebar3.erl does its feature injection by mandating the default value there. I'm somewhat nervous that this change could negatively impact some users and older compiler module versions, but if users stick to the rebar_dir interface, everything should keep working transparently. 3. The test directories' configuration is no longer silently dropped. Due to how rebar3.erl injected test state without looking for what the user may have specified, multiple extra_src_dirs entries existed at once and were run; one with the recursion set to true and one with whatever the user specified. If the user disabled recursion of the "test" extra_src_dir, then the injected value still ran it once... 4. The handling of extra files in the compiler module is fixed to actually use the rebar_dir interface properly, and reinjects non-default directory recursion settings into the swapped options for the shimmed extra apps. Not doing this annotation step resulted in the write for swapped opts to actually drop the configured recursion value and make everything recursive all the time. A single new test actually validates all of that behaviour and seems to work fine.
5 år sedan
Split up the compiler DAG This is another tricky commit towards replacing the current module analysis with EPP. The compiler DAG is now being shared across multiple applications being compiled, rather than a per-application basis, which promises to allow better ordering, parallelism, and more thorough invalidation of runtime dependencies when they are modified. This however required changes: - The compiler DAG is no longer private to `rebar_compiler`, and has been extracted to the `rebar_compiler_dag` module - The compiler DAG is now started by the `rebar_prv_compile` module, which oversees the calls to `rebar_compiler` for each OTP application - The compiler DAG has been refactored to use a "dirty flag" to know if it was modified, rather than just tracking modifications in a functional manner, since the scope change (going multi-app) makes it impossible to cleanly use the functional approach without much larger changes - The DAG used to be cached within each OTP application. This is no longer possible since it is shared. Instead the DAG is stored in the state's deps_dir, which allows to cleanly split caches between regular apps for the user's project and plugins - The DAG supported a "label" mode that was used to store distinct DAGs for extra_src_dir runs and regular modules; this label is now used (and extended to `rebar_prv_compile` internals) to distinguish between "compile runs", such as "project_apps", or just "apps" (deps). The label is optional (i.e. not used by plugins which have no such need) - The extra_src_dirs for each app is now compiled using the main app's DAG, but the run takes place later in the compilation process. This may need changing to detect and prevent dependencies from src_dirs into extra_src_dirs, but this should not technically be a problem for runtime anyway. - Reworked the support for extra_src_dirs that are at the root of an umbrella project (and therefore do not belong to any single app) to use the new structure, also as part of the project_apps DAG. All tests keep passing, and this puts us in a better place to use EPP with cross-app support in the near-future.
5 år sedan
Fix directory recursion in compiler This patch contains two behaviour changes and reasserts other behaviours that now line things up with user and documentation expectations: 1. The src directories remain recursive. We turned it on by accident at some point in the past and now people rely on it, so we're stuck with it. However a new test ensures that the feature can be turned off on-demand as documented on the website. 2. The test directories are no longer recursive by default. The fix is done by properly fixing how rebar3.erl does its feature injection by mandating the default value there. I'm somewhat nervous that this change could negatively impact some users and older compiler module versions, but if users stick to the rebar_dir interface, everything should keep working transparently. 3. The test directories' configuration is no longer silently dropped. Due to how rebar3.erl injected test state without looking for what the user may have specified, multiple extra_src_dirs entries existed at once and were run; one with the recursion set to true and one with whatever the user specified. If the user disabled recursion of the "test" extra_src_dir, then the injected value still ran it once... 4. The handling of extra files in the compiler module is fixed to actually use the rebar_dir interface properly, and reinjects non-default directory recursion settings into the swapped options for the shimmed extra apps. Not doing this annotation step resulted in the write for swapped opts to actually drop the configured recursion value and make everything recursive all the time. A single new test actually validates all of that behaviour and seems to work fine.
5 år sedan
Split up the compiler DAG This is another tricky commit towards replacing the current module analysis with EPP. The compiler DAG is now being shared across multiple applications being compiled, rather than a per-application basis, which promises to allow better ordering, parallelism, and more thorough invalidation of runtime dependencies when they are modified. This however required changes: - The compiler DAG is no longer private to `rebar_compiler`, and has been extracted to the `rebar_compiler_dag` module - The compiler DAG is now started by the `rebar_prv_compile` module, which oversees the calls to `rebar_compiler` for each OTP application - The compiler DAG has been refactored to use a "dirty flag" to know if it was modified, rather than just tracking modifications in a functional manner, since the scope change (going multi-app) makes it impossible to cleanly use the functional approach without much larger changes - The DAG used to be cached within each OTP application. This is no longer possible since it is shared. Instead the DAG is stored in the state's deps_dir, which allows to cleanly split caches between regular apps for the user's project and plugins - The DAG supported a "label" mode that was used to store distinct DAGs for extra_src_dir runs and regular modules; this label is now used (and extended to `rebar_prv_compile` internals) to distinguish between "compile runs", such as "project_apps", or just "apps" (deps). The label is optional (i.e. not used by plugins which have no such need) - The extra_src_dirs for each app is now compiled using the main app's DAG, but the run takes place later in the compilation process. This may need changing to detect and prevent dependencies from src_dirs into extra_src_dirs, but this should not technically be a problem for runtime anyway. - Reworked the support for extra_src_dirs that are at the root of an umbrella project (and therefore do not belong to any single app) to use the new structure, also as part of the project_apps DAG. All tests keep passing, and this puts us in a better place to use EPP with cross-app support in the near-future.
5 år sedan
Fix handling of updated files in extra_src_dirs This change fixes cases where changes in .hrl files would not be picked up in .erl files that are in extra source directories (such as those defined with `extra_src_dirs` or modules in the test/ directory during a CT or Eunit run). The problem was due to the way the Directed Acyclic Graph (DAG) of dependencies between files was being loaded and stored by the compiler modules. Prior to this fix, a single DAG would be used for all runs. On a regular run, the prior DAG is loaded from disk, re-checked, and if changed, it would get re-written to disk with the changes deciding what to re-compile. However, whenever extra source directories were specified, a second run would be done which swaps target directories around in the compiler modules. Bug 1: this second run was done without properly tracking the private .hrl files (in src/), so the changes were invisible. This has been fixed by re-adding the paths. The problem is that the DAG handling is self-contained; just invoking it was sufficient to get it loaded and rewritten to disk. But since runs with extra src dirs were done on different sets, the compilation of extra src dirs would be done with bad historical data (all the modules in src/ are dropped, all those in test/ are re-added); this DAG was then written to disk once again, polluting the next non-extra run. This is bug 2, and it is fixed by adding an optional label to each run so that a regular or extra compile round can be distinguished, each tracking their own files in their own DAG. A single test (and a lot of diffing) were sufficient for this.
6 år sedan
  1. -module(rebar_compiler).
  2. -export([analyze_all/2,
  3. analyze_all_extras/2,
  4. compile_analyzed/3,
  5. compile_all/2,
  6. clean/2,
  7. needs_compile/3,
  8. ok_tuple/2,
  9. error_tuple/4,
  10. maybe_report/1,
  11. format_error_source/2,
  12. report/1]).
  13. -include("rebar.hrl").
  14. -type extension() :: string().
  15. -type out_mappings() :: [{extension(), file:filename()}].
  16. -callback context(rebar_app_info:t()) -> #{src_dirs => [file:dirname()], % mandatory
  17. include_dirs => [file:dirname()], % mandatory
  18. src_ext => extension(), % mandatory
  19. out_mappings => out_mappings(), % mandatory
  20. dependencies_opts => term()}. % optional
  21. -callback needed_files(digraph:graph(), [file:filename()], out_mappings(),
  22. rebar_app_info:t()) ->
  23. {{[file:filename()], term()}, % ErlFirstFiles (erl_opts global priority)
  24. {[file:filename()] | % [Sequential]
  25. {[file:filename()], [file:filename()]}, % {Sequential, Parallel}
  26. term()}}.
  27. -callback dependencies(file:filename(), file:dirname(), [file:dirname()]) -> [file:filename()].
  28. -callback dependencies(file:filename(), file:dirname(), [file:dirname()], term()) -> [file:filename()].
  29. -callback compile(file:filename(), out_mappings(), rebar_dict(), list()) ->
  30. ok | {ok, [string()]} | error | {error, [string()], [string()]} | skipped.
  31. -callback compile_and_track(file:filename(), out_mappings(), rebar_dict(), list()) ->
  32. {ok, [{file:filename(), file:filename(), term()}]} |
  33. {ok, [{file:filename(), file:filename(), term()}], [string()]} |
  34. {error, [string()], [string()]} | error.
  35. -callback clean([file:filename()], rebar_app_info:t()) -> _.
  36. -optional_callbacks([dependencies/4, compile_and_track/4]).
  37. %% @doc analysis by the caller, in order to let an OTP app
  38. %% find and resolve all its dependencies as part of compile_all's new
  39. %% API, which presumes a partial analysis is done ahead of time
  40. -spec analyze_all(DAG, [App, ...]) -> {map(), [App]} when
  41. DAG :: {module(), digraph:graph()},
  42. App :: rebar_app_info:t().
  43. analyze_all({Compiler, G}, Apps) ->
  44. prepare_compiler_env(Compiler, Apps),
  45. %% Analyze apps one by one
  46. %% then cover the include files in the digraph to update them
  47. %% then propagate?
  48. Contexts = gather_contexts(Compiler, Apps),
  49. AppRes = [analyze_app({Compiler, G}, Contexts, AppInfo) || AppInfo <- Apps],
  50. {AppOutPaths, AbsSources} = lists:unzip(AppRes),
  51. SrcExt = maps:get(src_ext, Contexts),
  52. OutExt = maps:get(artifact_exts, Contexts),
  53. rebar_compiler_dag:prune(
  54. G, SrcExt, OutExt, lists:append(AbsSources), lists:append(AppOutPaths)
  55. ),
  56. rebar_compiler_dag:populate_deps(G, SrcExt, OutExt),
  57. rebar_compiler_dag:propagate_stamps(G),
  58. AppPaths = [{rebar_app_info:name(AppInfo),
  59. rebar_utils:to_list(rebar_app_info:dir(AppInfo))}
  60. || AppInfo <- Apps],
  61. AppNames = rebar_compiler_dag:compile_order(G, AppPaths),
  62. {Contexts, sort_apps(AppNames, Apps)}.
  63. %% @doc same as analyze_all/2, but over extra_src_apps,
  64. %% which are a big cheat.
  65. -spec analyze_all_extras(DAG, [App, ...]) -> {map(), [App]} when
  66. DAG :: {module(), digraph:graph()},
  67. App :: rebar_app_info:t().
  68. analyze_all_extras(DAG, Apps) ->
  69. case lists:append([annotate_extras(App) || App <- Apps]) of
  70. [] -> {#{}, []};
  71. ExtraApps -> analyze_all(DAG, ExtraApps)
  72. end.
  73. -spec compile_analyzed({module(), digraph:graph()}, rebar_app_info:t(), map()) -> ok.
  74. compile_analyzed({Compiler, G}, AppInfo, Contexts) -> % > 3.13.2
  75. run(G, Compiler, AppInfo, Contexts),
  76. ok.
  77. -spec compile_all([module(), ...], rebar_app_info:t()) -> ok.
  78. compile_all(Compilers, AppInfo) -> % =< 3.13.0 interface; plugins use this!
  79. %% Support the old-style API by re-declaring a local DAG for the
  80. %% compile steps needed.
  81. lists:foreach(fun(Compiler) ->
  82. OutDir = rebar_app_info:out_dir(AppInfo),
  83. G = rebar_compiler_dag:init(OutDir, Compiler, undefined, []),
  84. {Ctx, _} = analyze_all({Compiler, G}, [AppInfo]),
  85. compile_analyzed({Compiler, G}, AppInfo, Ctx),
  86. rebar_compiler_dag:maybe_store(G, OutDir, Compiler, undefined, []),
  87. rebar_compiler_dag:terminate(G)
  88. end, Compilers).
  89. %% @doc remove compiled artifacts from an AppDir.
  90. -spec clean([module()], rebar_app_info:t()) -> 'ok'.
  91. clean(Compilers, AppInfo) ->
  92. lists:foreach(fun(CompilerMod) ->
  93. clean_(CompilerMod, AppInfo, undefined),
  94. Extras = annotate_extras(AppInfo),
  95. [clean_(CompilerMod, ExtraApp, "extra") || ExtraApp <- Extras]
  96. end, Compilers).
  97. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  98. %%% COMPILER UTIL EXPORTS %%%
  99. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  100. %% These functions are here for the ultimate goal of getting rid of
  101. %% rebar_base_compiler. This can't be done because of existing plugins.
  102. -spec needs_compile(filename:all(), extension(), [{extension(), file:dirname()}]) -> boolean().
  103. needs_compile(Source, OutExt, Mappings) ->
  104. Ext = filename:extension(Source),
  105. BaseName = filename:basename(Source, Ext),
  106. {_, OutDir} = lists:keyfind(OutExt, 1, Mappings),
  107. Target = filename:join(OutDir, BaseName++OutExt),
  108. filelib:last_modified(Source) > filelib:last_modified(Target).
  109. ok_tuple(Source, Ws) ->
  110. rebar_base_compiler:ok_tuple(Source, Ws).
  111. error_tuple(Source, Es, Ws, Opts) ->
  112. rebar_base_compiler:error_tuple(Source, Es, Ws, Opts).
  113. maybe_report(Reportable) ->
  114. rebar_base_compiler:maybe_report(Reportable).
  115. format_error_source(Path, Opts) ->
  116. rebar_base_compiler:format_error_source(Path, Opts).
  117. report(Messages) ->
  118. rebar_base_compiler:report(Messages).
  119. %%%%%%%%%%%%%%%
  120. %%% PRIVATE %%%
  121. %%%%%%%%%%%%%%%
  122. gather_contexts(Compiler, Apps) ->
  123. Default = default_ctx(),
  124. Contexts = [{rebar_app_info:name(AppInfo),
  125. maps:merge(Default, Compiler:context(AppInfo))}
  126. || AppInfo <- Apps],
  127. ContextMap = maps:from_list(Contexts),
  128. %% only support one extension type at once for now
  129. [{_, #{src_ext := SrcExt}} | _] = Contexts,
  130. %% gather multi-app stuff once to avoid recomputing it
  131. ArtifactExts = lists:usort(
  132. [Ext || {_, #{out_mappings := Mappings}} <- Contexts,
  133. {Ext, _Dir} <- Mappings]
  134. ),
  135. InDirs = gather_in_dirs(lists:zip(Apps, [Context || {_, Context} <- Contexts])),
  136. ContextMap#{src_ext => SrcExt,
  137. artifact_exts => ArtifactExts,
  138. in_dirs => InDirs}.
  139. gather_in_dirs(AppCtx) ->
  140. gather_in_dirs(AppCtx, []).
  141. gather_in_dirs([], Paths) ->
  142. lists:usort(Paths);
  143. gather_in_dirs([{AppInfo, Ctx} | Rest], Acc) ->
  144. #{include_dirs := InclDirs,
  145. src_dirs := SrcDirs} = Ctx,
  146. BaseDir = rebar_utils:to_list(rebar_app_info:dir(AppInfo)),
  147. AbsIncl = [filename:join(BaseDir, InclDir) || InclDir <- InclDirs],
  148. AbsSrc = [filename:join(BaseDir, SrcDir) || SrcDir <- SrcDirs],
  149. gather_in_dirs(Rest, AbsSrc ++ AbsIncl ++ Acc).
  150. analyze_app({Compiler, G}, Contexts, AppInfo) ->
  151. AppName = rebar_app_info:name(AppInfo),
  152. BaseDir = rebar_utils:to_list(rebar_app_info:dir(AppInfo)),
  153. OutDir = rebar_utils:to_list(rebar_app_info:out_dir(AppInfo)),
  154. BaseOpts = rebar_app_info:opts(AppInfo),
  155. #{src_dirs := SrcDirs,
  156. src_ext := SrcExt,
  157. out_mappings := [{_OutExt, OutPath}|_], % prune one dir for now (compat mode!)
  158. dependencies_opts := DepOpts} = maps:get(AppName, Contexts),
  159. %% Local resources
  160. ArtifactDir = filename:join([OutDir, OutPath]),
  161. AbsSources = find_source_files(BaseDir, SrcExt, SrcDirs, BaseOpts),
  162. %% Multi-app resources
  163. InDirs = maps:get(in_dirs, Contexts),
  164. %% Run the analysis
  165. rebar_compiler_dag:populate_sources(
  166. G, Compiler, InDirs, AbsSources, DepOpts
  167. ),
  168. {[{filename:join([BaseDir, SrcDir]), ArtifactDir} || SrcDir <- SrcDirs],
  169. AbsSources}.
  170. sort_apps(Names, Apps) ->
  171. NamedApps = [{rebar_app_info:name(App), App} || App <- Apps],
  172. [App || Name <- Names,
  173. {_, App} <- [lists:keyfind(Name, 1, NamedApps)]].
  174. prepare_compiler_env(Compiler, Apps) ->
  175. lists:foreach(
  176. fun(AppInfo) ->
  177. EbinDir = rebar_utils:to_list(rebar_app_info:ebin_dir(AppInfo)),
  178. %% Make sure that outdir is on the path
  179. ok = rebar_file_utils:ensure_dir(EbinDir),
  180. true = code:add_patha(filename:absname(EbinDir))
  181. end,
  182. Apps
  183. ),
  184. %% necessary for erlang:function_exported/3 to work as expected
  185. %% called here for clarity as it's required by both opts_changed/2
  186. %% and erl_compiler_opts_set/0 in needed_files
  187. _ = code:ensure_loaded(compile),
  188. _ = code:ensure_loaded(Compiler),
  189. ok.
  190. run(G, CompilerMod, AppInfo, Contexts) ->
  191. Name = rebar_app_info:name(AppInfo),
  192. #{src_dirs := SrcDirs,
  193. src_ext := SrcExt,
  194. out_mappings := Mappings} = maps:get(Name, Contexts),
  195. BaseDir = rebar_utils:to_list(rebar_app_info:dir(AppInfo)),
  196. BaseOpts = rebar_app_info:opts(AppInfo),
  197. FoundFiles = find_source_files(BaseDir, SrcExt, SrcDirs, BaseOpts),
  198. {{FirstFiles, FirstFileOpts},
  199. {RestFiles, Opts}} = CompilerMod:needed_files(G, FoundFiles, Mappings, AppInfo),
  200. Tracked =
  201. compile_each(FirstFiles, FirstFileOpts, BaseOpts, Mappings, CompilerMod)
  202. ++ case RestFiles of
  203. {Sequential, Parallel} -> % parallelizable form
  204. compile_each(Sequential, Opts, BaseOpts, Mappings, CompilerMod) ++
  205. lists:append(
  206. compile_parallel(Parallel, Opts, BaseOpts, Mappings, CompilerMod)
  207. );
  208. _ when is_list(RestFiles) -> % traditional sequential build
  209. compile_each(RestFiles, Opts, BaseOpts, Mappings, CompilerMod)
  210. end,
  211. store_artifacts(G, Tracked).
  212. compile_each([], _Opts, _Config, _Outs, _CompilerMod) ->
  213. [];
  214. compile_each([Source | Rest], Opts, Config, Outs, CompilerMod) ->
  215. case erlang:function_exported(CompilerMod, compile_and_track, 4) of
  216. false ->
  217. do_compile(CompilerMod, Source, Outs, Config, Opts),
  218. compile_each(Rest, Opts, Config, Outs, CompilerMod);
  219. true ->
  220. do_compile_and_track(CompilerMod, Source, Outs, Config, Opts)
  221. ++ compile_each(Rest, Opts, Config, Outs, CompilerMod)
  222. end.
  223. do_compile(CompilerMod, Source, Outs, Config, Opts) ->
  224. case CompilerMod:compile(Source, Outs, Config, Opts) of
  225. ok ->
  226. ?DEBUG("~tsCompiled ~ts", [rebar_utils:indent(1), filename:basename(Source)]);
  227. {ok, Warnings} ->
  228. report(Warnings),
  229. ?DEBUG("~tsCompiled ~ts", [rebar_utils:indent(1), filename:basename(Source)]);
  230. skipped ->
  231. ?DEBUG("~tsSkipped ~ts", [rebar_utils:indent(1), filename:basename(Source)]);
  232. Error ->
  233. NewSource = format_error_source(Source, Config),
  234. ?ERROR("Compiling ~ts failed", [NewSource]),
  235. maybe_report(Error),
  236. ?DEBUG("Compilation failed: ~p", [Error]),
  237. ?ABORT
  238. end.
  239. do_compile_and_track(CompilerMod, Source, Outs, Config, Opts) ->
  240. case CompilerMod:compile_and_track(Source, Outs, Config, Opts) of
  241. {ok, Tracked} ->
  242. ?DEBUG("~tsCompiled ~ts", [rebar_utils:indent(1), filename:basename(Source)]),
  243. Tracked;
  244. {ok, Tracked, Warnings} ->
  245. report(Warnings),
  246. ?DEBUG("~tsCompiled ~ts", [rebar_utils:indent(1), filename:basename(Source)]),
  247. Tracked;
  248. skipped ->
  249. ?DEBUG("~tsSkipped ~ts", [rebar_utils:indent(1), filename:basename(Source)]),
  250. [];
  251. Error ->
  252. NewSource = format_error_source(Source, Config),
  253. ?ERROR("Compiling ~ts failed", [NewSource]),
  254. maybe_report(Error),
  255. ?DEBUG("Compilation failed: ~p", [Error]),
  256. ?ABORT
  257. end.
  258. store_artifacts(_G, []) ->
  259. ok;
  260. store_artifacts(G, [{Source, Target, Meta}|Rest]) ->
  261. %% Assume the source exists since it was tracked to be compiled
  262. rebar_compiler_dag:store_artifact(G, Source, Target, Meta),
  263. store_artifacts(G, Rest).
  264. compile_parallel(Targets, Opts, BaseOpts, Mappings, CompilerMod) ->
  265. Tracking = erlang:function_exported(CompilerMod, compile_and_track, 4),
  266. rebar_parallel:queue(
  267. Targets,
  268. fun compile_worker/2, [Opts, BaseOpts, Mappings, CompilerMod],
  269. fun compile_handler/2, [BaseOpts, Tracking]
  270. ).
  271. compile_worker(Source, [Opts, Config, Outs, CompilerMod]) ->
  272. Result = case erlang:function_exported(CompilerMod, compile_and_track, 4) of
  273. false ->
  274. CompilerMod:compile(Source, Outs, Config, Opts);
  275. true ->
  276. CompilerMod:compile_and_track(Source, Outs, Config, Opts)
  277. end,
  278. %% Bundle the source to allow proper reporting in the handler:
  279. {Result, Source}.
  280. compile_handler({ok, Source}, _Args) ->
  281. ?DEBUG("~sCompiled ~s", [rebar_utils:indent(1), filename:basename(Source)]),
  282. ok;
  283. compile_handler({{ok, Tracked}, Source}, [_, Tracking]) when Tracking ->
  284. ?DEBUG("~sCompiled ~s", [rebar_utils:indent(1), filename:basename(Source)]),
  285. {ok, Tracked};
  286. compile_handler({{ok, Warnings}, Source}, _Args) ->
  287. report(Warnings),
  288. ?DEBUG("~sCompiled ~s", [rebar_utils:indent(1), filename:basename(Source)]),
  289. ok;
  290. compile_handler({{ok, Tracked, Warnings}, Source}, [_, Tracking]) when Tracking ->
  291. report(Warnings),
  292. ?DEBUG("~sCompiled ~s", [rebar_utils:indent(1), filename:basename(Source)]),
  293. {ok, Tracked};
  294. compile_handler({skipped, Source}, _Args) ->
  295. ?DEBUG("~sSkipped ~s", [rebar_utils:indent(1), filename:basename(Source)]),
  296. ok;
  297. compile_handler({Error, Source}, [Config | _Rest]) ->
  298. NewSource = format_error_source(Source, Config),
  299. ?ERROR("Compiling ~ts failed", [NewSource]),
  300. maybe_report(Error),
  301. ?ABORT.
  302. clean_(CompilerMod, AppInfo, _Label) ->
  303. #{src_dirs := SrcDirs,
  304. src_ext := SrcExt} = CompilerMod:context(AppInfo),
  305. BaseDir = rebar_app_info:dir(AppInfo),
  306. Opts = rebar_app_info:opts(AppInfo),
  307. FoundFiles = find_source_files(BaseDir, SrcExt, SrcDirs, Opts),
  308. CompilerMod:clean(FoundFiles, AppInfo),
  309. ok.
  310. annotate_extras(AppInfo) ->
  311. AppOpts = rebar_app_info:opts(AppInfo),
  312. ExtraDirs = rebar_dir:extra_src_dirs(AppOpts, []),
  313. OldSrcDirs = rebar_dir:src_dirs(AppOpts, ["src"]),
  314. %% Re-annotate the directories with non-default options if it is the
  315. %% case; otherwise, later down the line, the options get dropped with
  316. %% profiles. All of this must be done with the rebar_dir functionality
  317. %% which properly tracks and handles the various legacy formats for
  318. %% recursion setting (erl_opts vs. dir options and profiles)
  319. ExtraDirsOpts = [case rebar_dir:recursive(AppOpts, Dir) of
  320. false -> {Dir, [{recursive, false}]};
  321. true -> Dir
  322. end || Dir <- ExtraDirs],
  323. OldSrcDirsOpts = [case rebar_dir:recursive(AppOpts, Dir) of
  324. false -> {Dir, [{recursive, false}]};
  325. true -> Dir
  326. end || Dir <- OldSrcDirs],
  327. AppDir = rebar_app_info:dir(AppInfo),
  328. lists:map(fun({DirOpt, Dir}) ->
  329. EbinDir = filename:join(rebar_app_info:out_dir(AppInfo), Dir),
  330. %% need a unique name to prevent lookup issues that clobber entries
  331. AppName = unicode:characters_to_binary(
  332. [rebar_app_info:name(AppInfo), "_", Dir]
  333. ),
  334. AppInfo0 = rebar_app_info:name(AppInfo, AppName),
  335. AppInfo1 = rebar_app_info:ebin_dir(AppInfo0, EbinDir),
  336. AppInfo2 = rebar_app_info:set(AppInfo1, src_dirs, [DirOpt]),
  337. AppInfo3 = rebar_app_info:set(AppInfo2, extra_src_dirs, OldSrcDirsOpts),
  338. add_to_includes( % give access to .hrl in app's src/
  339. AppInfo3,
  340. [filename:join([AppDir, D]) || D <- OldSrcDirs]
  341. )
  342. end,
  343. [T || T = {_DirOpt, ExtraDir} <- lists:zip(ExtraDirsOpts, ExtraDirs),
  344. filelib:is_dir(filename:join(AppDir, ExtraDir))]
  345. ).
  346. find_source_files(BaseDir, SrcExt, SrcDirs, Opts) ->
  347. SourceExtRe = "^(?!\\._).*\\" ++ SrcExt ++ [$$],
  348. lists:flatmap(fun(SrcDir) ->
  349. Recursive = rebar_dir:recursive(Opts, SrcDir),
  350. rebar_utils:find_files_in_dirs([filename:join(BaseDir, SrcDir)], SourceExtRe, Recursive)
  351. end, SrcDirs).
  352. add_to_includes(AppInfo, Dirs) ->
  353. Opts = rebar_app_info:opts(AppInfo),
  354. List = rebar_opts:get(Opts, erl_opts, []),
  355. NewErlOpts = [{i, Dir} || Dir <- Dirs] ++ List,
  356. NewOpts = rebar_opts:set(Opts, erl_opts, NewErlOpts),
  357. rebar_app_info:opts(AppInfo, NewOpts).
  358. default_ctx() ->
  359. #{dependencies_opts => []}.