1
1
defmodule Mix.Compilers.Elixir do
2
2
@ moduledoc false
3
3
4
- @ manifest_vsn :v3
4
+ @ manifest_vsn :v2
5
5
6
6
@ doc """
7
7
Compiles stale Elixir files.
@@ -18,12 +18,10 @@ defmodule Mix.Compilers.Elixir do
18
18
def compile ( manifest , srcs , skip , exts , dest , force , on_start ) do
19
19
keep = srcs -- skip
20
20
all = Mix.Utils . extract_files ( keep , exts )
21
- { all_entries , skip_entries , all_sources , skip_sources } = parse_manifest ( manifest , keep )
22
-
23
- modified = Mix.Utils . last_modified ( manifest )
21
+ { all_entries , skip_entries } = parse_manifest ( manifest , keep )
24
22
25
23
removed =
26
- for { source , _files } <- all_sources ,
24
+ for { _b , _m , _k , source , _cd , _rd , _f , _bin } <- all_entries ,
27
25
not ( source in all ) ,
28
26
do: source
29
27
@@ -33,44 +31,44 @@ defmodule Mix.Compilers.Elixir do
33
31
# changed, let's just compile everything
34
32
all
35
33
else
36
- sources_mtimes = mtimes ( all_sources )
34
+ modified = Mix.Utils . last_modified ( manifest )
35
+ all_mtimes = mtimes ( all_entries )
37
36
38
- # Otherwise let's start with the new sources
37
+ # Otherwise let's start with the new ones
38
+ # plus the ones that have changed
39
39
for ( source <- all ,
40
- not Map . has_key? ( all_sources , source ) ,
40
+ not Enum . any? ( all_entries , fn { _b , _m , _k , s , _cd , _rd , _f , _bin } -> s == source end ) ,
41
41
do: source )
42
42
++
43
- # Plus the sources that have changed in disk
44
- for ( { source , files } <- all_sources ,
45
- times = Enum . map ( [ source | files ] , & Map . fetch! ( sources_mtimes , & 1 ) ) ,
43
+ for ( { _b , _m , _k , source , _cd , _rd , files , _bin } <- all_entries ,
44
+ times = Enum . map ( [ source | files ] , & Map . fetch! ( all_mtimes , & 1 ) ) ,
46
45
Mix.Utils . stale? ( times , [ modified ] ) ,
47
46
do: source )
48
47
end
49
48
50
- sources = update_stale_sources ( all_sources , removed , changed )
51
- { entries , changed } = update_stale_entries ( all_entries , removed ++ changed ,
52
- stale_local_deps ( modified ) )
53
-
49
+ { entries , changed } = remove_stale_entries ( all_entries , removed ++ changed )
54
50
stale = changed -- removed
55
- new_entries = entries ++ skip_entries
56
- new_sources = Map . merge ( sources , skip_sources )
57
51
58
52
cond do
59
53
stale != [ ] ->
60
- compile_manifest ( manifest , new_entries , new_sources , stale , dest , on_start )
54
+ compile_manifest ( manifest , entries ++ skip_entries , stale , dest , on_start )
61
55
:ok
62
56
removed != [ ] ->
63
- write_manifest ( manifest , new_entries , new_sources )
57
+ write_manifest ( manifest , entries ++ skip_entries )
64
58
:ok
65
59
true ->
66
60
:noop
67
61
end
68
62
end
69
63
70
- defp mtimes ( sources ) do
71
- Enum . reduce ( sources , % { } , fn { source , files } , map ->
72
- Enum . reduce ( [ source | files ] , map , fn file , map ->
73
- Map . put_new_lazy ( map , file , fn -> Mix.Utils . last_modified ( file ) end )
64
+ defp mtimes ( entries ) do
65
+ Enum . reduce ( entries , % { } , fn { _b , _m , _k , source , _cd , _rd , files , _bin } , dict ->
66
+ Enum . reduce ( [ source | files ] , dict , fn file , dict ->
67
+ if Map . has_key? ( dict , file ) do
68
+ dict
69
+ else
70
+ Map . put ( dict , file , Mix.Utils . last_modified ( file ) )
71
+ end
74
72
end )
75
73
end )
76
74
end
@@ -79,24 +77,22 @@ defmodule Mix.Compilers.Elixir do
79
77
Removes compiled files.
80
78
"""
81
79
def clean ( manifest ) do
82
- Enum . each read_manifest ( manifest ) , fn
83
- { beam , _ , _ , _ , _ , _ , _ } ->
84
- File . rm ( beam )
85
- { _ , _ } ->
86
- :ok
80
+ Enum . map read_manifest ( manifest ) , fn { beam , _ , _ , _ , _ , _ , _ , _ } ->
81
+ File . rm ( beam )
87
82
end
83
+ :ok
88
84
end
89
85
90
86
@ doc """
91
87
Returns protocols and implementations for the given manifest.
92
88
"""
93
89
def protocols_and_impls ( manifest ) do
94
- for { beam , module , kind , _ , _ , _ , _ } <- read_manifest ( manifest ) ,
90
+ for { _ , module , kind , _ , _ , _ , _ , _ } <- read_manifest ( manifest ) ,
95
91
match? ( :protocol , kind ) or match? ( { :impl , _ } , kind ) ,
96
- do: { module , kind , beam }
92
+ do: { module , kind }
97
93
end
98
94
99
- defp compile_manifest ( manifest , entries , sources , stale , dest , on_start ) do
95
+ defp compile_manifest ( manifest , entries , stale , dest , on_start ) do
100
96
Mix.Project . ensure_structure ( )
101
97
true = Code . prepend_path ( dest )
102
98
@@ -105,16 +101,16 @@ defmodule Mix.Compilers.Elixir do
105
101
106
102
# Starts a server responsible for keeping track which files
107
103
# were compiled and the dependencies between them.
108
- { :ok , pid } = Agent . start_link ( fn -> { entries , sources } end )
104
+ { :ok , pid } = Agent . start_link ( fn -> entries end )
109
105
110
106
try do
111
107
_ = Kernel.ParallelCompiler . files :lists . usort ( stale ) ,
112
108
each_module: & each_module ( pid , dest , cwd , & 1 , & 2 , & 3 ) ,
113
109
each_file: & each_file ( & 1 ) ,
114
110
dest: dest
115
- Agent . cast pid , fn { entries , sources } ->
116
- write_manifest ( manifest , entries , sources )
117
- { entries , sources }
111
+ Agent . cast pid , fn entries ->
112
+ write_manifest ( manifest , entries )
113
+ entries
118
114
end
119
115
after
120
116
Agent . stop ( pid , :normal , :infinity )
@@ -144,12 +140,8 @@ defmodule Mix.Compilers.Elixir do
144
140
kind = detect_kind ( module )
145
141
source = Path . relative_to ( source , cwd )
146
142
files = get_external_resources ( module , cwd )
147
-
148
- Agent . cast pid , fn { entries , sources } ->
149
- entries = List . keystore ( entries , beam , 0 , { beam , module , kind , source , compile , runtime , binary } )
150
- sources = Map . update ( sources , source , files , & files ++ & 1 )
151
- { entries , sources }
152
- end
143
+ tuple = { beam , module , kind , source , compile , runtime , files , binary }
144
+ Agent . cast pid , & :lists . keystore ( beam , 1 , & 1 , tuple )
153
145
end
154
146
155
147
defp detect_kind ( module ) do
@@ -171,27 +163,22 @@ defmodule Mix.Compilers.Elixir do
171
163
do: relative
172
164
end
173
165
174
- defp each_file ( source ) do
175
- Mix . shell . info "Compiled #{ source } "
166
+ defp each_file ( file ) do
167
+ Mix . shell . info "Compiled #{ file } "
176
168
end
177
169
178
170
## Resolution
179
171
180
- defp update_stale_sources ( sources , removed , changed ) do
181
- Enum . reduce changed , Map . drop ( sources , removed ) , & Map . put ( & 2 , & 1 , [ ] )
182
- end
183
-
184
172
# This function receives the manifest entries and some source
185
173
# files that have changed. It then, recursively, figures out
186
174
# all the files that changed (via the module dependencies) and
187
175
# return the non-changed entries and the removed sources.
188
- defp update_stale_entries ( all , [ ] , stale ) when stale == % { } do
176
+ defp remove_stale_entries ( all , [ ] ) do
189
177
{ all , [ ] }
190
178
end
191
179
192
- defp update_stale_entries ( all , changed , stale ) do
193
- removed = Enum . into ( changed , % { } , & { & 1 , true } )
194
- remove_stale_entries ( all , stale , removed )
180
+ defp remove_stale_entries ( all , changed ) do
181
+ remove_stale_entries ( all , % { } , Enum . into ( changed , % { } , & { & 1 , true } ) )
195
182
end
196
183
197
184
defp remove_stale_entries ( entries , old_stale , old_removed ) do
@@ -206,13 +193,15 @@ defmodule Mix.Compilers.Elixir do
206
193
end
207
194
end
208
195
209
- defp remove_stale_entry ( { beam , module , _kind , source , compile , runtime , _bin } = entry ,
196
+ defp remove_stale_entry ( { beam , module , _kind , source , compile , runtime , _f , _bin } = entry ,
210
197
{ rest , stale , removed } ) do
211
198
cond do
212
199
# If I changed in disk or have a compile time dependency
213
200
# on something stale, I need to be recompiled.
214
201
Map . has_key? ( removed , source ) or Enum . any? ( compile , & Map . has_key? ( stale , & 1 ) ) ->
215
- remove_and_purge ( beam , module )
202
+ _ = File . rm ( beam )
203
+ _ = :code . purge ( module )
204
+ _ = :code . delete ( module )
216
205
{ rest , Map . put ( stale , module , true ) , Map . put ( removed , source , true ) }
217
206
218
207
# If I have a runtime time dependency on something stale,
@@ -226,22 +215,6 @@ defmodule Mix.Compilers.Elixir do
226
215
end
227
216
end
228
217
229
- defp stale_local_deps ( modified ) do
230
- for % { scm: scm } = dep <- Mix.Dep . children ,
231
- not scm . fetchable? ,
232
- path <- Mix.Dep . load_paths ( dep ) ,
233
- beam <- Path . wildcard ( Path . join ( path , "*.beam" ) ) ,
234
- Mix.Utils . last_modified ( beam ) > modified ,
235
- do: { beam |> Path . basename |> Path . rootname |> String . to_atom , true } ,
236
- into: % { }
237
- end
238
-
239
- defp remove_and_purge ( beam , module ) do
240
- _ = File . rm ( beam )
241
- _ = :code . purge ( module )
242
- _ = :code . delete ( module )
243
- end
244
-
245
218
## Manifest handling
246
219
247
220
defp read_manifest ( manifest ) do
@@ -251,58 +224,31 @@ defmodule Mix.Compilers.Elixir do
251
224
end
252
225
end
253
226
254
- # Similar to read manifest but supports data migration.
255
227
defp parse_manifest ( manifest , keep_paths ) do
256
- state = { [ ] , [ ] , % { } , % { } }
257
-
258
- case :file . consult ( manifest ) do
259
- { :ok , [ @ manifest_vsn | data ] } ->
260
- parse_manifest ( data , keep_paths , state )
261
- { :ok , [ :v2 | data ] } ->
262
- for { beam , module , _ , _ , _ , _ , _ , _ } <- data do
263
- remove_and_purge ( beam , module )
264
- end
265
- state
266
- _ ->
267
- state
268
- end
269
- end
270
-
271
- defp parse_manifest ( data , keep_paths , state ) do
272
- Enum . reduce data , state , fn
273
- { _ , _ , _ , source , _ , _ , _ } = entry , { keep , skip , keep_sources , skip_sources } ->
274
- if String . starts_with? ( source , keep_paths ) do
275
- { [ entry | keep ] , skip , keep_sources , skip_sources }
276
- else
277
- { keep , [ entry | skip ] , keep_sources , skip_sources }
278
- end
279
- { source , files } , { keep , skip , keep_sources , skip_sources } ->
228
+ Enum . reduce read_manifest ( manifest ) , { [ ] , [ ] } , fn
229
+ { _ , _ , _ , source , _ , _ , _ , _ } = entry , { keep , skip } ->
280
230
if String . starts_with? ( source , keep_paths ) do
281
- { keep , skip , Map . put ( keep_sources , source , files ) , skip_sources }
231
+ { [ entry | keep ] , skip }
282
232
else
283
- { keep , skip , keep_sources , Map . put ( skip_sources , source , files ) }
233
+ { keep , [ entry | skip ] }
284
234
end
285
235
end
286
236
end
287
237
288
- defp write_manifest ( manifest , [ ] , sources ) when sources == % { } do
238
+ defp write_manifest ( manifest , [ ] ) do
289
239
File . rm ( manifest )
290
240
:ok
291
241
end
292
242
293
- defp write_manifest ( manifest , entries , sources ) do
243
+ defp write_manifest ( manifest , entries ) do
294
244
File . mkdir_p! ( Path . dirname ( manifest ) )
295
245
296
246
File . open! ( manifest , [ :write ] , fn device ->
297
247
:io . format ( device , '~p.~n' , [ @ manifest_vsn ] )
298
248
299
- Enum . each entries , fn { beam , _ , _ , _ , _ , _ , binary } = entry ->
249
+ Enum . map entries , fn { beam , _ , _ , _ , _ , _ , _ , binary } = entry ->
300
250
if binary , do: File . write! ( beam , binary )
301
- :io . format ( device , '~p.~n' , [ put_elem ( entry , 6 , nil ) ] )
302
- end
303
-
304
- Enum . each sources , fn { _ , _ } = entry ->
305
- :io . format ( device , '~p.~n' , [ entry ] )
251
+ :io . format ( device , '~p.~n' , [ put_elem ( entry , 7 , nil ) ] )
306
252
end
307
253
308
254
:ok
0 commit comments