外部C++源文件变更时强制重新生成shim .csproj项目的方法
问题根源
MSBuild 基于输入/输出文件的时间戳做增量构建判定:如果指定目标的所有输出文件时间戳都晚于输入文件,就会判定目标为最新状态直接跳过执行。你当前自定义的BuildNative目标没有声明输入和输出集合,首次构建完成后MSBuild会默认判定该项目无需重新构建,自然不会触发cmake编译流程。之前尝试把C++源文件加入<Content>项无效,是因为<Content>项仅控制文件是否复制到输出目录,不会参与自定义构建目标的增量判定逻辑。
解决方案
方案1:增量构建(仅C++文件变更时触发重编译,推荐)
给BuildNative目标显式声明Inputs(输入文件集合)和Outputs(输出文件集合),MSBuild会自动完成时间戳比对,只有输入文件有修改、或输出文件缺失时才执行目标。修改后的完整BuildNative.csproj配置如下:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net6.0</TargetFramework> <SkipCopyBuildProduct>true</SkipCopyBuildProduct> <DebugSymbols>false</DebugSymbols> <DebugType>none</DebugType> <GenerateDependencyFile>false</GenerateDependencyFile> </PropertyGroup> <PropertyGroup> <NativeRootDir>$(ProjectDir)../../../Native/</NativeRootDir> <NativeBuildDir>$(NativeRootDir)dotnet-build/</NativeBuildDir> </PropertyGroup> <!-- 声明所有参与原生构建的输入文件:C++源码、头文件、CMake配置 --> <ItemGroup> <NativeSource Include="$(NativeRootDir)**/*.cpp;$(NativeRootDir)**/*.h;$(NativeRootDir)**/*.hpp;$(NativeRootDir)**/CMakeLists.txt;$(NativeRootDir)**/*.cmake" Visible="false"/> </ItemGroup> <ItemGroup> <!-- cmake构建输出,自动复制到引用该项目的C#项目输出目录 --> <Content Include="$(NativeBuildDir)$(Configuration)/**" CopyToOutputDirectory="PreserveNewest" Visible="false"/> </ItemGroup> <!-- 绑定输入输出,启用MSBuild增量判定 --> <Target Name="BuildNative" BeforeTargets="Build" Inputs="@(NativeSource)" Outputs="$(NativeBuildDir)$(Configuration)/*.dll;$(NativeBuildDir)$(Configuration)/*.lib;$(NativeBuildDir)$(Configuration)/*.exe;$(NativeBuildDir)$(Configuration)/*.pdb"> <MakeDir Directories="$(NativeBuildDir)" /> <Exec Command="cmake .." WorkingDirectory="$(NativeBuildDir)" /> <Exec Command="cmake --build . --target ALL_BUILD --config $(Configuration) -- /nologo /verbosity:minimal /maxcpucount" WorkingDirectory="$(NativeBuildDir)" /> </Target> </Project>
配置说明:
- 只要
NativeSource集合内的任意文件被修改,时间戳晚于输出的原生二进制,就会自动触发BuildNative目标 - 如果做跨平台开发,需要生成
.so、.dylib等其他后缀的原生文件,直接在Outputs里补充对应通配规则即可 - 即使触发了
BuildNative目标,cmake本身也自带增量编译逻辑,只会重新编译修改过的C++文件,不会做无意义的全量构建 - 配置完成后,F5启动调试、常规构建依赖该项目的C#项目时,都会自动检查原生代码变更,按需触发重编译
方案2:强制每次构建都重编译原生代码
如果不需要增量检查,要求每次构建都全量执行原生编译,只需要让BuildNative目标的输出判定永远不成立即可,最简单的实现方式是将输出设置为一个不存在的文件:
<Target Name="BuildNative" BeforeTargets="Build" Outputs="$(NonExistentFile)"> <MakeDir Directories="$(NativeBuildDir)" /> <Exec Command="cmake .." WorkingDirectory="$(NativeBuildDir)" /> <Exec Command="cmake --build . --target ALL_BUILD --config $(Configuration) -- /nologo /verbosity:minimal /maxcpucount" WorkingDirectory="$(NativeBuildDir)" /> </Target>
注意:该方案每次构建都会完整执行cmake配置和编译流程,会明显拖慢整体构建速度,无特殊需求优先选择增量构建方案。
排查提示
如果配置后仍有判定异常,可以打开Visual Studio的「工具-选项-项目和解决方案-生成并运行」,将MSBuild生成输出详细度调整为「详细」,构建时可以直接在输出日志里看到BuildNative目标是被跳过还是执行,方便定位规则配置问题。
内容的提问来源于stack exchange,提问作者Timo
相关产品推荐
相关产品推荐

