Skip to content

Conversation

@jsun-splunk
Copy link
Contributor

@jsun-splunk jsun-splunk commented Oct 20, 2025

This is an attempt to add a rule to build MSBuild project from bazel. This rule only supports MSBuild.exe from MSVC. dotnet msbuild is not supported.

MSBuild, aka Microsoft Build Engine is a platform for building applications. This engine provides an XML schema for a project file that controls how the build platform processes and builds software.

There are no source code to build from for MSBuild. There are also no prebuilt binaries that is available. Therefore, this rule is only availabe for pre-installed toolchain. This usually involves installing MSBuild along side MSVC.

MSBuild generates compile and link flags from project configuration and solution files. I have not been able to find a definitve way to override that so we can applied only the bazel generated flags. As a compromised, the generated bazel flags from cc_toolchain are added to a msbuild.props files and passed to MSBuild via the -p:ForceImportAfterCppTargets property. This at the minimum confirms bazel flags are applied.

@jsun-splunk jsun-splunk force-pushed the jsun-msbuild branch 16 times, most recently from 6dbc50d to cd48691 Compare October 27, 2025 00:32
@jsun-splunk jsun-splunk changed the title msbuild wip feat: add MSBuild rule to build MSBuild projects. Nov 4, 2025
@jsun-splunk jsun-splunk marked this pull request as ready for review November 4, 2025 03:58
- "//..."
# The min supported version of rules_rust is `>=7`
- "-//rust/..."
# 6.5.0 seem to have a bug that does not honor target_compatible_with, so the msbuild target is getting triggered.
Copy link
Contributor

Choose a reason for hiding this comment

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

can you provide a link?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wasn't able to pinpoint an issue or PR. However, using a later version of bazel solves this problem.

The error was that the target under //msbuild_simple/... what had a target_compatible_with for windows was still being executed on linux.

@jsun-splunk jsun-splunk force-pushed the jsun-msbuild branch 4 times, most recently from d29e8e6 to 511adb0 Compare November 18, 2025 08:31
out_lib_dir = "",
out_static_libs = ["mylib.lib"],
sln_file = "mysolution.sln",
target_compatible_with = ["@platforms//os:windows"],
Copy link
Contributor

Choose a reason for hiding this comment

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

won't it only ever be this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this is true for MSVC msbuild, which is the one we adding. However, rule() doesn't allow you to set default target_compatible_with. It does support exec_compatible_with which also applies in this case. I've added that instead.

Copy link
Contributor

Choose a reason for hiding this comment

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

It seems crazy to have to set this every time you use the rule. Could we use a macro wrapper?

@@ -0,0 +1,199 @@
"""# [MSBuild](#msbuild)
Copy link
Contributor

Choose a reason for hiding this comment

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

do you know if this will auto-magically get a dedicated doc page?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That is what i'm hoping, but I haven't worked out a way to verify yet.

load("@rules_cc//cc:defs.bzl", "cc_test")
load("@rules_foreign_cc//foreign_cc:defs.bzl", "msbuild")

msbuild(
Copy link
Contributor

Choose a reason for hiding this comment

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

when I build this on my local I see

mylib.lib(mylib.obj) : MSIL .netmodule or module compiled with /GL found; restarting link with /LTCG; add /LTCG to the link command line to improve linker performance

but /GL is not coming from the default msvc toolchain, it seems to be populated by msbuiild itself. Which is strange that it would actually say to add a different flag to the one it already added :)

@matt-sm
Copy link
Contributor

matt-sm commented Nov 21, 2025

I'm trying to picture how deps would work eg. msbuild with dep for rfcc/cc_library

@jsun-splunk
Copy link
Contributor Author

I'm trying to picture how deps would work eg. msbuild with dep for rfcc/cc_library

wouldn't it be the same as other rules? i.e what you declare in out_shared_lib or out_static_lib become part of the CcInfo to be use downstream?

@matt-sm
Copy link
Contributor

matt-sm commented Dec 2, 2025

I'm trying to picture how deps would work eg. msbuild with dep for rfcc/cc_library

wouldn't it be the same as other rules? i.e what you declare in out_shared_lib or out_static_lib become part of the CcInfo to be use downstream?

I am thinking the other way - if msbuild had a dep on another ccinfo

Comment on lines +45 to +70
def _create_props_file_text(flags, include_dirs, ext_build_dirs, workspace_name):
props_template = """<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);{include_dirs}</AdditionalIncludeDirectories>
<AdditionalOptions>{cxx_flags}</AdditionalOptions>
</ClCompile>
<Link>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories);{lib_dirs}</AdditionalLibraryDirectories>
<AdditionalOptions>{linker_flags}</AdditionalOptions>
</Link>
<Lib>
<AdditionalOptions>{static_linker_flags}</AdditionalOptions>
</Lib>
</ItemDefinitionGroup>
</Project>
"""

return props_template.format(
include_dirs = ";".join(["$$EXT_BUILD_DEPS$$"] + ["$$EXT_BUILD_DEPS$$/{}".format(d) for d in include_dirs]),
lib_dirs = ";".join(["$$EXT_BUILD_DEPS$$/{}".format(d.basename) for d in ext_build_dirs]),
cxx_flags = join_flags_list(workspace_name, flags.cxx),
linker_flags = join_flags_list(workspace_name, flags.cxx_linker_executable),
static_linker_flags = join_flags_list(workspace_name, flags.cxx_linker_static),
)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@matt-sm - I think the include_dirs and lib_dirs generated from their counterparts in foreign_cc context should allow deps to be passed to msbuild.

I kind of follow what create_cmake_script did with these same variables.

Copy link
Contributor

Choose a reason for hiding this comment

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

Because I suppose you're going to have to set them up in your sln file anyway?

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