11defmodule Mix.Compilers.Elixir do
22 @ moduledoc false
33
4- @ manifest_vsn 26
4+ @ manifest_vsn 27
55 @ checkpoint_vsn 2
66
77 import Record
@@ -46,9 +46,8 @@ defmodule Mix.Compilers.Elixir do
4646 all_paths = Mix.Utils . extract_files ( srcs , [ :ex ] )
4747
4848 { all_modules , all_sources , all_local_exports , old_parents , old_cache_key , old_deps_config ,
49- old_project_mtime ,
50- old_config_mtime } =
51- parse_manifest ( manifest , dest )
49+ old_project_mtime , old_config_mtime ,
50+ old_protocols_and_impls } = parse_manifest ( manifest , dest )
5251
5352 # Prepend ourselves early because of __mix_recompile__? checks
5453 # and also that, in case of nothing compiled, we already need
@@ -129,7 +128,7 @@ defmodule Mix.Compilers.Elixir do
129128 { false , stale , old_deps_config }
130129 end
131130
132- { stale_modules , stale_exports , all_local_exports } =
131+ { stale_modules , stale_exports , all_local_exports , protocols_and_impls } =
133132 stale_local_deps ( local_deps , manifest , stale , modified , all_local_exports )
134133
135134 prev_paths = Map . keys ( all_sources )
@@ -157,26 +156,41 @@ defmodule Mix.Compilers.Elixir do
157156 { sources , removed_modules } =
158157 update_stale_sources ( sources , stale , removed_modules , sources_stats )
159158
160- if stale != [ ] or stale_modules != % { } do
159+ consolidation_status =
160+ if Mix.Project . umbrella? ( ) do
161+ :off
162+ else
163+ Mix.Compilers.Protocol . status ( config_mtime > old_config_mtime , opts )
164+ end
165+
166+ if stale != [ ] or stale_modules != % { } or removed != [ ] or deps_changed? or
167+ consolidation_status == :force do
161168 path = opts [ :purge_consolidation_path_if_stale ]
162169
163170 if is_binary ( path ) and Code . delete_path ( path ) do
164171 purge_modules_in_path ( path )
165172 end
166173
167- Mix.Utils . compiling_n ( length ( stale ) , :ex )
174+ if stale != [ ] do
175+ Mix.Utils . compiling_n ( length ( stale ) , :ex )
176+ end
177+
168178 Mix.Project . ensure_structure ( )
169179
170180 # We don't want to cache this path as we will write to it
171181 true = Code . prepend_path ( dest )
172182 previous_opts = set_compiler_opts ( opts )
173183
184+ # Group consolidation information to pass to compiler
185+
174186 try do
175- state = { % { } , exports , sources , [ ] , modules , removed_modules }
187+ consolidation = { consolidation_status , old_protocols_and_impls , protocols_and_impls }
188+ state = { % { } , exports , sources , [ ] , modules , removed_modules , consolidation }
176189 compiler_loop ( stale , stale_modules , dest , timestamp , opts , state )
177190 else
178191 { :ok , % { runtime_warnings: runtime_warnings , compile_warnings: compile_warnings } , state } ->
179- { modules , _exports , sources , _changed , pending_modules , _stale_exports } = state
192+ { modules , _exports , sources , _changed , pending_modules , _stale_exports ,
193+ protocols_and_impls } = state
180194
181195 previous_warnings =
182196 if Keyword . get ( opts , :all_warnings , true ) ,
@@ -197,6 +211,7 @@ defmodule Mix.Compilers.Elixir do
197211 new_deps_config ,
198212 project_mtime ,
199213 config_mtime ,
214+ protocols_and_impls ,
200215 timestamp
201216 )
202217
@@ -217,7 +232,7 @@ defmodule Mix.Compilers.Elixir do
217232 else: { errors , r_warnings ++ c_warnings }
218233
219234 # In case of errors, we show all previous warnings and all new ones.
220- { _ , _ , sources , _ , _ , _ } = state
235+ { _ , _ , sources , _ , _ , _ , _ } = state
221236 errors = Enum . map ( errors , & diagnostic / 1 )
222237 warnings = Enum . map ( warnings , & diagnostic / 1 )
223238 all_warnings = Keyword . get ( opts , :all_warnings , errors == [ ] )
@@ -226,39 +241,13 @@ defmodule Mix.Compilers.Elixir do
226241 Code . compiler_options ( previous_opts )
227242 end
228243 else
229- # We need to return ok if deps_changed? or stale_modules changed,
230- # even if no code was compiled, because we need to propagate the changed
231- # status to compile.protocols. This will be the case whenever:
232- #
233- # * the lock file or a config changes
234- # * any module in a path dependency changes
235- # * the mix.exs changes
236- # * the Erlang manifest updates (Erlang files are compiled)
237- #
238- # In the first case, we will consolidate from scratch. In the remaining, we
239- # will only compute the diff with current protocols. In fact, there is no
240- # need to reconsolidate if an Erlang file changes and it doesn't trigger
241- # any other change, but the diff check should be reasonably fast anyway.
242- status = if removed != [ ] or deps_changed? or stale_modules != % { } , do: :ok , else: :noop
243-
244- if status != :noop do
245- write_manifest (
246- manifest ,
247- modules ,
248- sources ,
249- all_local_exports ,
250- new_parents ,
251- new_cache_key ,
252- new_deps_config ,
253- project_mtime ,
254- config_mtime ,
255- timestamp
256- )
257- end
258-
259244 all_warnings = Keyword . get ( opts , :all_warnings , true )
260245 previous_warnings = previous_warnings ( sources , all_warnings )
246+ <<< <<< < HEAD
261247 unless_warnings_as_errors ( opts , { status , previous_warnings } )
248+ === === =
249+ unless_previous_warnings_as_errors ( previous_warnings , opts , { :noop , previous_warnings } )
250+ >>> >>> > 737162 c52 ( Merge protocol consolidation into compile. elixir )
262251 end
263252 end
264253
@@ -295,24 +284,6 @@ defmodule Mix.Compilers.Elixir do
295284 end )
296285 end
297286
298- @ doc """
299- Returns protocols and implementations for the given `manifest`.
300- """
301- def protocols_and_impls ( paths ) do
302- Enum . reduce ( paths , { % { } , % { } } , fn path , acc ->
303- { modules , _ } = read_manifest ( Path . join ( path , ".mix/compile.elixir" ) )
304-
305- Enum . reduce ( modules , acc , fn
306- { module , module ( kind: kind , timestamp: timestamp ) } , { protocols , impls } ->
307- case kind do
308- :protocol -> { Map . put ( protocols , module , timestamp ) , impls }
309- { :impl , protocol } -> { protocols , Map . put ( impls , module , protocol ) }
310- _ -> { protocols , impls }
311- end
312- end )
313- end )
314- end
315-
316287 @ doc """
317288 Reads the manifest for external consumption.
318289 """
@@ -331,7 +302,7 @@ defmodule Mix.Compilers.Elixir do
331302 Retrieves all diagnostics from the given manifest.
332303 """
333304 def diagnostics ( manifest , dest ) do
334- { _ , all_sources , _ , _ , _ , _ , _ , _ } = parse_manifest ( manifest , dest )
305+ { _ , all_sources , _ , _ , _ , _ , _ , _ , _ } = parse_manifest ( manifest , dest )
335306 previous_warnings ( all_sources , false )
336307 end
337308
@@ -622,8 +593,8 @@ defmodule Mix.Compilers.Elixir do
622593 for % { app: app , opts: opts } <- local_deps ,
623594 manifest = Path . join ( [ opts [ :build ] , ".mix" , base ] ) ,
624595 Mix.Utils . last_modified ( manifest ) > modified ,
625- reduce: { stale_modules , stale_modules , deps_exports } do
626- { modules , exports , deps_exports } ->
596+ reduce: { stale_modules , stale_modules , deps_exports , protocols_and_impls ( ) } do
597+ { modules , exports , deps_exports , protocols_and_impls } ->
627598 { manifest_modules , manifest_sources } = read_manifest ( manifest )
628599
629600 dep_modules =
@@ -676,7 +647,8 @@ defmodule Mix.Compilers.Elixir do
676647 modules = modules |> Map . merge ( dep_modules ) |> Map . merge ( removed )
677648 exports = Map . merge ( exports , removed )
678649 deps_exports = Map . put ( deps_exports , app , new_exports )
679- { modules , exports , deps_exports }
650+ protocols_and_impls = protocols_and_impls_from_modules ( modules , protocols_and_impls )
651+ { modules , exports , deps_exports , protocols_and_impls }
680652 end
681653 end
682654
@@ -882,7 +854,7 @@ defmodule Mix.Compilers.Elixir do
882854
883855 ## Manifest handling
884856
885- @ default_manifest { % { } , % { } , % { } , [ ] , nil , nil , 0 , 0 }
857+ @ default_manifest { % { } , % { } , % { } , [ ] , nil , nil , 0 , 0 , { % { } , % { } } }
886858
887859 # Similar to read_manifest, but for internal consumption and with data migration support.
888860 defp parse_manifest ( manifest , compile_path ) do
@@ -893,9 +865,9 @@ defmodule Mix.Compilers.Elixir do
893865 @ default_manifest
894866 else
895867 { @ manifest_vsn , modules , sources , local_exports , parent , cache_key , deps_config ,
896- project_mtime , config_mtime } ->
868+ project_mtime , config_mtime , protocols_and_impls } ->
897869 { modules , sources , local_exports , parent , cache_key , deps_config , project_mtime ,
898- config_mtime }
870+ config_mtime , protocols_and_impls }
899871
900872 # {vsn, %{module => record}, sources, ...} v22-?
901873 # {vsn, [module_record], sources, ...} v5-v21
@@ -949,6 +921,7 @@ defmodule Mix.Compilers.Elixir do
949921 deps_config ,
950922 project_mtime ,
951923 config_mtime ,
924+ protocols_and_impls ,
952925 timestamp
953926 ) do
954927 if modules == % { } and sources == % { } do
@@ -958,7 +931,7 @@ defmodule Mix.Compilers.Elixir do
958931
959932 term =
960933 { @ manifest_vsn , modules , sources , exports , parents , cache_key , deps_config , project_mtime ,
961- config_mtime }
934+ config_mtime , protocols_and_impls }
962935
963936 manifest_data = :erlang . term_to_binary ( term , [ :compressed ] )
964937 File . write! ( manifest , manifest_data )
@@ -1057,7 +1030,7 @@ defmodule Mix.Compilers.Elixir do
10571030 spawn_link ( fn ->
10581031 compile_opts = [
10591032 each_cycle: fn ->
1060- compiler_call ( parent , ref , { :each_cycle , stale_modules , dest , timestamp } )
1033+ compiler_call ( parent , ref , { :each_cycle , stale_modules , dest , timestamp , opts } )
10611034 end ,
10621035 each_file: fn file , lexical ->
10631036 compiler_call ( parent , ref , { :each_file , file , lexical , verbose } )
@@ -1093,8 +1066,8 @@ defmodule Mix.Compilers.Elixir do
10931066
10941067 defp compiler_loop ( ref , pid , state , cwd ) do
10951068 receive do
1096- { ^ ref , { :each_cycle , stale_modules , dest , timestamp } } ->
1097- { response , state } = each_cycle ( stale_modules , dest , timestamp , state )
1069+ { ^ ref , { :each_cycle , stale_modules , dest , timestamp , opts } } ->
1070+ { response , state } = each_cycle ( stale_modules , dest , timestamp , state , opts )
10981071 send ( pid , { ref , response } )
10991072 compiler_loop ( ref , pid , state , cwd )
11001073
@@ -1122,8 +1095,8 @@ defmodule Mix.Compilers.Elixir do
11221095 end
11231096 end
11241097
1125- defp each_cycle ( stale_modules , compile_path , timestamp , state ) do
1126- { modules , _exports , sources , changed , pending_modules , stale_exports } = state
1098+ defp each_cycle ( stale_modules , compile_path , timestamp , state , opts ) do
1099+ { modules , _exports , sources , changed , pending_modules , stale_exports , consolidation } = state
11271100
11281101 { pending_modules , exports , changed } =
11291102 update_stale_entries ( pending_modules , sources , changed , % { } , stale_exports , compile_path )
@@ -1166,7 +1139,8 @@ defmodule Mix.Compilers.Elixir do
11661139 runtime_paths =
11671140 Enum . map ( runtime_modules , & { & 1 , Path . join ( compile_path , Atom . to_string ( & 1 ) <> ".beam" ) } )
11681141
1169- state = { modules , exports , sources , [ ] , pending_modules , stale_exports }
1142+ protocols_and_impls = maybe_consolidate ( consolidation , modules , opts )
1143+ state = { modules , exports , sources , [ ] , pending_modules , stale_exports , protocols_and_impls }
11701144 { { :runtime , runtime_paths , [ ] } , state }
11711145 else
11721146 Mix.Utils . compiling_n ( length ( changed ) , :ex )
@@ -1193,7 +1167,7 @@ defmodule Mix.Compilers.Elixir do
11931167
11941168 defp each_file ( file , references , verbose , state , cwd ) do
11951169 { compile_references , export_references , runtime_references , compile_env } = references
1196- { modules , exports , sources , changed , pending_modules , stale_exports } = state
1170+ { modules , exports , sources , changed , pending_modules , stale_exports , consolidate } = state
11971171
11981172 file = Path . relative_to ( file , cwd )
11991173
@@ -1221,11 +1195,11 @@ defmodule Mix.Compilers.Elixir do
12211195 )
12221196
12231197 sources = Map . replace! ( sources , file , source )
1224- { modules , exports , sources , changed , pending_modules , stale_exports }
1198+ { modules , exports , sources , changed , pending_modules , stale_exports , consolidate }
12251199 end
12261200
12271201 defp each_module ( file , module , kind , external , new_export , state , timestamp , cwd ) do
1228- { modules , exports , sources , changed , pending_modules , stale_exports } = state
1202+ { modules , exports , sources , changed , pending_modules , stale_exports , consolidate } = state
12291203
12301204 file = Path . relative_to ( file , cwd )
12311205 external = process_external_resources ( external , cwd )
@@ -1278,7 +1252,7 @@ defmodule Mix.Compilers.Elixir do
12781252 % { } -> changed
12791253 end
12801254
1281- { modules , exports , sources , changed , pending_modules , stale_exports }
1255+ { modules , exports , sources , changed , pending_modules , stale_exports , consolidate }
12821256 end
12831257
12841258 defp detect_kind ( module ) do
@@ -1307,4 +1281,50 @@ defmodule Mix.Compilers.Elixir do
13071281 { Path . relative_to ( file , cwd ) , Mix.Utils . last_modified_and_size ( file ) , digest }
13081282 end
13091283 end
1284+
1285+ ## Consolidation
1286+
1287+ @ doc """
1288+ Returns protocols and implementations for the given `manifest`.
1289+ """
1290+ def protocols_and_impls_from_paths ( paths ) do
1291+ Enum . reduce ( paths , protocols_and_impls ( ) , fn path , acc ->
1292+ { modules , _ } = read_manifest ( Path . join ( path , ".mix/compile.elixir" ) )
1293+ protocols_and_impls_from_modules ( modules , acc )
1294+ end )
1295+ end
1296+
1297+ defp protocols_and_impls_from_modules ( modules , protocols_and_impls ) do
1298+ Enum . reduce ( modules , protocols_and_impls , fn
1299+ { module , module ( kind: kind , timestamp: timestamp ) } , { protocols , impls } ->
1300+ case kind do
1301+ :protocol -> { Map . put ( protocols , module , timestamp ) , impls }
1302+ { :impl , protocol } -> { protocols , Map . put ( impls , module , protocol ) }
1303+ _ -> { protocols , impls }
1304+ end
1305+ end )
1306+ end
1307+
1308+ defp protocols_and_impls ( ) , do: { % { } , % { } }
1309+
1310+ defp maybe_consolidate ( { :off , _ , _ } , _ , _ ) do
1311+ protocols_and_impls ( )
1312+ end
1313+
1314+ defp maybe_consolidate (
1315+ { on_or_force , old_protocols_and_impls , protocols_and_impls } ,
1316+ modules ,
1317+ opts
1318+ ) do
1319+ protocols_and_impls = protocols_and_impls_from_modules ( modules , protocols_and_impls )
1320+
1321+ Mix.Compilers.Protocol . compile (
1322+ on_or_force == :force ,
1323+ old_protocols_and_impls ,
1324+ protocols_and_impls ,
1325+ opts
1326+ )
1327+
1328+ protocols_and_impls
1329+ end
13101330end
0 commit comments