Skip to content

Comments

feat: Add automatic .csproj/.fsproj project scanning and Bazel target generation#528

Open
scottpledger wants to merge 1 commit intobazel-contrib:masterfrom
scottpledger:feat/csproj-support
Open

feat: Add automatic .csproj/.fsproj project scanning and Bazel target generation#528
scottpledger wants to merge 1 commit intobazel-contrib:masterfrom
scottpledger:feat/csproj-support

Conversation

@scottpledger
Copy link

Summary

This PR introduces a major new feature that allows existing .csproj and .fsproj files to serve as the source of truth for Bazel builds. The dotnet module extension now supports a scan_projects() tag that automatically discovers .NET project files in the workspace and generates corresponding Bazel targets, enabling seamless integration with existing IDE infrastructure.

Motivation

Maintaining both .csproj/.fsproj files (for IDE support) and BUILD.bazel files (for Bazel builds) creates duplication and synchronization challenges. This feature allows developers to keep their existing .NET project files and have Bazel automatically derive build targets from them.

Features

Automatic Project Scanning

  • Scans workspace for all .csproj and .fsproj files
  • Parses project files using pure Starlark XML parsing (via xml_tools)
  • Extracts target frameworks, output types, source files, project references, and NuGet packages
  • Generates .bzl files with auto_dotnet_targets() macros in the generated dotnet_projects repository

Toolchain Validation

  • Validates that registered toolchains cover all discovered target frameworks
  • Provides suggestions for missing toolchain registrations
  • Generates TOOLCHAIN_COVERAGE.md summary in the @dotnet_projects repository (mostly for debugging)

NuGet Package Collection

  • Collects all PackageReference elements across projects
  • Resolves version conflicts using highest-version strategy
  • Generates paket.dependencies.generated to bootstrap Paket integration

IDE Support for Generated Code

  • New dotnet_generated_props rule for syncing Bazel-generated sources with IDEs
  • Uses write_source_file from bazel-lib to maintain .props files
  • Enables IntelliSense and Go-to-Definition for generated code in IDEs without Bazel support

Cross-Platform Support

  • Works on macOS, Linux, and Windows
  • Platform-specific file discovery (Unix find vs PowerShell/cmd)
  • Proper path normalization across platforms

Usage

# MODULE.bazel
dotnet = use_extension("@rules_dotnet//dotnet:extensions.bzl", "dotnet")
dotnet.toolchain(dotnet_version = "9.0.100")
dotnet.scan_projects()
use_repo(dotnet, "dotnet_toolchains", "dotnet_projects")

register_toolchains("@dotnet_toolchains//:all")
# BUILD.bazel (in directory with MyProject.csproj)
load("@dotnet_projects//path/to:MyProject.csproj.bzl", "auto_dotnet_targets")

auto_dotnet_targets(
    name = "MyProject",
    visibility = ["//visibility:public"],
)

Configuration Options

dotnet.scan_projects(
    # Exclude patterns (glob syntax)
    exclude_patterns = ["**/tests/**", "**/legacy/**"],
    
    # Fail build if TFM not covered by toolchains (default: True)
    fail_on_missing_toolchain = True,
    
    # Custom NuGet repository name
    nuget_repo_name = "dotnet_projects.nuget",
)

New Dependencies

  • xml_tools: Pure Starlark XML parser. Used for parsing .csproj/.fsproj files

Breaking Changes

None - this is an additive feature. Existing rules_dotnet usage is unaffected.

File Change Detection

Bazel automatically re-scans when:

  • Existing .csproj/.fsproj files are modified
  • New project files are added to directories already containing projects

Manual sync required (bazel sync --only=@dotnet_projects) when:

  • New project files are added to entirely new directories

Test Plan

  • Unit tests for parser (parser_test.bzl)
  • Unit tests for generator (generator_test.bzl)
  • Unit tests for NuGet collector (nuget_collector_test.bzl)
  • Unit tests for TFM utilities (tfm_utils_test.bzl)
  • E2E tests for .NET 8.0 (e2e/dotnet_projects_net8.0/)
  • E2E tests for .NET 9.0 (e2e/dotnet_projects_net9.0/)
  • E2E tests for .NET 10.0 (e2e/dotnet_projects_net10.0/)
  • All existing tests pass
  • buildifier lint passes

@scottpledger scottpledger force-pushed the feat/csproj-support branch 6 times, most recently from 56e210e to 13c6418 Compare February 1, 2026 19:07
@scottpledger scottpledger marked this pull request as ready for review February 1, 2026 19:12
@scottpledger scottpledger marked this pull request as draft February 1, 2026 19:13
@scottpledger
Copy link
Author

scottpledger commented Feb 1, 2026

This is basically ready, but I need bazelbuild/bazel-central-registry#7387 to go in, along with a follow-up release to be published so I can drop the git_overrides for xml_tools.

@scottpledger scottpledger marked this pull request as ready for review February 2, 2026 15:31
@purkhusid
Copy link
Collaborator

Thanks for taking your time to add this. I'll take a look as soon as I can, am busy with other things at the moment.
Just a quick question, is there any reason for not using Gazelle for the build file generation instead of going with this repository rule approach? Just want to get some context on why you decided on this approach.

@scottpledger
Copy link
Author

scottpledger commented Feb 4, 2026

No problem! Totally understand needing some time on this one - it's rather large.

To answer your question, I wouldn't mind using Gazelle for it, but that requires devs to make sure they run it whenever csproj/fsproj files change. My hope with this is to provide (slightly) better IDE support by allowing both IDEs and Bazel to read the csproj/fsproj directly and use them as the primary "source of truth".

I'm also considering trying to add native NuGet support now that xml.bzl exists to parse it, if that's something you'd be open to.

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.

2 participants