Skip to content

Commit a19cbe2

Browse files
committed
Initial gazelle extension implementation
Gazelle language extension for erlang. Should be considered experimental. Breaking changes may still be made in the extension without a major version bump to rules_erlang. Supports BUILD file generation for hex, rebar & "bare" erlang apps (erlang sources in the typical layout). Not all hex and rebar information is imported - notably rebar "profiles" are not yet handled. The extension generates a "fine-grained" build. In "classic" rules_erlang, the erlang_app/test_erlang_app macros compile all beam files for a given application in one or two invocations of `erlc`. The gazelle extension instead generates a rule per beam file, resulting in an invocation of `erlc` for each. This improves caching, though is probably somewhat slower on the first compilation of an app. It also allows apps that require more than 2 compilation "phases" to work directly. As this results in many more rules in the BUILD file, the `erlang_generate_beam_files_macro` directive can be used to instead declare most of rules in an `app.bzl` file imported by the BUILD file. The extension also provides some support for the gazelle "update-repos" command. The -from_file option can be used with rebar.lock files. Can also be run with references to hex.pm or github.com to import dependencies directly. This is non-recursive, though transitive deps are printed for convenience. This is deliberate design decision, as bazel prior to version 6 with bzlmod does not have a built-in mechanism for handling of transitive deps. The extension generates a `moduleindex.yaml` file in the repo root, which is used as an index of which modules are contained in a given erlang application. This allows calls at runtime of a given module to be mapped to the appropriate dependency. It's updated by both the default fix/update commands, as well as update-repos, so it's recommended to check it into VCS. A number of gazelle directives are supported to help customize BUILD file generation: - erlang_behaviour_source_lib - erlang_exclude_when_rule_of_kind_exists - erlang_generate_beam_files_macro - erlang_always_generate_test_beam_files - erlang_skip_rules - erlang_apps_dirs - erlang_app_dep - erlang_app_dep_ignore - erlang_app_extra_app - erlang_no_tests Other changes: Some new versions of existing rules are added, such as `erlang_bytecode2` and `assert_suites2`. erlang_bytecode2 compiles with transitive deps since gazelle generates minimal deps, we need to include the transitive deps so that, for instance, headers include headers of deps are present. For compilation, we actually don't need all the files from a dep, just public headers and compiled behaviours. In fact, only the transient headers are needed, not the transient behaviours. So this could be optimized. erlang_build rule produces tars, not directories - this simpilies the "installation" of internal erlang builds in other rules assert_suites2 macro does not require passing the list of existing rules explicitly self referencing include_lib directives are supported The -hidden flag is used for ct_run
1 parent 50a8658 commit a19cbe2

File tree

170 files changed

+44153
-244
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

170 files changed

+44153
-244
lines changed

.bazelversion

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
latest
1+
last_rc

.github/workflows/test.yaml

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@ jobs:
2929
--noexperimental_enable_bzlmod \
3030
--color=yes
3131
- name: RESOVLE TEST LOGS PATH
32+
if: always()
3233
working-directory: test
3334
run: |
3435
echo "::set-output name=LOGS_PATH::$(readlink -f bazel-testlogs)"
3536
id: resolve-test-logs-path
3637
- name: CAPTURE TEST LOGS
38+
if: always()
3739
uses: actions/upload-artifact@v3
3840
with:
3941
name: bazel-testlogs-${{matrix.otp}}
@@ -58,6 +60,7 @@ jobs:
5860
shell: bash
5961
run: |
6062
cat << EOF >> user.bazelrc
63+
startup --output_user_root=C:/tmp
6164
startup --windows_enable_symlinks
6265
build --enable_runfiles
6366
build --color=yes
@@ -74,17 +77,20 @@ jobs:
7477
set ERLANG_HOME=%ERL_PATH:\bin\erl.exe=%
7578
7679
bazelisk test //... --noexperimental_enable_bzlmod
77-
#! https://github.com/actions/upload-artifact/issues/240
78-
#! - name: RESOVLE TEST LOGS PATH
79-
#! run: |
80-
#! Set-Variable -Name logs_path -Value (Get-Item bazel-testlogs).Target
81-
#! Write-Output "::set-output name=LOGS_PATH::$logs_path"
82-
#! id: resolve-test-logs-path
83-
#! - name: CAPTURE TEST LOGS
84-
#! uses: actions/upload-artifact@v3
85-
#! with:
86-
#! name: bazel-testlogs-${{matrix.otp}}
87-
#! path: ${{ steps.resolve-test-logs-path.outputs.LOGS_PATH }}
80+
- name: RESOVLE TEST LOGS PATH
81+
if: always()
82+
working-directory: test
83+
shell: bash
84+
run: |
85+
LOGS_TAR=$PWD/logs.tar
86+
cd bazel-testlogs
87+
tar -cf $LOGS_TAR *
88+
- name: CAPTURE TEST LOGS
89+
if: always()
90+
uses: actions/upload-artifact@v3
91+
with:
92+
name: bazel-testlogs-windows-${{matrix.otp}}
93+
path: test/logs.tar
8894
test-bzlmod:
8995
runs-on: ubuntu-20.04
9096
strategy:
@@ -105,11 +111,13 @@ jobs:
105111
run: |
106112
bazelisk test //...
107113
- name: RESOVLE TEST LOGS PATH
114+
if: always()
108115
working-directory: test
109116
run: |
110117
echo "::set-output name=LOGS_PATH::$(readlink -f bazel-testlogs)"
111118
id: resolve-test-logs-path
112119
- name: CAPTURE TEST LOGS
120+
if: always()
113121
uses: actions/upload-artifact@v3
114122
with:
115123
name: bazel-testlogs-bzlmod-${{matrix.otp}}
@@ -133,6 +141,7 @@ jobs:
133141
shell: bash
134142
run: |
135143
cat << EOF >> user.bazelrc
144+
startup --output_user_root=C:/tmp
136145
startup --windows_enable_symlinks
137146
build --enable_runfiles
138147
build --color=yes
@@ -143,6 +152,20 @@ jobs:
143152
shell: cmd
144153
run: |
145154
bazelisk test //...
155+
- name: RESOVLE TEST LOGS PATH
156+
if: always()
157+
working-directory: test
158+
shell: bash
159+
run: |
160+
LOGS_TAR=$PWD/logs.tar
161+
cd bazel-testlogs
162+
tar -cf $LOGS_TAR *
163+
- name: CAPTURE TEST LOGS
164+
if: always()
165+
uses: actions/upload-artifact@v3
166+
with:
167+
name: bazel-testlogs-bzlmod-windows-${{matrix.otp}}
168+
path: test/logs.tar
146169
test-bzlmod-internal-erlang:
147170
runs-on: ubuntu-20.04
148171
steps:
@@ -170,14 +193,16 @@ jobs:
170193
--toolchain_resolution_debug="@rules_erlang.*" \
171194
--verbose_failures
172195
- name: RESOVLE TEST LOGS PATH
196+
if: always()
173197
working-directory: test
174198
run: |
175199
echo "::set-output name=LOGS_PATH::$(readlink -f bazel-testlogs)"
176200
id: resolve-test-logs-path
177201
- name: CAPTURE TEST LOGS
202+
if: always()
178203
uses: actions/upload-artifact@v3
179204
with:
180-
name: bazel-testlogs-bzlmod-${{matrix.otp}}
205+
name: bazel-testlogs-bzlmod-internal-erlang
181206
path: ${{ steps.resolve-test-logs-path.outputs.LOGS_PATH }}/*
182207
test-host-erlang-change-detected:
183208
runs-on: ubuntu-20.04
@@ -212,3 +237,31 @@ jobs:
212237
if [[ "${{ steps.before.outputs.otp }}" == "${{ steps.after.outputs.otp }}" ]]; then
213238
echo "Fail"
214239
fi
240+
test-gazelle-extension:
241+
runs-on: ubuntu-20.04
242+
steps:
243+
- name: CHECKOUT
244+
uses: actions/checkout@v2
245+
- name: CONFIGURE ERLANG
246+
uses: erlef/setup-beam@v1
247+
with:
248+
otp-version: 25
249+
- name: TEST GAZELLE PACKAGE
250+
run: |
251+
cat << EOF >> user.bazelrc
252+
build --color=yes
253+
EOF
254+
255+
bazelisk test //gazelle:all \
256+
|| bazelisk test //gazelle:all
257+
- name: RESOVLE TEST LOGS PATH
258+
if: always()
259+
run: |
260+
echo "::set-output name=LOGS_PATH::$(readlink -f bazel-testlogs)"
261+
id: resolve-test-logs-path
262+
- name: CAPTURE TEST LOGS
263+
if: always()
264+
uses: actions/upload-artifact@v2
265+
with:
266+
name: bazel-testlogs-gazelle
267+
path: ${{ steps.resolve-test-logs-path.outputs.LOGS_PATH }}/*

BUILD.bazel

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
load(
2-
"@bazel_skylib//rules:common_settings.bzl",
3-
"string_flag",
4-
)
1+
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
2+
load("@bazel_gazelle//:def.bzl", "gazelle")
53

64
config_setting(
75
name = "debug_build",
@@ -33,3 +31,19 @@ string_flag(
3331
build_setting_default = "b",
3432
visibility = ["//visibility:public"],
3533
)
34+
35+
# Gazelle configuration options.
36+
# See https://github.com/bazelbuild/bazel-gazelle#running-gazelle-with-bazel
37+
# gazelle:prefix github.com/rabbitmq/rules_erlang
38+
# gazelle:exclude bazel-out
39+
gazelle(name = "gazelle")
40+
41+
gazelle(
42+
name = "update_go_deps",
43+
args = [
44+
"-from_file=go.mod",
45+
"-to_macro=gazelle/deps.bzl%gazelle_deps",
46+
"-prune",
47+
],
48+
command = "update-repos",
49+
)

MODULE.bazel

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,45 @@ module(
44
version = "3.8.5",
55
)
66

7+
bazel_dep(
8+
name = "rules_go",
9+
repo_name = "io_bazel_rules_go",
10+
version = "0.37.0",
11+
)
712
bazel_dep(
813
name = "rules_pkg",
914
version = "0.5.1",
1015
)
1116
bazel_dep(
1217
name = "bazel_skylib",
13-
version = "1.2.0",
18+
version = "1.3.0",
19+
)
20+
bazel_dep(
21+
name = "gazelle",
22+
repo_name = "bazel_gazelle",
23+
version = "0.28.0",
24+
)
25+
26+
# The union of go deps from bazel-gazelle and this repo
27+
go_deps = use_extension("@bazel_gazelle//:extensions.bzl", "go_deps")
28+
29+
go_deps.from_file(go_mod = "@rules_erlang//:go.mod")
30+
31+
use_repo(
32+
go_deps,
33+
"com_github_bazelbuild_buildtools",
34+
"com_github_bmatcuk_doublestar_v4",
35+
"com_github_fsnotify_fsnotify",
36+
"com_github_emirpasic_gods",
37+
"com_github_google_go_cmp",
38+
"com_github_pelletier_go_toml",
39+
"com_github_pmezard_go_difflib",
40+
"in_gopkg_yaml_v2",
41+
"org_golang_x_mod",
42+
"org_golang_x_sync",
43+
"org_golang_x_tools",
44+
# Used internally by the go_deps module extension.
45+
"bazel_gazelle_go_repository_directives",
1446
)
1547

1648
erlang_package = use_extension(

WORKSPACE.bazel

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,22 @@
1-
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
1+
workspace(name = "rules_erlang")
22

3-
http_archive(
4-
name = "rules_pkg",
5-
sha256 = "038f1caa773a7e35b3663865ffb003169c6a71dc995e39bf4815792f385d837d",
6-
urls = [
7-
"https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.4.0/rules_pkg-0.4.0.tar.gz",
8-
"https://github.com/bazelbuild/rules_pkg/releases/download/0.4.0/rules_pkg-0.4.0.tar.gz",
9-
],
10-
)
3+
load(":internal_deps.bzl", "rules_erlang_internal_deps")
114

12-
load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies")
5+
rules_erlang_internal_deps()
136

14-
rules_pkg_dependencies()
7+
load(":internal_setup.bzl", "rules_erlang_internal_setup")
158

16-
http_archive(
17-
name = "bazel_skylib",
18-
sha256 = "af87959afe497dc8dfd4c6cb66e1279cb98ccc84284619ebfec27d9c09a903de",
19-
urls = [
20-
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.2.0/bazel-skylib-1.2.0.tar.gz",
21-
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.2.0/bazel-skylib-1.2.0.tar.gz",
22-
],
23-
)
9+
rules_erlang_internal_setup()
2410

25-
load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
11+
load("//gazelle:deps.bzl", "gazelle_deps")
2612

27-
bazel_skylib_workspace()
13+
# gazelle:repository_macro gazelle/deps.bzl%gazelle_deps
14+
gazelle_deps()
2815

29-
load(":rules_erlang.bzl", "erlang_config", "rules_erlang_dependencies")
16+
load(":rules_erlang.bzl", "erlang_config")
3017

3118
erlang_config(rules_erlang_workspace = "@")
3219

33-
rules_erlang_dependencies()
34-
3520
load("@erlang_config//:defaults.bzl", "register_defaults")
3621

3722
register_defaults()

app_file.bzl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@ load(
33
_app_file = "app_file",
44
)
55

6-
def app_file(**kwargs):
6+
def app_file(
7+
app_name = "",
8+
dest = "ebin",
9+
**kwargs):
710
_app_file(
811
app_file_tool = Label("//tools/app_file_tool:app_file_tool"),
12+
app_name = app_name,
13+
out = "%s/%s.app" % (dest, app_name),
914
private_stamp_detect = select({
1015
Label("//private:private_stamp_detect"): True,
1116
"//conditions:default": False,

app_file2.bzl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
load(
2+
"//private:app_file.bzl",
3+
_app_file = "app_file",
4+
)
5+
6+
def app_file(**kwargs):
7+
_app_file(
8+
app_file_tool = Label("//tools/app_file_tool:app_file_tool"),
9+
private_stamp_detect = select({
10+
Label("//private:private_stamp_detect"): True,
11+
"//conditions:default": False,
12+
}),
13+
**kwargs
14+
)

ct.bzl

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ load(
44
)
55
load(
66
"//private:ct.bzl",
7-
"ct_test",
87
_code_paths = "code_paths",
8+
_ct_test = "ct_test",
99
_sanitize_sname = "sanitize_sname",
1010
)
1111
load(
@@ -51,7 +51,8 @@ def ct_suite(
5151
ct_suite_variant(
5252
name = name,
5353
suite_name = suite_name,
54-
deps = deps + runtime_deps,
54+
deps = deps,
55+
runtime_deps = runtime_deps,
5556
**kwargs
5657
)
5758

@@ -70,7 +71,7 @@ def ct_suite_variant(
7071

7172
data_dir_files = native.glob(["test/{}_data/**/*".format(suite_name)])
7273

73-
ct_test(
74+
_ct_test(
7475
shard_suite = Label("@rules_erlang//tools/shard_suite:shard_suite"),
7576
name = name,
7677
suite_name = suite_name,
@@ -86,10 +87,42 @@ def ct_suite_variant(
8687

8788
return suite_name
8889

90+
def ct_test(
91+
name = None,
92+
suite_name = None,
93+
compiled_suites = None,
94+
deps = [":test_erlang_app"],
95+
shard_suite = Label("@rules_erlang//tools/shard_suite:shard_suite"),
96+
**kwargs):
97+
if suite_name == None or suite_name == "":
98+
suite_name = name
99+
if compiled_suites == None:
100+
compiled_suites = [":{}_beam_files".format(suite_name)]
101+
_ct_test(
102+
name = name,
103+
suite_name = suite_name,
104+
compiled_suites = compiled_suites,
105+
deps = deps,
106+
shard_suite = shard_suite,
107+
is_windows = select({
108+
"@bazel_tools//src/conditions:host_windows": True,
109+
"//conditions:default": False,
110+
}),
111+
**kwargs
112+
)
113+
89114
def assert_suites(suite_names, suite_files = None):
90115
if suite_files == None:
91116
suite_files = native.glob(["test/**/*_SUITE.erl"])
92117
for f in suite_files:
93118
sn = f.rpartition("/")[-1].replace(".erl", "")
94119
if not sn in suite_names:
95-
fail("A bazel rule has not been defined for {} (expected {} in {}".format(f, sn, suite_names))
120+
fail("A bazel rule has not been defined for {} (expected {} in {})".format(f, sn, suite_names))
121+
122+
def assert_suites2(suite_files = None):
123+
if suite_files == None:
124+
suite_files = native.glob(["test/**/*_SUITE.erl"])
125+
for f in suite_files:
126+
sn = f.rpartition("/")[-1].replace(".erl", "")
127+
if not sn in native.existing_rules():
128+
fail("A bazel rule has not been defined for {}".format(f))

0 commit comments

Comments
 (0)