Skip to content

Commit 3a3fc55

Browse files
mergify[bot]HoloRinmichaelklishin
authored
Rework elixir dialyze (backport #6919) (backport #6991) (#6992)
* Rework plt/dialyze for rabbitmqctl and plugins that depend on it This allows us to stop ignorning undefined callback warnings When mix compiles rabbitmqctl, it produces a 'consolidated' directory alongside the 'ebin' dir. Some of the modules in consolidated are intended to be used instead of those provided by elixir. We now handle the conflicts properly in the bazel build. (cherry picked from commit b84e746) (cherry picked from commit 766bf1c) # Conflicts: # deps/rabbitmq_stream/BUILD.bazel * Fix dialyzer warnings revealed from previous commit (cherry picked from commit 08d641a) (cherry picked from commit ce0ef70) # Conflicts: # deps/rabbitmq_stream/src/Elixir.RabbitMQ.CLI.Ctl.Commands.ListStreamConsumerGroupsCommand.erl # deps/rabbitmq_stream/src/Elixir.RabbitMQ.CLI.Ctl.Commands.ListStreamGroupConsumersCommand.erl * Fixup docstring (cherry picked from commit 057f776) (cherry picked from commit 8853ffe) * Quote vars in shell in deps/rabbitmq_cli/rabbitmqctl.bzl (cherry picked from commit 3b2513e) (cherry picked from commit a6ce478) * Resolve conflicts * Add ranch to the rabbitmq_stream deps * Fixup spec in in info_keys.ex Co-authored-by: Rin Kuryloski <[email protected]> Co-authored-by: Michael Klishin <[email protected]>
1 parent 2df494e commit 3a3fc55

22 files changed

+258
-102
lines changed

bazel/elixir/elixir_as_app.bzl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ cp -r "{elixir_home}"/lib/elixir/ebin/* {ebin}
3131
include = [],
3232
beam = [ebin],
3333
priv = [],
34+
license_files = [],
35+
srcs = [],
3436
deps = [],
3537
),
3638
]

deps/rabbitmq_amqp1_0/BUILD.bazel

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ APP_NAME = "rabbitmq_amqp1_0"
2323
APP_DESCRIPTION = "AMQP 1.0 support for RabbitMQ"
2424

2525
BUILD_DEPS = [
26-
"//deps/rabbitmq_cli:rabbitmqctl",
26+
"//deps/rabbitmq_cli:erlang_app",
2727
]
2828

2929
DEPS = [
@@ -58,17 +58,13 @@ plt(
5858
"stdlib",
5959
"ssl",
6060
],
61-
# this ought to include BUILD_DEPS, but not sure how to
62-
# get dialyze of elixir code working yet
63-
deps = without(
64-
"//deps/rabbitmq_cli:rabbitmqctl",
65-
BUILD_DEPS + DEPS + RUNTIME_DEPS,
66-
),
61+
libs = ["//deps/rabbitmq_cli:elixir"],
62+
deps = ["//deps/rabbitmq_cli:elixir"] + BUILD_DEPS + DEPS + RUNTIME_DEPS,
6763
)
6864

6965
dialyze(
7066
size = "medium",
71-
dialyzer_opts = RABBITMQ_DIALYZER_OPTS + ["-Wno_undefined_callbacks"],
67+
dialyzer_opts = RABBITMQ_DIALYZER_OPTS,
7268
plt = ":base_plt",
7369
)
7470

deps/rabbitmq_amqp1_0/src/Elixir.RabbitMQ.CLI.Ctl.Commands.ListAmqp10ConnectionsCommand.erl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ switches() -> [{verbose, boolean}].
2929
aliases() -> [{'V', verbose}].
3030

3131
validate(Args, _) ->
32+
ValidKeys = lists:map(fun atom_to_list/1, ?INFO_ITEMS),
3233
case 'Elixir.RabbitMQ.CLI.Ctl.InfoKeys':validate_info_keys(Args,
33-
?INFO_ITEMS) of
34+
ValidKeys) of
3435
{ok, _} -> ok;
3536
Error -> Error
3637
end.

deps/rabbitmq_auth_backend_oauth2/BUILD.bazel

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ EXTRA_APPS = [
2323

2424
BUILD_DEPS = [
2525
"//deps/rabbit_common:erlang_app",
26-
"//deps/rabbitmq_cli:rabbitmqctl",
26+
"//deps/rabbitmq_cli:erlang_app",
2727
]
2828

2929
DEPS = [
@@ -40,29 +40,27 @@ rabbitmq_app(
4040
app_description = APP_DESCRIPTION,
4141
app_name = APP_NAME,
4242
build_deps = BUILD_DEPS,
43+
extra_apps = EXTRA_APPS,
4344
runtime_deps = RUNTIME_DEPS,
4445
deps = DEPS,
45-
extra_apps = EXTRA_APPS,
4646
)
4747

4848
xref(
4949
additional_libs = [
50-
"//deps/rabbitmq_cli:rabbitmqctl",
50+
"//deps/rabbitmq_cli:erlang_app",
5151
],
5252
)
5353

5454
plt(
5555
name = "base_plt",
5656
apps = EXTRA_APPS,
57+
libs = ["//deps/rabbitmq_cli:elixir"],
5758
plt = "//:base_plt",
58-
deps = without(
59-
"//deps/rabbitmq_cli:rabbitmqctl",
60-
BUILD_DEPS + DEPS + RUNTIME_DEPS,
61-
),
59+
deps = ["//deps/rabbitmq_cli:elixir"] + BUILD_DEPS + DEPS + RUNTIME_DEPS,
6260
)
6361

6462
dialyze(
65-
dialyzer_opts = RABBITMQ_DIALYZER_OPTS + ["-Wno_undefined_callbacks"],
63+
dialyzer_opts = RABBITMQ_DIALYZER_OPTS,
6664
plt = ":base_plt",
6765
)
6866

deps/rabbitmq_cli/lib/rabbitmq/cli/command_behaviour.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ defmodule RabbitMQ.CLI.CommandBehaviour do
4747
@callback printer() :: atom()
4848
@callback scopes() :: [atom()] | nil
4949
@callback description() :: String.t()
50-
@callback help_section() :: String.t()
50+
@callback help_section() :: String.t() | {:plugin, atom()}
5151
@callback usage_additional() ::
5252
String.t()
5353
| [String.t()]

deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/info_keys.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ defmodule RabbitMQ.CLI.Ctl.InfoKeys do
6666
with_valid_info_keys(args, valid_keys, [], fun)
6767
end
6868

69-
@spec with_valid_info_keys([charlist], [charlist], aliases, fun([atom])) :: any
69+
@spec with_valid_info_keys([charlist], [charlist], aliases, ([atom] -> any)) :: any
7070
def with_valid_info_keys(args, valid_keys, aliases, fun) do
7171
case validate_info_keys(args, valid_keys, aliases) do
7272
{:ok, info_keys} -> fun.(:proplists.get_keys(info_keys))

deps/rabbitmq_cli/rabbitmqctl.bzl

Lines changed: 176 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,38 @@ load(
1818
"maybe_install_erlang",
1919
)
2020

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+
2136
def deps_dir_contents(ctx, deps, dir):
2237
files = []
2338
for dep in deps:
2439
lib_info = dep[ErlangAppInfo]
2540
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)
3753
return files
3854

3955
def _impl(ctx):
@@ -42,6 +58,7 @@ def _impl(ctx):
4258

4359
escript = ctx.actions.declare_file(path_join("escript", "rabbitmqctl"))
4460
ebin = ctx.actions.declare_directory("ebin")
61+
consolidated = ctx.actions.declare_directory("consolidated")
4562
mix_invocation_dir = ctx.actions.declare_directory("{}_mix".format(ctx.label.name))
4663
fetched_srcs = ctx.actions.declare_file("deps.tar")
4764

@@ -66,6 +83,7 @@ else
6683
ABS_ELIXIR_HOME=$PWD/{elixir_home}
6784
fi
6885
ABS_EBIN_DIR=$PWD/{ebin_dir}
86+
ABS_CONSOLIDATED_DIR=$PWD/{consolidated_dir}
6987
ABS_ESCRIPT_PATH=$PWD/{escript_path}
7088
ABS_FETCHED_SRCS=$PWD/{fetched_srcs}
7189
@@ -99,7 +117,7 @@ fi
99117
cp escript/rabbitmqctl ${{ABS_ESCRIPT_PATH}}
100118
101119
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}}
103121
104122
tar --file ${{ABS_FETCHED_SRCS}} \\
105123
--create deps
@@ -116,6 +134,7 @@ find . -type l -delete
116134
deps_dir = deps_dir,
117135
escript_path = escript.path,
118136
ebin_dir = ebin.path,
137+
consolidated_dir = consolidated.path,
119138
fetched_srcs = fetched_srcs.path,
120139
)
121140

@@ -130,12 +149,18 @@ find . -type l -delete
130149

131150
ctx.actions.run_shell(
132151
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+
],
134159
command = script,
135160
mnemonic = "MIX",
136161
)
137162

138-
runfiles = ctx.runfiles([ebin]).merge_all([
163+
runfiles = ctx.runfiles([ebin, consolidated]).merge_all([
139164
erlang_runfiles,
140165
elixir_runfiles,
141166
] + [
@@ -146,13 +171,15 @@ find . -type l -delete
146171
return [
147172
DefaultInfo(
148173
executable = escript,
149-
files = depset([ebin, fetched_srcs]),
174+
files = depset([ebin, consolidated, fetched_srcs]),
150175
runfiles = runfiles,
151176
),
152-
ErlangAppInfo(
153-
app_name = "rabbitmq_cli",
177+
ElixirAppInfo(
178+
app_name = "rabbitmqctl", # mix generates 'rabbitmqctl.app'
179+
extra_apps = ["elixir", "logger"],
154180
include = [],
155-
beam = [ebin],
181+
beam = ebin,
182+
consolidated = consolidated,
156183
priv = [],
157184
license_files = ctx.files.license_files,
158185
srcs = ctx.files.srcs,
@@ -180,15 +207,144 @@ rabbitmqctl_private = rule(
180207
toolchains = [
181208
"//bazel/elixir:toolchain_type",
182209
],
183-
provides = [ErlangAppInfo],
210+
provides = [ElixirAppInfo],
184211
executable = True,
185212
)
186213

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+
188326
rabbitmqctl_private(
327+
name = name,
189328
is_windows = select({
190329
"@bazel_tools//src/conditions:host_windows": True,
191330
"//conditions:default": False,
192331
}),
332+
visibility = visibility,
193333
**kwargs
194334
)
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

Comments
 (0)