@@ -18,22 +18,38 @@ load(
18
18
"maybe_install_erlang" ,
19
19
)
20
20
21
+ ElixirAppInfo = provider (
22
+ doc = "Compiled Elixir Application" ,
23
+ fields = {
24
+ "app_name" : "Name of the erlang application" ,
25
+ "extra_apps" : "Extra applications in the applications key of the .app file" ,
26
+ "include" : "Public header files" ,
27
+ "beam" : "ebin directory produced by mix" ,
28
+ "consolidated" : "consolidated directory produced by mix" ,
29
+ "priv" : "Additional files" ,
30
+ "license_files" : "License files" ,
31
+ "srcs" : "Source files" ,
32
+ "deps" : "Runtime dependencies of the compiled sources" ,
33
+ },
34
+ )
35
+
21
36
def deps_dir_contents (ctx , deps , dir ):
22
37
files = []
23
38
for dep in deps :
24
39
lib_info = dep [ErlangAppInfo ]
25
40
for src in lib_info .include + lib_info .beam + lib_info .srcs :
26
- rp = additional_file_dest_relative_path (dep .label , src )
27
- f = ctx .actions .declare_file (path_join (
28
- dir ,
29
- lib_info .app_name ,
30
- rp ,
31
- ))
32
- ctx .actions .symlink (
33
- output = f ,
34
- target_file = src ,
35
- )
36
- files .append (f )
41
+ if not src .is_directory :
42
+ rp = additional_file_dest_relative_path (dep .label , src )
43
+ f = ctx .actions .declare_file (path_join (
44
+ dir ,
45
+ lib_info .app_name ,
46
+ rp ,
47
+ ))
48
+ ctx .actions .symlink (
49
+ output = f ,
50
+ target_file = src ,
51
+ )
52
+ files .append (f )
37
53
return files
38
54
39
55
def _impl (ctx ):
@@ -42,6 +58,7 @@ def _impl(ctx):
42
58
43
59
escript = ctx .actions .declare_file (path_join ("escript" , "rabbitmqctl" ))
44
60
ebin = ctx .actions .declare_directory ("ebin" )
61
+ consolidated = ctx .actions .declare_directory ("consolidated" )
45
62
mix_invocation_dir = ctx .actions .declare_directory ("{}_mix" .format (ctx .label .name ))
46
63
fetched_srcs = ctx .actions .declare_file ("deps.tar" )
47
64
66
83
ABS_ELIXIR_HOME=$PWD/{elixir_home}
67
84
fi
68
85
ABS_EBIN_DIR=$PWD/{ebin_dir}
86
+ ABS_CONSOLIDATED_DIR=$PWD/{consolidated_dir}
69
87
ABS_ESCRIPT_PATH=$PWD/{escript_path}
70
88
ABS_FETCHED_SRCS=$PWD/{fetched_srcs}
71
89
99
117
cp escript/rabbitmqctl ${{ABS_ESCRIPT_PATH}}
100
118
101
119
cp _build/${{MIX_ENV}}/lib/rabbitmqctl/ebin/* ${{ABS_EBIN_DIR}}
102
- cp _build/${{MIX_ENV}}/lib/rabbitmqctl/consolidated/* ${{ABS_EBIN_DIR }}
120
+ cp _build/${{MIX_ENV}}/lib/rabbitmqctl/consolidated/* ${{ABS_CONSOLIDATED_DIR }}
103
121
104
122
tar --file ${{ABS_FETCHED_SRCS}} \\
105
123
--create deps
@@ -116,6 +134,7 @@ find . -type l -delete
116
134
deps_dir = deps_dir ,
117
135
escript_path = escript .path ,
118
136
ebin_dir = ebin .path ,
137
+ consolidated_dir = consolidated .path ,
119
138
fetched_srcs = fetched_srcs .path ,
120
139
)
121
140
@@ -130,12 +149,18 @@ find . -type l -delete
130
149
131
150
ctx .actions .run_shell (
132
151
inputs = inputs ,
133
- outputs = [escript , ebin , mix_invocation_dir , fetched_srcs ],
152
+ outputs = [
153
+ escript ,
154
+ ebin ,
155
+ consolidated ,
156
+ mix_invocation_dir ,
157
+ fetched_srcs ,
158
+ ],
134
159
command = script ,
135
160
mnemonic = "MIX" ,
136
161
)
137
162
138
- runfiles = ctx .runfiles ([ebin ]).merge_all ([
163
+ runfiles = ctx .runfiles ([ebin , consolidated ]).merge_all ([
139
164
erlang_runfiles ,
140
165
elixir_runfiles ,
141
166
] + [
@@ -146,13 +171,15 @@ find . -type l -delete
146
171
return [
147
172
DefaultInfo (
148
173
executable = escript ,
149
- files = depset ([ebin , fetched_srcs ]),
174
+ files = depset ([ebin , consolidated , fetched_srcs ]),
150
175
runfiles = runfiles ,
151
176
),
152
- ErlangAppInfo (
153
- app_name = "rabbitmq_cli" ,
177
+ ElixirAppInfo (
178
+ app_name = "rabbitmqctl" , # mix generates 'rabbitmqctl.app'
179
+ extra_apps = ["elixir" , "logger" ],
154
180
include = [],
155
- beam = [ebin ],
181
+ beam = ebin ,
182
+ consolidated = consolidated ,
156
183
priv = [],
157
184
license_files = ctx .files .license_files ,
158
185
srcs = ctx .files .srcs ,
@@ -180,15 +207,144 @@ rabbitmqctl_private = rule(
180
207
toolchains = [
181
208
"//bazel/elixir:toolchain_type" ,
182
209
],
183
- provides = [ErlangAppInfo ],
210
+ provides = [ElixirAppInfo ],
184
211
executable = True ,
185
212
)
186
213
187
- def rabbitmqctl (** kwargs ):
214
+ def _elixir_app_to_erlang_app (ctx ):
215
+ app_consolidated = ctx .attr .elixir_app [ElixirAppInfo ].consolidated
216
+ app_ebin = ctx .attr .elixir_app [ElixirAppInfo ].beam
217
+
218
+ elixir_ebin = ctx .attr .elixir_as_app [ErlangAppInfo ].beam [0 ].path
219
+
220
+ ebin = ctx .actions .declare_directory (path_join (ctx .label .name , "ebin" ))
221
+
222
+ if ctx .attr .mode == "elixir" :
223
+ ctx .actions .run_shell (
224
+ inputs = ctx .files .elixir_as_app + ctx .files .elixir_app ,
225
+ outputs = [ebin ],
226
+ command = """\
227
+ set -euo pipefail
228
+
229
+ cp "{elixir_ebin}"/* "{ebin}"
230
+
231
+ for beam in "{app_consolidated}"/*; do
232
+ find "{ebin}" -name "$(basename $beam)" -exec cp -f "$beam" "{ebin}" \\ ;
233
+ done
234
+ """ .format (
235
+ elixir_ebin = elixir_ebin ,
236
+ app_consolidated = app_consolidated .path ,
237
+ ebin = ebin .path ,
238
+ ),
239
+ )
240
+
241
+ lib_info = ctx .attr .elixir_as_app [ErlangAppInfo ]
242
+ return [
243
+ DefaultInfo (files = depset ([ebin ])),
244
+ ErlangAppInfo (
245
+ app_name = "elixir" ,
246
+ include = lib_info .include ,
247
+ beam = [ebin ],
248
+ priv = lib_info .priv ,
249
+ deps = lib_info .deps ,
250
+ ),
251
+ ]
252
+ elif ctx .attr .mode == "app" :
253
+ ctx .actions .run_shell (
254
+ inputs = ctx .files .elixir_as_app + ctx .files .elixir_app ,
255
+ outputs = [ebin ],
256
+ command = """\
257
+ set -euo pipefail
258
+
259
+ cp "{app_ebin}"/* "{ebin}"
260
+ cp -f "{app_consolidated}"/* "{ebin}"
261
+
262
+ for beam in "{elixir_ebin}"/*; do
263
+ find "{ebin}" -name "$(basename $beam)" -delete
264
+ done
265
+ """ .format (
266
+ elixir_ebin = elixir_ebin ,
267
+ app_ebin = app_ebin .path ,
268
+ app_consolidated = app_consolidated .path ,
269
+ ebin = ebin .path ,
270
+ ),
271
+ )
272
+
273
+ lib_info = ctx .attr .elixir_app [ElixirAppInfo ]
274
+ return [
275
+ DefaultInfo (files = depset ([ebin ])),
276
+ ErlangAppInfo (
277
+ app_name = lib_info .app_name ,
278
+ extra_apps = lib_info .extra_apps ,
279
+ include = lib_info .include ,
280
+ beam = [ebin ],
281
+ priv = lib_info .priv ,
282
+ license_files = lib_info .license_files ,
283
+ srcs = lib_info .srcs ,
284
+ deps = lib_info .deps ,
285
+ ),
286
+ ]
287
+
288
+ return []
289
+
290
+ elixir_app_to_erlang_app = rule (
291
+ implementation = _elixir_app_to_erlang_app ,
292
+ attrs = {
293
+ "elixir_as_app" : attr .label (
294
+ providers = [ErlangAppInfo ],
295
+ ),
296
+ "elixir_app" : attr .label (
297
+ providers = [ElixirAppInfo ],
298
+ ),
299
+ "mode" : attr .string (
300
+ values = [
301
+ "elixir" ,
302
+ "app" ,
303
+ ],
304
+ ),
305
+ },
306
+ provides = [ErlangAppInfo ],
307
+ )
308
+
309
+ def rabbitmqctl (
310
+ name = None ,
311
+ visibility = None ,
312
+ ** kwargs ):
313
+ # mix produces a consolidated directory alongside the ebin
314
+ # directory, which contains .beam files for modules that
315
+ # are extended by protocols
316
+ # When used with dialyzer, this results in module conflicts
317
+ # between the original versions in elixir, and the
318
+ # consolidated ones
319
+ # So, this macro compiles the cli, then derives a copy of
320
+ # elixir that can be loaded alongside it without conflict
321
+ # (but assumes that the two are used together)
322
+ # These each have to be separate rules, as a single rule
323
+ # cannot provide multiple erlang_app (ErlangAppInfo
324
+ # provider instances)
325
+
188
326
rabbitmqctl_private (
327
+ name = name ,
189
328
is_windows = select ({
190
329
"@bazel_tools//src/conditions:host_windows" : True ,
191
330
"//conditions:default" : False ,
192
331
}),
332
+ visibility = visibility ,
193
333
** kwargs
194
334
)
335
+
336
+ elixir_app_to_erlang_app (
337
+ name = "elixir" ,
338
+ elixir_as_app = Label ("//bazel/elixir:erlang_app" ),
339
+ elixir_app = ":" + name ,
340
+ mode = "elixir" ,
341
+ visibility = visibility ,
342
+ )
343
+
344
+ elixir_app_to_erlang_app (
345
+ name = "erlang_app" ,
346
+ elixir_as_app = Label ("//bazel/elixir:erlang_app" ),
347
+ elixir_app = ":" + name ,
348
+ mode = "app" ,
349
+ visibility = visibility ,
350
+ )
0 commit comments