@@ -92,6 +92,13 @@ defmodule Mix.Tasks.Compile.App do
9292 * `--compile-path` - where to find `.beam` files and write the
9393 resulting `.app` file, defaults to `Mix.Project.compile_path/0`
9494
95+ ## Configuration
96+
97+ * `:reliable_dir_mtime` - this task relies on the operating system
98+ changing the mtime on a directory whenever a file is added or removed.
99+ You can set this option to false if your system does not provide
100+ reliable mtimes. Defaults to false on Windows.
101+
95102 ## Phases
96103
97104 Applications provide a start phases mechanism which will be called,
@@ -151,7 +158,20 @@ defmodule Mix.Tasks.Compile.App do
151158
152159 current_properties = current_app_properties ( target )
153160
154- if opts [ :force ] || new_mtime > Mix.Utils . last_modified ( target ) do
161+ { changed? , modules } =
162+ cond do
163+ opts [ :force ] || new_mtime > Mix.Utils . last_modified ( target ) ->
164+ { true , nil }
165+
166+ Keyword . get ( config , :reliable_dir_mtime , fn -> not match? ( { :win32 , _ } , :os . type ( ) ) end ) ->
167+ { false , nil }
168+
169+ true ->
170+ modules = modules_from ( compile_path )
171+ { modules != Keyword . get ( current_properties , :modules , [ ] ) , modules }
172+ end
173+
174+ if changed? do
155175 properties =
156176 [
157177 description: to_charlist ( config [ :description ] || app ) ,
@@ -161,7 +181,7 @@ defmodule Mix.Tasks.Compile.App do
161181 |> merge_project_application ( project )
162182 |> handle_extra_applications ( config )
163183 |> add_compile_env ( current_properties )
164- |> add_modules ( compile_path )
184+ |> add_modules ( modules , compile_path )
165185
166186 contents = :io_lib . format ( "~p.~n" , [ { :application , app , properties } ] )
167187 :application . load ( { :application , app , properties } )
@@ -210,9 +230,11 @@ defmodule Mix.Tasks.Compile.App do
210230 defp modules_from ( path ) do
211231 case File . ls ( path ) do
212232 { :ok , entries } ->
213- for entry <- entries ,
214- String . ends_with? ( entry , ".beam" ) ,
215- do: entry |> binary_part ( 0 , byte_size ( entry ) - 5 ) |> String . to_atom ( )
233+ Enum . sort (
234+ for entry <- entries ,
235+ String . ends_with? ( entry , ".beam" ) ,
236+ do: entry |> binary_part ( 0 , byte_size ( entry ) - 5 ) |> String . to_atom ( )
237+ )
216238
217239 { :error , _ } ->
218240 [ ]
@@ -341,15 +363,19 @@ defmodule Mix.Tasks.Compile.App do
341363 defp typed_app? ( _ ) , do: false
342364
343365 defp add_compile_env ( properties , current_properties ) do
366+ # If someone calls compile.elixir and then compile.app across two
367+ # separate OS calls, then the compile_env won't be properly reflected.
368+ # This is ok because compile_env is not used for correctness. It is
369+ # simply to catch possible errors early.
344370 case Mix.ProjectStack . compile_env ( nil ) do
345371 nil -> Keyword . take ( current_properties , [ :compile_env ] ) ++ properties
346372 [ ] -> properties
347373 compile_env -> Keyword . put ( properties , :compile_env , compile_env )
348374 end
349375 end
350376
351- defp add_modules ( properties , path ) do
352- Keyword . put_new_lazy ( properties , :modules , fn -> path |> modules_from ( ) |> Enum . sort ( ) end )
377+ defp add_modules ( properties , modules , compile_path ) do
378+ Keyword . put_new_lazy ( properties , :modules , fn -> modules || modules_from ( compile_path ) end )
353379 end
354380
355381 defp handle_extra_applications ( properties , config ) do
0 commit comments