Visual Studio自定义构建工具每次仅生成一个SVG转头文件
问题:VS自定义构建工具每次仅处理一个SVG文件,多次构建才能完成转换
问题背景
编写了一个批处理文件svg_header.bat,用于将SVG文件转换为C++头文件,代码如下:
echo %1 -> %2 @( echo const char *%~n1_svg = R"***( type %~1 echo ^)***"; ) > %~2
执行示例:svg_header.bat images\normal.svg images\normal_svg.h,生成的头文件可通过#include "images\\normal_svg.h"在C++代码中引用。
在Visual Studio项目中添加了3个SVG文件,并为它们配置了Custom Build Tool,但出现异常:每次构建项目时仅能成功处理一个文件,第一次构建输出如下:
Build started... 1>------ Build started: Project: mic_muter, Configuration: Debug x64 ------ 1>SVG to Header microphone_mute 1>C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Microsoft\VC\v170\Microsoft.CppCommon.targets(247,5): warning MSB8065: Custom build for item "images\microphone_normal.svg" succeeded, but specified output "u:\dev\hid_kbd\mic_muter\mic_muter\images\microphone_normal_svg.h" has not been created. This may cause incremental build to work incorrectly. 1>C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Microsoft\VC\v170\Microsoft.CppCommon.targets(247,5): warning MSB8065: Custom build for item "images\microphone_base.svg" succeeded, but specified output "u:\dev\hid_kbd\mic_muter\mic_muter\images\microphone_base_svg.h" has not been created. This may cause incremental build to work incorrectly. 1>mic_muter.cpp 1>U:\dev\hid_kbd\mic_muter\mic_muter\mic_muter.cpp(5,10): fatal error C1083: Cannot open include file: 'images/microphone_normal_svg.h': No such file or directory 1>Done building project "mic_muter.vcxproj" -- FAILED. ========== Build: 0 succeeded, 1 failed, 2 up-to-date, 0 skipped ==========
再次构建会处理下一个文件,第三次构建才能完成所有文件转换,项目构建成功。
每个SVG文件在.vcxproj中的配置段基本一致(仅源文件名不同):
<CustomBuild Include="images\microphone_mute.svg"> <FileType>Document</FileType> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</DeploymentContent> <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">SVG to Header %(Filename)</Message> <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">SVG to Header %(Filename)</Message> <Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">SVG to Header %(Filename)</Message> <Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">SVG to Header %(Filename)</Message> <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectDir)images\%(Filename)_svg.h</Outputs> <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectDir)images\%(Filename)_svg.h</Outputs> <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectDir)images\%(Filename)_svg.h</Outputs> <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir)images\%(Filename)_svg.h</Outputs> <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectDir)svg_to_header.bat %(FullPath) $(ProjectDir)images\%(Filename)_svg.h</Command> <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectDir)svg_to_header.bat %(FullPath) $(ProjectDir)images\%(Filename)_svg.h</Command> <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectDir)svg_to_header.bat %(FullPath) $(ProjectDir)images\%(Filename)_svg.h</Command> <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir)svg_to_header.bat %(FullPath) $(ProjectDir)images\%(Filename)_svg.h</Command> <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectDir)svg_to_header.bat</AdditionalInputs> <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectDir)svg_to_header.bat</AdditionalInputs> <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectDir)svg_to_header.bat</AdditionalInputs> <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir)svg_to_header.bat</AdditionalInputs> <LinkObjects Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</LinkObjects> <LinkObjects Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkObjects> <LinkObjects Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</LinkObjects> <LinkObjects Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkObjects> <OutputItemType Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">ClInclude</OutputItemType> <OutputItemType Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">ClInclude</OutputItemType> <OutputItemType Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">ClInclude</OutputItemType> <OutputItemType Condition="'$(Configuration)|$(Platform)'=='Release|x64'">ClInclude</OutputItemType> <VerifyInputsAndOutputsExist Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</VerifyInputsAndOutputsExist> <VerifyInputsAndOutputsExist Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</VerifyInputsAndOutputsExist> <VerifyInputsAndOutputsExist Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</VerifyInputsAndOutputsExist> <VerifyInputsAndOutputsExist Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</VerifyInputsAndOutputsExist> </CustomBuild>
原因分析
- MSBuild并行构建冲突:Visual Studio默认启用并行构建,多个Custom Build任务会同时执行,批处理文件在写入输出文件时可能遭遇文件系统竞争条件(如临时文件锁定、输出流混乱),导致部分文件写入失败,但批处理进程返回的退出码仍为0,MSBuild误认为任务成功,仅在下次构建时重试。
- 批处理文件路径未加引号:批处理中重定向目标路径未用引号包裹,若路径包含空格或特殊字符(即使当前路径无空格,也可能因MSBuild传递参数的方式触发问题),会导致文件写入失败。
- 批处理文件名不一致(可能笔误):问题描述中批处理文件名为
svg_header.bat,但项目配置中命令调用的是svg_to_header.bat,若实际文件未重命名,会导致部分任务无法找到脚本,输出文件无法生成。
解决方案
1. 修复批处理文件
修改svg_header.bat,添加引号包裹路径,避免路径解析错误,并关闭多余输出:
@echo off echo %1 -> %2 @( echo const char *%~n1_svg = R"***( type "%~1" echo ^)***"; ) > "%~2"
2. 禁用并行构建
在项目文件.vcxproj的<PropertyGroup>节点中添加以下配置,强制MSBuild串行处理自定义构建任务:
<DisableParallelBuilds>true</DisableParallelBuilds>
或针对单个CustomBuild项添加:
<DisableParallelBuilds>true</DisableParallelBuilds>
3. 修正批处理文件名(若存在笔误)
确保项目配置中Command节点调用的批处理文件名与实际文件名一致,比如将配置中的svg_to_header.bat改为svg_header.bat。
验证
修改后执行一次完整构建,所有SVG文件应被一次性转换为对应的头文件,项目构建成功,无MSB8065警告及文件找不到错误。
内容的提问来源于stack exchange,提问作者Charlie Skilbeck
相关产品推荐
相关产品推荐

