指定生成事件 (C#)
使用生成事件,可以指定生成开始之前或生成完成之后运行的命令。
指定生成事件
在“解决方案资源管理器”中,选择要为其指定生成事件的项目 。
在 “项目” 菜单上,单击 “属性” 。
选择“生成事件”选项卡。
在“预先生成事件命令行”框中指定生成事件的语法。
注意
如果项目是最新的且没有触发任何生成,则不会运行预生成事件。
在“后期生成事件命令行”框中指定生成事件的语法。
注意
在运行 .bat 文件的所有生成后命令之前添加
call
语句。 例如,call MyFile.bat
或call MyFile.bat call MyFile2.bat
。 路径可以是绝对路径,也可以相对于输出文件夹。在“运行后期生成事件”框中,指定运行后期生成事件的条件。
注意
若要添加长语法,或从预先生成事件/生成后事件命令行对话框中选择任何生成宏,请单击省略号按钮 (…) 以显示编辑框。
在“解决方案资源管理器”中,选择要为其指定生成事件的项目 。
在“项目”菜单上,单击“{ProjectName} 属性”(或在“解决方案资源管理器”中,按 Alt+Enter)。
选择“生成”>“事件”。
在“预生成事件”部分中,指定生成事件的语法。
注意
如果项目是最新的且没有触发任何生成,则不会运行预生成事件。
在“生成后事件”部分中,指定生成事件的语法。
注意
在运行 .bat 文件的所有生成后命令之前添加
call
语句。 例如,call MyFile.bat
或call MyFile.bat call MyFile2.bat
。 路径可以是绝对路径,也可以是项目文件夹的相对路径。在“何时运行生成后事件”部分中,指定运行生成后事件的条件。
创建生成事件命令
生成事件命令可以包含命令提示符处或 .bat 文件中有效的任何命令。 Windows 命令参考中记录了可用命令。 批处理文件名的前面应带有 call
,以确保执行后面的所有命令。 批处理文件本身从输出文件夹运行,例如 bin/Debug
。 如果需要对所有配置使用相同的批处理文件,则可以将其放在项目文件所在的同一文件夹中,并使用它的相对路径,例如 call ../../prebuild.bat
。
可以通过输入类似于 PowerShell MyPowerShellScript.ps1
的命令来执行 PowerShell 脚本。 PowerShell 脚本的路径可以是绝对路径,也可以是项目目录的相对路径。 需要确保在操作系统上正确设置 PowerShell 脚本的执行策略才能运行该脚本。 请参阅关于执行策略。
如果要使用其他 shell(如 bash),通常会使用与从 Windows 命令提示符启动 shell 脚本所用的相同命令语法。 关于第三方 shell 的使用不在本文档的介绍范围内,但查阅 Stack Overflow 等网站的内容可能会有所帮助。
在项目文件中
当你执行上述步骤时,Visual Studio 会通过添加 PreBuild
或 PostBuild
目标以及执行所提供步骤必需的 MSBuild 代码来修改项目文件。 可以打开项目文件并查看步骤。 修改项目文件中的步骤也是可以的。 保存更改后,你将在项目属性的“生成”>“事件”部分看到这些更改。
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="call prebuild.bat" />
</Target>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="call postbuild.bat" />
</Target>
该 Exec
元素指的是 MSBuild Exec
任务。 有关可用于自定义执行的其他参数的信息,请参阅 Exec 任务。 例如,可使用 WorkingDirectory
来设置运行可执行文件的文件夹。 默认值为包含项目文件的目录。
<Exec Command="call prebuild.bat" WorkingDirectory="$(OutDir)">
可以使用 MSBuild 属性(宏),例如前面示例中的 OutDir
,稍后将在本文的宏这一主题中进行讨论。
错误和其他输出
生成事件的输出将写入“输出窗口”的“生成”部分。 若要将其打开,请选择“查看”>“其他窗口”、“输出窗口”或按 Ctrl+Alt+O。 在“显示输出来源”旁边的下拉列表中,选择“生成”。
如果预生成事件或生成后事件未成功完成,可通过使用除零 (0) 之外的代码退出事件操作来终止生成。 零退出代码表示操作成功;任何其他退出代码均被视为出错。
如果预生成事件失败,可能会在“错误列表”窗口中看到如下所示的错误:
MSB3073 The command "call c:\source\repos\prebuild.bat" exited with code 1.
如果“错误列表”窗口中没有足够的信息,可以尝试使用“输出窗口”查看完整的生成输出,包括来自批处理文件的任何输出。
提示
“错误列表”窗口只限于一行输出,即你为事件输入的第一行。 如果“错误列表”窗口输出对你很重要,请避免在事件中放置多行。 从 Windows 命令提示符或在操作系统中创建批处理文件,然后仅对事件使用 call mybatchfile.bat
。 将命令包含在批处理文件本身中。
有关可在批处理文件中使用的命令的指南,请参阅 Windows 命令。
宏
MSBuild 通用属性中列出了通用的“宏”(实际上是 MSBuild 属性)。 对于 .NET SDK 项目(.NET Core 或 .NET 5 及更高版本),Microsoft.NET.Sdk 的 MSBuild 属性中列出了其他属性。
在生成事件的脚本中,可能需要引用某些项目级变量的值,例如项目的名称或输出文件夹的位置。 在以前版本的 Visual Studio 中,这些被称为“宏”。 与最新版本的 Visual Studio 中的宏等效的是 MSBuild 属性。 MSBuild 是 Visual Studio 在执行生成时用于处理项目文件的生成引擎。 IDE 中的生成事件会在项目文件中产生一个 MSBuild 目标。 你可以使用项目文件的目标中可用的任何 MSBuild 属性(例如,$(OutDir)
或 $(Configuration)
)。 在这些事件中可供你使用的 MSBuild 属性取决于在项目文件中隐式或显式导入的文件(如 .props
和 .targets
文件)以及在项目文件中设置的属性(如 PropertyGroup
元素)。 请小心使用每个属性的准确拼写。 如果将属性拼写错误,不会报告任何错误;但未定义的属性的计算结果是一个空字符串。
例如,假设指定一个如下所示的预生成事件:
该预生成事件会生成以下条目,在项目文件中称为 Target
:
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="echo Configuration: $(Configuration)
echo DevEnvDir: $(DevEnvDir)
echo OutDir: $(OutDir)
echo ProjectDir: $(ProjectDir)
echo VisualStudioVersion: $(VisualStudioVersion)
echo AssemblySearchPaths: $(AssemblySearchPaths)
echo AssemblyName: $(AssemblyName)
echo BaseIntermediateOutputPath: $(BaseIntermediateOutputPath)
echo CscToolPath: $(CscToolPath)" />
</Target>
生成事件作为一个目标出现,其中包含 Exec 任务和你指定为 Command
的输入。 换行符使用 XML 进行编码。
当你在此示例中生成项目时,预生成事件将打印某些属性的值。 在本示例中,$(CscToolPath)
不会生成任何输出,因为它未经定义。 它是一个可选属性,你可以在项目文件中定义该属性,提供指向 C# 编译器的自定义实例的路径(例如,如果要测试不同版本的 csc.exe 或试验性编译器)。
生成事件的输出将写入生成输出,你可在“输出”窗口中找到该输出。 在“显示输出来源”下拉列表中,选择“生成”。
Build started...
1>------ Build started: Project: ConsoleApp4, Configuration: Debug Any CPU ------
1>You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview
1>Configuration: Debug
1>DevEnvDir: C:\Program Files\Microsoft Visual Studio\2022\Preview\Common7\IDE\
1>OutDir: bin\Debug\net6.0\
1>ProjectDir: C:\source\repos\ConsoleApp4\ConsoleApp4\
1>VisualStudioVersion: 17.0
1>ALToolsPath:
1>AssemblySearchPaths: {CandidateAssemblyFiles};{HintPathFromItem};{TargetFrameworkDirectory};{RawFileName}
1>AssemblyName: ConsoleApp4
1>BaseIntermediateOutputPath: obj\
1>CscToolsPath:
1>Skipping analyzers to speed up the build. You can execute 'Build' or 'Rebuild' command to run analyzers.
1>ConsoleApp4 -> C:\source\repos\ConsoleApp4\ConsoleApp4\bin\Debug\net6.0\ConsoleApp4.dll
注意
有些场景需要比生成事件所能做到的更复杂的生成操作。 例如,对于许多常见的代码生成场景,需要处理清理和重新生成操作,并且可能需要为代码生成步骤启用增量生成,这样,只有当输出相对于输入而言已过期时,步骤才会运行。 MSBuild 旨在智能地处理所有这些场景。 考虑创建一个自定义目标,用于指定 AfterTargets
或 BeforeTargets
在生成过程中的特定点运行,为了在高级场景中进一步控制,可以考虑创建自定义任务,或查看可自定义生成的不同方式。
示例
在项目文件夹中创建名为
postbuild.bat
的批处理文件,包含以下内容:echo Copying output file %1 to %1.copy copy %1 %1.copy
回想一下,在批处理文件中,
%1
指的是传入的第一个参数。在项目属性的“生成后事件”部分中调用批处理文件,并使用 MSBuild 属性
$(TargetPath)
传递参数。call postbuild.bat $(TargetPath)
生成项目并检查输出文件夹。 生成的程序集旁边应会显示复制的文件。 在“输出窗口”的“生成”部分中,应会看到批处理文件输出:
1>Output file is C:\source\repos\ConsoleApp-BuildEvents\ConsoleApp-BuildEvents\bin\Debug\net6.0\ConsoleApp-BuildEvents.dll 1> 1 file(s) copied. ========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ========== ========== Build started at 12:00 PM and took 00.723 seconds ==========