Skip to content

Conversation

@alexeagle
Copy link
Contributor

@alexeagle alexeagle commented Nov 23, 2025

This technique is sometimes needed when a proc_macro derives information from the build, and it needs to be consumed by some other tool.

For example this one generates a JSON file as an intermediate output
https://github.com/napi-rs/napi-rs/blob/main/crates/macro/src/expand/typedef/type_def.rs#L11-L12
and then expects developers to call this via a Node.js binary which wraps rustc and also transforms that file to a TypeScript type definition file.

Under Bazel, this is better modeled as a rust_shared_library that produces the binding file (.so or .dylib for example) along with that JSON output, then run the Node.js binary as a separate target.

@alexeagle alexeagle marked this pull request as ready for review November 26, 2025 23:40
@UebelAndre UebelAndre self-requested a review November 27, 2025 06:11
@alexeagle
Copy link
Contributor Author

@UebelAndre ping!

Copy link
Collaborator

@illicitonion illicitonion left a comment

Choose a reason for hiding this comment

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

This generally looks reasonable - could you add a small test showing this working? Thanks!

@alexeagle
Copy link
Contributor Author

@illicitonion done, thanks PTAL

…s will be created

This technique is sometimes needed when a proc_macro derives information from the build, and it needs to be consumed by some other tool

sort

cursor wrote a new unit test for this PR

fmt
@alexeagle
Copy link
Contributor Author

Test failures do not look related to my change, FWICT

Copy link
Collaborator

@illicitonion illicitonion left a comment

Choose a reason for hiding this comment

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

Generally looks good, thanks! A couple of small things :)

Copy link
Contributor Author

@alexeagle alexeagle left a comment

Choose a reason for hiding this comment

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

PTAL

Copy link
Collaborator

@illicitonion illicitonion left a comment

Choose a reason for hiding this comment

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

LGTM! Thanks!

@illicitonion
Copy link
Collaborator

Ok we're down to a real test failure!

@alexeagle alexeagle force-pushed the extra_rust_outdirs branch 2 times, most recently from 1216b17 to 2efdf44 Compare January 14, 2026 17:03
@alexeagle
Copy link
Contributor Author

@illicitonion I'm trying to fix, but I don't know either Rust or Windows and don't have local repro. I believe the bug is in the test, not the three-line prod change, can I just tag the test to skip on windows?

@alexeagle
Copy link
Contributor Author

@illicitonion ping! would like to merge before I give up on contributing...

@illicitonion
Copy link
Collaborator

Sorry for the delay here - it looks like even with the test skipping, CI was flaking, but hopefully looks good now!

@alexeagle
Copy link
Contributor Author

Here's the real-world macro where I use this feature, as an example:

load("@rules_rust//rust:defs.bzl", "rust_shared_library")
load("@bazel_skylib//rules:select_file.bzl", "select_file")
load("@aspect_rules_js//js:defs.bzl", "js_run_binary")

TMP_FOLDER_NAME = "napi_type_def_tmp"

def napi_rust_shared_library(name, deps, **kwargs):
  """Create a NAPI-RS Rust shared library and generate the NAPI type definitions and typegen binary.

  Args:
    name: The name of the target.
    deps: The dependencies for the Rust library. The nodejs headers are added automatically.
    **kwargs: Additional arguments for the rust_shared_library rule.
  """
  lib_target = "_{}_rust_shared_library".format(name)
  bindings_target = "_{}_napi_type_defs".format(name)
  types_target = "_{}_typegen".format(name)
  rust_shared_library(
      name = lib_target,
      extra_outdirs = [TMP_FOLDER_NAME],
      rustc_env = {
          "NAPI_TYPE_DEF_TMP_FOLDER": "$(BINDIR)/{}/{}".format(native.package_name(), TMP_FOLDER_NAME),
      },
      rustc_flags = select({
          "@platforms//os:macos": [
              "--codegen=link-arg=-undefined",
              "--codegen=link-arg=dynamic_lookup",
          ],
          "//conditions:default": [],
      }),
      deps = deps + [
          "@rules_nodejs//nodejs/headers:current_node_cc_headers",
      ],
      **kwargs,
  )

  select_file(
      name = bindings_target,
      srcs = lib_target,
      subpath = TMP_FOLDER_NAME,
      visibility = ["//visibility:public"],
  )

  js_run_binary(
      name = types_target,
      srcs = [bindings_target],
      tool = Label("//tools/napi-rs:typegen_bin"),
      args = [
          "$(rootpath {})".format(bindings_target),
          "$(rootpath index.d.ts)",
      ],
      tags = [
          # TODO(alexeagle): continue the struggle against it finding no bindings in the sandbox
          "local",
      ],
      outs = ["index.d.ts"],
  )

  # Final production is a pair of .so/.dylib rust binding and .d.ts type definitions.
  native.filegroup(
    name = name,
    srcs = [
        lib_target,
        types_target,
    ],
  )

@alexeagle
Copy link
Contributor Author

I now have to rebase this patch at customer sites to get @blorente fixes, can we merge it please.

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.

3 participants