Skip to content

Commit 8eccc99

Browse files
committed
Initial commit
0 parents  commit 8eccc99

20 files changed

+2038
-0
lines changed

.bazelignore

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name: CI
2+
on:
3+
push:
4+
jobs:
5+
test:
6+
runs-on: ubuntu-latest
7+
steps:
8+
- uses: actions/checkout@v4
9+
- run: bazel test //...
10+
working-directory: example

.github/workflows/ci.yml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
name: CI
2+
on:
3+
push:
4+
branches: [main]
5+
pull_request:
6+
jobs:
7+
test:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@v4
11+
- run: bazel test //...
12+
working-directory: example

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
bazel-*

LICENSE.txt

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 lalten
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

MODULE.bazel

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module(name = "rules_cpan", version = "0.0.0")
2+
3+
bazel_dep(name = "rules_perl", version = "0.2.0")
4+
git_override(
5+
# https://github.com/bazelbuild/rules_perl/pull/62
6+
module_name = "rules_perl",
7+
remote = "https://github.com/lalten/rules_perl",
8+
commit = "973efb79defe0c417aa9655ac24a09148d599e9e",
9+
)
10+
11+
bazel_dep(name = "rules_python", version = "0.34.0")

Readme.md

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# rules_cpan
2+
3+
Parse a `cpanfile.snapshot` file and provide the Perl dependencies as Bazel targets.
4+
5+
## Usage
6+
7+
See the tutorial in the [example](example) directory.
8+
9+
First, generate a `cpanfile.snapshot.lock.json` file from your `cpanfile.snapshot` using `bazel run @rules_cpan//lock`.
10+
11+
Then, add this to `MODULE.bazel`:
12+
```Starlark
13+
bazel_dep(name = "rules_cpan", version = "0.0.1")
14+
cpan = use_extension("@rules_cpan//cpan:extensions.bzl", "cpan")
15+
cpan.install(
16+
name = "cpan_deps",
17+
lock = "//:cpanfile.snapshot.lock.json",
18+
)
19+
use_repo(cpan, "cpan_deps")
20+
```
21+
22+
Finally, use the `cpan_deps` target in your `BUILD` file:
23+
```Starlark
24+
load("@rules_perl//perl:perl.bzl", "perl_library")
25+
26+
perl_library(
27+
name = "my_perl_lib",
28+
srcs = ["lib/MyModule.pm"],
29+
deps = ["@cpan_deps"],
30+
)
31+
```

cpan/BUILD

Whitespace-only changes.

cpan/extensions.bzl

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
load("@rules_cpan//cpan:install.bzl", "install")
2+
3+
def _cpan_impl(module_ctx):
4+
for mod in module_ctx.modules:
5+
for attr in mod.tags.install:
6+
install(
7+
name = attr.name,
8+
lock = attr.lock,
9+
)
10+
11+
cpan = module_extension(
12+
implementation = _cpan_impl,
13+
tag_classes = {
14+
"install": tag_class(
15+
attrs = {
16+
"name": attr.string(mandatory = True),
17+
"lock": attr.label(mandatory = True),
18+
},
19+
),
20+
},
21+
)

cpan/install.bzl

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
_MODULE_BUILD_TEMPLATE = """\
2+
# AUTOGENERATED BY rules_cpan
3+
4+
load("@rules_perl//perl:perl.bzl", "perl_library")
5+
6+
perl_library(
7+
name = "{distribution}",
8+
srcs = glob(["lib/**/*"]),
9+
visibility = ["//visibility:public"],
10+
)
11+
"""
12+
13+
_REPO_BUILD_TEMPLATE = """\
14+
# AUTOGENERATED BY rules_cpan
15+
16+
load("@rules_perl//perl:perl.bzl", "perl_library")
17+
18+
exports_files(["**/*"])
19+
20+
perl_library(
21+
name = "{repo}",
22+
deps = {deps},
23+
visibility = ["//visibility:public"],
24+
)
25+
"""
26+
27+
def _install_impl(rctx):
28+
rctx = rctx # type: repository_ctx
29+
lockfile = json.decode(rctx.read(rctx.attr.lock))
30+
for distribution, item in lockfile.items():
31+
rctx.download_and_extract(
32+
url = item["url"],
33+
sha256 = item["sha256"],
34+
stripPrefix = item["release"],
35+
output = distribution,
36+
)
37+
rctx.file(distribution + "/BUILD", _MODULE_BUILD_TEMPLATE.format(distribution = distribution), executable = False)
38+
39+
rctx.file("BUILD", _REPO_BUILD_TEMPLATE.format(
40+
repo = rctx.name.split("~")[-1],
41+
deps = ["//" + dep for dep in lockfile.keys()],
42+
), executable = False)
43+
rctx.file("WORKSPACE", "", executable = False)
44+
45+
install = repository_rule(
46+
attrs = {
47+
"lock": attr.label(allow_single_file = True, doc = "cpanfile snapshot lock file"),
48+
},
49+
implementation = _install_impl,
50+
)

example/BUILD

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
sh_test(
2+
name = "integration_test",
3+
srcs = ["integration_test.sh"],
4+
data = ["@lcov//:genhtml"],
5+
deps = ["@bazel_tools//tools/bash/runfiles"],
6+
)

example/MODULE.bazel

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
module(name = "rules_cpan_example", version = "0.0.0")
2+
3+
bazel_dep(name = "rules_perl")
4+
git_override(
5+
# https://github.com/bazelbuild/rules_perl/pull/62
6+
module_name = "rules_perl",
7+
remote = "https://github.com/lalten/rules_perl",
8+
commit = "973efb79defe0c417aa9655ac24a09148d599e9e",
9+
)
10+
11+
bazel_dep(name = "rules_cpan")
12+
local_path_override(
13+
module_name = "rules_cpan",
14+
path = "..",
15+
)
16+
17+
cpan = use_extension("@rules_cpan//cpan:extensions.bzl", "cpan")
18+
cpan.install(
19+
name = "cpan_deps",
20+
lock = "//:cpanfile.snapshot.lock.json",
21+
)
22+
use_repo(cpan, "cpan_deps")
23+
24+
http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
25+
http_archive(
26+
name = "lcov",
27+
build_file = "//:lcov.BUILD",
28+
url = "https://github.com/linux-test-project/lcov/releases/download/v2.1/lcov-2.1.tar.gz",
29+
integrity = "sha256-TQHZ9VGj8OhozoR0L7YKrEQH4/wWImNaB+KdcOOPH68=",
30+
strip_prefix = "lcov-2.1",
31+
)

example/Readme.md

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# rules_cpan e2e example
2+
3+
Demonstrates how to use rules_cpan by building https://github.com/linux-test-project/lcov.
4+
5+
## cpanfile
6+
7+
Some projects already deliver a [cpanfile](https://metacpan.org/dist/Module-CPANfile).
8+
9+
In the case of Lcov the Perl dependencies are just listed in the Readme: https://github.com/linux-test-project/lcov/blob/v2.1/README#L116.
10+
It's straightforward to convert this into `requires 'Capture::Tiny';` (and so on) for the cpanfile.
11+
12+
## cpanfile.snapshot
13+
14+
rules_cpan needs a `cpanfile.snapshot` as input.
15+
16+
The user needs to install and run [Carton](https://metacpan.org/pod/Carton) manually to generate this file.
17+
18+
This will actually install the dependencies into a local directory.
19+
We just remove this directory afterwards.
20+
21+
```sh
22+
curl -L https://cpanmin.us | perl - --sudo App::cpanminus
23+
sudo cpanm --notest Carton
24+
25+
cd e2e
26+
carton
27+
28+
rm -rf local
29+
```
30+
31+
## cpanfile.snapshot.lock.json
32+
33+
The `cpanfile.snapshot.lock.json` is generated by lock.py of rules_cpan:
34+
35+
```sh
36+
bazel run @rules_cpan//lock -- cpanfile.snapshot cpanfile.snapshot.lock.json
37+
```
38+
39+
It contains the URLs and checksums of the cpan dependencies that will be downloaded by Bazel.
40+
41+
## MODULE.bazel
42+
43+
Until https://github.com/bazelbuild/rules_perl/pull/62 is merged you need to add a rules_perl dependency with a git_override:
44+
45+
```Starlark
46+
bazel_dep(name = "rules_perl")
47+
git_override(
48+
module_name = "rules_perl",
49+
remote = "https://github.com/lalten/rules_perl",
50+
commit = "973efb79defe0c417aa9655ac24a09148d599e9e",
51+
)
52+
```
53+
54+
Now you can use the cpan extension of rules_cpan to generate the perl_library targets.
55+
56+
```Starlark
57+
cpan = use_extension("@rules_cpan//cpan:extensions.bzl", "cpan")
58+
cpan.install(
59+
name = "cpan_deps",
60+
lock = "//:cpanfile.snapshot.lock.json",
61+
)
62+
use_repo(cpan, "cpan_deps")
63+
```
64+
65+
In this example we don't have any Perl code of our own and just pull in the latest Lcov release as `http_archive`.
66+
67+
## BUILD
68+
69+
Now we can finally depend on the cpan targets in our BUILD file:
70+
71+
```Starlark
72+
load("@rules_perl//perl:perl.bzl", "perl_binary", "perl_library")
73+
74+
perl_library(
75+
name = "liblcov",
76+
srcs = glob(["lib/**/*"]),
77+
deps = ["@cpan_deps"],
78+
)
79+
```
80+
81+
## Testing
82+
83+
In this example we have an integration test that runs the `gehtml --version`:
84+
```sh
85+
bazel test //:integration_test --test_output=all
86+
```

example/cpanfile

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
requires 'Capture::Tiny';
2+
requires 'DateTime';
3+
requires 'Devel::Cover';
4+
requires 'Digest::MD5';
5+
requires 'File::Spec';
6+
requires 'JSON::XS';
7+
requires 'Memory::Process';
8+
requires 'Module::Load::Conditional';
9+
requires 'Scalar::Util';
10+
requires 'Time::HiRes';

0 commit comments

Comments
 (0)