Skip to content

lean: add lean toolchain#661

Open
ffortier wants to merge 3 commits into
mainfrom
lean
Open

lean: add lean toolchain#661
ffortier wants to merge 3 commits into
mainfrom
lean

Conversation

@ffortier
Copy link
Copy Markdown
Owner

@ffortier ffortier commented Mar 3, 2026

Summary by CodeRabbit

  • New Features

    • Added Lean language support with cross-platform toolchains and integrated Lean compiler/tooling.
    • Included a simple "Hello, World!" Lean example that builds to a native executable.
  • Chores

    • Registered and configured Lean toolchains for multiple platforms and exposed build-time variables for Lean tools (lean, leanc, leanchecker) for use in builds.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 3, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 995a4162-5613-44b2-a83d-7ac604afceaa

📥 Commits

Reviewing files that changed from the base of the PR and between 915f956 and 886ea20.

⛔ Files ignored due to path filters (1)
  • modules/toolchains_cc65/MODULE.bazel.lock is excluded by !**/*.lock
📒 Files selected for processing (1)
  • third_party/lean/lean_cc_library.bzl
🚧 Files skipped from review as they are similar to previous changes (1)
  • third_party/lean/lean_cc_library.bzl

📝 Walkthrough

Walkthrough

Adds Lean language support: new Bazel Starlark rules and toolchain targets, http_archive entries for platform-specific Lean toolchains, a resolved toolchain exporter exposing LEAN/LEANC/LEANCHECKER, build helpers for Lean-generated C, a minimal Hello.lean example, and env wiring for tool substitutions.

Changes

Cohort / File(s) Summary
Top-level build / module
BUILD.bazel, MODULE.bazel
Load bazel_env in BUILD.bazel; include //third_party/lean:lean.MODULE.bazel in MODULE.bazel.
Third-party manifests & archives
third_party/lean/lean.MODULE.bazel, third_party/lean/BUILD.bazel
Add three http_archive entries for Lean 4.28.0 (linux x86_64, linux aarch64, darwin aarch64) and register corresponding toolchain targets; scaffold platform/toolchain targets.
Toolchain rules & resolver
third_party/lean/lean_toolchain.bzl, third_party/lean/resolved_toolchain.bzl
Introduce lean_toolchain/default_lean_toolchain and resolved_toolchain that expose LEAN/LEANC/LEANCHECKER via TemplateVariableInfo and provide tool files.
Tool packaging & binaries
third_party/lean/lean.BUILD.bazel, third_party/lean/lean.MODULE.bazel
Add native binaries (lean, leanc, leanchecker), cc_imports for runtime libs, leanrt cc_library stub, and generate default toolchain targets from a platforms list.
Build helpers (Starlark)
third_party/lean/lean_cc_library.bzl, third_party/lean/lean_binary.bzl
Add lean_cc_library (genrule to produce C from Lean + cc_library with platform deps) and lean_binary (wraps generated cc target into a cc_binary).
Example / experiments
experiments/leaning/BUILD.bazel, experiments/leaning/Hello.lean
Add hello lean_binary target and Hello.lean with def main : IO Unit := IO.println "Hello, World!".
Environment wiring
env/BUILD.bazel
Register toolchain //third_party/lean:resolved and add tool substitutions LEAN, LEANC, LEANCHECKER to env/tools mapping.

Sequence Diagram(s)

sequenceDiagram
    participant DEV as Developer
    participant BAZEL as Bazel
    participant REG as ToolchainRegistry
    participant ARCH as http_archive
    participant TC as resolved_toolchain
    participant GEN as genrule/action
    participant TOOL as Lean binaries

    DEV->>BAZEL: Invoke build referencing $(LEAN)/$(LEANC)/$(LEANCHECKER)
    BAZEL->>REG: Lookup registered toolchains
    REG->>ARCH: Fetch & unpack archives
    REG->>TC: Instantiate toolchain (toolchain_type -> tool labels)
    TC-->>BAZEL: Provide TemplateVariableInfo (LEAN, LEANC, LEANCHECKER) + tool files
    BAZEL->>GEN: Run genrule using provided tool paths
    GEN->>TOOL: Invoke lean/leanc/leanchecker
    TOOL-->>GEN: Emit generated C/artifacts
    GEN-->>BAZEL: Produce outputs
    BAZEL-->>DEV: Build complete
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐇
I hopped into the repo with cheer,
Pulled toolchains close and brought them near,
Archives fetched, and builds took flight,
Hello.lean beamed a carrot-bright light.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'lean: add lean toolchain' accurately summarizes the primary change—adding Lean toolchain integration with Bazel configurations, toolchain definitions, and supporting build infrastructure across multiple files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch lean

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
experiments/leaning/BUILD.bazel (1)

11-11: Harden the genrule command for determinism.

Since srcs has a single file, prefer $< and explicit file paths instead of unquoted $(SRCS) and * expansion.

Proposed command tweak
-    cmd = "mkdir $(RULEDIR)/src && cp $(SRCS) $(RULEDIR)/src && $(LEAN) -c $@ $(RULEDIR)/src/*",
+    cmd = "mkdir -p $(RULEDIR)/src && cp $< $(RULEDIR)/src/Hello.lean && $(LEAN) -c $@ $(RULEDIR)/src/Hello.lean",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@experiments/leaning/BUILD.bazel` at line 11, The genrule command is brittle
due to unquoted $(SRCS) and wildcard '*' expansion; update the genrule so it
uses the single-source automatic variable ($<) and explicit file paths into
$(RULEDIR)/src instead of $(SRCS) and *: copy $< into $(RULEDIR)/src (preserving
filename), then invoke $(LEAN) -c $@ on that specific copied filename (use
$(notdir $<) or equivalent to reference the basename) so the rule is
deterministic and avoids shell globbing/word-splitting issues involving $(SRCS)
and '*'.
third_party/lean/lean.MODULE.bazel (2)

7-7: Consider adding a backup mirror URL per archive for fetch resilience.

Each archive currently has a single GitHub URL. A secondary mirror can reduce CI flakiness during upstream outages/rate limits.

Also applies to: 15-15, 23-23

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@third_party/lean/lean.MODULE.bazel` at line 7, Update each urls = [...]
attribute in third_party/lean/lean.MODULE.bazel to include a secondary mirror
entry for each archive (i.e., add a backup URL alongside the existing GitHub
URL). Locate the three occurrences of the urls attribute in this MODULE file and
append a reliable mirror or CDN URL as the next item in the list so fetch
operations can fall back without changing the attribute name; keep the original
GitHub URL first and ensure both entries are valid and use the same archive
filename.

3-25: Reduce repeated Lean release literals to avoid version-drift mistakes.

The three http_archive blocks duplicate version/build-file/release-prefix strings. Centralizing constants will make upgrades safer.

♻️ Suggested refactor
+LEAN_VERSION = "4.28.0"
+LEAN_BUILD_FILE = "@//third_party/lean:lean.BUILD.bazel"
+LEAN_RELEASE_PREFIX = "https://github.com/leanprover/lean4/releases/download/v4.28.0"
+
 http_archive(
     name = "lean_linux_x86_64",
     sha256 = "ceb3a3f844f7aebf63245e2b51c28d5b0ed38942c19f93cf3febd520302160bd",
     strip_prefix = "lean-4.28.0-linux",
-    urls = ["https://github.com/leanprover/lean4/releases/download/v4.28.0/lean-4.28.0-linux.tar.zst"],
-    build_file = "@//third_party/lean:lean.BUILD.bazel",
+    urls = [LEAN_RELEASE_PREFIX + "/lean-4.28.0-linux.tar.zst"],
+    build_file = LEAN_BUILD_FILE,
 )
 
 http_archive(
     name = "lean_linux_aarch64",
     sha256 = "c865801261c747d4f15d08beca9abc20aca907904abbb284de25a37f4b4558bc",
     strip_prefix = "lean-4.28.0-linux_aarch64",
-    urls = ["https://github.com/leanprover/lean4/releases/download/v4.28.0/lean-4.28.0-linux_aarch64.tar.zst"],
-    build_file = "@//third_party/lean:lean.BUILD.bazel",
+    urls = [LEAN_RELEASE_PREFIX + "/lean-4.28.0-linux_aarch64.tar.zst"],
+    build_file = LEAN_BUILD_FILE,
 )
 
 http_archive(
     name = "lean_darwin_aarch64",
     sha256 = "61942f9d1907db918020154a517c87fb64841e48cebb0032fc0909df8d189a05",
     strip_prefix = "lean-4.28.0-darwin_aarch64",
-    urls = ["https://github.com/leanprover/lean4/releases/download/v4.28.0/lean-4.28.0-darwin_aarch64.tar.zst"],
-    build_file = "@//third_party/lean:lean.BUILD.bazel",
+    urls = [LEAN_RELEASE_PREFIX + "/lean-4.28.0-darwin_aarch64.tar.zst"],
+    build_file = LEAN_BUILD_FILE,
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@third_party/lean/lean.MODULE.bazel` around lines 3 - 25, The three
http_archive entries (names: lean_linux_x86_64, lean_linux_aarch64,
lean_darwin_aarch64) duplicate the Lean
release/version/strip_prefix/urls/build_file literals; extract shared constants
(e.g., LEAN_VERSION = "4.28.0", BASE_URL =
"https://github.com/leanprover/lean4/releases/download/v{LEAN_VERSION}/",
BUILD_FILE = "@//third_party/lean:lean.BUILD.bazel") and per-arch values (sha256
and strip_prefix suffixes) into variables at the top of the MODULE, then
reference those variables in each http_archive call (replace inline urls,
strip_prefix and build_file literals with the variables) so upgrades only change
the centralized constants and avoid version-drift across lean_linux_x86_64 /
lean_linux_aarch64 / lean_darwin_aarch64.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@third_party/lean/lean.BUILD.bazel`:
- Around line 3-21: The BUILD currently only declares native_binary targets
("lean", "leanc", "leanchecker") but does not model the Lean runtime shared
library, causing CI failures for libInit_shared.so; add a cc_library (e.g., name
= "libinit_shared" or "lean_runtime_shared") that declares the runtime
sources/headers and produces a shared library (.so/.dylib) and then make the
binaries depend on it (add it to the dep list of the corresponding
native_binary/cc_binary targets or export it via a cc_library wrapper) so the
runtime shared object is built and linked into the distribution; ensure correct
visibility and platform-specific linkopts so CI can find libInit_shared at
runtime.

In `@third_party/lean/resolved_toolchain.bzl`:
- Around line 4-11: The DefaultInfo currently only exposes the executable files
and omits transitive runfiles; collect and merge the default_runfiles (or
runfiles) from toolchain.tools.lean, toolchain.tools.leanc, and
toolchain.tools.leanchecker into a single depset (following the pattern in
tools/c64/basic_binary.bzl) and pass that merged runfiles via
DefaultInfo(default_runfiles = merged_runfiles, files = depset([...])) so
dynamically linked shared libraries are propagated; update the return list near
the TemplateVariableInfo block and the DefaultInfo invocation that references
toolchain.tools.lean, toolchain.tools.leanc, and toolchain.tools.leanchecker.

---

Nitpick comments:
In `@experiments/leaning/BUILD.bazel`:
- Line 11: The genrule command is brittle due to unquoted $(SRCS) and wildcard
'*' expansion; update the genrule so it uses the single-source automatic
variable ($<) and explicit file paths into $(RULEDIR)/src instead of $(SRCS) and
*: copy $< into $(RULEDIR)/src (preserving filename), then invoke $(LEAN) -c $@
on that specific copied filename (use $(notdir $<) or equivalent to reference
the basename) so the rule is deterministic and avoids shell
globbing/word-splitting issues involving $(SRCS) and '*'.

In `@third_party/lean/lean.MODULE.bazel`:
- Line 7: Update each urls = [...] attribute in
third_party/lean/lean.MODULE.bazel to include a secondary mirror entry for each
archive (i.e., add a backup URL alongside the existing GitHub URL). Locate the
three occurrences of the urls attribute in this MODULE file and append a
reliable mirror or CDN URL as the next item in the list so fetch operations can
fall back without changing the attribute name; keep the original GitHub URL
first and ensure both entries are valid and use the same archive filename.
- Around line 3-25: The three http_archive entries (names: lean_linux_x86_64,
lean_linux_aarch64, lean_darwin_aarch64) duplicate the Lean
release/version/strip_prefix/urls/build_file literals; extract shared constants
(e.g., LEAN_VERSION = "4.28.0", BASE_URL =
"https://github.com/leanprover/lean4/releases/download/v{LEAN_VERSION}/",
BUILD_FILE = "@//third_party/lean:lean.BUILD.bazel") and per-arch values (sha256
and strip_prefix suffixes) into variables at the top of the MODULE, then
reference those variables in each http_archive call (replace inline urls,
strip_prefix and build_file literals with the variables) so upgrades only change
the centralized constants and avoid version-drift across lean_linux_x86_64 /
lean_linux_aarch64 / lean_darwin_aarch64.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5f96311 and b6f4fad.

⛔ Files ignored due to path filters (1)
  • MODULE.bazel.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • BUILD.bazel
  • MODULE.bazel
  • experiments/leaning/BUILD.bazel
  • experiments/leaning/Hello.lean
  • third_party/lean/BUILD.bazel
  • third_party/lean/lean.BUILD.bazel
  • third_party/lean/lean.MODULE.bazel
  • third_party/lean/lean_toolchain.bzl
  • third_party/lean/resolved_toolchain.bzl

Comment on lines +3 to +21
native_binary(
name = "lean",
src = "bin/lean",
visibility = ["//visibility:public"],
)

native_binary(
name = "leanc",
src = "bin/leanc",
visibility = ["//visibility:public"],
)

native_binary(
name = "leanchecker",
src = "bin/leanchecker",
visibility = ["//visibility:public"],
)

# TODO: Add cc_library for lean runtime
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Lean runtime shared libraries are not modeled, and CI is already failing.

The current setup exports executables (Lines 3-19), but runtime .so/.dylib dependencies are not represented; this matches the libInit_shared.so failure from CI. Line 21’s TODO is now a release blocker, not a follow-up item.

Suggested direction
+# Expose Lean runtime shared libraries so toolchain consumers can propagate them.
+filegroup(
+    name = "lean_runtime_libs",
+    srcs = glob([
+        "lib/**/*.so",
+        "lib/**/*.dylib",
+    ]),
+    visibility = ["//visibility:public"],
+)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
native_binary(
name = "lean",
src = "bin/lean",
visibility = ["//visibility:public"],
)
native_binary(
name = "leanc",
src = "bin/leanc",
visibility = ["//visibility:public"],
)
native_binary(
name = "leanchecker",
src = "bin/leanchecker",
visibility = ["//visibility:public"],
)
# TODO: Add cc_library for lean runtime
native_binary(
name = "lean",
src = "bin/lean",
visibility = ["//visibility:public"],
)
native_binary(
name = "leanc",
src = "bin/leanc",
visibility = ["//visibility:public"],
)
native_binary(
name = "leanchecker",
src = "bin/leanchecker",
visibility = ["//visibility:public"],
)
# TODO: Add cc_library for lean runtime
# Expose Lean runtime shared libraries so toolchain consumers can propagate them.
filegroup(
name = "lean_runtime_libs",
srcs = glob([
"lib/**/*.so",
"lib/**/*.dylib",
]),
visibility = ["//visibility:public"],
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@third_party/lean/lean.BUILD.bazel` around lines 3 - 21, The BUILD currently
only declares native_binary targets ("lean", "leanc", "leanchecker") but does
not model the Lean runtime shared library, causing CI failures for
libInit_shared.so; add a cc_library (e.g., name = "libinit_shared" or
"lean_runtime_shared") that declares the runtime sources/headers and produces a
shared library (.so/.dylib) and then make the binaries depend on it (add it to
the dep list of the corresponding native_binary/cc_binary targets or export it
via a cc_library wrapper) so the runtime shared object is built and linked into
the distribution; ensure correct visibility and platform-specific linkopts so CI
can find libInit_shared at runtime.

Comment on lines +4 to +11
return [
platform_common.TemplateVariableInfo({
"LEAN": toolchain.tools.lean.path,
"LEANC": toolchain.tools.leanc.path,
"LEANCHECKER": toolchain.tools.leanchecker.path,
}),
DefaultInfo(files = depset([toolchain.tools.lean, toolchain.tools.leanc, toolchain.tools.leanchecker])),
]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify resolved_toolchain currently exports only files and no runfiles propagation.
rg -n -C3 'TemplateVariableInfo|DefaultInfo\(|files\s*=\s*depset|runfiles\s*=' third_party/lean/resolved_toolchain.bzl

Repository: ffortier/mono

Length of output: 529


🏁 Script executed:

#!/bin/bash
# Find the toolchain definition and understand how Lean tools are provided
fd -t f "toolchain" third_party/lean/ 2>/dev/null | head -20

echo "=== Searching for Lean toolchain_type definition ==="
rg -n "toolchain_type" third_party/lean/

echo "=== Searching for libInit_shared.so references ==="
rg -r "libInit_shared" .

echo "=== Looking at Lean tool definitions ==="
rg -n -B2 -A5 "tools\.lean\|tools\.leanc" third_party/lean/ | head -50

Repository: ffortier/mono

Length of output: 50372


🏁 Script executed:

#!/bin/bash
# Examine lean_toolchain.bzl to understand tool structure
head -100 third_party/lean/lean_toolchain.bzl

echo "=== Look for tool definitions and how they're created ==="
rg -n -A10 "tools\s*=" third_party/lean/lean_toolchain.bzl | head -60

Repository: ffortier/mono

Length of output: 1772


🏁 Script executed:

#!/bin/bash
# Find where the actual lean tools are sourced from
find third_party/lean -name "*.bzl" -o -name "*.bazel" | head -20

echo "=== Check the BUILD file ==="
cat third_party/lean/BUILD.bazel 2>/dev/null || echo "File not found"

echo "=== Look for any references to lean tools being provided with deps ==="
rg -n "lean|leanc|leanchecker" third_party/lean/ --type bazel -B2 -A2 | head -100

Repository: ffortier/mono

Length of output: 7636


🏁 Script executed:

#!/bin/bash
# Check the full lean.BUILD.bazel to see if there are any data/deps on shared libraries
cat third_party/lean/lean.BUILD.bazel

echo "=== Look for any data files or runtime dependencies ==="
rg -n "data\s*=|runfiles|\.so" third_party/lean/

echo "=== Check if lean tools are dynamically linked (look for lib directories in archive) ==="
rg -n "lib/" third_party/lean/

Repository: ffortier/mono

Length of output: 725


🏁 Script executed:

#!/bin/bash
# Understand the issue better: check if native_binary targets have runfiles
echo "=== Search for native_binary usage and runfiles pattern ==="
rg -n "native_binary" . --type bazel -B2 -A5 | head -100

echo "=== Check if there are any Bazel configuration or CI files that document the libInit_shared.so error ==="
fd -e log -e txt -e sh | xargs rg "libInit_shared" 2>/dev/null | head -20

echo "=== Look at how other toolchains handle DefaultInfo with tools ==="
rg -n "DefaultInfo.*files.*depset" . --type bazel -B3 -A3 | head -80

Repository: ffortier/mono

Length of output: 3820


Propagate tool runfiles to include shared library dependencies.

The DefaultInfo at line 10 exposes only the executable files without propagating their transitive runfiles. For dynamically-linked Lean tools, this drops required shared libraries at runtime. Merge the default_runfiles from each tool target (following the pattern in tools/c64/basic_binary.bzl) and pass the merged runfiles to DefaultInfo.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@third_party/lean/resolved_toolchain.bzl` around lines 4 - 11, The DefaultInfo
currently only exposes the executable files and omits transitive runfiles;
collect and merge the default_runfiles (or runfiles) from toolchain.tools.lean,
toolchain.tools.leanc, and toolchain.tools.leanchecker into a single depset
(following the pattern in tools/c64/basic_binary.bzl) and pass that merged
runfiles via DefaultInfo(default_runfiles = merged_runfiles, files =
depset([...])) so dynamically linked shared libraries are propagated; update the
return list near the TemplateVariableInfo block and the DefaultInfo invocation
that references toolchain.tools.lean, toolchain.tools.leanc, and
toolchain.tools.leanchecker.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
third_party/lean/lean.BUILD.bazel (1)

5-21: ⚠️ Potential issue | 🔴 Critical

Model the shared Lean runtime, not just the static archives.

leanrt only wraps headers plus .a files, and Line 51 still leaves the actual runtime shared objects as a TODO. The exported native_binary targets therefore still have no way to carry the Lean shared runtime in runfiles, so lean/leanc/leanchecker remain broken on platforms where the distribution expects libInit_shared.so/.dylib beside the executable.

Suggested direction
+filegroup(
+    name = "lean_runtime_shared",
+    srcs = glob([
+        "lib/**/*.so",
+        "lib/**/*.dylib",
+    ]),
+    visibility = ["//visibility:public"],
+)
+
-# TODO: Add cc_library for lean runtime

Then wire :lean_runtime_shared into whatever rule packages or executes these native_binary targets so the shared objects land in runfiles/artifacts.

Also applies to: 23-51

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@third_party/lean/lean.BUILD.bazel` around lines 5 - 21, The native binaries
(targets named "lean", "leanc", "leanchecker") are not shipping the Lean shared
runtime; update the packaging so the shared runtime artifacts are modeled and
included: create or update the target (referenced as :lean_runtime_shared or
expand leanrt) to list the platform shared libraries (libInit_shared.so/.dylib
etc.) as outputs/inputs and then add that target to each native_binary's
runfiles by adding data = [":lean_runtime_shared"] (or equivalent attribute used
in this repo) to the native_binary declarations so the shared objects are
present in runfiles alongside the executables.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@third_party/lean/lean_binary.bzl`:
- Around line 4-15: The lean_binary wrapper currently passes deps explicitly to
cc_binary and also forwards **kwargs, which will conflict if a caller supplies
deps; update lean_binary (and similarly the lean_cc_library wrapper) to remove
or merge any deps from kwargs before forwarding: in lean_binary capture deps =
kwargs.pop("deps", []) (or equivalent), then call cc_binary with deps = [":" +
name + "_cc"] + deps and the remaining **kwargs; do the analogous pop/merge for
the select(...) deps in the lean_cc_library helper so no attribute is passed
twice.

In `@third_party/lean/lean_cc_library.bzl`:
- Around line 16-19: The deps select in lean_cc_library.bzl currently keys only
on OS and hardcodes aarch64 runtimes, so add CPU-aware cases or mirror toolchain
constraints: update the select(...) used for deps to include combinations for
both OS and CPU (e.g. entries that match linux + x86_64 ->
"@lean_linux_x86_64//:leanrt" and linux + aarch64 ->
"@lean_linux_aarch64//:leanrt", and similarly for darwin), ensuring the labels
referenced match the toolchain names registered (e.g.
lean_linux_x86_64_toolchain / lean_linux_aarch64_toolchain); alternatively, if
x86_64 runtimes are not supported, remove the x86_64 toolchain registration
instead of leaving an incorrect runtime mapping.
- Around line 3-10: The lean_cc_library wrapper currently allows multiple
entries in srcs but generates outs and uses $@ in cmd (which only works for a
single out) and flattens sources into one directory; update the lean_cc_library
function to guard against multi-file targets by checking srcs and failing fast
when len(srcs) != 1 (use Starlark's fail() with a clear message referencing
srcs, outs and the genrule invocation), so only single Lean source files are
allowed until full multi-file support (and proper copying/preservation of module
paths) is implemented.

---

Duplicate comments:
In `@third_party/lean/lean.BUILD.bazel`:
- Around line 5-21: The native binaries (targets named "lean", "leanc",
"leanchecker") are not shipping the Lean shared runtime; update the packaging so
the shared runtime artifacts are modeled and included: create or update the
target (referenced as :lean_runtime_shared or expand leanrt) to list the
platform shared libraries (libInit_shared.so/.dylib etc.) as outputs/inputs and
then add that target to each native_binary's runfiles by adding data =
[":lean_runtime_shared"] (or equivalent attribute used in this repo) to the
native_binary declarations so the shared objects are present in runfiles
alongside the executables.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: de739612-f486-4763-a610-d33608096980

📥 Commits

Reviewing files that changed from the base of the PR and between b6f4fad and 7e4ab15.

📒 Files selected for processing (4)
  • experiments/leaning/BUILD.bazel
  • third_party/lean/lean.BUILD.bazel
  • third_party/lean/lean_binary.bzl
  • third_party/lean/lean_cc_library.bzl
🚧 Files skipped from review as they are similar to previous changes (1)
  • experiments/leaning/BUILD.bazel

Comment on lines +4 to +15
def lean_binary(name, srcs, **kwargs):
lean_cc_library(
name = name + "_cc",
srcs = srcs,
)

cc_binary(
name = name,
srcs = [],
deps = [":" + name + "_cc"],
**kwargs
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "lean_binary.bzl" -o -name "lean_cc_library.bzl" | head -20

Repository: ffortier/mono

Length of output: 130


🏁 Script executed:

cat -n ./third_party/lean/lean_binary.bzl

Repository: ffortier/mono

Length of output: 491


🏁 Script executed:

cat -n ./third_party/lean/lean_cc_library.bzl

Repository: ffortier/mono

Length of output: 997


🌐 Web query:

Bazel legacy macro kwargs duplicate keyword argument collision behavior

💡 Result:

In Bazel legacy macros (regular Starlark functions), the expansion ultimately performs normal Starlark function calls to rules (e.g. native.cc_library(...)). In Starlark, keyword argument names must be unique across the entire call, including keywords coming from **kwargs. If the same keyword appears twice (e.g. once explicitly and once inside **kwargs, or in two different ** expansions), the call fails with an error (duplicate keyword argument) rather than picking one value. [1], [2]

What this means for macros that forward **kwargs

This is an error pattern:

def my_macro(name, **kwargs):
    native.cc_library(
        name = name,
        linkstatic = 1,   # macro hard-codes
        **kwargs,         # user might also pass linkstatic
    )

If the caller does my_macro(name="x", linkstatic=0), the inner native.cc_library(...) call has linkstatic twice ⇒ error. [1], [2]

Common ways to avoid collisions

  1. Pop the key before forwarding:
def my_macro(name, **kwargs):
    linkstatic = kwargs.pop("linkstatic", 1)
    native.cc_library(name = name, linkstatic = linkstatic, **kwargs)
  1. Merge into a dict so only one value is passed, with a defined precedence (example: “user wins”):
def my_macro(name, **kwargs):
    attrs = dict(kwargs)
    attrs.setdefault("linkstatic", 1)
    native.cc_library(name = name, **attrs)

If you want an explicit “last wins” merge helper, bazel-skylib’s dicts.add documents that later dictionaries override earlier ones (useful for building attrs), but you must still ensure the final call doesn’t pass the same keyword twice. [3]

Sources:
[1] Starlark Rust API docs: named argument names must be unique (explicit + **kwargs)
[2] starlark-go implementation: raises duplicate keyword arg
[3] bazel-skylib dicts.add: later dict overrides earlier ones in merges


Don't expose deps via **kwargs.

Both files have explicit deps attributes followed by **kwargs forwarding. In Bazel legacy macros (Starlark), passing a keyword argument twice—once explicitly and once inside **kwargs—causes a runtime error. If a caller passes deps = [...] to lean_binary(), the deps = [":" + name + "_cc"] on line 13 will collide with the kwargs, causing the call to fail.

The same pattern exists in third_party/lean/lean_cc_library.bzl (lines 16–20), where explicit deps = select({...}) conflicts with any deps in kwargs.

Extract conflicting attributes before forwarding **kwargs, either by popping from kwargs or by merging into a single dict before calling the inner rule. For example:

def lean_binary(name, srcs, **kwargs):
    lean_cc_library(name = name + "_cc", srcs = srcs)
    deps = kwargs.pop("deps", [])
    cc_binary(
        name = name,
        srcs = [],
        deps = [":" + name + "_cc"] + deps,
        **kwargs
    )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@third_party/lean/lean_binary.bzl` around lines 4 - 15, The lean_binary
wrapper currently passes deps explicitly to cc_binary and also forwards
**kwargs, which will conflict if a caller supplies deps; update lean_binary (and
similarly the lean_cc_library wrapper) to remove or merge any deps from kwargs
before forwarding: in lean_binary capture deps = kwargs.pop("deps", []) (or
equivalent), then call cc_binary with deps = [":" + name + "_cc"] + deps and the
remaining **kwargs; do the analogous pop/merge for the select(...) deps in the
lean_cc_library helper so no attribute is passed twice.

Comment on lines +3 to +10
def lean_cc_library(name, srcs, **kwargs):
native.genrule(
name = name + "_source",
srcs = srcs,
outs = [s + ".c" for s in srcs],
# TODO: Lean don't like symlinks. When building the actual rules, we will need to create a proper source root with hardlinks or copies.
cmd = "mkdir $(RULEDIR)/src && cp $(SRCS) $(RULEDIR)/src && $(LEAN) -c $@ $(RULEDIR)/src/*",
toolchains = ["//third_party/lean:resolved"],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "lean_cc_library.bzl" -type f

Repository: ffortier/mono

Length of output: 95


🏁 Script executed:

cat -n ./third_party/lean/lean_cc_library.bzl

Repository: ffortier/mono

Length of output: 997


🏁 Script executed:

rg "lean_cc_library" --type bzl -B 2 -A 5

Repository: ffortier/mono

Length of output: 84


🏁 Script executed:

rg "lean_cc_library" -B 2 -A 5

Repository: ffortier/mono

Length of output: 1269


🏁 Script executed:

rg "lean_binary\(" -B 3 -A 10

Repository: ffortier/mono

Length of output: 1158


🏁 Script executed:

rg "srcs\s*=\s*\[" experiments/leaning/BUILD.bazel -A 2

Repository: ffortier/mono

Length of output: 85


🌐 Web query:

Bazel genrule $@ variable multiple outputs outs semantics

💡 Result:

In a genrule, $@ is only defined when outs has exactly one file. If outs has multiple entries, using $@ (or @) is a build error. Use $(OUTS) or per-output $(location ...)/$(locations ...) instead. [1]

What to use with multiple outputs

  • All outputs as a list: $(OUTS) expands to the outs list (space-delimited). [1]
    Caveat: it’s not robust if filenames can contain spaces/special characters. [1]

  • A specific output: $(location :some_out_file) where some_out_file is one of the filenames in outs (Bazel allows referencing an output file by name here). [2]

  • Output directory:

    • Prefer $(RULEDIR) for the rule’s output directory (stable semantics). [1]
    • $(@d) means:
      • with one output: directory containing that output
      • with multiple outputs: the package’s root under the genfiles tree (even if all outs are in the same subdir). [1]

Example pattern (multiple outs)

genrule(
    name = "g",
    outs = ["a.txt", "b.txt"],
    cmd = "echo hi > $(location :a.txt) && echo bye > $(location :b.txt)",
)

This avoids $@ entirely and is explicit per output. [1][2]


🏁 Script executed:

rg "lean_binary\|lean_cc_library" -A 5 | grep -E "srcs\s*=" | head -20

Repository: ffortier/mono

Length of output: 39


🏁 Script executed:

find . -name "BUILD.bazel" -type f -exec grep -l "lean_binary\|lean_cc_library" {} \; | head -10

Repository: ffortier/mono

Length of output: 90


🏁 Script executed:

cat -n ./experiments/leaning/BUILD.bazel

Repository: ffortier/mono

Length of output: 219


Rule only supports single Lean source files.

outs generates multiple outputs for each file in srcs, but the command uses $@, which Bazel only defines when outs has exactly one file. Multi-source targets will fail with a build error. Additionally, the cp $(SRCS) $(RULEDIR)/src step collapses all sources into one directory, breaking module paths and causing basename collisions. Add a guard to enforce single-source constraint until full multi-file support is implemented.

Fail-fast guard
 def lean_cc_library(name, srcs, **kwargs):
+    if len(srcs) != 1:
+        fail("lean_cc_library currently supports exactly one Lean source")
     native.genrule(
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def lean_cc_library(name, srcs, **kwargs):
native.genrule(
name = name + "_source",
srcs = srcs,
outs = [s + ".c" for s in srcs],
# TODO: Lean don't like symlinks. When building the actual rules, we will need to create a proper source root with hardlinks or copies.
cmd = "mkdir $(RULEDIR)/src && cp $(SRCS) $(RULEDIR)/src && $(LEAN) -c $@ $(RULEDIR)/src/*",
toolchains = ["//third_party/lean:resolved"],
def lean_cc_library(name, srcs, **kwargs):
if len(srcs) != 1:
fail("lean_cc_library currently supports exactly one Lean source")
native.genrule(
name = name + "_source",
srcs = srcs,
outs = [s + ".c" for s in srcs],
# TODO: Lean don't like symlinks. When building the actual rules, we will need to create a proper source root with hardlinks or copies.
cmd = "mkdir $(RULEDIR)/src && cp $(SRCS) $(RULEDIR)/src && $(LEAN) -c $@ $(RULEDIR)/src/*",
toolchains = ["//third_party/lean:resolved"],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@third_party/lean/lean_cc_library.bzl` around lines 3 - 10, The
lean_cc_library wrapper currently allows multiple entries in srcs but generates
outs and uses $@ in cmd (which only works for a single out) and flattens sources
into one directory; update the lean_cc_library function to guard against
multi-file targets by checking srcs and failing fast when len(srcs) != 1 (use
Starlark's fail() with a clear message referencing srcs, outs and the genrule
invocation), so only single Lean source files are allowed until full multi-file
support (and proper copying/preservation of module paths) is implemented.

Comment thread third_party/lean/lean_cc_library.bzl
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
third_party/lean/BUILD.bazel (1)

6-10: Missing macOS x86_64 platform support.

The platforms list covers Linux (x86_64, aarch64) and macOS aarch64, but omits macOS x86_64 (Intel Macs). If Intel Mac support is needed, add:

("macos", "x86_64", "lean_darwin_x86_64"),

This would also require a corresponding http_archive in lean.MODULE.bazel.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@third_party/lean/BUILD.bazel` around lines 6 - 10, The platforms list in the
BUILD.bazel fragment (the platforms variable) is missing macOS x86_64 support;
add the tuple ("macos", "x86_64", "lean_darwin_x86_64") to the platforms list
and ensure you also add the corresponding http_archive entry named
lean_darwin_x86_64 in lean.MODULE.bazel so the new platform artifact is fetched
and available.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@third_party/lean/BUILD.bazel`:
- Around line 6-10: The platforms list in the BUILD.bazel fragment (the
platforms variable) is missing macOS x86_64 support; add the tuple ("macos",
"x86_64", "lean_darwin_x86_64") to the platforms list and ensure you also add
the corresponding http_archive entry named lean_darwin_x86_64 in
lean.MODULE.bazel so the new platform artifact is fetched and available.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6da3acc3-d17e-489a-bbd8-feb339f07289

📥 Commits

Reviewing files that changed from the base of the PR and between 7e4ab15 and 915f956.

📒 Files selected for processing (12)
  • BUILD.bazel
  • MODULE.bazel
  • env/BUILD.bazel
  • experiments/leaning/BUILD.bazel
  • experiments/leaning/Hello.lean
  • third_party/lean/BUILD.bazel
  • third_party/lean/lean.BUILD.bazel
  • third_party/lean/lean.MODULE.bazel
  • third_party/lean/lean_binary.bzl
  • third_party/lean/lean_cc_library.bzl
  • third_party/lean/lean_toolchain.bzl
  • third_party/lean/resolved_toolchain.bzl
🚧 Files skipped from review as they are similar to previous changes (6)
  • third_party/lean/lean_binary.bzl
  • third_party/lean/lean_cc_library.bzl
  • experiments/leaning/BUILD.bazel
  • third_party/lean/lean_toolchain.bzl
  • BUILD.bazel
  • experiments/leaning/Hello.lean

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant