指定生成事件 (C#)

使用生成事件,可以指定生成开始之前或生成完成之后运行的命令。

指定生成事件

  1. 在“解决方案资源管理器”中,选择要为其指定生成事件的项目 。

  2. “项目” 菜单上,单击 “属性”

  3. 选择“生成事件”选项卡

  4. 在“预先生成事件命令行”框中指定生成事件的语法

    注意

    如果项目是最新的且没有触发任何生成,则不会运行预生成事件。

  5. 在“后期生成事件命令行”框中指定生成事件的语法

    注意

    在运行 .bat 文件的所有生成后命令之前添加 call 语句。 例如,call MyFile.batcall MyFile.bat call MyFile2.bat。 路径可以是绝对路径,也可以是项目文件夹的相对路径。

  6. 在“运行后期生成事件”框中,指定运行后期生成事件的条件

    注意

    若要添加长语法,或从预先生成事件/生成后事件命令行对话框中选择任何生成宏,请单击省略号按钮 (…) 以显示编辑框。

  1. 在“解决方案资源管理器”中,选择要为其指定生成事件的项目 。

  2. 在“项目”菜单上,单击“{ProjectName} 属性”(或在“解决方案资源管理器”中,按 Alt+Enter)。

  3. 选择“生成”>“事件”。

    显示“生成事件”设置的屏幕截图。

  4. 在“预生成事件”部分中,指定生成事件的语法。

    注意

    如果项目是最新的且没有触发任何生成,则不会运行预生成事件。

  5. 在“生成后事件”部分中,指定生成事件的语法。

    注意

    在运行 .bat 文件的所有生成后命令之前添加 call 语句。 例如,call MyFile.batcall MyFile.bat call MyFile2.bat。 路径可以是绝对路径,也可以是项目文件夹的相对路径。

  6. 在“何时运行生成后事件”部分中,指定运行生成后事件的条件。

创建生成事件命令

生成事件命令可以包含命令提示符处或 .bat 文件中有效的任何命令。 批处理文件名的前面应带有 call,以确保执行后面的所有命令。 批处理文件本身从输出文件夹运行,例如 bin/Debug。 如果需要对所有配置使用相同的批处理文件,则可以将其放在项目文件所在的同一文件夹中,并使用它的相对路径,例如 call ../../prebuild.bat

可以通过输入类似于 PowerShell MyPowerShellScript.ps1 的命令来执行 PowerShell 脚本。 PowerShell 脚本的路径可以是绝对路径,也可以是项目目录的相对路径。 需要确保在操作系统上正确设置 PowerShell 脚本的执行策略才能运行该脚本。 请参阅关于执行策略

如果要使用其他 shell(如 bash),通常会使用与从 Windows 命令提示符启动 shell 脚本所用的相同命令语法。 关于第三方 shell 的使用不在本文档的介绍范围内,但查阅 Stack Overflow 等网站的内容可能会有所帮助。

在项目文件中

当你执行上述步骤时,Visual Studio 会通过添加 PreBuildPostBuild 目标以及执行所提供步骤必需的 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)&#xD;&#xA;echo DevEnvDir: $(DevEnvDir)&#xD;&#xA;echo OutDir: $(OutDir)&#xD;&#xA;echo ProjectDir: $(ProjectDir)&#xD;&#xA;echo VisualStudioVersion: $(VisualStudioVersion)&#xD;&#xA;echo AssemblySearchPaths: $(AssemblySearchPaths)&#xD;&#xA;echo AssemblyName: $(AssemblyName)&#xD;&#xA;echo BaseIntermediateOutputPath: $(BaseIntermediateOutputPath)&#xD;&#xA;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 旨在智能地处理所有这些场景。 考虑创建一个自定义目标,用于指定 AfterTargetsBeforeTargets 在生成过程中的特定点运行,为了在高级场景中进一步控制,可以考虑创建自定义任务,或查看可自定义生成的不同方式。

示例

  1. 在项目文件夹中创建名为 postbuild.bat 的批处理文件,包含以下内容:

    echo Copying output file %1 to %1.copy
    copy %1 %1.copy
    

    回想一下,在批处理文件中,%1 指的是传入的第一个参数。

  2. 在项目属性的“生成后事件”部分中调用批处理文件,并使用 MSBuild 属性 $(TargetPath) 传递参数。

    call postbuild.bat $(TargetPath)
    
  3. 生成项目并检查输出文件夹。 生成的程序集旁边应会显示复制的文件。 在“输出窗口”的“生成”部分中,应会看到批处理文件输出:

    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 ==========