Skip to content

Commit 19d29d2

Browse files
committedMar 18, 2022
Initial commit
0 parents  commit 19d29d2

26 files changed

+977
-0
lines changed
 

‎.bazelrc

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Bazel settings that apply to this repository.
2+
# Take care to document any settings that you expect users to apply.
3+
# Settings that apply only to CI are in .github/workflows/ci.bazelrc
4+
5+
6+
# Load any settings specific to the current user.
7+
# .bazelrc.user should appear in .gitignore so that settings are not shared with team members
8+
# This needs to be last statement in this
9+
# config, as the user configuration should be able to overwrite flags from this file.
10+
# See https://docs.bazel.build/versions/master/best-practices.html#bazelrc
11+
# (Note that we use .bazelrc.user so the file appears next to .bazelrc in directory listing,
12+
# rather than user.bazelrc as suggested in the Bazel docs)
13+
try-import %workspace%/.bazelrc.user

‎.bazelversion

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
5.0.0
2+
# The first line of this file is used by Bazelisk and Bazel to be sure
3+
# the right version of Bazel is used to build and test this repo.
4+
# This also defines which version is used on CI.
5+
#
6+
# Note that you should also run integration_tests against other Bazel
7+
# versions you support.

‎.github/workflows/ci.bazelrc

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# This file contains Bazel settings to apply on CI only.
2+
# It is referenced with a --bazelrc option in the call to bazel in ci.yaml
3+
4+
# Debug where options came from
5+
build --announce_rc
6+
# This directory is configured in GitHub actions to be persisted between runs.
7+
build --disk_cache=~/.cache/bazel
8+
build --repository_cache=~/.cache/bazel-repo
9+
# Don't rely on test logs being easily accessible from the test runner,
10+
# though it makes the log noisier.
11+
test --test_output=errors
12+
# Allows tests to run bazelisk-in-bazel, since this is the cache folder used
13+
test --test_env=XDG_CACHE_HOME

‎.github/workflows/ci.yaml

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: CI
2+
3+
# Controls when the action will run.
4+
on:
5+
# Triggers the workflow on push or pull request events but only for the main branch
6+
push:
7+
branches: [main]
8+
pull_request:
9+
branches: [main]
10+
11+
# Allows you to run this workflow manually from the Actions tab
12+
workflow_dispatch:
13+
14+
jobs:
15+
test:
16+
# The type of runner that the job will run on
17+
runs-on: ubuntu-latest
18+
19+
# Steps represent a sequence of tasks that will be executed as part of the job
20+
steps:
21+
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
22+
- uses: actions/checkout@v2
23+
# Cache build and external artifacts so that the next ci build is incremental.
24+
# Because github action caches cannot be updated after a build, we need to
25+
# store the contents of each build in a unique cache key, then fall back to loading
26+
# it on the next ci run. We use hashFiles(...) in the key and restore-keys- with
27+
# the prefix to load the most recent cache for the branch on a cache miss. You
28+
# should customize the contents of hashFiles to capture any bazel input sources,
29+
# although this doesn't need to be perfect. If none of the input sources change
30+
# then a cache hit will load an existing cache and bazel won't have to do any work.
31+
# In the case of a cache miss, you want the fallback cache to contain most of the
32+
# previously built artifacts to minimize build time. The more precise you are with
33+
# hashFiles sources the less work bazel will have to do.
34+
- name: Mount bazel caches
35+
uses: actions/cache@v2
36+
with:
37+
path: |
38+
"~/.cache/bazel"
39+
"~/.cache/bazel-repo"
40+
key: bazel-cache-${{ hashFiles('**/BUILD.bazel', '**/*.bzl', 'WORKSPACE') }}
41+
restore-keys: bazel-cache-
42+
- name: bazel test //...
43+
env:
44+
# Bazelisk will download bazel to here, ensure it is cached between runs.
45+
XDG_CACHE_HOME: ~/.cache/bazel-repo
46+
run: bazel --bazelrc=.github/workflows/ci.bazelrc --bazelrc=.bazelrc test //...

‎.github/workflows/release.yml

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Cut a release whenever a new tag is pushed to the repo.
2+
# You should use an annotated tag, like `git tag -a v1.2.3`
3+
# and put the release notes into the commit message for the tag.
4+
name: Release
5+
6+
on:
7+
push:
8+
tags:
9+
- "v*.*.*"
10+
11+
jobs:
12+
build:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: Checkout
16+
uses: actions/checkout@v2
17+
- name: Mount bazel caches
18+
uses: actions/cache@v2
19+
with:
20+
path: |
21+
"~/.cache/bazel"
22+
"~/.cache/bazel-repo"
23+
key: bazel-cache-${{ hashFiles('**/BUILD.bazel', '**/*.bzl', 'WORKSPACE') }}
24+
restore-keys: bazel-cache-
25+
- name: bazel test //...
26+
env:
27+
# Bazelisk will download bazel to here
28+
XDG_CACHE_HOME: ~/.cache/bazel-repo
29+
run: bazel --bazelrc=.github/workflows/ci.bazelrc --bazelrc=.bazelrc test //...
30+
- name: Prepare workspace snippet
31+
run: .github/workflows/workspace_snippet.sh ${{ env.GITHUB_REF_NAME }} > release_notes.txt
32+
- name: Release
33+
uses: softprops/action-gh-release@v1
34+
with:
35+
prerelease: true
36+
# Use GH feature to populate the changelog automatically
37+
generate_release_notes: true
38+
body_path: release_notes.txt
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/usr/bin/env bash
2+
3+
set -o errexit -o nounset -o pipefail
4+
5+
# Set by GH actions, see
6+
# https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables
7+
TAG=${GITHUB_REF_NAME}
8+
PREFIX="rules_mylang-${TAG:1}"
9+
SHA=$(git archive --format=tar --prefix=${PREFIX}/ ${TAG} | gzip | shasum -a 256 | awk '{print $1}')
10+
11+
cat << EOF
12+
WORKSPACE snippet:
13+
\`\`\`starlark
14+
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
15+
http_archive(
16+
name = "com_myorg_rules_mylang",
17+
sha256 = "${SHA}",
18+
strip_prefix = "${PREFIX}",
19+
url = "https://github.com/myorg/rules_mylang/archive/refs/tags/${TAG}.tar.gz",
20+
)
21+
22+
# Fetches the rules_mylang dependencies.
23+
# If you want to have a different version of some dependency,
24+
# you should fetch it *before* calling this.
25+
# Alternatively, you can skip calling this function, so long as you've
26+
# already fetched all the dependencies.
27+
load("@com_myorg_rules_mylang//mylang:repositories.bzl", "rules_mylang_dependencies")
28+
rules_mylang_dependencies()
29+
30+
\`\`\`
31+
EOF

‎.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
bazel-*
2+
.bazelrc.user

‎.pre-commit-config.yaml

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# See CONTRIBUTING.md for instructions.
2+
# See https://pre-commit.com for more information
3+
# See https://pre-commit.com/hooks.html for more hooks
4+
5+
# Commitizen runs in commit-msg stage
6+
# but we don't want to run the other hooks on commit messages
7+
default_stages: [commit]
8+
9+
repos:
10+
# Check formatting and lint for starlark code
11+
- repo: https://github.com/keith/pre-commit-buildifier
12+
rev: 4.0.1.1
13+
hooks:
14+
- id: buildifier
15+
- id: buildifier-lint
16+
# Enforce that commit messages allow for later changelog generation
17+
- repo: https://github.com/commitizen-tools/commitizen
18+
rev: v2.18.0
19+
hooks:
20+
# Requires that commitizen is already installed
21+
- id: commitizen
22+
stages: [commit-msg]
23+
- repo: https://github.com/pre-commit/mirrors-prettier
24+
rev: "v2.4.0"
25+
hooks:
26+
- id: prettier

‎.prettierignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
docs/*.md

‎BUILD.bazel

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
2+
load("@bazel_gazelle//:def.bzl", "gazelle", "gazelle_binary")
3+
4+
gazelle_binary(
5+
name = "gazelle_bin",
6+
languages = ["@bazel_skylib//gazelle/bzl"],
7+
)
8+
9+
gazelle(
10+
name = "gazelle",
11+
gazelle = "gazelle_bin",
12+
)
13+
14+
bzl_library(
15+
name = "internal_deps",
16+
srcs = ["internal_deps.bzl"],
17+
visibility = ["//visibility:public"],
18+
deps = [
19+
"@bazel_tools//tools/build_defs/repo:http.bzl",
20+
"@bazel_tools//tools/build_defs/repo:utils.bzl",
21+
],
22+
)

‎CONTRIBUTING.md

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# How to Contribute
2+
3+
## Formatting
4+
5+
Starlark files should be formatted by buildifier.
6+
We suggest using a pre-commit hook to automate this.
7+
First [install pre-commit](https://pre-commit.com/#installation),
8+
then run
9+
10+
```shell
11+
pre-commit install
12+
```
13+
14+
Otherwise later tooling on CI may yell at you about formatting/linting violations.
15+
16+
## Updating BUILD files
17+
18+
Some targets are generated from sources.
19+
Currently this is just the `bzl_library` targets.
20+
Run `bazel run //:gazelle` to keep them up-to-date.
21+
22+
## Using this as a development dependency of other rules
23+
24+
You'll commonly find that you develop in another WORKSPACE, such as
25+
some other ruleset that depends on rules_mylang, or in a nested
26+
WORKSPACE in the integration_tests folder.
27+
28+
To always tell Bazel to use this directory rather than some release
29+
artifact or a version fetched from the internet, run this from this
30+
directory:
31+
32+
```sh
33+
OVERRIDE="--override_repository=rules_mylang=$(pwd)/rules_mylang"
34+
echo "build $OVERRIDE" >> ~/.bazelrc
35+
echo "fetch $OVERRIDE" >> ~/.bazelrc
36+
echo "query $OVERRIDE" >> ~/.bazelrc
37+
```
38+
39+
This means that any usage of `@rules_mylang` on your system will point to this folder.
40+
41+
## Releasing
42+
43+
1. Determine the next release version, following semver (could automate in the future from changelog)
44+
1. Tag the repo and push it (or create a tag in GH UI)
45+
1. Watch the automation run on GitHub actions

‎LICENSE

+201
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
Apache License
2+
Version 2.0, January 2004
3+
http://www.apache.org/licenses/
4+
5+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6+
7+
1. Definitions.
8+
9+
"License" shall mean the terms and conditions for use, reproduction,
10+
and distribution as defined by Sections 1 through 9 of this document.
11+
12+
"Licensor" shall mean the copyright owner or entity authorized by
13+
the copyright owner that is granting the License.
14+
15+
"Legal Entity" shall mean the union of the acting entity and all
16+
other entities that control, are controlled by, or are under common
17+
control with that entity. For the purposes of this definition,
18+
"control" means (i) the power, direct or indirect, to cause the
19+
direction or management of such entity, whether by contract or
20+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
21+
outstanding shares, or (iii) beneficial ownership of such entity.
22+
23+
"You" (or "Your") shall mean an individual or Legal Entity
24+
exercising permissions granted by this License.
25+
26+
"Source" form shall mean the preferred form for making modifications,
27+
including but not limited to software source code, documentation
28+
source, and configuration files.
29+
30+
"Object" form shall mean any form resulting from mechanical
31+
transformation or translation of a Source form, including but
32+
not limited to compiled object code, generated documentation,
33+
and conversions to other media types.
34+
35+
"Work" shall mean the work of authorship, whether in Source or
36+
Object form, made available under the License, as indicated by a
37+
copyright notice that is included in or attached to the work
38+
(an example is provided in the Appendix below).
39+
40+
"Derivative Works" shall mean any work, whether in Source or Object
41+
form, that is based on (or derived from) the Work and for which the
42+
editorial revisions, annotations, elaborations, or other modifications
43+
represent, as a whole, an original work of authorship. For the purposes
44+
of this License, Derivative Works shall not include works that remain
45+
separable from, or merely link (or bind by name) to the interfaces of,
46+
the Work and Derivative Works thereof.
47+
48+
"Contribution" shall mean any work of authorship, including
49+
the original version of the Work and any modifications or additions
50+
to that Work or Derivative Works thereof, that is intentionally
51+
submitted to Licensor for inclusion in the Work by the copyright owner
52+
or by an individual or Legal Entity authorized to submit on behalf of
53+
the copyright owner. For the purposes of this definition, "submitted"
54+
means any form of electronic, verbal, or written communication sent
55+
to the Licensor or its representatives, including but not limited to
56+
communication on electronic mailing lists, source code control systems,
57+
and issue tracking systems that are managed by, or on behalf of, the
58+
Licensor for the purpose of discussing and improving the Work, but
59+
excluding communication that is conspicuously marked or otherwise
60+
designated in writing by the copyright owner as "Not a Contribution."
61+
62+
"Contributor" shall mean Licensor and any individual or Legal Entity
63+
on behalf of whom a Contribution has been received by Licensor and
64+
subsequently incorporated within the Work.
65+
66+
2. Grant of Copyright License. Subject to the terms and conditions of
67+
this License, each Contributor hereby grants to You a perpetual,
68+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69+
copyright license to reproduce, prepare Derivative Works of,
70+
publicly display, publicly perform, sublicense, and distribute the
71+
Work and such Derivative Works in Source or Object form.
72+
73+
3. Grant of Patent License. Subject to the terms and conditions of
74+
this License, each Contributor hereby grants to You a perpetual,
75+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76+
(except as stated in this section) patent license to make, have made,
77+
use, offer to sell, sell, import, and otherwise transfer the Work,
78+
where such license applies only to those patent claims licensable
79+
by such Contributor that are necessarily infringed by their
80+
Contribution(s) alone or by combination of their Contribution(s)
81+
with the Work to which such Contribution(s) was submitted. If You
82+
institute patent litigation against any entity (including a
83+
cross-claim or counterclaim in a lawsuit) alleging that the Work
84+
or a Contribution incorporated within the Work constitutes direct
85+
or contributory patent infringement, then any patent licenses
86+
granted to You under this License for that Work shall terminate
87+
as of the date such litigation is filed.
88+
89+
4. Redistribution. You may reproduce and distribute copies of the
90+
Work or Derivative Works thereof in any medium, with or without
91+
modifications, and in Source or Object form, provided that You
92+
meet the following conditions:
93+
94+
(a) You must give any other recipients of the Work or
95+
Derivative Works a copy of this License; and
96+
97+
(b) You must cause any modified files to carry prominent notices
98+
stating that You changed the files; and
99+
100+
(c) You must retain, in the Source form of any Derivative Works
101+
that You distribute, all copyright, patent, trademark, and
102+
attribution notices from the Source form of the Work,
103+
excluding those notices that do not pertain to any part of
104+
the Derivative Works; and
105+
106+
(d) If the Work includes a "NOTICE" text file as part of its
107+
distribution, then any Derivative Works that You distribute must
108+
include a readable copy of the attribution notices contained
109+
within such NOTICE file, excluding those notices that do not
110+
pertain to any part of the Derivative Works, in at least one
111+
of the following places: within a NOTICE text file distributed
112+
as part of the Derivative Works; within the Source form or
113+
documentation, if provided along with the Derivative Works; or,
114+
within a display generated by the Derivative Works, if and
115+
wherever such third-party notices normally appear. The contents
116+
of the NOTICE file are for informational purposes only and
117+
do not modify the License. You may add Your own attribution
118+
notices within Derivative Works that You distribute, alongside
119+
or as an addendum to the NOTICE text from the Work, provided
120+
that such additional attribution notices cannot be construed
121+
as modifying the License.
122+
123+
You may add Your own copyright statement to Your modifications and
124+
may provide additional or different license terms and conditions
125+
for use, reproduction, or distribution of Your modifications, or
126+
for any such Derivative Works as a whole, provided Your use,
127+
reproduction, and distribution of the Work otherwise complies with
128+
the conditions stated in this License.
129+
130+
5. Submission of Contributions. Unless You explicitly state otherwise,
131+
any Contribution intentionally submitted for inclusion in the Work
132+
by You to the Licensor shall be under the terms and conditions of
133+
this License, without any additional terms or conditions.
134+
Notwithstanding the above, nothing herein shall supersede or modify
135+
the terms of any separate license agreement you may have executed
136+
with Licensor regarding such Contributions.
137+
138+
6. Trademarks. This License does not grant permission to use the trade
139+
names, trademarks, service marks, or product names of the Licensor,
140+
except as required for reasonable and customary use in describing the
141+
origin of the Work and reproducing the content of the NOTICE file.
142+
143+
7. Disclaimer of Warranty. Unless required by applicable law or
144+
agreed to in writing, Licensor provides the Work (and each
145+
Contributor provides its Contributions) on an "AS IS" BASIS,
146+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147+
implied, including, without limitation, any warranties or conditions
148+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149+
PARTICULAR PURPOSE. You are solely responsible for determining the
150+
appropriateness of using or redistributing the Work and assume any
151+
risks associated with Your exercise of permissions under this License.
152+
153+
8. Limitation of Liability. In no event and under no legal theory,
154+
whether in tort (including negligence), contract, or otherwise,
155+
unless required by applicable law (such as deliberate and grossly
156+
negligent acts) or agreed to in writing, shall any Contributor be
157+
liable to You for damages, including any direct, indirect, special,
158+
incidental, or consequential damages of any character arising as a
159+
result of this License or out of the use or inability to use the
160+
Work (including but not limited to damages for loss of goodwill,
161+
work stoppage, computer failure or malfunction, or any and all
162+
other commercial damages or losses), even if such Contributor
163+
has been advised of the possibility of such damages.
164+
165+
9. Accepting Warranty or Additional Liability. While redistributing
166+
the Work or Derivative Works thereof, You may choose to offer,
167+
and charge a fee for, acceptance of support, warranty, indemnity,
168+
or other liability obligations and/or rights consistent with this
169+
License. However, in accepting such obligations, You may act only
170+
on Your own behalf and on Your sole responsibility, not on behalf
171+
of any other Contributor, and only if You agree to indemnify,
172+
defend, and hold each Contributor harmless for any liability
173+
incurred by, or claims asserted against, such Contributor by reason
174+
of your accepting any such warranty or additional liability.
175+
176+
END OF TERMS AND CONDITIONS
177+
178+
APPENDIX: How to apply the Apache License to your work.
179+
180+
To apply the Apache License to your work, attach the following
181+
boilerplate notice, with the fields enclosed by brackets "[]"
182+
replaced with your own identifying information. (Don't include
183+
the brackets!) The text should be enclosed in the appropriate
184+
comment syntax for the file format. We also recommend that a
185+
file or class name and description of purpose be included on the
186+
same "printed page" as the copyright notice for easier
187+
identification within third-party archives.
188+
189+
Copyright [yyyy] [name of copyright owner]
190+
191+
Licensed under the Apache License, Version 2.0 (the "License");
192+
you may not use this file except in compliance with the License.
193+
You may obtain a copy of the License at
194+
195+
http://www.apache.org/licenses/LICENSE-2.0
196+
197+
Unless required by applicable law or agreed to in writing, software
198+
distributed under the License is distributed on an "AS IS" BASIS,
199+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200+
See the License for the specific language governing permissions and
201+
limitations under the License.

‎README.md

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Template for Bazel rules
2+
3+
Copy this template to create a Bazel ruleset.
4+
5+
Features:
6+
7+
- follows the official style guide at https://docs.bazel.build/versions/main/skylark/deploying.html
8+
- includes Bazel formatting as a pre-commit hook (using [buildifier])
9+
- includes stardoc API documentation generator
10+
- includes typical toolchain setup
11+
- CI configured with GitHub Actions
12+
- Release on GitHub Actions when pushing a tag
13+
14+
See https://docs.bazel.build/versions/main/skylark/deploying.html#readme
15+
16+
[buildifier]: https://github.com/bazelbuild/buildtools/tree/master/buildifier#readme
17+
18+
Ready to get started? Copy this repo, then
19+
20+
1. search for "com_myorg_rules_mylang" and replace with the name you'll use for your workspace
21+
1. search for "myorg" and replace with GitHub org
22+
1. search for "mylang" and replace with the language/tool your rules are for
23+
1. rename directory "mylang" similarly
24+
1. run `pre-commit install` to get lints (see CONTRIBUTING.md)
25+
1. if you don't need to fetch platform-dependent tools, then remove anything toolchain-related.
26+
1. update the `actions/cache@v2` bazel cache key in [.github/workflows/ci.yaml](.github/workflows/ci.yaml) and [.github/workflows/release.yml](.github/workflows/release.yml) to be a hash of your source files.
27+
1. delete this section of the README (everything up to the SNIP).
28+
29+
---- SNIP ----
30+
31+
# Bazel rules for mylang
32+
33+
## Installation
34+
35+
From the release you wish to use:
36+
<https://github.com/myorg/rules_mylang/releases>
37+
copy the WORKSPACE snippet into your `WORKSPACE` file.

‎WORKSPACE

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Declare the local Bazel workspace.
2+
workspace(
3+
# If your ruleset is "official"
4+
# (i.e. is in the bazelbuild GitHub org)
5+
# then this should just be named "rules_mylang"
6+
# see https://docs.bazel.build/versions/main/skylark/deploying.html#workspace
7+
name = "com_myorg_rules_mylang",
8+
)
9+
10+
load(":internal_deps.bzl", "rules_mylang_internal_deps")
11+
12+
# Fetch deps needed only locally for development
13+
rules_mylang_internal_deps()
14+
15+
load("//mylang:repositories.bzl", "mylang_register_toolchains", "rules_mylang_dependencies")
16+
17+
# Fetch dependencies which users need as well
18+
rules_mylang_dependencies()
19+
20+
mylang_register_toolchains(
21+
name = "mylang1_14",
22+
mylang_version = "1.14.2",
23+
)
24+
25+
# For running our own unit tests
26+
load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
27+
28+
bazel_skylib_workspace()
29+
30+
############################################
31+
# Gazelle, for generating bzl_library targets
32+
load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies")
33+
load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies")
34+
35+
go_rules_dependencies()
36+
37+
go_register_toolchains(version = "1.17.2")
38+
39+
gazelle_dependencies()

‎docs/BUILD.bazel

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# This load statement must be in the docs/ package rather than anything users depend on
2+
# so that the dependency on stardoc doesn't leak to them.
3+
load("@aspect_bazel_lib//lib:docs.bzl", "stardoc_with_diff_test", "update_docs")
4+
5+
stardoc_with_diff_test(
6+
bzl_library_target = "//mylang:defs",
7+
out_label = "//docs:rules.md",
8+
)
9+
10+
update_docs(
11+
name = "update",
12+
docs_folder = "docs",
13+
)

‎docs/rules.md

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<!-- Generated with Stardoc: http://skydoc.bazel.build -->
2+
3+
Public API re-exports
4+

‎internal_deps.bzl

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
"""Our "development" dependencies
2+
3+
Users should *not* need to install these. If users see a load()
4+
statement from these, that's a bug in our distribution.
5+
"""
6+
7+
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
8+
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
9+
10+
def rules_mylang_internal_deps():
11+
"Fetch deps needed for local development"
12+
maybe(
13+
http_archive,
14+
name = "build_bazel_integration_testing",
15+
urls = [
16+
"https://github.com/bazelbuild/bazel-integration-testing/archive/165440b2dbda885f8d1ccb8d0f417e6cf8c54f17.zip",
17+
],
18+
strip_prefix = "bazel-integration-testing-165440b2dbda885f8d1ccb8d0f417e6cf8c54f17",
19+
sha256 = "2401b1369ef44cc42f91dc94443ef491208dbd06da1e1e10b702d8c189f098e3",
20+
)
21+
22+
maybe(
23+
http_archive,
24+
name = "io_bazel_rules_go",
25+
sha256 = "2b1641428dff9018f9e85c0384f03ec6c10660d935b750e3fa1492a281a53b0f",
26+
urls = [
27+
"https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.29.0/rules_go-v0.29.0.zip",
28+
"https://github.com/bazelbuild/rules_go/releases/download/v0.29.0/rules_go-v0.29.0.zip",
29+
],
30+
)
31+
32+
maybe(
33+
http_archive,
34+
name = "bazel_gazelle",
35+
sha256 = "de69a09dc70417580aabf20a28619bb3ef60d038470c7cf8442fafcf627c21cb",
36+
urls = [
37+
"https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.24.0/bazel-gazelle-v0.24.0.tar.gz",
38+
"https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.24.0/bazel-gazelle-v0.24.0.tar.gz",
39+
],
40+
)
41+
42+
# Override bazel_skylib distribution to fetch sources instead
43+
# so that the gazelle extension is included
44+
# see https://github.com/bazelbuild/bazel-skylib/issues/250
45+
maybe(
46+
http_archive,
47+
name = "bazel_skylib",
48+
sha256 = "07b4117379dde7ab382345c3b0f5edfc6b7cff6c93756eac63da121e0bbcc5de",
49+
strip_prefix = "bazel-skylib-1.1.1",
50+
urls = [
51+
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/archive/1.1.1.tar.gz",
52+
"https://github.com/bazelbuild/bazel-skylib/archive/refs/tags/1.1.1.tar.gz",
53+
],
54+
)
55+
56+
maybe(
57+
http_archive,
58+
name = "io_bazel_stardoc",
59+
sha256 = "c9794dcc8026a30ff67cf7cf91ebe245ca294b20b071845d12c192afe243ad72",
60+
urls = [
61+
"https://mirror.bazel.build/github.com/bazelbuild/stardoc/releases/download/0.5.0/stardoc-0.5.0.tar.gz",
62+
"https://github.com/bazelbuild/stardoc/releases/download/0.5.0/stardoc-0.5.0.tar.gz",
63+
],
64+
)
65+
66+
maybe(
67+
http_archive,
68+
name = "aspect_bazel_lib",
69+
sha256 = "8c8cf0554376746e2451de85c4a7670cc8d7400c1f091574c1c1ed2a65021a4c",
70+
url = "https://github.com/aspect-build/bazel-lib/releases/download/v0.2.6/bazel_lib-0.2.6.tar.gz",
71+
)

‎mylang/BUILD.bazel

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
2+
3+
# For stardoc to reference the files
4+
exports_files(["defs.bzl"])
5+
6+
# This is the target rule authors should put in their "toolchains"
7+
# attribute in order to get a runtime for the correct platform.
8+
# See https://docs.bazel.build/versions/main/toolchains.html#writing-rules-that-use-toolchains
9+
toolchain_type(
10+
name = "toolchain_type",
11+
visibility = ["//visibility:public"],
12+
)
13+
14+
bzl_library(
15+
name = "repositories",
16+
srcs = ["repositories.bzl"],
17+
visibility = ["//visibility:public"],
18+
deps = [
19+
"//mylang/private:toolchains_repo",
20+
"//mylang/private:versions",
21+
"@bazel_tools//tools/build_defs/repo:http.bzl",
22+
"@bazel_tools//tools/build_defs/repo:utils.bzl",
23+
],
24+
)
25+
26+
bzl_library(
27+
name = "defs",
28+
srcs = ["defs.bzl"],
29+
visibility = ["//visibility:public"],
30+
)
31+
32+
bzl_library(
33+
name = "toolchain",
34+
srcs = ["toolchain.bzl"],
35+
visibility = ["//visibility:public"],
36+
)

‎mylang/defs.bzl

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"Public API re-exports"

‎mylang/private/BUILD.bazel

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
2+
3+
bzl_library(
4+
name = "toolchains_repo",
5+
srcs = ["toolchains_repo.bzl"],
6+
visibility = ["//mylang:__subpackages__"],
7+
)
8+
9+
bzl_library(
10+
name = "versions",
11+
srcs = ["versions.bzl"],
12+
visibility = ["//mylang:__subpackages__"],
13+
)

‎mylang/private/toolchains_repo.bzl

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
"""Create a repository to hold the toolchains
2+
3+
This follows guidance here:
4+
https://docs.bazel.build/versions/main/skylark/deploying.html#registering-toolchains
5+
"
6+
Note that in order to resolve toolchains in the analysis phase
7+
Bazel needs to analyze all toolchain targets that are registered.
8+
Bazel will not need to analyze all targets referenced by toolchain.toolchain attribute.
9+
If in order to register toolchains you need to perform complex computation in the repository,
10+
consider splitting the repository with toolchain targets
11+
from the repository with <LANG>_toolchain targets.
12+
Former will be always fetched,
13+
and the latter will only be fetched when user actually needs to build <LANG> code.
14+
"
15+
The "complex computation" in our case is simply downloading large artifacts.
16+
This guidance tells us how to avoid that: we put the toolchain targets in the alias repository
17+
with only the toolchain attribute pointing into the platform-specific repositories.
18+
"""
19+
20+
# Add more platforms as needed to mirror all the binaries
21+
# published by the upstream project.
22+
PLATFORMS = {
23+
"x86_64-apple-darwin": struct(
24+
compatible_with = [
25+
"@platforms//os:macos",
26+
"@platforms//cpu:x86_64",
27+
],
28+
),
29+
"aarch64-apple-darwin": struct(
30+
compatible_with = [
31+
"@platforms//os:macos",
32+
"@platforms//cpu:aarch64",
33+
],
34+
),
35+
"x86_64-unknown-linux-gnu": struct(
36+
compatible_with = [
37+
"@platforms//os:linux",
38+
"@platforms//cpu:x86_64",
39+
],
40+
),
41+
"x86_64-pc-windows-msvc": struct(
42+
compatible_with = [
43+
"@platforms//os:windows",
44+
"@platforms//cpu:x86_64",
45+
],
46+
),
47+
}
48+
49+
def _toolchains_repo_impl(repository_ctx):
50+
# Expose a concrete toolchain which is the result of Bazel resolving the toolchain
51+
# for the execution or target platform.
52+
# Workaround for https://github.com/bazelbuild/bazel/issues/14009
53+
starlark_content = """# Generated by toolchains_repo.bzl
54+
55+
# Forward all the providers
56+
def _resolved_toolchain_impl(ctx):
57+
toolchain_info = ctx.toolchains["@com_myorg_rules_mylang//mylang:toolchain_type"]
58+
return [
59+
toolchain_info,
60+
toolchain_info.default,
61+
toolchain_info.mylanginfo,
62+
toolchain_info.template_variables,
63+
]
64+
65+
# Copied from java_toolchain_alias
66+
# https://cs.opensource.google/bazel/bazel/+/master:tools/jdk/java_toolchain_alias.bzl
67+
resolved_toolchain = rule(
68+
implementation = _resolved_toolchain_impl,
69+
toolchains = ["@com_myorg_rules_mylang//mylang:toolchain_type"],
70+
incompatible_use_toolchain_transition = True,
71+
)
72+
"""
73+
repository_ctx.file("defs.bzl", starlark_content)
74+
75+
build_content = """# Generated by toolchains_repo.bzl
76+
#
77+
# These can be registered in the workspace file or passed to --extra_toolchains flag.
78+
# By default all these toolchains are registered by the mylang_register_toolchains macro
79+
# so you don't normally need to interact with these targets.
80+
81+
load(":defs.bzl", "resolved_toolchain")
82+
83+
resolved_toolchain(name = "resolved_toolchain", visibility = ["//visibility:public"])
84+
85+
"""
86+
87+
for [platform, meta] in PLATFORMS.items():
88+
build_content += """
89+
# Declare a toolchain Bazel will select for running the tool in an action
90+
# on the execution platform.
91+
toolchain(
92+
name = "{platform}_toolchain",
93+
exec_compatible_with = {compatible_with},
94+
toolchain = "@{user_repository_name}_{platform}//:mylang_toolchain",
95+
toolchain_type = "@com_myorg_rules_mylang//mylang:toolchain_type",
96+
)
97+
""".format(
98+
platform = platform,
99+
name = repository_ctx.attr.name,
100+
user_repository_name = repository_ctx.attr.user_repository_name,
101+
compatible_with = meta.compatible_with,
102+
)
103+
104+
# Base BUILD file for this repository
105+
repository_ctx.file("BUILD.bazel", build_content)
106+
107+
toolchains_repo = repository_rule(
108+
_toolchains_repo_impl,
109+
doc = """Creates a repository with toolchain definitions for all known platforms
110+
which can be registered or selected.""",
111+
attrs = {
112+
"user_repository_name": attr.string(doc = "what the user chose for the base name"),
113+
},
114+
)

‎mylang/private/versions.bzl

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""Mirror of release info
2+
3+
TODO: generate this file from GitHub API"""
4+
5+
# The integrity hashes can be computed with
6+
# shasum -b -a 384 [downloaded file] | awk '{ print $1 }' | xxd -r -p | base64
7+
TOOL_VERSIONS = {
8+
"1.14.2": {
9+
"x86_64-apple-darwin": "sha384-ws4+rANvv0YxM1SgIBUXSG9jT8dKw83nls6R5qYkEKzPUB+viBIEozSsyq2e6i+f",
10+
"aarch64-apple-darwin": "sha384-HcvJbxoJtGSavkGu0e7CyD00cBlmDb0TBWJ4JSaNa70zuU3N7XlMOYm3bbQcAv2U",
11+
"x86_64-pc-windows-msvc": "sha384-35YN6TKpT0L9qyRBmq48NucvyXEtHnkeC+txf2YZmmJTmOzrAKREA74BA0EZvpar",
12+
"x86_64-unknown-linux-gnu": "sha384-QgGOwTaetxY0h5HWCKc/3ZtBs4N/fgaaORthn7UcEv++Idm9W+ntCCZRwvBdwHPD",
13+
},
14+
}

‎mylang/repositories.bzl

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
"""Declare runtime dependencies
2+
3+
These are needed for local dev, and users must install them as well.
4+
See https://docs.bazel.build/versions/main/skylark/deploying.html#dependencies
5+
"""
6+
7+
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
8+
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
9+
load("//mylang/private:toolchains_repo.bzl", "PLATFORMS", "toolchains_repo")
10+
load("//mylang/private:versions.bzl", "TOOL_VERSIONS")
11+
12+
# WARNING: any changes in this function may be BREAKING CHANGES for users
13+
# because we'll fetch a dependency which may be different from one that
14+
# they were previously fetching later in their WORKSPACE setup, and now
15+
# ours took precedence. Such breakages are challenging for users, so any
16+
# changes in this function should be marked as BREAKING in the commit message
17+
# and released only in semver majors.
18+
def rules_mylang_dependencies():
19+
# The minimal version of bazel_skylib we require
20+
maybe(
21+
http_archive,
22+
name = "bazel_skylib",
23+
sha256 = "c6966ec828da198c5d9adbaa94c05e3a1c7f21bd012a0b29ba8ddbccb2c93b0d",
24+
urls = [
25+
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.1.1/bazel-skylib-1.1.1.tar.gz",
26+
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.1.1/bazel-skylib-1.1.1.tar.gz",
27+
],
28+
)
29+
30+
########
31+
# Remaining content of the file is only used to support toolchains.
32+
########
33+
_DOC = "Fetch external tools needed for mylang toolchain"
34+
_ATTRS = {
35+
"mylang_version": attr.string(mandatory = True, values = TOOL_VERSIONS.keys()),
36+
"platform": attr.string(mandatory = True, values = PLATFORMS.keys()),
37+
}
38+
39+
def _mylang_repo_impl(repository_ctx):
40+
url = "https://github.com/someorg/someproject/releases/download/v{0}/mylang-{1}.zip".format(
41+
repository_ctx.attr.mylang_version,
42+
repository_ctx.attr.platform,
43+
)
44+
repository_ctx.download_and_extract(
45+
url = url,
46+
integrity = TOOL_VERSIONS[repository_ctx.attr.mylang_version][repository_ctx.attr.platform],
47+
)
48+
build_content = """#Generated by mylang/repositories.bzl
49+
load("@com_myorg_rules_mylang//mylang:toolchain.bzl", "mylang_toolchain")
50+
mylang_toolchain(name = "mylang_toolchain", target_tool = select({
51+
"@bazel_tools//src/conditions:host_windows": "mylang_tool.exe",
52+
"//conditions:default": "mylang_tool",
53+
}),
54+
)
55+
"""
56+
57+
# Base BUILD file for this repository
58+
repository_ctx.file("BUILD.bazel", build_content)
59+
60+
mylang_repositories = repository_rule(
61+
_mylang_repo_impl,
62+
doc = _DOC,
63+
attrs = _ATTRS,
64+
)
65+
66+
# Wrapper macro around everything above, this is the primary API
67+
def mylang_register_toolchains(name, **kwargs):
68+
"""Convenience macro for users which does typical setup.
69+
70+
- create a repository for each built-in platform like "mylang_linux_amd64" -
71+
this repository is lazily fetched when node is needed for that platform.
72+
- TODO: create a convenience repository for the host platform like "mylang_host"
73+
- create a repository exposing toolchains for each platform like "mylang_platforms"
74+
- register a toolchain pointing at each platform
75+
Users can avoid this macro and do these steps themselves, if they want more control.
76+
Args:
77+
name: base name for all created repos, like "mylang1_14"
78+
**kwargs: passed to each node_repositories call
79+
"""
80+
for platform in PLATFORMS.keys():
81+
mylang_repositories(
82+
name = name + "_" + platform,
83+
platform = platform,
84+
**kwargs
85+
)
86+
native.register_toolchains("@%s_toolchains//:%s_toolchain" % (name, platform))
87+
88+
toolchains_repo(
89+
name = name + "_toolchains",
90+
user_repository_name = name,
91+
)

‎mylang/tests/BUILD.bazel

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
load(":versions_test.bzl", "versions_test_suite")
2+
3+
versions_test_suite(name = "versions_test")

‎mylang/tests/versions_test.bzl

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
"""Unit tests for starlark helpers
2+
See https://docs.bazel.build/versions/main/skylark/testing.html#for-testing-starlark-utilities
3+
"""
4+
5+
load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest")
6+
load("//mylang/private:versions.bzl", "TOOL_VERSIONS")
7+
8+
def _smoke_test_impl(ctx):
9+
env = unittest.begin(ctx)
10+
asserts.equals(env, "1.14.2", TOOL_VERSIONS.keys()[0])
11+
return unittest.end(env)
12+
13+
# The unittest library requires that we export the test cases as named test rules,
14+
# but their names are arbitrary and don't appear anywhere.
15+
_t0_test = unittest.make(_smoke_test_impl)
16+
17+
def versions_test_suite(name):
18+
unittest.suite(name, _t0_test)

‎mylang/toolchain.bzl

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
"""This module implements the language-specific toolchain rule.
2+
"""
3+
4+
MylangInfo = provider(
5+
doc = "Information about how to invoke the tool executable.",
6+
fields = {
7+
"target_tool_path": "Path to the tool executable for the target platform.",
8+
"tool_files": """Files required in runfiles to make the tool executable available.
9+
10+
May be empty if the target_tool_path points to a locally installed tool binary.""",
11+
},
12+
)
13+
14+
# Avoid using non-normalized paths (workspace/../other_workspace/path)
15+
def _to_manifest_path(ctx, file):
16+
if file.short_path.startswith("../"):
17+
return "external/" + file.short_path[3:]
18+
else:
19+
return ctx.workspace_name + "/" + file.short_path
20+
21+
def _mylang_toolchain_impl(ctx):
22+
if ctx.attr.target_tool and ctx.attr.target_tool_path:
23+
fail("Can only set one of target_tool or target_tool_path but both were set.")
24+
if not ctx.attr.target_tool and not ctx.attr.target_tool_path:
25+
fail("Must set one of target_tool or target_tool_path.")
26+
27+
tool_files = []
28+
target_tool_path = ctx.attr.target_tool_path
29+
30+
if ctx.attr.target_tool:
31+
tool_files = ctx.attr.target_tool.files.to_list()
32+
target_tool_path = _to_manifest_path(ctx, tool_files[0])
33+
34+
# Make the $(tool_BIN) variable available in places like genrules.
35+
# See https://docs.bazel.build/versions/main/be/make-variables.html#custom_variables
36+
template_variables = platform_common.TemplateVariableInfo({
37+
"MYLANG_BIN": target_tool_path,
38+
})
39+
default = DefaultInfo(
40+
files = depset(tool_files),
41+
runfiles = ctx.runfiles(files = tool_files),
42+
)
43+
mylanginfo = MylangInfo(
44+
target_tool_path = target_tool_path,
45+
tool_files = tool_files,
46+
)
47+
48+
# Export all the providers inside our ToolchainInfo
49+
# so the resolved_toolchain rule can grab and re-export them.
50+
toolchain_info = platform_common.ToolchainInfo(
51+
mylanginfo = mylanginfo,
52+
template_variables = template_variables,
53+
default = default,
54+
)
55+
return [
56+
default,
57+
toolchain_info,
58+
template_variables,
59+
]
60+
61+
mylang_toolchain = rule(
62+
implementation = _mylang_toolchain_impl,
63+
attrs = {
64+
"target_tool": attr.label(
65+
doc = "A hermetically downloaded executable target for the target platform.",
66+
mandatory = False,
67+
allow_single_file = True,
68+
),
69+
"target_tool_path": attr.string(
70+
doc = "Path to an existing executable for the target platform.",
71+
mandatory = False,
72+
),
73+
},
74+
doc = """Defines a mylang compiler/runtime toolchain.
75+
76+
For usage see https://docs.bazel.build/versions/main/toolchains.html#defining-toolchains.
77+
""",
78+
)

0 commit comments

Comments
 (0)
Please sign in to comment.