Initial commit

This commit is contained in:
Alex Eagle 2021-11-08 06:20:26 -08:00
commit 394f900560
23 changed files with 998 additions and 0 deletions

13
.bazelrc Normal file
View File

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

7
.bazelversion Normal file
View File

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

7
.github/workflows/ci.bazelrc vendored Normal file
View File

@ -0,0 +1,7 @@
# Bazel settings to apply on CI only
# Included with a --bazelrc option in the call to bazel
build --announce_rc
test --test_output=errors
build --disk_cache=$HOME/.cache/bazel
build --repository_cache=$HOME/.cache/bazel-repo
test --test_env=XDG_CACHE_HOME

38
.github/workflows/ci.yaml vendored Normal file
View File

@ -0,0 +1,38 @@
name: CI
# Controls when the action will run.
on:
# Triggers the workflow on push or pull request events but only for the main branch
push:
branches: [main]
pull_request:
branches: [main]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
test:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- uses: bazelbuild/setup-bazelisk@v1
- name: Mount bazel action cache
uses: actions/cache@v2
with:
path: "~/.cache/bazel"
key: bazel
- name: Mount bazel repo cache
uses: actions/cache@v2
with:
path: "~/.cache/bazel-repo"
key: bazel-repo
- name: bazel test //...
env:
# Bazelisk will download bazel to here
XDG_CACHE_HOME: ~/.cache/bazel-repo
run: bazel --bazelrc=.github/workflows/ci.bazelrc --bazelrc=.bazelrc test //...

32
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,32 @@
# Cut a release whenever a new tag is pushed to the repo.
# You should use an annotated tag, like `git tag -a v1.2.3`
# and put the release notes into the commit message for the tag.
name: Release
on:
push:
tags:
- "v*.*.*"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- uses: bazelbuild/setup-bazelisk@v1
- name: check git state is clean
run: git status --porcelain
- name: bazel test //...
env:
# Bazelisk will download bazel to here
XDG_CACHE_HOME: ~/.cache/bazel-repo
run:
bazel --bazelrc=.github/workflows/ci.bazelrc --bazelrc=.bazelrc test
--config=release //...
- name: Release
uses: softprops/action-gh-release@v1
with:
prerelease: true
files: |
bazel-out/*/bin/path/to/some/output

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
bazel-*

26
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,26 @@
# See CONTRIBUTING.md for instructions.
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
# Commitizen runs in commit-msg stage
# but we don't want to run the other hooks on commit messages
default_stages: [commit]
repos:
# Check formatting and lint for starlark code
- repo: https://github.com/keith/pre-commit-buildifier
rev: 4.0.1.1
hooks:
- id: buildifier
- id: buildifier-lint
# Enforce that commit messages allow for later changelog generation
- repo: https://github.com/commitizen-tools/commitizen
rev: v2.18.0
hooks:
# Requires that commitizen is already installed
- id: commitizen
stages: [commit-msg]
- repo: https://github.com/pre-commit/mirrors-prettier
rev: "v2.4.0"
hooks:
- id: prettier

47
BUILD.bazel Normal file
View File

@ -0,0 +1,47 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
load("@rules_pkg//:pkg.bzl", "pkg_tar")
load(":version.bzl", "VERSION")
load("@bazel_gazelle//:def.bzl", "gazelle", "gazelle_binary")
gazelle_binary(
name = "gazelle_bin",
languages = ["@bazel_skylib//gazelle/bzl"],
)
gazelle(
name = "gazelle",
gazelle = "gazelle_bin",
)
# This declares the release artifact users
pkg_tar(
name = "rules_mylang-" + VERSION,
srcs = [
"LICENSE",
"README.md",
"version.bzl",
"//mylang:package_content",
],
extension = "tar.gz",
# It is all source code, so make it read-only.
mode = "0444",
# Make it owned by root so it does not have the uid of the CI robot.
owner = "0.0",
strip_prefix = ".",
)
bzl_library(
name = "internal_deps",
srcs = ["internal_deps.bzl"],
visibility = ["//visibility:public"],
deps = [
"@bazel_tools//tools/build_defs/repo:http.bzl",
"@bazel_tools//tools/build_defs/repo:utils.bzl",
],
)
bzl_library(
name = "version",
srcs = ["version.bzl"],
visibility = ["//visibility:public"],
)

38
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,38 @@
# How to Contribute
## Formatting
Starlark files should be formatted by buildifier.
We suggest using a pre-commit hook to automate this.
First [install pre-commit](https://pre-commit.com/#installation),
then run
```shell
pre-commit install
```
Otherwise later tooling on CI may yell at you about formatting/linting violations.
## Updating BUILD files
Some targets are generated from sources.
Currently this is just the `bzl_library` targets.
Run `bazel run //:gazelle` to keep them up-to-date.
## Using this as a development dependency of other rules
You'll commonly find that you develop in another WORKSPACE, such as
some other ruleset that depends on rules_mylang, or in a nested
WORKSPACE in the integration_tests folder.
To always tell Bazel to use this directory rather than some release
artifact or a version fetched from the internet, run this from this
directory:
```sh
OVERRIDE="--override_repository=rules_mylang=$(pwd)/rules_mylang"
echo "build $OVERRIDE" >> ~/.bazelrc
echo "query $OVERRIDE" >> ~/.bazelrc
```
This means that any usage of `@rules_mylang` on your system will point to this folder.

202
LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

55
README.md Normal file
View File

@ -0,0 +1,55 @@
# Template for Bazel rules
Copy this template to create a Bazel ruleset.
Features:
- follows the official style guide at https://docs.bazel.build/versions/main/skylark/deploying.html
- includes Bazel formatting as a pre-commit hook (using [buildifier])
- includes typical toolchain setup
- CI configured with GitHub Actions
- Release on GitHub Actions when pushing a tag
See https://docs.bazel.build/versions/main/skylark/deploying.html#readme
[buildifier]: https://github.com/bazelbuild/buildtools/tree/master/buildifier#readme
Ready to get started? Copy this repo, then
1. search for "com_myorg_rules_mylang" and replace with the name you'll use for your workspace
1. search for "mylang" and replace with the language/tool your rules are for
1. rename directory "mylang" similarly
1. run `pre-commit install` to get lints (see CONTRIBUTING.md)
1. if you don't need to fetch platform-dependent tools, then remove anything toolchain-related.
1. delete this section of the README (everything up to the SNIP).
---- SNIP ----
# Bazel rules for mylang
## Installation
Include this in your WORKSPACE file:
```starlark
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "com_myorg_rules_mylang",
url = "https://github.com/myorg/rules_mylang/releases/download/0.0.0/rules_mylang-0.0.0.tar.gz",
sha256 = "",
)
load("@com_myorg_rules_mylang//mylang:repositories.bzl", "mylang_rules_dependencies")
# This fetches the rules_mylang dependencies, which are:
# - bazel_skylib
# If you want to have a different version of some dependency,
# you should fetch it *before* calling this.
# Alternatively, you can skip calling this function, so long as you've
# already fetched these dependencies.
rules_mylang_dependencies()
```
> note, in the above, replace the version and sha256 with the one indicated
> in the release notes for rules_mylang
> In the future, our release automation should take care of this.

35
WORKSPACE Normal file
View File

@ -0,0 +1,35 @@
# Declare the local Bazel workspace.
# This is *not* included in the published distribution.
workspace(
# If your ruleset is "official"
# (i.e. is in the bazelbuild GitHub org)
# then this should just be named "rules_mylang"
# see https://docs.bazel.build/versions/main/skylark/deploying.html#workspace
name = "com_myorg_rules_mylang",
)
load(":internal_deps.bzl", "rules_mylang_internal_deps")
# Fetch deps needed only locally for development
rules_mylang_internal_deps()
load("//mylang:repositories.bzl", "mylang_register_toolchains", "rules_mylang_dependencies")
# Fetch our "runtime" dependencies which users need as well
rules_mylang_dependencies()
mylang_register_toolchains(
name = "mylang1_14",
mylang_version = "1.14.2",
)
############################################
# Gazelle, for generating bzl_library targets
load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies")
load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies")
go_rules_dependencies()
go_register_toolchains(version = "1.17.2")
gazelle_dependencies()

53
docs/BUILD.bazel Normal file
View File

@ -0,0 +1,53 @@
# These loads are in the docs/ package rather than anything users depend on
# so that the dependency on stardoc doesn't leak to them.
load("@bazel_skylib//rules:diff_test.bzl", "diff_test")
load("@bazel_skylib//rules:write_file.bzl", "write_file")
load("@io_bazel_stardoc//stardoc:stardoc.bzl", "stardoc")
# Dictionary mapping from the label of a starlark module
# (like //mylang:some.bzl) to the markdown file in this
# folder where the API docs are written.
_DOCS = {
"//mylang:defs.bzl": "rules.md",
}
[
stardoc(
name = md + "_gen",
out = md + "_",
input = tgt,
deps = [tgt.replace(".bzl", "")],
)
for [
tgt,
md,
] in _DOCS.items()
]
[
diff_test(
name = "check_" + md,
failure_message = "Run bazel run //docs:update",
file1 = md,
file2 = md + "_",
)
for md in _DOCS.values()
]
write_file(
name = "gen_update",
out = "update.sh",
content = [
"#!/usr/bin/env bash",
"cd $BUILD_WORKSPACE_DIRECTORY",
] + [
"cp -fv bazel-bin/docs/{0}_ docs/{0}".format(md)
for md in _DOCS.values()
],
)
sh_binary(
name = "update",
srcs = ["update.sh"],
data = [md + "_" for md in _DOCS.values()],
)

4
docs/rules.md Normal file
View File

@ -0,0 +1,4 @@
<!-- Generated with Stardoc: http://skydoc.bazel.build -->
Public API re-exports

74
internal_deps.bzl Normal file
View File

@ -0,0 +1,74 @@
"""Our "development" dependencies
Users should *not* need to install these. If users see a load()
statement from these, that's a bug in our distribution.
"""
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
def rules_mylang_internal_deps():
"Fetch deps needed for local development"
maybe(
http_archive,
name = "rules_pkg",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.2.4/rules_pkg-0.2.4.tar.gz",
"https://github.com/bazelbuild/rules_pkg/releases/download/0.2.4/rules_pkg-0.2.4.tar.gz",
],
sha256 = "4ba8f4ab0ff85f2484287ab06c0d871dcb31cc54d439457d28fd4ae14b18450a",
)
maybe(
http_archive,
name = "build_bazel_integration_testing",
urls = [
"https://github.com/bazelbuild/bazel-integration-testing/archive/165440b2dbda885f8d1ccb8d0f417e6cf8c54f17.zip",
],
strip_prefix = "bazel-integration-testing-165440b2dbda885f8d1ccb8d0f417e6cf8c54f17",
sha256 = "2401b1369ef44cc42f91dc94443ef491208dbd06da1e1e10b702d8c189f098e3",
)
maybe(
http_archive,
name = "io_bazel_rules_go",
sha256 = "2b1641428dff9018f9e85c0384f03ec6c10660d935b750e3fa1492a281a53b0f",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.29.0/rules_go-v0.29.0.zip",
"https://github.com/bazelbuild/rules_go/releases/download/v0.29.0/rules_go-v0.29.0.zip",
],
)
maybe(
http_archive,
name = "bazel_gazelle",
sha256 = "de69a09dc70417580aabf20a28619bb3ef60d038470c7cf8442fafcf627c21cb",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.24.0/bazel-gazelle-v0.24.0.tar.gz",
"https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.24.0/bazel-gazelle-v0.24.0.tar.gz",
],
)
# Override bazel_skylib distribution to fetch sources instead
# so that the gazelle extension is included
# see https://github.com/bazelbuild/bazel-skylib/issues/250
maybe(
http_archive,
name = "bazel_skylib",
sha256 = "07b4117379dde7ab382345c3b0f5edfc6b7cff6c93756eac63da121e0bbcc5de",
strip_prefix = "bazel-skylib-1.1.1",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/archive/1.1.1.tar.gz",
"https://github.com/bazelbuild/bazel-skylib/archive/1.1.1.tar.gz",
],
)
maybe(
http_archive,
name = "io_bazel_stardoc",
sha256 = "c9794dcc8026a30ff67cf7cf91ebe245ca294b20b071845d12c192afe243ad72",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/stardoc/releases/download/0.5.0/stardoc-0.5.0.tar.gz",
"https://github.com/bazelbuild/stardoc/releases/download/0.5.0/stardoc-0.5.0.tar.gz",
],
)

47
mylang/BUILD.bazel Normal file
View File

@ -0,0 +1,47 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
# For stardoc to reference the files
exports_files(["defs.bzl"])
# This is the target rule authors should put in their "toolchains"
# attribute in order to get a runtime for the correct platform.
# See https://docs.bazel.build/versions/main/toolchains.html#writing-rules-that-use-toolchains
toolchain_type(
name = "toolchain_type",
visibility = ["//visibility:public"],
)
filegroup(
name = "package_content",
srcs = glob([
"*.bzl",
"*.bazel",
]) + [
"//mylang/private:package_content",
],
visibility = ["//:__pkg__"],
)
bzl_library(
name = "defs",
srcs = ["defs.bzl"],
visibility = ["//visibility:public"],
)
bzl_library(
name = "repositories",
srcs = ["repositories.bzl"],
visibility = ["//visibility:public"],
deps = [
"//mylang/private:toolchains_repo",
"//mylang/private:versions",
"@bazel_tools//tools/build_defs/repo:http.bzl",
"@bazel_tools//tools/build_defs/repo:utils.bzl",
],
)
bzl_library(
name = "toolchain",
srcs = ["toolchain.bzl"],
visibility = ["//visibility:public"],
)

1
mylang/defs.bzl Normal file
View File

@ -0,0 +1 @@
"Public API re-exports"

View File

@ -0,0 +1,22 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
filegroup(
name = "package_content",
srcs = glob([
"*.bzl",
"*.bazel",
]),
visibility = ["//mylang:__pkg__"],
)
bzl_library(
name = "toolchains_repo",
srcs = ["toolchains_repo.bzl"],
visibility = ["//mylang:__subpackages__"],
)
bzl_library(
name = "versions",
srcs = ["versions.bzl"],
visibility = ["//mylang:__subpackages__"],
)

View File

@ -0,0 +1,113 @@
"""Create a repository to hold the toolchains
This follows guidance here:
https://docs.bazel.build/versions/main/skylark/deploying.html#registering-toolchains
"
Note that in order to resolve toolchains in the analysis phase
Bazel needs to analyze all toolchain targets that are registered.
Bazel will not need to analyze all targets referenced by toolchain.toolchain attribute.
If in order to register toolchains you need to perform complex computation in the repository,
consider splitting the repository with toolchain targets
from the repository with <LANG>_toolchain targets.
Former will be always fetched,
and the latter will only be fetched when user actually needs to build <LANG> code.
"
The "complex computation" in our case is simply downloading large artifacts.
This guidance tells us how to avoid that: we put the toolchain targets in the alias repository
with only the toolchain attribute pointing into the platform-specific repositories.
"""
# Add more platforms as needed to mirror all the binaries
# published by the upstream project.
PLATFORMS = {
"x86_64-apple-darwin": struct(
compatible_with = [
"@platforms//os:macos",
"@platforms//cpu:x86_64",
],
),
"aarch64-apple-darwin": struct(
compatible_with = [
"@platforms//os:macos",
"@platforms//cpu:aarch64",
],
),
"x86_64-unknown-linux-gnu": struct(
compatible_with = [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
],
),
"x86_64-pc-windows-msvc": struct(
compatible_with = [
"@platforms//os:windows",
"@platforms//cpu:x86_64",
],
),
}
def _toolchains_repo_impl(repository_ctx):
# Expose a concrete toolchain which is the result of Bazel resolving the toolchain
# for the execution or target platform.
# Workaround for https://github.com/bazelbuild/bazel/issues/14009
starlark_content = """# Generated by toolchains_repo.bzl
# Forward all the providers
def _resolved_toolchain_impl(ctx):
toolchain_info = ctx.toolchains["@com_myorg_rules_mylang//mylang:toolchain_type"]
return [
toolchain_info,
toolchain_info.default,
toolchain_info.mylanginfo,
toolchain_info.template_variables,
]
# Copied from java_toolchain_alias
# https://cs.opensource.google/bazel/bazel/+/master:tools/jdk/java_toolchain_alias.bzl
resolved_toolchain = rule(
implementation = _resolved_toolchain_impl,
toolchains = ["@com_myorg_rules_mylang//mylang:toolchain_type"],
incompatible_use_toolchain_transition = True,
)
"""
repository_ctx.file("defs.bzl", starlark_content)
build_content = """# Generated by toolchains_repo.bzl
#
# These can be registered in the workspace file or passed to --extra_toolchains flag.
# By default all these toolchains are registered by the mylang_register_toolchains macro
# so you don't normally need to interact with these targets.
load(":defs.bzl", "resolved_toolchain")
resolved_toolchain(name = "resolved_toolchain", visibility = ["//visibility:public"])
"""
for [platform, meta] in PLATFORMS.items():
build_content += """
toolchain(
name = "{platform}_toolchain",
exec_compatible_with = {compatible_with},
target_compatible_with = {compatible_with},
toolchain = "@{user_repository_name}_{platform}//:mylang_toolchain",
toolchain_type = "@com_myorg_rules_mylang//mylang:toolchain_type",
)
""".format(
platform = platform,
name = repository_ctx.attr.name,
user_repository_name = repository_ctx.attr.user_repository_name,
compatible_with = meta.compatible_with,
)
# Base BUILD file for this repository
repository_ctx.file("BUILD.bazel", build_content)
toolchains_repo = repository_rule(
_toolchains_repo_impl,
doc = """Creates a repository with toolchain definitions for all known platforms
which can be registered or selected.""",
attrs = {
"user_repository_name": attr.string(doc = "what the user chose for the base name"),
},
)

View File

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

88
mylang/repositories.bzl Normal file
View File

@ -0,0 +1,88 @@
"""Declare runtime dependencies
These are needed for local dev, and users must install them as well.
See https://docs.bazel.build/versions/main/skylark/deploying.html#dependencies
"""
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
load("//mylang/private:toolchains_repo.bzl", "PLATFORMS", "toolchains_repo")
load("//mylang/private:versions.bzl", "TOOL_VERSIONS")
# WARNING: any changes in this function may be BREAKING CHANGES for users
# because we'll fetch a dependency which may be different from one that
# they were previously fetching later in their WORKSPACE setup, and now
# ours took precedence. Such breakages are challenging for users, so any
# changes in this function should be marked as BREAKING in the commit message
# and released only in semver majors.
def rules_mylang_dependencies():
# The minimal version of bazel_skylib we require
maybe(
http_archive,
name = "bazel_skylib",
sha256 = "c6966ec828da198c5d9adbaa94c05e3a1c7f21bd012a0b29ba8ddbccb2c93b0d",
urls = [
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.1.1/bazel-skylib-1.1.1.tar.gz",
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.1.1/bazel-skylib-1.1.1.tar.gz",
],
)
_DOC = "TODO"
_ATTRS = {
"mylang_version": attr.string(mandatory = True, values = TOOL_VERSIONS.keys()),
"platform": attr.string(mandatory = True, values = PLATFORMS.keys()),
}
def _mylang_repo_impl(repository_ctx):
url = "https://github.com/someorg/someproject/releases/download/v{0}/mylang-{1}.zip".format(
repository_ctx.attr.mylang_version,
repository_ctx.attr.platform,
)
repository_ctx.download_and_extract(
url = url,
integrity = TOOL_VERSIONS[repository_ctx.attr.mylang_version][repository_ctx.attr.platform],
)
build_content = """#Generated by mylang/repositories.bzl
load("@com_myorg_rules_mylang//mylang:toolchain.bzl", "mylang_toolchain")
mylang_toolchain(name = "mylang_toolchain", target_tool = select({
"@bazel_tools//src/conditions:host_windows": "mylang_tool.exe",
"//conditions:default": "mylang_tool",
}),
)
"""
# Base BUILD file for this repository
repository_ctx.file("BUILD.bazel", build_content)
mylang_repositories = repository_rule(
_mylang_repo_impl,
doc = _DOC,
attrs = _ATTRS,
)
# Wrapper macro around everything above, this is the primary API
def mylang_register_toolchains(name, **kwargs):
"""Convenience macro for users which does typical setup.
- create a repository for each built-in platform like "mylang_linux_amd64" -
this repository is lazily fetched when node is needed for that platform.
- TODO: create a convenience repository for the host platform like "mylang_host"
- create a repository exposing toolchains for each platform like "mylang_platforms"
- register a toolchain pointing at each platform
Users can avoid this macro and do these steps themselves, if they want more control.
Args:
name: base name for all created repos, like "mylang1_14"
**kwargs: passed to each node_repositories call
"""
for platform in PLATFORMS.keys():
mylang_repositories(
name = name + "_" + platform,
platform = platform,
**kwargs
)
native.register_toolchains("@%s_toolchains//:%s_toolchain" % (name, platform))
toolchains_repo(
name = name + "_toolchains",
user_repository_name = name,
)

78
mylang/toolchain.bzl Normal file
View File

@ -0,0 +1,78 @@
"""This module implements the language-specific toolchain rule.
"""
MylangInfo = provider(
doc = "Information about how to invoke the tool executable.",
fields = {
"target_tool_path": "Path to the tool executable for the target platform.",
"tool_files": """Files required in runfiles to make the tool executable available.
May be empty if the target_tool_path points to a locally installed tool binary.""",
},
)
# Avoid using non-normalized paths (workspace/../other_workspace/path)
def _to_manifest_path(ctx, file):
if file.short_path.startswith("../"):
return "external/" + file.short_path[3:]
else:
return ctx.workspace_name + "/" + file.short_path
def _mylang_toolchain_impl(ctx):
if ctx.attr.target_tool and ctx.attr.target_tool_path:
fail("Can only set one of target_tool or target_tool_path but both were set.")
if not ctx.attr.target_tool and not ctx.attr.target_tool_path:
fail("Must set one of target_tool or target_tool_path.")
tool_files = []
target_tool_path = ctx.attr.target_tool_path
if ctx.attr.target_tool:
tool_files = ctx.attr.target_tool.files.to_list()
target_tool_path = _to_manifest_path(ctx, tool_files[0])
# Make the $(tool_BIN) variable available in places like genrules.
# See https://docs.bazel.build/versions/main/be/make-variables.html#custom_variables
template_variables = platform_common.TemplateVariableInfo({
"MYLANG_BIN": target_tool_path,
})
default = DefaultInfo(
files = depset(tool_files),
runfiles = ctx.runfiles(files = tool_files),
)
mylanginfo = MylangInfo(
target_tool_path = target_tool_path,
tool_files = tool_files,
)
# Export all the providers inside our ToolchainInfo
# so the resolved_toolchain rule can grab and re-export them.
toolchain_info = platform_common.ToolchainInfo(
mylanginfo = mylanginfo,
template_variables = template_variables,
default = default,
)
return [
default,
toolchain_info,
template_variables,
]
mylang_toolchain = rule(
implementation = _mylang_toolchain_impl,
attrs = {
"target_tool": attr.label(
doc = "A hermetically downloaded executable target for the target platform.",
mandatory = False,
allow_single_file = True,
),
"target_tool_path": attr.string(
doc = "Path to an existing executable for the target platform.",
mandatory = False,
),
},
doc = """Defines a mylang compiler/runtime toolchain.
For usage see https://docs.bazel.build/versions/main/toolchains.html#defining-toolchains.
""",
)

3
version.bzl Normal file
View File

@ -0,0 +1,3 @@
"Constants to track versioning info"
VERSION = "0.1.0"