@@ -725,37 +725,79 @@ defmodule Mix.Compilers.Elixir do
725725
726726 for % { scm: scm , opts: opts } = dep <- Mix.Dep . cached ( ) ,
727727 not scm . fetchable? ,
728- Mix.Utils . last_modified ( Path . join ( [ opts [ :build ] , ".mix" , base ] ) ) > modified ,
729- path <- Mix.Dep . load_paths ( dep ) ,
730- beam <- Path . wildcard ( Path . join ( path , "*.beam" ) ) ,
731- Mix.Utils . last_modified ( beam ) > modified ,
728+ manifest = Path . join ( [ opts [ :build ] , ".mix" , base ] ) ,
729+ Mix.Utils . last_modified ( manifest ) > modified ,
732730 reduce: { stale_modules , % { } , old_exports } do
733731 { modules , exports , new_exports } ->
734- module = beam |> Path . basename ( ) |> Path . rootname ( ) |> String . to_atom ( )
735- export = exports_md5 ( module , false )
736- modules = Map . put ( modules , module , [ ] )
737-
738- # If the exports are the same, then the API did not change,
739- # so we do not mark the export as stale. Note this has to
740- # be very conservative. If the module is not loaded or if
741- # the exports were not there, we need to consider it a stale
742- # export.
743- exports =
744- if export && old_exports [ module ] == export ,
745- do: exports ,
746- else: Map . put ( exports , module , [ ] )
747-
748- # In any case, we always store it as the most update export
749- # that we have, otherwise we delete it.
750- new_exports =
751- if export ,
752- do: Map . put ( new_exports , module , export ) ,
753- else: Map . delete ( new_exports , module )
754-
755- { modules , exports , new_exports }
732+ { _manifest_modules , dep_sources } = read_manifest ( manifest )
733+
734+ # TODO: Use :maps.from_keys/2 on Erlang/OTP 24+
735+ dep_modules =
736+ for path <- Mix.Dep . load_paths ( dep ) ,
737+ beam <- Path . wildcard ( Path . join ( path , "*.beam" ) ) ,
738+ Mix.Utils . last_modified ( beam ) > modified ,
739+ do: { beam |> Path . basename ( ) |> Path . rootname ( ) |> String . to_atom ( ) , [ ] } ,
740+ into: % { }
741+
742+ # If any module has a compile time dependency on a changed module
743+ # within the dependnecy, they will be recompiled. However, export
744+ # and runtime dependencies won't have recompiled so we need to
745+ # propagate them to the parent app.
746+ dep_modules = fixpoint_dep_modules ( dep_sources , dep_modules , false , [ ] )
747+
748+ # Update exports
749+ { exports , new_exports } =
750+ for { module , _ } <- dep_modules , reduce: { exports , new_exports } do
751+ { exports , new_exports } ->
752+ export = exports_md5 ( module , false )
753+
754+ # If the exports are the same, then the API did not change,
755+ # so we do not mark the export as stale. Note this has to
756+ # be very conservative. If the module is not loaded or if
757+ # the exports were not there, we need to consider it a stale
758+ # export.
759+ exports =
760+ if export && old_exports [ module ] == export ,
761+ do: exports ,
762+ else: Map . put ( exports , module , [ ] )
763+
764+ # In any case, we always store it as the most update export
765+ # that we have, otherwise we delete it.
766+ new_exports =
767+ if export ,
768+ do: Map . put ( new_exports , module , export ) ,
769+ else: Map . delete ( new_exports , module )
770+
771+ { exports , new_exports }
772+ end
773+
774+ { Map . merge ( modules , dep_modules ) , exports , new_exports }
775+ end
776+ end
777+
778+ defp fixpoint_dep_modules ( [ source | sources ] , modules , new_modules? , acc_sources ) do
779+ source ( export_references: export_refs , runtime_references: runtime_refs ) = source
780+
781+ if has_any_key? ( modules , compile_refs ) or has_any_key? ( modules , export_refs ) or
782+ has_any_key? ( modules , runtime_refs ) do
783+ new_modules = Enum . reject ( source ( source , :modules ) , & Map . has_key? ( modules , & 1 ) )
784+ new_modules? = new_modules? or new_modules != [ ]
785+ modules = Enum . reduce ( new_modules , modules , & Map . put ( & 2 , & 1 , [ ] ) )
786+ fixpoint_dep_modules ( sources , modules , new_modules? , acc_sources )
787+ else
788+ fixpoint_dep_modules ( sources , modules , new_modules? , [ source | acc_sources ] )
756789 end
757790 end
758791
792+ defp fixpoint_dep_modules ( [ ] , modules , false , _ ) ,
793+ do: modules
794+
795+ defp fixpoint_dep_modules ( [ ] , modules , true , [ ] ) ,
796+ do: modules
797+
798+ defp fixpoint_dep_modules ( [ ] , modules , true , sources ) ,
799+ do: fixpoint_dep_modules ( sources , modules , false , [ ] )
800+
759801 defp exports_md5 ( module , use_attributes? ) do
760802 cond do
761803 function_exported? ( module , :__info__ , 1 ) ->
0 commit comments