@@ -725,37 +725,79 @@ defmodule Mix.Compilers.Elixir do
725
725
726
726
for % { scm: scm , opts: opts } = dep <- Mix.Dep . cached ( ) ,
727
727
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 ,
732
730
reduce: { stale_modules , % { } , old_exports } do
733
731
{ 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 ] )
756
789
end
757
790
end
758
791
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
+
759
801
defp exports_md5 ( module , use_attributes? ) do
760
802
cond do
761
803
function_exported? ( module , :__info__ , 1 ) ->
0 commit comments